繁体   English   中英

c# 使用运行时泛型类型调用委托

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM