【问题标题】:RTTI information for method pointer方法指针的 RTTI 信息
【发布时间】:2013-02-05 10:16:55
【问题描述】:

是否可以获得关于TMethod 的RTTI 信息?

我可以通过

Instance := TObject(Method.Data);

所以我可以获得实例的 RTTI 类型,但我怎样才能获得正确的 TRttiMethod?我想检查使用方法指针传入的方法的属性。

【问题讨论】:

    标签: delphi delphi-xe2 rtti


    【解决方案1】:

    这种方法在理论上可行,并且在实践中会有很好的改变,但有几件事可能会阻止您获得TRttiMethod

    • TMethod 记录显示 Data: Pointer,而不是 TObject。这意味着可能有除TObject 之外的其他东西作为Data!这是一个严重的问题,因为如果 Data 不是 TObject,那么尝试从中提取 RTTI 将导致运行时错误。
    • 并非所有方法都有 RTTI。默认情况下,私有区域中的方法没有 RTTI,也可以使用{$RTTI} 停止为公共或已发布成员生成 RTTI。

    这两个问题对于我们在 Delphi 中的通常类型的事件实现来说不是问题(在 Object Inspector 中双击事件的名称并填写代码),但我不认为你在谈论“vanila”实现。没有多少人会用属性来装饰默认事件处理程序!

    演示以上所有内容的代码:

    program Project15;
    
    {$APPTYPE CONSOLE}
    
    uses
      SysUtils, RTTI;
    
    type
      // Closure/Event type
      TEventType = procedure of object;
    
      // An object that has a method compatible with the declaration above
      TImplementation = class
      private
        procedure PrivateImplementation;
      public
        procedure HasRtti;
    
        procedure GetPrivateImpEvent(out Ev:TEventType);
      end;
    
      TRecord = record
        procedure RecordProc;
      end;
    
      // an object that has a compatible method but provides no RTTI
      {$RTTI EXPLICIT METHODS([])}
      TNoRttiImplementation = class
      public
        procedure NoRttiAvailable;
      end;
    
    procedure TImplementation.GetPrivateImpEvent(out Ev:TEventType);
    begin
      Ev := PrivateImplementation;
    end;
    
    procedure TImplementation.HasRtti;
    begin
      WriteLn('HasRtti');
    end;
    
    procedure TNoRttiImplementation.NoRttiAvailable;
    begin
      WriteLn('No RTTI Available');
    end;
    
    procedure TRecord.RecordProc;
    begin
      WriteLn('This is written from TRecord.RecordProc');
    end;
    
    procedure TImplementation.PrivateImplementation;
    begin
      WriteLn('PrivateImplementation');
    end;
    
    procedure TotalyFakeImplementation(Instance:Pointer);
    begin
      WriteLn('Totaly fake implementation, TMethod.Data is nil');
    end;
    
    procedure SomethingAboutMethod(X: TEventType);
    var Ctx: TRttiContext;
        Typ: TRttiType;
        Method: TRttiMethod;
        Found: Boolean;
    begin
      WriteLn('Invoke the method to prove it works:');
      X;
      // Try extract information about the event
      Ctx := TRttiContext.Create;
      try
        Typ := Ctx.GetType(TObject(TMethod(X).Data).ClassType);
        Found := False;
        for Method in Typ.GetMethods do
          if Method.CodeAddress = TMethod(X).Code then
          begin
            // Got the Method!
            WriteLn('Found method: ' + Typ.Name + '.' + Method.Name);
            Found := True;
          end;
        if not Found then
          WriteLn('Method not found.');
      finally Ctx.Free;
      end;
    end;
    
    var Ev: TEventType;
        R: TRecord;
    
    begin
      try
        try
          WriteLn('First test, using a method that has RTTI available:');
          SomethingAboutMethod(TImplementation.Create.HasRtti);
          WriteLn;
    
          WriteLn('Second test, using a method that has NO rtti available:');
          SomethingAboutMethod(TNoRttiImplementation.Create.NoRttiAvailable);
          WriteLn;
    
          WriteLn('Third test, private method, default settings:');
          TImplementation.Create.GetPrivateImpEvent(Ev);
          SomethingAboutMethod(Ev);
          WriteLn;
    
          WriteLn('Assign event handler using handler from a record');
          try
            SomethingAboutMethod(R.RecordProc);
          except on E:Exception do WriteLn(E.Message);
          end;
          WriteLn;
    
          WriteLn('Assign event handler using static procedure');
          try
            TMethod(Ev).Data := nil;
            TMethod(Ev).Code := @TotalyFakeImplementation;
            SomethingAboutMethod(Ev);
          except on E:Exception do WriteLn(E.Message);
          end;
          WriteLn;
    
        except
          on E: Exception do Writeln(E.ClassName, ': ', E.Message);
        end;
      finally ReadLn;
      end;
    end.
    

    【讨论】:

    • CodeAddress 似乎正是我想要的...我会尝试并稍后提供反馈。
    • 您的第一个问题对我来说不是问题,因为我将procedure of object 转换为TMethod 来存储它,所以TMethod 后面总是有一个对象实例。第二个问题确实可能是个问题。
    • @Smasher,这是一个严重的问题; procedure of object 只是语法,您可以将记录的方法分配给事件处理程序就好了。没有黑客,没有编译器警告。我将再次更新代码以包含两种会产生运行时错误的情况。
    • 我提供了更多示例:使用 Record 实现事件处理程序和使用静态过程实现事件处理程序(即:不是类或记录的方法)。就Delphi而言,两者都很好,如果您对TMethod.Data进行假设,两者都会产生运行时错误@
    • @Cosmin,方法指针中不允许指向静态方法的指针。方法指针可以是指向实例方法或类方法的指针,或者显然是记录方法。但是,将指针分配给静态方法是错误的。编译器应该标记它。 (如果你在赋值中使用了@ 运算符,那么你做错了。)TMethod.Data 成员将是TObjectTClass 引用(或者显然是一个记录指针)。在设计时,它可以保存 IDE 为事件处理程序选择的任何簿记信息。
    猜你喜欢
    • 2010-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-05
    • 1970-01-01
    • 2015-09-17
    • 2012-05-09
    • 1970-01-01
    相关资源
    最近更新 更多