Search code examples
arraysdelphidelphi-7

Returning a dynamic array from a function


I'm working with Delphi 7.

Here's my base class. The function load_for_edit() is supposed to return an array of strings which sounds like it does. The problem is not particularly here but further.

    ...

type
    TStringArray = array of string;

    ...

    function load_for_edit(): TStringArray;

    ...

    numberOfFields: integer;

    ...

function TBaseForm.load_for_edit(): TStringArray;
var
    query: TADOQuery;
    i: integer;
begin
    query := TADOQuery.Create(self);
    query.Connection := DataModule1.ADOConnection;
    query.SQL.Add('CALL get_' + self.table_name + '_id(' + self.id + ')');
    query.Open;

    numberOfFields := query.Fields.Count;
    SetLength(result, query.Fields.Count);
    for i := 0 to query.Fields.Count - 1 do
        result[i] := query.Fields[i].Value.AsString;
end;

Next is the class that is a descendant of the base class and it's trying to receive an array from the base class's load_for_edit() function.

    ...

type
    TStringArray = array of string;

    ...

procedure TPublisherEditForm.FormShow(Sender: TObject);
var
    rec: TStringArray;
begin
    inherited;
    SetLength(rec, self.numberOfFields);
    rec := load_for_edit();                 // Compilation stops here
end;

But, the application won't compile. Delphi spits out this error message:

Incompatible types

So, what this means is that the function load_for_edit() returns a data type that is different from the variable rec's data type, though, as can be seen from their respective type declaration sections, their declarations are absolutely identical. I don't know what's going on here and what to do. Please, help me come up with a solution.


Solution

  • You've got two separate declarations of TStringArray (one in each unit), and they are not the same. (The fact that they're in two separate units makes them different. UnitA.TStringArray is not the same as UnitB.TStringArray, even if they're both declared as type TStringArray = array of string;.)

    You need to use a single type declaration:

    unit
      BaseFormUnit;
    
    interface
    
    uses
      ...
    type
      TStringArray: array of string;
    
      TBaseForm = class(...)
        numberOfFields: Integer;
        function load_for_edit: TStringArray;
      end;
    
    implementation
    
    // Not sure here. It looks like you should use a try..finally to
    // free the query after loading, but if you need to use it later
    // to save the edits you should create it in the TBaseForm constructor
    // and free it in the TBaseForm destructor instead, which means it
    // should also be a field of the class declaration above.
    function TBaseForm.load_for_edit(): TStringArray;
    var
      query: TADOQuery;
      i: integer;
    begin
      query := TADOQuery.Create(self);
      query.Connection := DataModule1.ADOConnection;
      query.SQL.Add('CALL get_' + self.table_name + '_id(' + self.id + ')');
      query.Open;
    
      numberOfFields := query.Fields.Count;
      SetLength(result, numberOfFields);
      for i := 0 to numberOfFields - 1 do
        Result[i] := query.Fields[i].Value.AsString;
    end;
    ...
    end.
    

    Now your descendant class can access it:

    unit
      PublisherEditFormUnit;
    
    interface
    
    uses
      ...  // Usual Forms, Windows, etc.
      BaseFormUnit;
    
    type
      TPublisherEditForm = class(TBaseForm)
      ...
        procedure FormShow(Sender: TObject);
      end;
    
    implementation
    
    procedure TPublisherEditForm.FormShow(Sender: TObject);
    var
      rec: TStringArray;
    begin
      // No need to call SetLength - the base class does.
      rec := load_for_edit();                 // Compilation stops here
    end;
    ...
    end.