从Linq表达式获取参数值

我有以下课程

public class MyClass { public bool Delete(Product product) { // some code. } } 

现在我有一个看起来像这样的助手类

 public class Helper { public Type Type; public string Method; public Type[] ArgTypes; public object[] ArgValues; public Helper(Expression<Func> expression) { var body = (System.Linq.Expressions.MethodCallExpression)expression.Body; this.Type = typeof(T); this.Method = body.Method.Name; this.ArgTypes = body.Arguments.Select(x => x.Type).ToArray(); this.ArgValues = ??? } } 

这个想法是从某个地方使用这个代码:

 // I am returning a helper somewhere public Helper GetMethod() { var product = GetProduct(1); return new Helper(x => x.Delete(product)); } // some other class decides, when to execute the helper // Invoker already exists and is responsible for executing the method // that is the main reason I don't just comile and execute my Expression public bool ExecuteMethod(Helper helper) { var instance = new MyClass(); var Invoker = new Invoker(helper.Type, helper.Method, helper.ArgTypes, helper.ArgValues); return (bool)Invoker.Invoke(instance); } 

我被困的地方是如何从表达式本身中提取参数。

我找到了这种方式

 ((ConstantExpression)((MemberExpression)body.Arguments[0]).Expression).Value 

这似乎是一个带有“产品”字段的对象类型,但我相信必须有一个更简单的解决方案。

有什么建议。

更新

为了澄清,我根据我想要的内容修改了我的代码。 在我真正的单词应用程序中,我已经有一个类,但没有表达式树:

 var helper = new Helper(typeof(MyClass), "Delete", new Type[] { typeof(Product) }, new object[] {product})); 

我的Helper主要原因是如果方法签名有效,则进行编译时检查。

更新2

这是我目前的实现,是否有更好的方法来访问值,而不使用reflection?

 public Helper(Expression<Func> expression) { var body = (System.Linq.Expressions.MethodCallExpression)expression.Body; this.Type = typeof(T); this.Method = body.Method.Name; this.ArgTypes = body.Arguments.Select(x => x.Type).ToArray(); var values = new List(); foreach(var arg in body.Arguments) { values.Add( (((ConstantExpression)exp.Expression).Value).GetType() .GetField(exp.Member.Name) .GetValue(((ConstantExpression)exp.Expression).Value); ); } this.ArgValues = values.ToArray(); } 

这种方法效果很好。 它返回Expression>的参数类型和值

  private static KeyValuePair[] ResolveArgs(Expression> expression) { var body = (System.Linq.Expressions.MethodCallExpression)expression.Body; var values = new List>(); foreach (var argument in body.Arguments) { var exp = ResolveMemberExpression(argument); var type = argument.Type; var value = GetValue(exp); values.Add(new KeyValuePair(type, value)); } return values.ToArray(); } public static MemberExpression ResolveMemberExpression(Expression expression) { if (expression is MemberExpression) { return (MemberExpression)expression; } else if (expression is UnaryExpression) { // if casting is involved, Expression is not x => x.FieldName but x => Convert(x.Fieldname) return (MemberExpression)((UnaryExpression)expression).Operand; } else { throw new NotSupportedException(expression.ToString()); } } private static object GetValue(MemberExpression exp) { // expression is ConstantExpression or FieldExpression if (exp.Expression is ConstantExpression) { return (((ConstantExpression)exp.Expression).Value) .GetType() .GetField(exp.Member.Name) .GetValue(((ConstantExpression)exp.Expression).Value); } else if (exp.Expression is MemberExpression) { return GetValue((MemberExpression)exp.Expression); } else { throw new NotImplementedException(); } } 

您可以编译参数表达式,然后调用它来计算值:

 var values = new List(); foreach(var arg in body.Arguments) { var value = Expression.Lambda(argument).Compile().DynamicInvoke(); values.Add(value); } this.ArgValues = values.ToArray(); 

以下是使用lambda创建委托的示例。 使用名为closure的C#特性将对象实例封装到委托中。

 MyClass instance = new MyClass(); //This following line cannot be changed to var declaration //since C# can't infer the type. Func deleteDelegate = p => instance.Delete(p); Product product = new Product(); bool deleted = deleteDelegate(product); 

或者,您正在尝试创建一个自动化Currys的助手。

 public class Helper where T : new() { public TResult Execute(Func methodLambda) { var instance = new T(); return methodLamda(instance); } } public void Main() { var helper = new Helper(); var product = new Product(); helper.Execute(x => x.Delete(product)); } 

但是我不得不说这个问题看起来很像创建一个Helper类来处理WCF代理的生命周期….你知道……只是说…在这种情况下这不是我怎么会接近这个…只是因为这种方法将WCF特定代码泄漏到您的域中。