Jump to content
chkaufmann

Overloaded generic methods

Recommended Posts

Hi,

 

I try to create this helper:

 

  TComponentHelper = class helper for TComponent
  public
    function FindComponents<I:IInterface>: IBSEnumerable<I>; overload;
    function FindComponents<T:class>: IBSEnumerable<T>; overload;
  end;

but I get an error "methods with identical parameters". In my generic code I filter components either by using "InheritsFrom" or "Supports()". Therefore I need IInterface and class in the declaration or is there another way to do that?

 

Christian

Share this post


Link to post

Overloads based on generic type constraint are not possible.

Personally I would just make one method without constraint and use GetTypeKind(T) internally to decide if its tkInterface or tkClass and simply raise an exception if anyone put anything but a class or interface into T.

Share this post


Link to post

But if I don't declare T:class I cannot use it in the InheritsFrom() method. Same for an IInterface with the Support method:

 

function TComponentHelper.FindComponents<I:IInterface>: IBSEnumerable<I>;
var
  ix  : Integer;
  lst : IBSList<I>;
  tmp : I;
begin
  lst := TBSGenerics.GenList<I>;
  for ix := 0 to ComponentCount - 1
    do if Supports(Components[ix], I, tmp)
      then lst.Add(tmp);
  Result := tmp;
end;

function TComponentHelper.FindComponents<T:class>: IBSEnumerable<T>;
var
  ix  : Integer;
  lst : IBSList<T>;
  tmp : T;
begin
  Assert(T.InheritsFrom(TComponent), 'Nur für Subklassen von TComponent');
  lst := TBSGenerics.GenList<T>;
  for ix := 0 to ComponentCount - 1 do if Components[ix].InheritsFrom(T) then begin
    TComponent(tmp) := Components[ix];
    lst.Add(tmp);
  end;
  Result := lst;
end;

 

Christian

Share this post


Link to post

Must it be an overload?

Why not FindComponentsByClass vs FindComponentsWithInterface ?

Share this post


Link to post
10 hours ago, chkaufmann said:

I get an error "methods with identical parameters". In my generic code I filter components either by using "InheritsFrom" or "Supports()". Therefore I need IInterface and class in the declaration or is there another way to do that?

You can't overload on return type alone.  You need different parameter lists.  That is exactly what the error message is saying.  So a simple solution would be to change the return value into an output parameter, eg:

 

  TComponentHelper = class helper for TComponent
  public
    procedure FindComponents<I:IInterface>(out Enum: IBSEnumerable<I>); overload;
    procedure FindComponents<T:class>(out Enum: IBSEnumerable<T>); overload;
  end;
  
procedure TComponentHelper.FindComponents<I:IInterface>(out Enum: IBSEnumerable<I>);
var
  ix  : Integer;
  lst : IBSList<I>;
  tmp : I;
begin
  lst := TBSGenerics.GenList<I>;
  for ix := 0 to ComponentCount - 1 do begin
    if Supports(Components[ix], I, tmp) then
      lst.Add(tmp);
  end;
  Enum := lst;
end;

procedure TComponentHelper.FindComponents<T:class>(out Enum: IBSEnumerable<T>);
var
  ix  : Integer;
  lst : IBSList<T>;
  tmp : T;
  comp: TComponent;
begin
  Assert(T.InheritsFrom(TComponent), 'Nur für Subklassen von TComponent');
  lst := TBSGenerics.GenList<T>;
  for ix := 0 to ComponentCount - 1 do begin
    comp := Components[ix];
    if comp.InheritsFrom(T) then
      lst.Add(T(comp));
  end;
  Enum := lst;
end;

 

Edited by Remy Lebeau

Share this post


Link to post
On 2/4/2020 at 5:00 AM, chkaufmann said:

But if I don't declare T:class I cannot use it in the InheritsFrom() method. Same for an IInterface with the Support method

While it is true that you can't pass T itself directly without using a constraint on T, you can use T's RTTI to get its Interface Guid or Class Type at runtime, and then pass that to Supports()/InheritsFrom() instead of T.  For example, something like this:

 

uses
  ..., TypInfo;
  
type
  TComponentHelper = class helper for TComponent
  public
    function FindComponents<T>: IBSEnumerable<T>;
  end;

function TComponentHelper.FindComponents<T>: IBSEnumerable<T>;
type
  PIInterface = ^IInterface;
  PTComponent = ^TComponent;
var
  ix  : Integer;
  lst : IBSList<T>;
  tmp : T;
  comp : TComponent;
  ClsType: TClass;
  IntfGuid: TGUID;
begin
  case GetTypeKind(T){PTypeInfo(TypeInfo(T)).Kind} of
    tkClass: begin
      lst := TBSGenerics.GenList<T>;
      ClsType := GetTypeData(PTypeInfo(TypeInfo(T))).ClassType;
      for ix := 0 to ComponentCount - 1 do begin
        comp := Components[ix];
        if comp.InheritsFrom(ClsType) then begin
          PTComponent(@tmp)^ = comp;
          lst.Add(tmp);
        end;
      end;
    end;
    tkInterface: begin
      lst := TBSGenerics.GenList<T>;
      IntfGuid := GetTypeData(PTypeInfo(TypeInfo(T))).Guid;
      for ix := 0 to ComponentCount - 1 do begin
        if Supports(Components[ix], IntfGuid, PIInterface(@tmp)^) then
          lst.Add(tmp);
      end;
    end;
  else
    raise ...;
  end;
  Result := lst;
end;

 

Edited by Remy Lebeau
  • Like 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×