首页 > 解决方案 > Rtti:在类中调用方法时获取“无效的类类型转换”

问题描述

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

TSCSettings.Users.Accounting.LoadSettings(UserID);

并且它工作正常,但是在方法 DoSomeTask 中它无法调用实例 fAccounting 的方法 LoadSettings 与 Rtti 在行

meth.Invoke(vField, [1]); --> 带有消息“无效类类型转换”的异常类 EInvalidCast。

非常感谢你的帮助。

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.

标签: delphirttidelphi-10.2-tokyo

解决方案


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

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

您需要TObject从读取(字段的)的,例如:TRttiFieldfAccounting

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

推荐阅读