请考虑以下代码:
TMyList = class(TList<IMyItem>, IMyList)
Delphi向我显示错误:
[DCC Error] test.pas(104): E2003 Undeclared identifier: 'QueryInterface'
是否有实现IInterface
的通用列表?
答案 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