.Net - 传递可变数量的参数
.Net - passing variable number of arguments
我需要处理来自 类 使用反射导入的事件。为此,我创建了一个具有所需参数类型的动态方法,然后将其转换为委托并添加为事件处理程序。
我在动态方法中唯一需要做的就是调用一个接收可变数量参数的编译方法。因此,我 'pack' 任何类型的任意数量的参数将其传递给编译方法。
麻烦来了:看来我需要在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 中),这样我就可以将参数压入堆栈并调用它?
如果没有,那么有没有办法在根本不使用 IL 代码生成器的情况下实现类似的结果(将参数从通过反射导入的事件传递给已编译的函数)?
您可以在 C++/CLI "ref class"(托管 class)中定义具有可变数量参数的函数。该语法要求将“...”标记添加到包含这些参数的数组之前,并且必须是函数声明中的最后一个参数。示例:
public: void SomeFunction ( int iRequiredArgument1,
int iRequiredArgument2,
... array<int>^ aiVariableArguments)
{
// function implementation
return;
}
如果变量参数的类型在编译时未知,使用
array<Object^>^
并在运行时对其元素进行类型转换。
使用 this 教程,我想出了以下代码:
System::Reflection::Emit::DynamicMethod ^CreateHandler()
{
// Get event handler type
auto delegateType = _event->EventHandlerType;
assert(delegateType);
auto invoke = delegateType->GetMethod("Invoke");
assert(invoke);
// Get return type
auto returnType = invoke->ReturnType;
// Get parameters list
auto delegateParameters = GetParameterTypes(invoke);
// First parameter should be a pointer to my handler class
auto parameters = gcnew array<System::Type ^> (delegateParameters->Length + 1);
parameters[0] = MyHandlerClass::typeid;
delegateParameters->CopyTo(parameters, 1);
// Create dynanic method for my handler class
auto handler = gcnew System::Reflection::Emit::DynamicMethod(
_event->Name + "Handler",
returnType,
parameters,
MyHandlerClass::typeid
);
// This method should be called
auto method = MyHandlerClass::typeid->GetMethod("GeneralEventHandler");
// Add method body
auto codeGen = handler->GetILGenerator();
// Get type of 'object[]'
auto arrayType = System::Object::typeid->MakeArrayType();
// Create local variable 'args' (at index 0) of type 'object[]'
auto localArgs = codeGen->DeclareLocal(arrayType);
// Create an array of arguments of required size
codeGen->Emit(System::Reflection::Emit::OpCodes::Ldc_I4, delegateParameters->Length); // Array size
codeGen->Emit(System::Reflection::Emit::OpCodes::Newarr, System::Object::typeid); // Creating array
codeGen->Emit(System::Reflection::Emit::OpCodes::Stloc_0); // Store to local variable at index 0 ('args')
// Fill array
for (int i = 0; i < delegateParameters->Length; i++)
{
// Store (i + 1) argument to array at index i
codeGen->Emit(System::Reflection::Emit::OpCodes::Ldloc_0); // Refer to array
codeGen->Emit(System::Reflection::Emit::OpCodes::Ldc_I4, i); // Move to index i
codeGen->Emit(System::Reflection::Emit::OpCodes::Ldarg_S, i + 1); // Refer to (i + 1) argument
// Is argument of simple type?
if (delegateParameters[i]->IsValueType)
{
// Pack argument
codeGen->Emit(System::Reflection::Emit::OpCodes::Box, delegateParameters[i]);
}
// Store element to array
codeGen->Emit(System::Reflection::Emit::OpCodes::Stelem_Ref);
}
// Now we put everything on stack:
// 'this' pointer
codeGen->Emit(System::Reflection::Emit::OpCodes::Ldarg_0);
// Nane of event (for identification)
codeGen->Emit(System::Reflection::Emit::OpCodes::Ldstr, _event->Name);
// Array of arguments
codeGen->Emit(System::Reflection::Emit::OpCodes::Ldloc_0);
// Now call the method
codeGen->Emit(System::Reflection::Emit::OpCodes::Call, method);
// And return
codeGen->Emit(System::Reflection::Emit::OpCodes::Ret);
return handler;
}
处理程序 class 如下所示:
ref class MyHandlerClass
{
public:
void GeneralEventHandler(System::String ^name, array<System::Object ^> ^params);
}
然后我只使用这个动态方法连接到事件:
System::Delegate ^Connect(System::Object ^instance, MyHandlerClass ^handler)
{
auto method = CreateHandler();
assert(method);
// Создадим делегат
auto delegate = method->CreateDelegate(_event->EventHandlerType, handler);
assert(delegate);
// Добавим в список обработчиков
_event->AddEventHandler(instance, delegate);
return delegate;
}
代码是用 C++/CLI 编写的,但转换成 C# 相当容易。
思路和我最初的想法一样post,我只是添加了对象数组生成代码。
我需要处理来自 类 使用反射导入的事件。为此,我创建了一个具有所需参数类型的动态方法,然后将其转换为委托并添加为事件处理程序。
我在动态方法中唯一需要做的就是调用一个接收可变数量参数的编译方法。因此,我 'pack' 任何类型的任意数量的参数将其传递给编译方法。
麻烦来了:看来我需要在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 中),这样我就可以将参数压入堆栈并调用它?
如果没有,那么有没有办法在根本不使用 IL 代码生成器的情况下实现类似的结果(将参数从通过反射导入的事件传递给已编译的函数)?
您可以在 C++/CLI "ref class"(托管 class)中定义具有可变数量参数的函数。该语法要求将“...”标记添加到包含这些参数的数组之前,并且必须是函数声明中的最后一个参数。示例:
public: void SomeFunction ( int iRequiredArgument1,
int iRequiredArgument2,
... array<int>^ aiVariableArguments)
{
// function implementation
return;
}
如果变量参数的类型在编译时未知,使用
array<Object^>^
并在运行时对其元素进行类型转换。
使用 this 教程,我想出了以下代码:
System::Reflection::Emit::DynamicMethod ^CreateHandler()
{
// Get event handler type
auto delegateType = _event->EventHandlerType;
assert(delegateType);
auto invoke = delegateType->GetMethod("Invoke");
assert(invoke);
// Get return type
auto returnType = invoke->ReturnType;
// Get parameters list
auto delegateParameters = GetParameterTypes(invoke);
// First parameter should be a pointer to my handler class
auto parameters = gcnew array<System::Type ^> (delegateParameters->Length + 1);
parameters[0] = MyHandlerClass::typeid;
delegateParameters->CopyTo(parameters, 1);
// Create dynanic method for my handler class
auto handler = gcnew System::Reflection::Emit::DynamicMethod(
_event->Name + "Handler",
returnType,
parameters,
MyHandlerClass::typeid
);
// This method should be called
auto method = MyHandlerClass::typeid->GetMethod("GeneralEventHandler");
// Add method body
auto codeGen = handler->GetILGenerator();
// Get type of 'object[]'
auto arrayType = System::Object::typeid->MakeArrayType();
// Create local variable 'args' (at index 0) of type 'object[]'
auto localArgs = codeGen->DeclareLocal(arrayType);
// Create an array of arguments of required size
codeGen->Emit(System::Reflection::Emit::OpCodes::Ldc_I4, delegateParameters->Length); // Array size
codeGen->Emit(System::Reflection::Emit::OpCodes::Newarr, System::Object::typeid); // Creating array
codeGen->Emit(System::Reflection::Emit::OpCodes::Stloc_0); // Store to local variable at index 0 ('args')
// Fill array
for (int i = 0; i < delegateParameters->Length; i++)
{
// Store (i + 1) argument to array at index i
codeGen->Emit(System::Reflection::Emit::OpCodes::Ldloc_0); // Refer to array
codeGen->Emit(System::Reflection::Emit::OpCodes::Ldc_I4, i); // Move to index i
codeGen->Emit(System::Reflection::Emit::OpCodes::Ldarg_S, i + 1); // Refer to (i + 1) argument
// Is argument of simple type?
if (delegateParameters[i]->IsValueType)
{
// Pack argument
codeGen->Emit(System::Reflection::Emit::OpCodes::Box, delegateParameters[i]);
}
// Store element to array
codeGen->Emit(System::Reflection::Emit::OpCodes::Stelem_Ref);
}
// Now we put everything on stack:
// 'this' pointer
codeGen->Emit(System::Reflection::Emit::OpCodes::Ldarg_0);
// Nane of event (for identification)
codeGen->Emit(System::Reflection::Emit::OpCodes::Ldstr, _event->Name);
// Array of arguments
codeGen->Emit(System::Reflection::Emit::OpCodes::Ldloc_0);
// Now call the method
codeGen->Emit(System::Reflection::Emit::OpCodes::Call, method);
// And return
codeGen->Emit(System::Reflection::Emit::OpCodes::Ret);
return handler;
}
处理程序 class 如下所示:
ref class MyHandlerClass
{
public:
void GeneralEventHandler(System::String ^name, array<System::Object ^> ^params);
}
然后我只使用这个动态方法连接到事件:
System::Delegate ^Connect(System::Object ^instance, MyHandlerClass ^handler)
{
auto method = CreateHandler();
assert(method);
// Создадим делегат
auto delegate = method->CreateDelegate(_event->EventHandlerType, handler);
assert(delegate);
// Добавим в список обработчиков
_event->AddEventHandler(instance, delegate);
return delegate;
}
代码是用 C++/CLI 编写的,但转换成 C# 相当容易。
思路和我最初的想法一样post,我只是添加了对象数组生成代码。