【问题标题】:Searching for a more elegant way to turn a MethodInfo to a Func through Expressions寻找一种更优雅的方式通过表达式将 MethodInfo 转换为 Func
【发布时间】:2020-11-25 02:12:14
【问题描述】:

我有以下代码:

       public static Func<object[], object> CreateStaticFunc(this MethodInfo methodInfo)
    {
        var types = methodInfo.GetParameters().Select(p => p.ParameterType);



        if (methodInfo.ReturnType.Equals((typeof(void))))
        {
            Debug.LogError("Cannot create a Func from a method without a return type!");
            return null;
        }

        if (!methodInfo.IsStatic)
        {
            Debug.LogError("Cannot create a static Func from a non-static function!");
            return null;
        }

        var funcParams = methodInfo.GetParameters();

        var input = Expression.Parameter(typeof(object[]), "input");

        Func<object[], object> returned = null;

        //TODO research how to make this cleaner.
        switch (funcParams.Length)
        {
            case 0:
                returned = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo), input).Compile();
                break;
            case 1:
                returned = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo, 
                    Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(0)), funcParams[0].ParameterType)), input).Compile();
                break;
            case 2:
                returned = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo,
                    Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(0)), funcParams[0].ParameterType),
                    Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(1)), funcParams[1].ParameterType)), input).Compile();
                break;
            case 3:
                returned = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo,
                    Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(0)), funcParams[0].ParameterType),
                    Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(1)), funcParams[1].ParameterType),
                    Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(2)), funcParams[2].ParameterType)), input).Compile();
                break;
            case 4:
                returned = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo,
                    Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(0)), funcParams[0].ParameterType),
                    Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(1)), funcParams[1].ParameterType),
                    Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(2)), funcParams[2].ParameterType),
                    Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(3)), funcParams[3].ParameterType)), input).Compile();
                break;
            case 5:
                returned = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo,
                   Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(0)), funcParams[0].ParameterType),
                    Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(1)), funcParams[1].ParameterType),
                    Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(2)), funcParams[2].ParameterType),
                    Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(3)), funcParams[3].ParameterType),
                    Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(4)), funcParams[4].ParameterType)), input).Compile();
                break;
            case 6:
                returned = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo,
                   Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(0)), funcParams[0].ParameterType),
                    Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(1)), funcParams[1].ParameterType),
                    Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(2)), funcParams[2].ParameterType),
                    Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(3)), funcParams[3].ParameterType),
                    Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(4)), funcParams[4].ParameterType),
                    Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(5)), funcParams[5].ParameterType)), input).Compile();
                break;
        }
       

        return returned;
    }

}

这会将 MethodInfo 转换为 Func/delegate,以便可以缓存它以提高性能,因为 MethodInfo.Invoke 的性能不是很好。

代码似乎可以工作,但我无法编写更优雅/更简洁的方法,因为我不知道提前的参数数量。

有人可以帮忙吗?有没有可能?

【问题讨论】:

  • 也许您可以使用MethodCallExpression.Update() 为参数提供IEnumerable 表达式?

标签: c# reflection expression


【解决方案1】:

我猜你可以使用Expression.Lambda 的方法重载来完成它,即使你的方法没有参数。

像这样:

public static Func<object[], object> CreateStaticFunc(this MethodInfo methodInfo)
{
    var types = methodInfo.GetParameters().Select(p => p.ParameterType);

    if (methodInfo.ReturnType.Equals((typeof(void))))
    {
        Console.WriteLine("Cannot create a Func from a method without a return type!");
        return null;
    }

    if (!methodInfo.IsStatic)
    {
        Console.WriteLine("Cannot create a static Func from a non-static function!");
        return null;
    }

    var input = Expression.Parameter(typeof(object[]), "input");

    Func<object[], object> returned = GetParamatersExpressions(methodInfo, input);

    return returned;
}

private static Func<object[], object> GetParamatersExpressions(MethodInfo methodInfo, ParameterExpression input)
{
    Func<object[], object> result;
    var funcParams = methodInfo.GetParameters();

    IList<UnaryExpression> parms = new List<UnaryExpression>();
    for (int i = 0; i < funcParams.Length; i++)
        parms.Add(Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(i)), funcParams[i].ParameterType));

    result = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo, parms), input).Compile();
    return result;
}

希望对你有帮助

【讨论】:

    猜你喜欢
    • 2016-05-11
    • 1970-01-01
    • 1970-01-01
    • 2017-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多