Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any generics type that implements QueryInterface?

Consider the follow code:

TMyList = class(TList<IMyItem>, IMyList)

Delphi shows me the error:

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

Is there a generic list that implements IInterface?

like image 534
Rafael Colucci Avatar asked Mar 26 '12 18:03

Rafael Colucci


2 Answers

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>)
like image 188
David Heffernan Avatar answered Dec 28 '22 22:12

David Heffernan


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]

like image 30
Stefan Glienke Avatar answered Dec 28 '22 23:12

Stefan Glienke