Search code examples
delphidelphi-xe2

Wrong overloaded call using generic interfaces


suppose i have this definitions:

TMyClass1 = class
end;

TMyClass2 = class
end;

IModel<T : class> = interface
  ['{E8262D6C-DCAB-46AC-822E-EC369CF734F8}']
  function List() : TObjectList<T>;
end;

IPresenter<T : class> = interface
  ['{98FB7751-D75A-4C51-B55A-0E5FE68BE213}']
  function Retrieve() : TObjectList<T>;
end;

IView<T : class> = interface
  ['{59384CD6-30D6-4BD8-AB3D-7FCF4D1A8618}']
  procedure AssignPresenter(APresenter : IPresenter<T>);
end;

TModel<T : class> = class(TInterfacedObject, IModel<T>)
public
  function List() : TObjectList<T>; virtual; abstract;
end;

TPresenter<T : class> = class(TInterfacedObject, IPresenter<T>)
strict private
  { Private declarations }
  FModel : IModel<T>;
  FView : IView<T>;
public
  constructor Create(AView : IView<T>);
  function Retrieve() : TObjectList<T>; virtual; abstract;
end;

TModelClass1 = class(TModel<TMyClass1>);

TPresenterClass1 = class(TPresenter<TMyClass1>);

TModelClass2 = class(TModel<TMyClass2>);

TPresenterClass2 = class(TPresenter<TMyClass2>);

and i have this form that implements some of the things i defined:

TForm1 = class(TForm, IView<TMyClass1>, IView<TMyClass2>)
  procedure FormCreate(Sender: TObject);
private
  { Private declarations }
  FPresenter1 : IPresenter<TMyClass1>;
  FPresenter2 : IPresenter<TMyClass2>;
  procedure AssignPresenter(APresenter : IPresenter<TMyClass1>); overload;
  procedure AssignPresenter(APresenter : IPresenter<TMyClass2>); overload;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 TPresenterClass1.Create((Self as IView<TMyClass1>));
 TPresenterClass2.Create((Self as IView<TMyClass2>));
end;

procedure TForm1.AssignPresenter(APresenter: IPresenter<TMyClass1>);
begin
  Self.FPresenter1 := APresenter;
end;

procedure TForm1.AssignPresenter(APresenter: IPresenter<TMyClass2>);
begin
  Self.FPresenter2 := APresenter;
end;

so the problem here is that delphi can't figure out what method to call, in this example, only the AssignPresenter(APresenter: IPresenter<TMyClass2>) is called in both cases, so probably im missing something here but i can't figure out atm.

thx in advance.


Solution

  • This is probably a duplicate but I cannot find it right now.

    The problem is that the as operator for interfaces is not very compatible with generics. The as operator relies on the interface GUID. The interface is found by querying for an interface with matching GUID. And GUIDs don't fit at all well with generic instantiation.

    Now let's look at your code.

    TPresenterClass1.Create((Self as IView<TMyClass1>));
    TPresenterClass2.Create((Self as IView<TMyClass2>));
    

    The problem is that IView<TMyClass1> and IView<TMyClass2> have the same GUID:

    type
      IView<T : class> = interface
        ['{59384CD6-30D6-4BD8-AB3D-7FCF4D1A8618}']
        procedure AssignPresenter(APresenter : IPresenter<T>);
      end;
    

    So both IView<TMyClass1> and IView<TMyClass2> share the same GUID, and when you query using as, the same interface will be returned irrespective of whether or not you asked for IView<TMyClass1> or IView<TMyClass2>.

    So, the bottom line here is that as is rendered next to useless with generic interfaces as soon as an object implements ISomeInterface<T> twice with different T.

    Embarcadero really should implement as in a manner that supports generics. I wouldn't hold your breath though.

    You will need to find a different way to solve your problem.