代码之家  ›  专栏  ›  技术社区  ›  Omu

从lambda表达式方法参数获取结束值

  •  8
  • Omu  · 技术社区  · 15 年前

    基本上我想得到这样一个被调用方法的参数值:

    var x = 1;
    var a = 2;
    var b = 3;
    Do<HomeController>(o => o.Save(x, "Jimmy", a+b+5, Math.Sqrt(81)));
    
    public static void Do<T>(Expression<Action<T>> expression) where T : Controller
    {
      // get the values 1,Jimmy,10,9 here
    }
    
    5 回复  |  直到 12 年前
        1
  •  18
  •   Jon Skeet    15 年前

    好吧,你需要钻取表达式,找到 MethodCallExpression ,然后查看它的参数。注意,我们没有 o ,所以我们必须假设方法的参数不依赖于它。另外,我们仍然假设lambda表达式仅仅依赖于它是 方法调用表达式 ?

    编辑:好的,这里有一个编辑版本,它评估参数。但是,它假设你是 在参数中使用lambda表达式参数(这是 new object[1] 是关于-它有效地提供了一个空参数)。

    using System;
    using System.Linq.Expressions;
    
    class Foo
    {
        public void Save(int x, string y, int z, double d)
        {
        }
    }
    
    class Program
    {
        static void Main()
        {
            var x = 1;
            var a = 2;
            var b = 3;
            ShowValues<Foo>(o => o.Save(x, "Jimmy", a + b + 5, Math.Sqrt(81)));
        }
    
        static void ShowValues<T>(Expression<Action<T>> expression)
        {
            var call = expression.Body as MethodCallExpression;
            if (call == null)
            {
                throw new ArgumentException("Not a method call");
            }
            foreach (Expression argument in call.Arguments)
            {
                LambdaExpression lambda = Expression.Lambda(argument, 
                                                            expression.Parameters);
                Delegate d = lambda.Compile();
                object value = d.DynamicInvoke(new object[1]);
                Console.WriteLine("Got value: {0}", value);
            }
        }
    }
    
        2
  •  4
  •   Rohan West    15 年前

    如jon所说,您可以检查表达式是否是methodCallExpression

    class Program
    {
        static void Main(string[] args)
        {
            Program.Do<Controller>(c => c.Save(1, "Jimmy"));
        }
    
        public static void Do<T>(Expression<Action<T>> expression) where T : Controller
        {
            var body = expression.Body as MethodCallExpression;
            if (body != null)
            {
                foreach (var argument in body.Arguments)
                {
                    var constant = argument as ConstantExpression;
                    if (constant != null)
                    {
                        Console.WriteLine(constant.Value);
                    }
                }
            }
        }
    }
    
    public class Controller
    {
        public void Save(int id, string name)
        {
        }
    }
    
        3
  •  2
  •   Timwi    15 年前

    这里有一些设计用于任何表达式的代码,从根本上来说,它并不假设您正在传递一个方法调用表达式。然而,它并不完整。你得把剩下的填上。

    public static IEnumerable<object> ExtractConstants<T>(
            Expression<Action<T>> expression)
    {
        return extractConstants(expression);
    }
    private static IEnumerable<object> extractConstants(Expression expression)
    {
        if (expression == null)
            yield break;
    
        if (expression is ConstantExpression)
            yield return ((ConstantExpression) expression).Value;
    
        else if (expression is LambdaExpression)
            foreach (var constant in extractConstants(
                    ((LambdaExpression) expression).Body))
                yield return constant;
    
        else if (expression is UnaryExpression)
            foreach (var constant in extractConstants(
                    ((UnaryExpression) expression).Operand))
                yield return constant;
    
        else if (expression is MethodCallExpression)
        {
            foreach (var arg in ((MethodCallExpression) expression).Arguments)
                foreach (var constant in extractConstants(arg))
                    yield return constant;
            foreach (var constant in extractConstants(
                    ((MethodCallExpression) expression).Object))
                yield return constant;
        }
    
        else
            throw new NotImplementedException();
    }
    

    对于您提到的案例,这已经起作用了:

    // Prints:
    // Jimmy (System.String)
    // 1 (System.Int32)
    foreach (var constant in Ext.ExtractConstants<string>(
            str => Console.WriteLine("Jimmy", 1)))
        Console.WriteLine("{0} ({1})", constant.ToString(),
                                       constant.GetType().FullName);
    

    对于使用其他类型表达式节点的更复杂的lambda表达式,必须增量地扩展上述代码。每次你使用它,它就会抛出一个 NotImplementedException ,我要做的是:

    • 在调试器中打开“监视”窗口
    • 看看 expression 变量及其类型
    • 添加必要的代码以处理该表达式类型

    随着时间的推移,该方法将变得越来越完整。

        4
  •  2
  •   Simon Achmüller    12 年前

    我的普遍答案如下。我希望它能帮助你和其他人。

    var dict = new Dictionary<string, object>();
    var parameterExpressions = methodCallExpr.Arguments;
    foreach (var param in method.GetParameters())
    {
        var parameterExpression = parameterExpressions[counter];
        var paramValueAccessor = Expression.Lambda(parameterExpression);
        var paramValue = paramValueAccessor.Compile().DynamicInvoke();
        dict[param.Name] = paramValue;
    }
    
        5
  •  0
  •   Walk Walk    12 年前
      public override IQueryable<Image> FindAll(System.Linq.Expressions.Expression<Func<Image, dynamic>> Id)
            {
                dynamic currentType = Id.Parameters[0];
                var id = currentType.Type.GUID;
                var result = (_uniwOfWork as UnitOfWork).uspGetImages(id.ToString());
                return FindAll();
            }
    

    使用关键字dynamic。