Rtti:在 class 中调用方法时得到 "Invalid class typecast"

Rtti: getting "Invalid class typecast" when calling a method in a class

我做了一个我正在尝试做的小工作示例,一切正常,但不是我想用 Rtti 调用方法的部分,请检查方法 DoSomeTask 中的注释。 我想直视失败的行比解释更好,但我会尝试。简而言之,我有一些嵌套的 classes,主要的静态 class TSCSettings 和 public 属性(用户)是 class TUsers 的一个实例。 TUsers 有一个字段 fAccounting,它是 class TSettingsAccounting 的一个实例。这个想法是以这种方式加载用户设置:

TSCSettings.Users.Accounting.LoadSettings(用户ID);

它像那样工作得很好,但是在方法 DoSomeTask 中,它无法调用实例 fAccounting 的方法 LoadSettings,行中有 Rtti

meth.Invoke(vField, [1]); --> 异常 class EInvalidCast 消息 'Invalid class typecast'.

非常感谢您的帮助。

program Rtti_CallMethods;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  RTTI;

type

  TSettingsAccounting = Class(TObject)
      public
         function LoadSettings(UserID: Integer): Boolean;
  end;

  TUsers = Class(TObject)
      strict private
         fAccounting: TSettingsAccounting;
      public
         constructor Create; virtual;
         property Accounting: TSettingsAccounting read fAccounting write fAccounting;

         function DoSomeTask: Boolean;
  end;

  //Static class
  TSCSettings = Class(TObject)
      strict private
         class var fUsers: TUsers;
      public
         class property Users: TUsers read fUsers write fUsers;
         class constructor Create;

  end;


{ TSettingsAccounting }
function TSettingsAccounting.LoadSettings(UserID: Integer): Boolean;
begin
   Writeln('...Task load settings ' + UserID.ToString);
end;


{ TCLUsers }
constructor TUsers.Create;
begin
   fAccounting:= TSettingsAccounting.Create;
end;

function TUsers.DoSomeTask: Boolean;
var
   vCtx: TRttiContext;
   vType: TRttiType;
   vField: TRttiField;
   meth : TRttiMethod;
   sInfo: string;
begin

   //---- fAccounting is already instanciated and calling the method in 
   //this way works fine, but fails when I try to do the same below with Rtti
   fAccounting.LoadSettings(4); 
   //----

   vType := vCtx.GetType(self.ClassType); //--> TCLUsers
   for vField in vType.GetFields do
   begin
      sInfo:= vField.Name; // --> fAccounting

      //------ This way works fine
      meth := vField.FieldType.GetMethod('LoadSettings');
      meth.Invoke(fAccounting, [2]);
      //------

      //------ This way fails: ... exception class EInvalidCast with message 'Invalid class typecast'.
      meth.Invoke(vField, [1]);
      //------
   end;
end;

class constructor TSCSettings.Create;
begin
   fUsers:=  TUsers.Create;
end;


// -------------------------------
begin
   TSCSettings.Users.Accounting.LoadSettings(3); //This works fine
   TSCSettings.Users.DoSomeTask;
   readln;
end.

TRttiMethod.Invoke() 的第一个参数是指向调用方法的对象的指针。该指针可以作为直接 TObject 指针或作为包含 TObject 指针的 TValue 传递。

在这种情况下,需要一个 TSettingsAccounting 对象。这就是 meth.Invoke(fAccounting, [2]); 起作用的原因。但是在失败的行上,你给它一个指向 TRttiField 本身的指针,这就是 meth.Invoke(vField, [1]); 失败的原因。 TRttiMethod源自TObject,所以代码编译,但是传入错误TObject

您需要 TObject 从阅读 TRttiFieldvaluevalue fAccounting字段),例如:

meth.Invoke(vField.GetValue(Self), [1]);