c# 使用运行时泛型类型调用委托
Posted
技术标签:
【中文标题】c# 使用运行时泛型类型调用委托【英文标题】:c# Call delegate with runtime generic types 【发布时间】:2021-12-19 03:31:09 【问题描述】:我有一个在运行时创建的委托,因为我在设计时不知道它的参数类型。代码如下:
var delegMethodType = typeof(Func<,,,>).MakeGenericType(type1, type2, type3, returnType);
如您所见,我使用 Func 来表示 Func
之后,我以这种方式创建我的委托:
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()”一起使用,但它比使用其参数调用委托要慢。
如何用三个参数调用这个委托并获得它的返回值?
【问题讨论】:
除非您可以限制参数的类型,否则您必须使用反射。这已经有多慢了?也许您应该使用已编译的Expression
或动态方法
一点测试表明,在缓存的delegMethodType.GetMethod("Invoke")
上调用Invoke
实际上更快,请参阅dotnetfiddle.net/VgJpqo。故事寓意:race your horses
【参考方案1】:
可以直接调用 methodInfo,或者更复杂地使用 Linq 表达式库来创建一个 Func<object,object,...>
,可以调用它来执行所有必需的转换。
请注意,我假设您在编译时确实知道参数的数量,正如您的问题所表明的 Func<,,,>
用法。
这有点复杂,但基本上归结为使用Expression.Lambda(...).Compile()
创建将被调用的最终函数,Expression.Convert()
将对象转换为实际类型,以便可以使用Expression.Call()
调用方法在我们的对象Expression.Parameter
s中具有正确类型的对象上
这个例子展示了如何实现这两种方法。
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
的参数列表中。然后在调用的时候也把subject作为参数传入。
【讨论】:
以上是关于c# 使用运行时泛型类型调用委托的主要内容,如果未能解决你的问题,请参考以下文章