Search code examples
delphigenericsinterfacedelphi-2010

Generic method returning generic interface in Delphi 2010


Given the code below, wich is a very trimmed down version of the actual code, I get the following error:

[DCC Error] Unit3.pas(31): E2010 Incompatible types: 'IXList<Unit3.TXList<T>.FindAll.S>' and 'TXList<Unit3.TXList<T>.FindAll.S>'

In the FindAll<S> function.

I can't really see why since there is no problem with the previous very similar function.

Can anyone shed some light on it?
Is it me or is it a bug in the compiler?

unit Unit3;

interface
uses Generics.Collections;

type
  IXList<T> = interface
  end;

  TXList<T: class> = class(TList<T>, IXList<T>)
  protected
    FRefCount: Integer;
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  public
    function Find: IXList<T>;
    function FindAll<S>: IXList<S>;
  end;

implementation
uses Windows;

function TXList<T>.Find: IXList<T>;
begin
  Result := TXList<T>.Create;
end;

function TXList<T>.FindAll<S>: IXList<S>;
begin
  Result := TXList<S>.Create; // Error here  
end;

function TXList<T>.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  Result := E_NoInterface;
end;

function TXList<T>._AddRef: Integer;
begin
  InterlockedIncrement(FRefCount);
end;

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

end.

Thanks for the answers! It seems like a compiler bug with an acceptable workaround available.

With the interface declared as

IXList<T: class> = interface
   function GetEnumerator: TList<T>.TEnumerator;
end;

and findall implemented as

function TXList<T>.FindAll<S>: IXList<S>;
var
  lst: TXList<S>;
  i: T;
begin
  lst := TXList<S>.Create;
  for i in Self do
    if i.InheritsFrom(S) then lst.Add(S(TObject(i)));

  Result := IXList<S>(IUnknown(lst));
end;

I got it working in a simple example.

Doing something like:

var
  l: TXList<TAClass>;
  i: TASubclassOfTAClass;
begin
.
.
.
for i in l.FindAll<TASubclassOfTAClass> do
begin
   // Do something with i
end;

Solution

  • With three minor modification (IInterface, FindAll with "S: class" [Thanks Mason] and the typecasts in FindAll) I got it compiling.

    Full code:

    unit Unit16;
    
    interface
    
    uses
      Generics.Collections;
    
    type
      IXList<T> = interface
      end;
    
      TXList<T: class> = class(TList<T>, IInterface, IXList<T>)
      protected
        FRefCount: Integer;
        function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
        function _AddRef: Integer; stdcall;
        function _Release: Integer; stdcall;
      public
        function Find: IXList<T>;
        function FindAll<S: class>: IXList<S>;
      end;
    
    implementation
    uses Windows;
    
    function TXList<T>.Find: IXList<T>;
    begin
      Result := TXList<T>.Create;
    end;
    
    function TXList<T>.FindAll<S>: IXList<S>;
    begin
      Result := IXList<S>(IUnknown(TXList<S>.Create));
    end;
    
    function TXList<T>.QueryInterface(const IID: TGUID; out Obj): HResult;
    begin
      Result := E_NoInterface;
    end;
    
    function TXList<T>._AddRef: Integer;
    begin
      InterlockedIncrement(FRefCount);
    end;
    
    function TXList<T>._Release: Integer;
    begin
      InterlockedDecrement(FRefCount);
      if FRefCount = 0 then Self.Destroy;
    end;
    
    end.