Unable to invoke method declare in class implement generic interface method

Chau Chee Yang

Delphi support generic for IInterface. I have the follow construct using generic IInterface:

type
  IVisitor<T> = interface
  ['{9C353AD4-6A3A-44FD-B924-39B86A4CB14D}']
    procedure Visit(o: T);
  end;

  TMyVisitor = class(TInterfacedObject, IVisitor<TButton>, IVisitor<TEdit>)
    procedure Visit(o: TButton); overload;
    procedure Visit(o: TEdit); overload;
  end;

implementation

procedure TMyVisitor.Visit(o: TButton);
begin
  ShowMessage('Expected: TButton, Actual: ' + o.ClassName);
end;

procedure TMyVisitor.Visit(o: TEdit);
begin
  ShowMessage('Expected: TEdit, Actual: ' + o.ClassName);
end;

TMyVisitor class implement two interface: IVisitor<TButton> and IVisitor<TEdit>.

I attempt invoke the methods:

procedure TForm6.Button1Click(Sender: TObject);
var V: IInterface;
begin
  V := TMyVisitor.Create;
  (V as IVisitor<TButton>).Visit(Button1);
  (V as IVisitor<TEdit>).Visit(Edit1);
end;

The output I have is:

Expected: TEdit, Actual: TButton
Expected: TEdit, Actual: TEdit

Apparently, the code doesn't invoke procedure TMyVisitor.Visit(o: TButton) when execute (V as IVisitor<TButton>).Visit(Button1).

Is this a bug in Delphi or I should avoid implement multiple generic IInterface? All above codes have test in Delphi XE6.

Dalija Prasnikar

as operator requires interface GUID to be able to tell which interface you are referring to. Since generic interfaces share same GUID as operator will not work with them. Basically, compiler cannot tell the difference between IVisitor<TButton> and IVisitor<TEdit> interfaces.

However, you can solve your problem using enhanced RTTI:

type
  TCustomVisitor = class(TObject)
  public
    procedure Visit(Instance: TObject); 
  end;

  TVisitor = class(TCustomVisitor)
  public
    procedure VisitButton(Instance: TButton); overload;
    procedure VisitEdit(Instance: TEdit); overload;
  end;

procedure TCustomVisitor.Visit(Instance: TObject);
var
  Context: TRttiContext;
  CurrentClass: TClass;
  Params: TArray<TRttiParameter>;
  ParamType: TRttiType;
  SelfMethod: TRttiMethod;
  s: string;
begin
  Context := TRttiContext.Create;
  CurrentClass := Instance.ClassType;
  repeat
    s := CurrentClass.ClassName;
    Delete(s, 1, 1); // remove "T"
    for SelfMethod in Context.GetType(Self.ClassType).GetMethods('Visit' + s) do
      begin
        Params := SelfMethod.GetParameters;
        if (Length(Params) = 1) then
          begin
            ParamType := Params[0].ParamType;
            if ParamType.IsInstance and (ParamType.AsInstance.MetaclassType = CurrentClass) then
              begin
                SelfMethod.Invoke(Self, [Instance]);
                Exit;
              end;
          end;
      end;
    CurrentClass := CurrentClass.ClassParent;
  until CurrentClass = nil;
end; 

If you need to have Visitor interface you can change declarations to

type
  IVisitor = interface
  ['{9C353AD4-6A3A-44FD-B924-39B86A4CB14D}']
    procedure Visit(Instance: TObject);
  end;

  TCustomVisitor = class(TInterfacedObject, IVisitor)
  public
    procedure Visit(Instance: TObject); 
  end;

You can then use that in following manner, just by calling Visit and appropriate Visit method will be called.

procedure TForm6.Button1Click(Sender: TObject);
var V: IVisitor;
begin
  V := TMyVisitor.Create;
  V.Visit(Button1);
  V.Visit(Edit1);
end;

Above code is based on Uwe Raabe's code and you can read more http://www.uweraabe.de/Blog/?s=visitor

And here is extended visitor interface and class that can operate on non-class types. I have implemented only calls for string, but implementation for other types consists only of copy-paste code with different typecast.

  IVisitor = interface
  ['{9C353AD4-6A3A-44FD-B924-39B86A4CB14D}']
    procedure Visit(const Instance; InstanceType: PTypeInfo);
    procedure VisitObject(Instance: TObject);
  end;

  TCustomVisitor = class(TInterfacedObject, IVisitor)
  public
    procedure Visit(const Instance; InstanceType: PTypeInfo);
    procedure VisitObject(Instance: TObject);
  end;

procedure TCustomVisitor.Visit(const Instance; InstanceType: PTypeInfo);
var
  Context: TRttiContext;
  Params: TArray<TRttiParameter>;
  ParamType: TRttiType;
  SelfMethod: TRttiMethod;
begin
  Context := TRttiContext.Create;
  case InstanceType.Kind of
    tkClass : VisitObject(TObject(Instance));
    // template how to implement calls for non-class types
    tkUString :
      begin
        for SelfMethod in Context.GetType(Self.ClassType).GetMethods('VisitString') do
          begin
            Params := SelfMethod.GetParameters;
            if (Length(Params) = 1) then
              begin
                ParamType := Params[0].ParamType;
                if ParamType.TypeKind = tkUString then
                  begin
                    SelfMethod.Invoke(Self, [string(Instance)]);
                    Exit;
                  end;
              end;
          end;
      end;
  end;
