2017-06-14 156 views
2

我需要處理從反射導入的類的事件。爲此,我使用所需參數類型創建一個動態方法,然後將其轉換爲委託並添加爲事件處理程序。.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中看不到任何參數。

問題是:是否有一種方法來聲明一個可變參數函數(在C++/CLI中),這樣我只需將參數推送到堆棧並調用它?

如果不是,那麼是否有辦法實現類似的結果(將參數傳遞給通過Reflection導入的事件的編譯函數),而根本不使用IL代碼生成器?

+2

您無法繞過創建陣列的難度要求。編譯器通常會爲你做,但是當你自己生成IL時,它就是你的工作。不用擔心,編寫一個使用參數數組的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代碼中調用它。 –

相關問題