1

我正在使用这个块:

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;

但是,我不断收到访问冲突......或无效的强制转换指针类(或其他)......

4

1 回答 1

2

正如我在代码源中指出的那样,它会泄漏内存,因为它会创建给定类的实例而从未释放它们。不过,这不应该导致任何直接的运行时错误,因此它不是手头问题的原因。

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

当您在类引用变量上调用构造函数时(如在 中Target.Create),编译器使用它在编译时所知道的知识来决定调用哪个构造函数。在这种情况下,调用的目标是 a TClass,编译器知道的唯一可用于该类型的构造函数是TObject.Create,所以这就是被调用的构造函数。IfTFuncClass有其他构造函数——即使它与继承自的零参数签名匹配TObject——它也永远不会被调用。但是,创建的对象的类型仍将显示TFuncClass——ClassType函数将返回TFuncClass,并且is运算符将按预期工作。

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

但是,问题中显示的代码不应该有我描述的问题,因为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').

于 2012-11-06T15:58:53.833 回答