[英]c# Call delegate with runtime generic types
我有一个在运行时创建的委托,因为我在设计时不知道它的参数类型。 代码如下:
var delegMethodType = typeof(Func<,,,>).MakeGenericType(type1, type2, type3, returnType);
如您所见,我使用 Func<,,,> 来表示 Func<T1, T2, T3, TReturn>。 它完美地工作。
之后,我以这种方式创建了我的委托:
var deleg = Delegate.CreateDelegate(delegMethodType, obj, methodInfo);
其中“delegMethodType”是我之前创建的委托的类型,“obj”是接收调用的对象,“methodInfo”是“obj”的反射方法。 这完美地工作。
现在,我需要调用委托,但由于“Delegate.CreateDelegate”返回一个基本委托,我无法使用参数调用它:
// due to this delegate was created in runtime VS.NET doesn't recognize
// that the delegate allows three parameters and returns a value
var result = deleg(val1, val2, val3);
我可以将委托与“deleg.DynamicInvoke()”一起使用,但它比使用其参数调用委托要慢。
如何使用三个参数调用此委托并获取其返回值?
可以直接调用 methodInfo,或者更复杂地使用 Linq Expressions 库来创建一个Func<object,object,...>
,它可以被调用来执行所有必需的强制转换。
请注意,我假设您在编译时确实知道参数的数量,正如您的问题所指示的Func<,,,>
用法。
这有点复杂,但基本上归结为使用Expression.Lambda(...).Compile()
创建将被调用的最终 func, Expression.Convert()
将对象转换为实际类型,以便Expression.Call()
可用于从我们的对象Expression.Parameter
调用具有正确类型的对象上的方法
这个例子展示了如何实现这两种方法。
using System;
using System.Linq.Expressions;
namespace SO
{
class SO69847110
{
public TR fn<T1,T2,T3,TR>(T1 p1, T2 p2, T3 p3)
{
if(typeof(TR) == typeof(string))
{
return (TR)(object)"Hello World";
}
return default(TR);
}
static void Main(string[] args)
{
var returnType = typeof(string);
var paramType = typeof(int);
var delegMethodType = typeof(Func<,,,>).
MakeGenericType(
paramType,
paramType,
paramType,
returnType
);
var methodInfo = typeof(SO69847110).GetMethod("fn")
.MakeGenericMethod(
paramType,
paramType,
paramType,
returnType
);
var obj = new SO69847110();
//var deleg = Delegate.CreateDelegate(delegMethodType, obj, methodInfo);
var methodInfoInvoke = methodInfo.Invoke(obj, new object[] { 0, 1, 2 });
Console.WriteLine(methodInfoInvoke);
var param1 = Expression.Parameter(typeof(object));
var param2 = Expression.Parameter(typeof(object));
var param3 = Expression.Parameter(typeof(object));
var convertedParameterExpressions =
new Expression[]
{
Expression.Convert(param1,paramType),
Expression.Convert(param2,paramType),
Expression.Convert(param3,paramType),
};
var expressed = Expression.Lambda<Func<object, object, object, object>>(
Expression.Call(
Expression.Convert(Expression.Constant(obj), obj.GetType()),
methodInfo,
convertedParameterExpressions
),
tailCall:false,
parameters: new[]
{
param1,
param2,
param3
}
).Compile();
var expressedInvoked = expressed.Invoke(obj, 0, 1, 2);
Console.WriteLine(expressedInvoked);
}
}
}
如果您想为多个对象调用相同的函数,那么您也可以将主题设置为Func
参数 - 只需在开始时将其设置为单独的Expression.Parameter(typeof(object))
,包括该参数而不是Expression.Constant
,也包括在Expression.Lambda
的参数列表中。 然后在调用时也将主题作为参数传入。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.