Question

(EDIT: This is following on from Are objects reference counted in Windows-targeted Delphi applications, and if so, what is its purpose? and Dynamic arrays and memory management in Delphi).

I have two classes (TGenericHoldingSummary, TGenericHoldingResultSet) and one record (TGenericHoldingResult).

  • TGenericHoldingSummary contains a single TGenericHoldingResultSet, which is set to nil and lazy-loaded from the database if and when required.
  • The TGenericHoldingResultSet contains a dynamic array of TGenericHoldingResult records.

In the below, the error is at the assignment in the TGenericHoldingResultSet constructor.

TGenericHoldingResult = record
  code : Integer;
  level : String;
  msg : String;
end;

TGenericHoldingResultSet = class(TObject)
  public
    // Lifecycle
    constructor Create(parent : TGenericHoldingSummary; resArr : Array of TGenericHoldingResult);
    destructor Destroy;
    // Accessors
    function ResultCount() : Integer;
    function Result(i : Integer) : TGenericHoldingResult;
  private
    // Variables
    summary : TGenericHoldingSummary;
    resultArray : Array of TGenericHoldingResult;
end;

TGenericHoldingSummary = class(TObject)
public
  // Note that the summary object 'owns' the results, and deallocates
  // its memory in the destructor.
  function getResultSet: TGenericHoldingResultSet;
private
  // Member variables
  resultSet: TGenericHoldingResultSet;
end;

// Note that the summary object 'owns' the results, and deallocates
// its memory in the destructor.
function TGenericHoldingSummary.getResultSet() : TGenericHoldingResultSet;
var
  sql : String;
  i : Integer;
  resultArray : Array of TGenericHoldingResult;
begin
  if resultSet = nil then
  begin
    // Get results via SQL.
    SetLength(resultArray, holding.clientDataSet.RecordCount);

    for i := 0 to holding.clientDataSet.RecordCount - 1 do
    begin
      resultArray[i].code := holding.clientDataSet.FieldByName('code').AsInteger;
      resultArray[i].level := holding.clientDataSet.FieldByName('level').AsString;
      resultArray[i].msg := holding.clientDataSet.FieldByName('message').AsString;
    end;

    resultSet := TGenericHoldingResultSet.Create(self, resultArray);
  end;

  result := resultSet;
end;

// Lifecycle
constructor TGenericHoldingResultSet.Create(parent : TGenericHoldingSummary; resArr : Array of TGenericHoldingResult);
begin
  summary := parent;
  // The following *should* work, shouldn't it?
  // E.g., seeing as dynamic arrays a reference counted in Delphi for
  // all platforms, this should simply increment the reference count.
  resultArray := resArr;
end;

The error is below:

[DCC Error] GenericHolding.pas(302): E2010 Incompatible types: 'Dynamic array' and 'Array'
Was it helpful?

Solution

You cannot assign an open array to a dynamic array. See Open Array Parameters.

Note: The syntax of open array parameters resembles that of dynamic array types, but they do not mean the same thing. The previous example creates a function that takes any array of Char elements, including (but not limited to) dynamic arrays. To declare parameters that must be dynamic arrays, you need to specify a type identifier:

type TDynamicCharArray = array of Char;
function Find(const A: TDynamicCharArray): Integer;

A good summary of the use case of open arrays and the difference with a dynamic array can be found here: Open array parameters.


If you have a Delphi version that supports generics, it is possible to declare the constructor header:

constructor TGenericHoldingResultSet.Create(parent : TGenericHoldingSummary; 
  const resArr : TArray<TGenericHoldingResult>);

and your resultArray as TArray<TGenericHoldingResult>.

This will avoid having to declare a specific type for the array.

As noted by David, open arrays have a benefit since they have a broader use case, and should be used when possible.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top