为任何方法创建Func或Action(在c#中使用反射) [英] Create Func or Action for any method (using reflection in c#)

查看:193
本文介绍了为任何方法创建Func或Action(在c#中使用反射)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的应用程序可以根据设置动态加载dll. 从数据库(文件,类和方法名称)中获取.为了方便,加快和减少反射的使用,我希望有一个缓存....

My application works with loading dll's dynamically, based on settings from the database (file, class and method names). To facilitate, expedite and reduce the use of reflection I would like to have a cache....

遵循以下想法:

 MethodInfo.Invoke

没有什么表现力(反射性能-创建委托(属性C#) ) 我想翻译任何对方法的调用.我想到的是这样的东西:

Is nothing performative ( Reflection Performance - Create Delegate (Properties C#)) I would like to translate any call to methods. I thought of something that would work like this:

public static T Create<T>(Type type, string methodName) // or
public static T Create<T>(MethodInfo info) // to use like this:
var action = Create<Action<object>>(typeof(Foo), "AnySetValue");

一个要求是所有参数都可以是对象.

One requirement is that all the parameters, can be object.

我正在尝试处理表达式,到目前为止,我有这样的东西:

I'm trying to deal with expressions, and so far I have something like this:

    private void Sample()
    {
        var assembly = Assembly.GetAssembly(typeof(Foo));

        Type customType = assembly.GetType("Foo");

        var actionMethodInfo = customType.GetMethod("AnyMethod");
        var funcMethodInfo = customType.GetMethod("AnyGetString");
        var otherActionMethod = customType.GetMethod("AnySetValue");
        var otherFuncMethodInfo = customType.GetMethod("OtherGetString");

        var foo = Activator.CreateInstance(customType);
        var actionAccessor = (Action<object>)BuildSimpleAction(actionMethodInfo);
        actionAccessor(foo);

        var otherAction = (Action<object, object>)BuildOtherAction(otherActionMethod);
        otherAction(foo, string.Empty);

        var otherFuncAccessor = (Func<object, object>)BuildFuncAccessor(funcMethodInfo);
        otherFuncAccessor(foo);

        var funcAccessor = (Func<object,object,object>)BuildOtherFuncAccessor(otherFuncMethodInfo);
        funcAccessor(foo, string.Empty);
    }

    static Action<object> BuildSimpleAction(MethodInfo method)
    {
        var obj = Expression.Parameter(typeof(object), "o");

        Expression<Action<object>> expr =
            Expression.Lambda<Action<object>>(
                Expression.Call(
                    Expression.Convert(obj, method.DeclaringType),
                    method), obj);

        return expr.Compile();
    }

    static Func<object, object> BuildFuncAccessor(MethodInfo method)
    {
        var obj = Expression.Parameter(typeof(object), "o");

        Expression<Func<object, object>> expr =
            Expression.Lambda<Func<object, object>>(
                Expression.Convert(
                    Expression.Call(
                        Expression.Convert(obj, method.DeclaringType),
                        method),
                    typeof(object)),
                obj);

        return expr.Compile();

    }

    static Func<object, object, object> BuildOtherFuncAccessor(MethodInfo method)
    {
        var obj = Expression.Parameter(typeof(object), "o");
        var value = Expression.Parameter(typeof(object));

        Expression<Func<object, object, object>> expr =
            Expression.Lambda<Func<object, object, object>>(
                    Expression.Call(
                        Expression.Convert(obj, method.DeclaringType),
                        method,
                        Expression.Convert(value, method.GetParameters()[0].ParameterType)), 
                        obj, value);

        return expr.Compile();

    }

    static Action<object, object> BuildOtherAction(MethodInfo method)
    {
        var obj = Expression.Parameter(typeof(object), "o");
        var value = Expression.Parameter(typeof(object));

        Expression<Action<object, object>> expr =
            Expression.Lambda<Action<object, object>>(
                Expression.Call(
                    Expression.Convert(obj, method.DeclaringType),
                    method,
                    Expression.Convert(value, method.GetParameters()[0].ParameterType)),
                obj,
                value);

        return expr.Compile();
    }


public class Foo
{
    public void AnyMethod() {}

    public void AnySetValue(string value) {}

    public string AnyGetString()
    {            return string.Empty;        }

    public string OtherGetString(string value)
    {            return string.Empty;        }
}

有什么方法可以简化此代码? (我相信可以仅使用通用方法来创建方法.)当您有3、4、5的参数时,就像我一样吗?

Is there any way to simplify this code? (I believe it is possible to create a method only using generic..) And when you have 3, 4, 5, any parameters as I do?

我在想,如果有这样的事情怎么办:

I was thinking, what if there was something like this:

https://codereview.stackexchange.com/questions/1070 /generic-advanced-delegate-createdelegate-使用表达式树

但是我将有一个以上的参数(在动作或功能上),该参数(第一个参数)是一个对象要执行的. 这可能吗?

but I'll have more one parameter (in action or function), this parameter (first parameter) an object to perform. Is this possible?

推荐答案

我制作了一个满足您所有要求的示例程序(我认为!)

I have made a sample program that fulfills all your requirements (I think!)

class Program
{
    class MyType
    {
        public MyType(int i) { this.Value = i; }

        public void SetValue(int i) { this.Value = i; }

        public void SetSumValue(int a, int b) { this.Value = a + b; }

        public int Value { get; set; }
    }

    public static void Main()
    {
        Type type = typeof(MyType);

        var mi = type.GetMethod("SetValue");

        var obj1 = new MyType(1);
        var obj2 = new MyType(2);

        var action = DelegateBuilder.BuildDelegate<Action<object, int>>(mi);

        action(obj1, 3);
        action(obj2, 4);

        Console.WriteLine(obj1.Value);
        Console.WriteLine(obj2.Value);

        // Sample passing a default value for the 2nd param of SetSumValue.
        var mi2 = type.GetMethod("SetSumValue");

        var action2 = DelegateBuilder.BuildDelegate<Action<object, int>>(mi2, 10);

        action2(obj1, 3);
        action2(obj2, 4);

        Console.WriteLine(obj1.Value);
        Console.WriteLine(obj2.Value);

        // Sample without passing a default value for the 2nd param of SetSumValue.
        // It will just use the default int value that is 0.
        var action3 = DelegateBuilder.BuildDelegate<Action<object, int>>(mi2);

        action3(obj1, 3);
        action3(obj2, 4);

        Console.WriteLine(obj1.Value);
        Console.WriteLine(obj2.Value);
    }
}

DelegateBuilder类:

DelegateBuilder class:

public class DelegateBuilder
{
    public static T BuildDelegate<T>(MethodInfo method, params object[] missingParamValues)
    {
        var queueMissingParams = new Queue<object>(missingParamValues);

        var dgtMi = typeof(T).GetMethod("Invoke");
        var dgtRet = dgtMi.ReturnType;
        var dgtParams = dgtMi.GetParameters();

        var paramsOfDelegate = dgtParams
            .Select(tp => Expression.Parameter(tp.ParameterType, tp.Name))
            .ToArray();

        var methodParams = method.GetParameters();

        if (method.IsStatic)
        {
            var paramsToPass = methodParams
                .Select((p, i) => CreateParam(paramsOfDelegate, i, p, queueMissingParams))
                .ToArray();

            var expr = Expression.Lambda<T>(
                Expression.Call(method, paramsToPass),
                paramsOfDelegate);

            return expr.Compile();
        }
        else
        {
            var paramThis = Expression.Convert(paramsOfDelegate[0], method.DeclaringType);

            var paramsToPass = methodParams
                .Select((p, i) => CreateParam(paramsOfDelegate, i + 1, p, queueMissingParams))
                .ToArray();

            var expr = Expression.Lambda<T>(
                Expression.Call(paramThis, method, paramsToPass),
                paramsOfDelegate);

            return expr.Compile();
        }
    }

    private static Expression CreateParam(ParameterExpression[] paramsOfDelegate, int i, ParameterInfo callParamType, Queue<object> queueMissingParams)
    {
        if (i < paramsOfDelegate.Length)
            return Expression.Convert(paramsOfDelegate[i], callParamType.ParameterType);

        if (queueMissingParams.Count > 0)
            return Expression.Constant(queueMissingParams.Dequeue());

        if (callParamType.ParameterType.IsValueType)
            return Expression.Constant(Activator.CreateInstance(callParamType.ParameterType));

        return Expression.Constant(null);
    }
}

工作原理

核心是 BuildDelegate 方法:

static T BuildDelegate<T>(MethodInfo method)

  • T是您要创建的委托类型.
  • 方法是要由生成的委托调用的方法的MethodInfo.

示例调用:var action = BuildDelegate<Action<object, int>>(mi);

参数规则:

  • 如果传递的方法是实例方法,则生成的委托的第一个参数将接受包含方法本身的对象的实例.所有其他参数都将传递给该方法.

  • If the method passed is an instance method, first parameter of the generated delegate will accept the instance of the object, that contains the method itself. All other parameter will be passed to the method.

如果传递的方法是静态方法,则生成的委托的所有参数都将传递给该方法.

If the method passed is a static method, then all parameters of the generated delegate will be passed to the method.

缺少的参数将传递默认值.

Missing parameters will have default values passed.

这篇关于为任何方法创建Func或Action(在c#中使用反射)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