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)、コンパイラーはコンパイル時の知識を使用して、呼び出すコンストラクターを決定します。この場合、呼び出しのターゲットは であり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;

これはすべて、メソッドの文字列名である 2 番目のパラメーターが、実行時まで値が不明な変数によって実際に提供されることを前提としていることに注意してください。文字列リテラルを に渡す場合は、 のExecMethod呼び出しExecMethodをやめ、RTTI をいじるのをやめ、目的のメソッドを直接呼び出すだけですFuncClass.Test2('hey')

于 2012-11-06T15:58:53.833 に答える