2017-06-14 40 views
2

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からインポートしたイベントからコンパイルされた関数にパラメータを渡す)を実現する方法はありますか?

+2

アレイを作成するために必要なロックをバイパスすることはできません。通常はコンパイラがそれを行いますが、自分でILを生成するときは自分の仕事です。パニックになることはありません。param配列を使用するサンプルC#またはC++/CLIプログラムを作成し、逆アセンブラ(ildasm.exeなど)を使用して生成するILを調べます。それを複製する。 –

答えて

0

C++/CLIの "ref class"(管理対象クラス)で可変数の引数を使用して関数を定義できます。この構文では、 "..."トークンをそれらの引数を保持する配列の先頭に追加する必要があり、関数宣言の最後のパラメータでなければなりません。例:変数の引数の型はコンパイル時に知られていない場合は

public: void SomeFunction ( int   iRequiredArgument1, 
           int   iRequiredArgument2, 
          ... array<int>^ aiVariableArguments) 
    { 
    // function implementation 
    return; 
    } 

は、

array<Object^>^ 

を使用して、実行時にその要素を型キャスト。

+0

私はあなたが誤解していると思います。私はすでにさまざまな引数を持つ関数を持っています。私が必要とするのは、引数を埋めてMSILコードから呼び出すことです。 –

関連する問題