虚方法表

时间:2012-11-06 14:05:09

标签: 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.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]);

在这堂课上:

  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;

然后,我继续获得访问冲突...或无效的转换指针类(或其他)...

1 个答案:

答案 0 :(得分:2)

正如我在the source of your code所指出的那样,它会泄漏内存,因为它会创建给定类的实例而不会释放它们。但是,这不应该导致任何直接的运行时错误,因此它不是问题的原因。

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

当你在类引用变量上调用构造函数时(如在Target.Create中),编译器在编译时使用它作为的知识来决定调用哪个构造函数。在这种情况下,调用的目标是TClass,并且编译器知道的唯一构造函数可用于该类型TObject.Create,因此这是被调用的构造函数。如果TFuncClass有一些其他构造函数 - 即使它与从TObject继承的零参数签名匹配 - 它永远不会被调用。创建的对象的类型仍将显示为 TFuncClass,但ClassType函数将返回TFuncClassis运算符将起作用如预期的那样。

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

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

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

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')