2010-09-22 94 views
7

基本上我想这样的调用方法的参数的值:从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 
} 

回答

17

那么,你就需要钻到表达,发现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); 
     } 
    } 
} 
+2

@Omu:但是你真的想要破解的代码 - 只在执行时 - 如果你使用了*任何其他形式的lambda表达式?如果你总是调用Save,为什么不直接传入参数呢? – 2010-09-22 06:13:59

+0

@Jon Skeet,我打算用不同的参数调用很多其他方法,我不知道我只能用不变的值调用,我想我将不得不进一步调查这个 – Omu 2010-09-22 06:35:20

+0

@Omu:这只是将代码倾倒在一个没有解释的问题中的问题。我说过*它很脆弱,一旦你开始使用任何其他模式就会失败...... – 2010-09-22 06:56:55

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) 
    { 
    } 
} 
+0

我喜欢这个事实,我们的演示代码几乎完全相同:) – 2010-09-22 06:18:49

+0

@Jon,I没有时间做任何错误检查寿:( – 2010-09-22 06:20:32

+0

是的,这是非常脆弱的任何方式,我怀疑这是最好的方法,但我们没有太多的上下文 – 2010-09-22 06:27:22

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,时间在这里是我做的:

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

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

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

使用关键字动态。