我需要處理從反射導入的類的事件。爲此,我使用所需參數類型創建一個動態方法,然後將其轉換爲委託並添加爲事件處理程序。.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代碼生成器?
您無法繞過創建陣列的難度要求。編譯器通常會爲你做,但是當你自己生成IL時,它就是你的工作。不用擔心,編寫一個使用參數數組的C#或C++/CLI程序示例,並使用反彙編程序(如ildasm.exe)來查看它生成的IL。重複這一點。 –