pf1957
pf1957

Reputation: 1007

How to pass a generic list as a parameter to generic method

I've met a strange Delphi (Tokyo) compiler's restriction when trying to pass an instance of generic list into generic method whereas passing generic array to method (1) compiler accepts and everything works as expected.

(1) class procedure DoSomethingWithDynamicArray<T: class>(AArray: array of T);
(2) class procedure DoSomethingWithGenericArray<T: class>(AArray: TArray<T>);
(3) class procedure DoSomethingWithGenericList<T: class>(AList: TList<T>);

But when passing the same array to the method with signature (2), Delphi complains with error:

[dcc32 Error] Project8.dpr(49): E2010 Incompatible types: 'System.TArray<....TSomeClass.DoSomethingWithGenericArray.T>' and 'System.TArray<....TBaseClass>'

even if TArray<T> is declared as array of <T>.

The same error occurs when passing a generic list to the method (3).

What is the reason for such restriction? But primarily, how to implement such stuff in Delphi? (e.g. in C# there are no such restrictions).

I read some explanations concerning possible inheritance of <T>, but I dont accept such argument, because the same problem can occur with arrays and compiler accepts it:

SomeArray: TArray<TBaseClass>;

SomeArray := [TBaseClass.Create, TDescendantClass.Create,
  TGrandDescendantClass.Create];
TSomeClass.DoSomethingWithDynamicArray(SomeArray);

UPDATE: example

program Project8;

uses
  System.SysUtils, System.Generics.Collections;

type
  TBaseClass = class
  end;

  TDescendantClass = class(TBaseClass)
  end;

  TGrandDescendantClass = class(TDescendantClass)
  end;

  TSomeClass = class
    class procedure DoSomethingWithDynamicArray<T: class>(AArray: array of T);
    class procedure DoSomethingWithGenericArray<T: class>(AArray: TArray<T>);
    class procedure DoSomethingWithGenericList<T: class>(AList: TList<T>);
  end;

var
  SomeArray: TArray<TBaseClass>;
  SomeList: TList<TBaseClass>;

class procedure TSomeClass.DoSomethingWithDynamicArray<T>(AArray: array of T);
begin
end;

class procedure TSomeClass.DoSomethingWithGenericArray<T>(AArray: TArray<T>);
begin
end;

class procedure TSomeClass.DoSomethingWithGenericList<T>(AList: TList<T>);
begin
end;

begin
  try
    SomeArray := [TBaseClass.Create, TDescendantClass.Create,
        TGrandDescendantClass.Create];
    TSomeClass.DoSomethingWithDynamicArray(SomeArray);
    TSomeClass.DoSomethingWithGenericArray(SomeArray);  //E2010: Incompatible type

    SomeList := TList<TBaseClass>.Create;
    SomeList.AddRange([TBaseClass.Create, TDescendantClass.Create,
            TGrandDescendantClass.Create]);
    TSomeClass.DoSomethingWithGenericList(SomeList);   //E2010: Incompatible type
  except on E: Exception do
    Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Upvotes: 0

Views: 1238

Answers (1)

Stefan Glienke
Stefan Glienke

Reputation: 21758

Delphi wants you to specify the type parameter on the generic method because it has quite limited type inference from closed generic types.

We could call that type erasure, the compiler at this point sees the type of SomeList being TList<TBaseClass> but does not have any knowledge that it is a closed generic type. That means it cannot infer TBaseClass from the passed SomeList and see "Oh, that is a TList<T> with T being TBaseClass".

So you have to write:

TSomeClass.DoSomethingWithGenericList<TBaseClass>(SomeList);

Same applies to the TArray<T> overload.

For array of T which is an open array (array of ... as a parameter type is not a dynamic array!) the compiler knows to infer the type when passing your SomeArray variable.

Upvotes: 2

Related Questions