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

时间:2010-09-22 06:04:09

标签: c# .net lambda

基本上我想得到一个被调用方法的参数值,如下所示:

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 个答案:

答案 0 :(得分:18)

好吧,你需要钻进表达式,找到MethodCallExpression,然后查看它的参数。请注意,我们没有o的值,所以我们必须假设方法的参数不依赖于它。另外我们还假设lambda表达式只依赖于MethodCallExpression

编辑:好的,这是一个编辑版本,用于评估参数。但是,它假设你真正在参数中使用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);
        }
    }
}

答案 1 :(得分:4)

正如乔恩所说,你可以查看表达式是否为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)
    {
    }
}

答案 2 :(得分:2)

以下是一些设计用于处理任何表达式的代码 - 从某种意义上说,它没有从根本上假设您传入的是方法调用表达式。但是,它并不完整。你将不得不填补其余部分。

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时,我就会这样做:

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

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

答案 3 :(得分:2)

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

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;
}

答案 4 :(得分:0)

  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();
        }

使用关键字动态。