Is there any generics type that implements QueryInterface?
The classes in Generics.Collections
do not implement IInterface
. You will have to introduce it yourself in your derived classes and provide the standard implementations. Or find a different, third party, set of container classes to work with.
For example:
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;
You can then declare your specialised class:
TMyList = class(TInterfacedList<IMyItem>, IMyList)
Remember that you need to treat this class like any other that uses reference counted lifetime management. Only refer to it through interfaces.
You'd really want to do some more work before TInterfacedList<T>
was useful. You'd need to declare an IList<T>
which would expose the list capabilities. It would be something like this:
IList<T> = interface
function Add(const Value: T): Integer;
procedure Insert(Index: Integer; const Value: T);
.... etc. etc.
end;
You can then simply add IList<T>
to the list of interfaces supported by TInterfacedList<T>
and the base class TList<T>
would fulfil the interface contract.
TInterfacedList<T> = class(TList<T>, IInterface, IList<T>)
In addition to my comment above here some explanation why generic interfaces in Delphi don't work with guids.
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.
You see that it writes out the same guid for IList and for IList_String1 as IList got this guid and both are from this type. This results in Fail #1 because T does not matter when doing the supports call. Defining an alias for IList works (no Fail #2) but does not help because that still is the same guid. So what we need is what has been done with IList_String2. But that interface is not implemented by TList so of course we get Fail #3.
This has been reported long ago: http://qc.embarcadero.com/wc/qcmain.aspx?d=78458 [WayBack archived link]
Have a look at Alex Ciobanu's Collections library. It's got a bunch of generic collections, including replacements for the Generics.Collections
types, that are usable as interfaces. (They were done that way to facilitate the LINQ-style Enex behavior he set up.)