如何将泛型列表作为参数传递给泛型方法

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

我遇到了一个奇怪的 Delphi (Tokyo) 编译器的限制,当我尝试将泛型列表的实例传递给泛型方法,而将泛型数组传递给方法 (1) 时,编译器接受并且一切都按预期工作。

(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>);

但是当将相同的数组传递给带有签名 (2) 的方法时,Delphi 报错:

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

即使 TArray<T> 声明为 array of <T>

将泛型列表传递给方法 (3) 时发生同样的错误。

这样限制的原因是什么?但是主要是,如何在Delphi中实现这些东西? (例如,在 C# 中没有此类限制)。

我读了一些关于 <T> 的可能继承的解释,但我不接受这样的论点,因为同样的问题可能发生在数组中,而编译器接受它:

SomeArray: TArray<TBaseClass>;

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

更新:例子

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.

Delphi 希望您在泛型方法上指定类型参数,因为它对封闭泛型类型的类型推断非常有限。

我们可以调用该类型擦除,此时编译器看到 SomeList 的类型是 TList<TBaseClass> 但不知道它是一个封闭的泛型类型。这意味着它无法从传递的 SomeList 推断出 TBaseClass 并查看 "Oh, that is a TList<T> with T being TBaseClass".

所以你必须写:

TSomeClass.DoSomethingWithGenericList<TBaseClass>(SomeList);

同样适用于 TArray<T> 重载。

对于array of T这是一个开放数组(array of ...作为参数类型不是动态数组!)编译器知道推断类型传递 SomeArray 变量时。