Есть ли обобщенный тип, который реализует QueryInterface? - PullRequest
7 голосов
/ 26 марта 2012

Рассмотрим следующий код:

TMyList = class(TList<IMyItem>, IMyList)

Delphi показывает мне ошибку:

[DCC Error] test.pas(104): E2003 Undeclared identifier: 'QueryInterface'

Есть ли общий список, который реализует IInterface?

Ответы [ 4 ]

10 голосов
/ 26 марта 2012

Классы в Generics.Collections не реализуют IInterface. Вы должны будете сами представить его в своих производных классах и предоставить стандартные реализации. Или найдите другой сторонний набор классов контейнеров для работы.

Например:

TInterfacedList<T> = class(TList<T>, IInterface)
protected
  FRefCount: Integer;
  function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
  function _AddRef: Integer; stdcall;
  function _Release: Integer; stdcall;
end;

function TInterfacedList<T>.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  if GetInterface(IID, Obj) then
    Result := 0
  else
    Result := E_NOINTERFACE;
end;

function TInterfacedList<T>._AddRef: Integer;
begin
  Result := InterlockedIncrement(FRefCount);
end;

function TInterfacedList<T>._Release: Integer;
begin
  Result := InterlockedDecrement(FRefCount);
  if Result = 0 then
    Destroy;
end;

Затем вы можете объявить свой специализированный класс:

TMyList = class(TInterfacedList<IMyItem>, IMyList)

Помните, что вы должны относиться к этому классу как к любому другому, в котором используется управление временем жизни с подсчетом ссылок. Обращайтесь к нему только через интерфейсы.

Вы бы действительно хотели проделать дополнительную работу, прежде чем TInterfacedList<T> был бы полезен. Вам нужно будет объявить IList<T>, который предоставит возможности списка. Это было бы что-то вроде этого:

IList<T> = interface
  function Add(const Value: T): Integer;
  procedure Insert(Index: Integer; const Value: T);
  .... etc. etc.
end;

Затем вы можете просто добавить IList<T> в список интерфейсов, поддерживаемых TInterfacedList<T>, и базовый класс TList<T> выполнит контракт интерфейса.

TInterfacedList<T> = class(TList<T>, IInterface, IList<T>)
4 голосов
/ 27 марта 2012

В дополнение к моему комментарию, приведенному выше, приведено объяснение, почему универсальные интерфейсы в Delphi не работают с направляющими.

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils;


type
  IList<T> = interface
    ['{41FA0759-9BE4-49D7-B3DD-162CAA39CEC9}']
  end;

  IList_String1 = IList<string>;

  IList_String2 = interface(IList<string>)
    ['{FE0CB7A6-FC63-4748-B436-36C07D501B7B}']
  end;

  TList<T> = class(TInterfacedObject, IList<T>)
  end;

var
  list: TList<string>;
  guid: TGUID;
begin
  list := TList<string>.Create;

  guid := IList<Integer>;
  Writeln('IList<Integer> = ', guid.ToString);
  if Supports(list, IList<Integer>) then
    Writeln('FAIL #1');

  guid := IList_String1;
  Writeln('IList_String1 = ', guid.ToString);
  if not Supports(list, IList_String1) then
    Writeln('FAIL #2');

  guid := IList_String2;
  Writeln('IList_String2 = ', guid.ToString);
  if not Supports(list, IList_String2) then
    Writeln('FAIL #3');

  Readln;
end.

Вы видите, что для IList и для IList_String1 выдается то же самое руководство, что и для IList, и оба относятся к этому типу. Это приводит к ошибке # 1, потому что T не имеет значения при выполнении вызова support. Определение псевдонима для IList работает (нет Fail # 2), но не помогает, потому что это все тот же guid. Итак, нам нужно то, что было сделано с IList_String2. Но этот интерфейс не реализован TList, поэтому, конечно, мы получаем Fail # 3.

Об этом сообщалось давно: http://qc.embarcadero.com/wc/qcmain.aspx?d=78458 [Архивированная ссылка WayBack]

3 голосов
/ 26 марта 2012

Взгляните на библиотеку Алекса Чобану .У него есть набор общих коллекций, включая замены для типов Generics.Collections, которые можно использовать в качестве интерфейсов.(Они были сделаны таким образом, чтобы упростить настроенное им поведение Enex в стиле LINQ.)

0 голосов
/ 27 марта 2012

Другой альтернативой является создание оболочки, которая наследуется от TInterfacedObject, и использование композиции и делегирования для функций списка:

interface
type
  IList<T> = interface<IEnumerable<T>)
    function Add(const Value: T): Integer;
    ..
  end;

TList<T> = class(TInterfacedObject, IList<T>)
private
  FList: Generics.Collections.TList<T>;
public
  function Add(const Value:T): Integer;
  ..
end;

implementation
function TList<T>.Add(const Value:T): Integer;
begin
  Exit(FList.Add(Value));
end;

Обтекание TList<T> и TDictionary<T> занимает чуть более 500 строк кода.

Одно предупреждение ... Я не включил его здесь, но я создал IEnumerable<T> (а также связанный IEnumerator<T>), который не спускается с IEnumerable. Большинство сторонних библиотек и библиотек OSS делают то же самое. Если вас интересует, почему этот блог пост в значительной степени подводит итог.

Ну, на самом деле это не все упоминает. Даже если вы работаете над интерфейсами, наступающими друг на друга, у вас все равно будут проблемы. В противном случае идеальная реализация, которая удовлетворяет обоим интерфейсам, будет успешной, если вы выполните Build, но продолжите терпеть неудачу с тем же сообщением об ошибке, если вы делаете инкрементную компиляцию (по крайней мере, в D2009). Также неуниверсальный IEnumerator заставляет вас возвращать текущий элемент как TObject. Это в значительной степени препятствует тому, чтобы вы держали в коллекции что-либо, что нельзя привести к TObject. Вот почему ни одна из стандартных универсальных коллекций не реализует IEnumerable<T>

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