Delphi 虚拟方法表

Delphi 虚拟方法表,delphi,rtti,Delphi,Rtti,我正在使用这个区块: procedure ExecMethod(Target: TClass; const MethodName: string; const Args: array of TValue); var LContext: TRttiContext; LType: TRttiType; LMethod: TRttiMethod; begin LType := LContext.GetType(Target); for LMethod in LType.GetMet

我正在使用这个区块:

procedure ExecMethod(Target: TClass; const MethodName: string; const Args: array of TValue);
var
  LContext: TRttiContext;
  LType: TRttiType;
  LMethod: TRttiMethod;
begin
  LType := LContext.GetType(Target);
  for LMethod in LType.GetMethods do
    if (LMethod.Parent = LType) and (LMethod.Name = MethodName) then begin
      LMethod.Invoke(Target.Create, Args);
      break;
    end;
end;
像这样:

ExecMethod(TFuncClass, 'Test1', []);
ExecMethod(TFuncClass, 'Test2', ['hey']);
ExecMethod(TFuncClass, 'Test3', [100]);
procedure ExecMethod(Target: TObject; const MethodName: string; const Args: array of TValue);
var
  LContext: TRttiContext;
  LType: TRttiType;
  LMethod: TRttiMethod;
begin
  LType := LContext.GetType(Target.ClassType);
  for LMethod in LType.GetMethods(MethodName) do
    // TODO: Beware of overloaded methods
    LMethod.Invoke(Target, Args);
end;
在这门课上:

  TFuncClass = class(TObject)
    published
      procedure Test1;
      procedure Test2(const str: string);
      procedure Test3(i: integer);
      // there's more, each one with different prototype
  end;

var
  FuncClass: TFuncClass;
但后来,我不断收到访问违规。。。或者无效的强制转换指针类(或其他任何类型)

正如我在中所指出的,它会泄漏内存,因为它创建给定类的实例而从未释放它们。但是,这不应该立即导致任何运行时错误,因此这不是当前问题的原因

问题的代码将原始代码推广到任何给定的类上,这样做在技术上是错误的。要了解原因,您需要了解Delphi如何从类引用构造对象:

在类引用变量上调用构造函数时(如在
Target.Create
中),编译器使用编译时的知识来决定调用哪个构造函数。在本例中,调用的目标是
TClass
,编译器知道的唯一可用于该类型的构造函数是
TObject.Create
,因此调用的构造函数就是该构造函数。如果
TFuncClass
有其他构造函数,即使它与从
TObject
继承的零参数签名匹配,也不会调用它。所创建对象的类型仍将显示为
TFuncClass
,但是-
ClassType
函数将返回
TFuncClass
,并且
is
操作符将按预期工作

当代码对一个类调用错误的构造函数时,它最终会得到所需类的某个半有效实例。拥有无效实例可能会导致各种问题。如果其中包括访问冲突、无效类型转换、无效结果或其他内容,我不会感到惊讶

问题中显示的代码不应该有我描述的问题,因为
TFuncClass
没有新的构造函数。然而,给定的代码显然是不完整的,所以在这里可能过于简化了

最好让调用方负责提供一个实例来调用方法,如下所示:

ExecMethod(TFuncClass, 'Test1', []);
ExecMethod(TFuncClass, 'Test2', ['hey']);
ExecMethod(TFuncClass, 'Test3', [100]);
procedure ExecMethod(Target: TObject; const MethodName: string; const Args: array of TValue);
var
  LContext: TRttiContext;
  LType: TRttiType;
  LMethod: TRttiMethod;
begin
  LType := LContext.GetType(Target.ClassType);
  for LMethod in LType.GetMethods(MethodName) do
    // TODO: Beware of overloaded methods
    LMethod.Invoke(Target, Args);
end;
像这样使用该函数:

FuncClass := TFuncClass.Create(...);
try
  ExecMethod(FuncClass, 'Test1', []);
  ExecMethod(FuncClass, 'Test2', ['hey']);
  ExecMethod(FuncClass, 'Test3', [100]);
finally
  FuncClass.Free
end;

注意,这都是假设第二个参数,即方法的字符串名,实际上是由某个变量提供的,该变量的值在运行时之前是未知的。如果您要将字符串文本传递给
ExecMethod
,那么您应该停止调用
ExecMethod
,停止与RTTI混在一起,直接调用所需的方法:
FuncClass.Test2('hey')

,不清楚您需要什么。你只是不知道如何调用一个类方法吗?或者你需要从另一个类的方法中调用该方法吗?问题中的代码与你描述的相同。这个问题的标题与它的主体有什么关系?很抱歉,如果我很难理解我,让我重新表述一下我的意思。。。我希望ExecMethod成为类的一部分,并且它将在它所属的同一个类中执行方法。。而不必把目标放在课堂上……;]你说你有两种完全不同的错误,“或者别的什么。”请准确一点。复制并粘贴您得到的实际错误文本。说“或随便什么”告诉我你不必费心去注意这个问题。如果您不介意的话,为什么其他人要这么做呢?我试过这样的代码:键入MyClass=class发布的过程TestFunc;ExecMethod(MyClass,'TestFunc',[])<代码>给出异常..当然它没有编译,对吗?这是因为该示例中的
MyClass
不是类型为
TObject
的值
TestFunc
是一个实例方法。它要求您拥有
MyClass
的实例。RTTI不允许您回避该要求。是否希望
TestFunc
成为类方法而不是实例方法?您希望它是一个静态方法吗?请记住,没有人能读懂你的心思。你需要问你想要什么我试过你的密码。。这给了我一个例外。我用TClass替换了TObject。。。但我仍然会出错。
ExecMethod
已经可以处理所有这些问题。正如我之前所建议的,也许您可以发布一些代码来演示您所面临的问题。您可以单击问题下方的“编辑”链接并添加到其中;问题空间提供了比这些注释更好的格式。如果你发布的代码不起作用也没关系。很明显,你很难用文字来解释你的任务,所以也许用代码来解释对你来说会更容易。我现在注意到,事实上,这两种方法并不匹配。您将其添加到类中并引入了错误。我没有检查来源,因为我认为你发布了一些已知的好消息。今天和你一起工作特别困难。