Как передать универсальный список в качестве параметра универсальному методу - PullRequest
0 голосов
/ 24 ноября 2018

Я встретил странное ограничение компилятора Delphi (Токио) при попытке передать экземпляр универсального списка в универсальный метод, тогда как передача универсального массива в компилятор метода (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.

1 Ответ

0 голосов
/ 24 ноября 2018

Delphi хочет, чтобы вы указали параметр типа в универсальном методе, поскольку он имеет весьма ограниченный вывод типов из закрытых универсальных типов.

Мы могли бы назвать стирание этого типа, компилятор в этот момент видит тип SomeList является TList<TBaseClass>, но не знает, что это закрытый универсальный тип.Это означает, что он не может вывести TBaseClass из переданного SomeList и увидеть «О, это TList<T> с T, являющимся TBaseClass».

Так что вы должны написать:

TSomeClass.DoSomethingWithGenericList<TBaseClass>(SomeList);

То же самое относится к перегрузке TArray<T>.

Для array of T, который является открытым массивом (array of ... как тип параметра не динамический массив!) компилятор знает, как определить тип при передаче вашей переменной SomeArray.

...