end;

procedure TCustomVisitor.VisitObject(Instance: TObject);
var
  Context: TRttiContext;
  CurrentClass: TClass;
  Params: TArray<TRttiParameter>;
  ParamType: TRttiType;
  SelfMethod: TRttiMethod;
  s: string;
begin
  Context := TRttiContext.Create;
  CurrentClass := Instance.ClassType;
  repeat
    s := CurrentClass.ClassName;
    Delete(s, 1, 1); // remove "T"
    for SelfMethod in Context.GetType(Self.ClassType).GetMethods('Visit' + s) do
      begin
        Params := SelfMethod.GetParameters;
        if (Length(Params) = 1) then
          begin
            ParamType := Params[0].ParamType;
            if ParamType.IsInstance and (ParamType.AsInstance.MetaclassType = CurrentClass) then
              begin
                SelfMethod.Invoke(Self, [Instance]);
                Exit;
              end;
          end;
      end;
    CurrentClass := CurrentClass.ClassParent;
  until CurrentClass = nil;
end;

Enhanced Visitor can be used like this:

  TVisitor = class(TCustomVisitor)
  public
    procedure VisitButton(Instance: TButton); overload;
    procedure VisitEdit(Instance: TEdit); overload;
    procedure VisitString(Instance: string); overload;
  end;


var
  v: IVisitor;
  s: string;
begin
  s := 'this is string';
  v := TVisitor.Create;

  // class instances can be visited directly via VisitObject
  v.VisitObject(Button1); 

  v.Visit(Edit1, TypeInfo(TEdit));
  v.Visit(s, TypeInfo(string));
end;

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

Unable to invoke method declare in class implement generic interface method

From Dev

Declare Generic method in non generic interface

From Dev

How implement an interface Generic Method

From Dev

How implement an interface Generic Method

From Dev

How to just get the method in the implement class with a generic interface in Java

From Dev

Invoke generic method by type where type is interface

From Dev

Attempt to invoke interface method on a null object reference while implement interface

From Dev

Invoke method on generic type?

From Dev

How to implement the Iterable<> method in a generic class

From Dev

How to declare an interface method if i already have an implementation with generic

From Dev

Implement interface with method returning class of type parameter

From Dev

Implement interface with method returning class of type parameter

From Dev

CLI/C++ Interface class, Generic method calling template method

From Dev

CLI/C++ Interface class, Generic method calling template method

From Dev

Java how to implement interface with a variadic method and generic return type

From Dev

Invoke non-abstract method of super interface in concrete class

From Dev

Declare method outside of class

From Dev

Declare String in class or method

From Dev

Reflection: invoke a method with generic parameter

From Dev

Generic method inside interface

From Dev

Generic method inside interface

From Dev

Interface with the method of generic type

From Dev

Interface with Generic method

From Dev

Declare generic method returning enumeration

From Dev

Java - How to create a generic class that calls an interface method?

From Dev

Determine if a class implements a generic interface and then call a the interfaces method

From Dev

Java - How to create a generic class that calls an interface method?

From Dev

Implement only one method from interface in anonymous class

From Dev

Java: Generic class with generic method

Related Related

  1. 1

    Unable to invoke method declare in class implement generic interface method

  2. 2

    Declare Generic method in non generic interface

  3. 3

    How implement an interface Generic Method

  4. 4

    How implement an interface Generic Method

  5. 5

    How to just get the method in the implement class with a generic interface in Java

  6. 6

    Invoke generic method by type where type is interface

  7. 7

    Attempt to invoke interface method on a null object reference while implement interface

  8. 8

    Invoke method on generic type?

  9. 9

    How to implement the Iterable<> method in a generic class

  10. 10

    How to declare an interface method if i already have an implementation with generic

  11. 11

    Implement interface with method returning class of type parameter

  12. 12

    Implement interface with method returning class of type parameter

  13. 13

    CLI/C++ Interface class, Generic method calling template method

  14. 14

    CLI/C++ Interface class, Generic method calling template method

  15. 15

    Java how to implement interface with a variadic method and generic return type

  16. 16

    Invoke non-abstract method of super interface in concrete class

  17. 17

    Declare method outside of class

  18. 18

    Declare String in class or method

  19. 19

    Reflection: invoke a method with generic parameter

  20. 20

    Generic method inside interface

  21. 21

    Generic method inside interface

  22. 22

    Interface with the method of generic type

  23. 23

    Interface with Generic method

  24. 24

    Declare generic method returning enumeration

  25. 25

    Java - How to create a generic class that calls an interface method?

  26. 26

    Determine if a class implements a generic interface and then call a the interfaces method

  27. 27

    Java - How to create a generic class that calls an interface method?

  28. 28

    Implement only one method from interface in anonymous class

  29. 29

    Java: Generic class with generic method

HotTag

Archive