Reflectionでインポートされたクラスのイベントを処理する必要があります。これを行うために、必要な引数型を持つ動的メソッドを作成し、それをデリゲートに変換してイベントハンドラとして追加します。.Net - 引数の可変数を渡す
動的メソッドで行う必要があるのは、可変数の引数を受け取るコンパイル済みメソッドを呼び出すことだけです。したがって、コンパイルされたメソッドに引数を渡すためには、任意の数の引数をパックします。
ここで問題が発生します。単純にスタック上のすべての引数を押すのではなく、ILのオペコードで手動で配列を作成し、引数を埋め込む必要があるようです(少し複雑です)。ここで
コード(C++/CLI)である:
array<System::Type^> ^GetParameterTypes(System::Reflection::MethodInfo ^method)
{
auto parameters = method->GetParameters();
auto typeParameters = gcnew array<System::Type ^> (parameters->Length);
for (int i = 0; i < parameters->Length; i++)
typeParameters[i] = parameters[i]->ParameterType;
return typeParameters;
}
ref class HandlerClass
{
public:
void TestMethod(... array<System::Object ^> ^parameters)
{
System::Console::WriteLine("asdf");
}
}
System::Delegate ^AddHandler(HandlerClass ^handler, System::Reflection::EventInfo ^_event, System::Object ^instance)
{
// Get handler type
auto delegateType = _event->EventHandlerType;
assert(delegateType);
auto invoke = delegateType->GetMethod("Invoke");
assert(invoke);
// Get return type
auto returnType = invoke->ReturnType;
// Get parameter list
auto delegateParameters = GetParameterTypes(invoke);
auto parameters = gcnew array<System::Type ^> (delegateParameters->Length + 1);
parameters[0] = System::Object::typeid;
delegateParameters->CopyTo(parameters, 1);
// Create dynamic method
auto handlerMethod = gcnew System::Reflection::Emit::DynamicMethod("",
returnType,
parameters,
ProxyEvent::typeid);
auto method = HandlerClass::typeid->GetMethod("TestMethod");
// Add method body
auto codeGen = handlerMethod->GetILGenerator();
// 'this' pointer
codeGen->Emit(System::Reflection::Emit::OpCodes::Ldarg_0);
// Parameters
for (int i = 0; i < delegateParameters->Length; i++)
codeGen->Emit(System::Reflection::Emit::OpCodes::Ldarg, i + 1);
// Method call
codeGen->Emit(System::Reflection::Emit::OpCodes::Call, method);
//codeGen->EmitCall(System::Reflection::Emit::OpCodes::Call, method, parameters); //This one throws an exception that calling convention should be Vararg
// Return
codeGen->Emit(System::Reflection::Emit::OpCodes::Ret);
// Get delegate
auto compiled = handlerMethod->CreateDelegate(delegateType, handler);
// Add to handler list
_event->AddEventHandler(instance, compiled);
}
だから、あなたが見ることができるように、私のtestMethod機能はかなり本物の可変引数の関数ではありません。 C#に相当するのはvoid TestMethod(params object[] parameters);
です。これは、C++/CLIがキーワード__arglist
をサポートしていないためです。 このILコードは正しくありません。私のTestMethod
にはパラメータがありません。
質問は:私はちょうどスタック上のパラメータをプッシュして呼び出すようなvariadic関数(C++/CLIで)を宣言する方法はありますか?
いいえ、ILコードジェネレータをまったく使用せずに、同様の結果(Reflectionからインポートしたイベントからコンパイルされた関数にパラメータを渡す)を実現する方法はありますか?
アレイを作成するために必要なロックをバイパスすることはできません。通常はコンパイラがそれを行いますが、自分でILを生成するときは自分の仕事です。パニックになることはありません。param配列を使用するサンプルC#またはC++/CLIプログラムを作成し、逆アセンブラ(ildasm.exeなど)を使用して生成するILを調べます。それを複製する。 –