是否有任何泛型类型实现QueryInterface?

时间:2012-03-26 18:08:39

标签: delphi generics interface

请考虑以下代码:

TMyList = class(TList<IMyItem>, IMyList)

Delphi向我显示错误:

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

是否有实现IInterface的通用列表?

4 个答案:

答案 0 :(得分:10)

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

答案 1 :(得分:4)

除了上面的评论之外,还有一些解释为什么Delphi中的通用接口不能与guid一起使用。

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写出了相同的guid,因为IList得到了这个guid,两者都来自这种类型。这导致失败#1,因为在执行支持调用时T无关紧要。为IList工作定义别名(没有失败#2)但没有帮助,因为它仍然是相同的guid。所以我们需要的是IList_String2已经完成的工作。但是TList实现了这个界面,所以我们当然得到失败#3。

很久以前就已经报道过:http://qc.embarcadero.com/wc/qcmain.aspx?d=78458 [WayBack archived link]

答案 2 :(得分:3)

看看Alex Ciobanu的Collections图书馆。它有一堆通用集合,包括可用作接口的Generics.Collections类型的替换。 (他们这样做是为了促进他设置的LINQ式Enex行为。)

答案 3 :(得分:0)

另一种方法是创建一个继承自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>从{{下降} 1}}。大多数第三方和OSS集合库都这样做。如果你对this博客的原因很感兴趣,那么。

嗯,实际上并没有提到一切。即使你在相互踩踏的界面上工作,你仍然会遇到问题。如果你做Build,那么满足这两个接口的完美实现将会成功,但如果你进行增量编译(至少在D2009中),则会继续失败并显示相同的错误消息。此外,非泛型IEnumerator强制您将当前项作为IEnumerable返回。这几乎可以防止您在无法转换为TObject的集合中保存任何内容。这就是标准通用集合中没有一个实现TObject

的原因