Search code examples
delphigenericsdelphi-10.1-berlin

How to work with Generic methods and parameter array of array of < T >?


A lot of times when working with generic arrays I need to know Max length of the arrays I'm working with. For a longtime I was using:

MaxIntValue([Length(Array1), Length(Array2) , Length(Array3)]); // from Math unit

Then I simplified this (to skip typing Length()) with my method:

// array of array of TCardBrand
function GetMaxLength(const aArrays: array of TArrayOfCarBrand): integer;

Works good, but now I want to create a generic MaxLength and I can't make it work with

class function MaxLength<T>(aArrays: array of array of T): integer; - it gives error: [dcc32 Error] Unit2.pas(31): E2029 Identifier expected but 'ARRAY' found

Here is the code what I'm testing, with working example of MaxIntValue from Math unit and example of my method, but extending generic TArray doesn't work:

TCarBrand = record
    BrandID: integer;
    BrandName: string;
    BrandCountry: string;
  end;

  TArrayOfCarBrand = array of TCarBrand;

  TArray = class(System.Generics.Collections.TArray)
  public          
      // ERROR:  E2029 Identifier expected but 'ARRAY' found
       class function MaxLength<T>(aArrays: array of array of T): integer;
  end;

var  
  vCarsUS, vCarsEU, vCarsJP: TArrayOfCarBrand;

implementation

{$R *.dfm}


class function TArray.MaxLength<T>(aArrays: array of array of T): integer;
var
  i: Integer;
begin
  Result := 0;
  for i := Low(aArrays) to High(aArrays) do
    Result := MaxIntValue([Result, Length(aArrays[i])]);
end;


function GetMaxLength(const aArrays: array of TArrayOfCarBrand): integer;
var
  i: Integer;
begin
  Result := 0;
  for i := Low(aArrays) to High(aArrays) do
    Result := MaxIntValue([Result, Length(aArrays[i])]);
end;


procedure TForm2.Button1Click(Sender: TObject);
var vMaxLength: integer;
begin
  // test lengths
  SetLength(vCarsUS,1);
  SetLength(vCarsEU,10);
  SetLength(vCarsJP,100);

  // using MaxIntValue from Math unit - works OK
  vMaxLength := MaxIntValue([Length(vCarsUS), Length(vCarsEU), Length(vCarsJP)]);

  // using my method GetMaxLength - works OK
  vMaxLength := GetMaxLength([vCarsUS, vCarsEU, vCarsJP]);

  // trying to set Generic TArray.MaxLength - ERROR
  vMaxLength := TArray.MaxLength<TCarBrand>([vCarsUS, vCarsEU, vCarsJP]);
end;

How can I extend generic MaxValue to accept open array of array of < T >?


Solution

  • It's simple enough. Your function does not accept generic arrays, because the parameter list does not attempt to do so. Change it like this.

    class function MaxLength<T>(const aArrays: array of TArray<T>): Integer;
    

    The key here is to use TArray<T> exclusively. This is the generic dynamic array type and using it gives more flexible type compatibility than with array of dynamic array types. The issues are covered in detail here: What are the reasons to use TArray instead of Array of T?

    A consequence of this is that you will need to replace all of your dynamic type declarations and use TArray<T> instead. In fact you don't need to declare any dynamic array types at all. You can remove all declarations of this form:

    type
      TFooArray = array of TFoo;
    

    And then replace all occurrences of TFooArray with TArray<TFoo>. A side benefit is that you don't need to declare any of the array types and your code becomes less verbose.

    I would also comment that there's little point in using MaxIntValue when you only have two arguments. Use the Max function then.