C# 从Linq表达式获取参数值

C# 从Linq表达式获取参数值,c#,linq,expression-trees,C#,Linq,Expression Trees,我有以下课程 public class MyClass { public bool Delete(Product product) { // some code. } } 现在我有一个助手类,看起来像这样 public class Helper<T, TResult> { public Type Type; public string Method; public Type[] ArgTypes; publi

我有以下课程

public class MyClass
{
    public bool Delete(Product product)
    {
        // some code.
    }
}
现在我有一个助手类,看起来像这样

public class Helper<T, TResult>
{

    public Type Type;
    public string Method;
    public Type[] ArgTypes;
    public object[] ArgValues;

    public Helper(Expression<Func<T, TResult>> 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 = ???
    }
}
这似乎是一个带有字段“product”的对象类型,但我相信一定有一个更简单的解决方案

任何建议

更新

只是为了澄清,我根据我想要实现的目标修改了我的代码。在我的真实word应用程序中,我已经有了一个类,该类执行相同的操作,但没有表达式树:

var helper = new Helper(typeof(MyClass), "Delete", 
    new Type[] { typeof(Product) }, new object[] {product}));
我的
Helper
的主要原因是在编译时检查方法签名是否有效

更新2

这是我当前的实现,有没有更好的方法来访问值,而不使用反射

public Helper(Expression<Func<T, TResult>> 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<object>();
    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();
}
公共助手(表达式)
{
var body=(System.Linq.Expressions.MethodCallExpression)expression.body;
该类型=类型(T);
this.Method=body.Method.Name;
this.ArgTypes=body.Arguments.Select(x=>x.Type).ToArray();
var值=新列表();
foreach(body.Arguments中的变量arg)
{
值。添加(
(((ConstantExpression)exp.Expression).Value.GetType()
.GetField(exp.Member.Name)
.GetValue(((ConstantExpression)exp.exp.Expression).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<Product, bool> deleteDelegate = p => instance.Delete(p);
Product product = new Product();
bool deleted = deleteDelegate(product);
MyClass实例=新建MyClass();
//以下行不能更改为var声明
//因为C#无法推断类型。
Func deleteDelegate=p=>instance.Delete(p);
产品=新产品();
bool deleted=deleteDelegate(产品);
或者,您正在尝试创建一个自动执行curry操作的帮助器

public class Helper<T>
    where T : new()
{
    public TResult Execute<TResult>(Func<T, TResult> methodLambda)
    {
        var instance = new T();
        return methodLamda(instance);
    }
}

public void Main()
{
    var helper = new Helper<MyClass>();
    var product = new Product();
    helper.Execute(x => x.Delete(product));
}
公共类助手
其中T:new()
{
公共TResult执行(Func methodLambda)
{
var instance=newt();
返回方法lamda(实例);
}
}
公共图书馆
{
var helper=newhelper();
var product=新产品();
执行(x=>x.Delete(product));
}

然而,我不得不说,这个问题看起来可疑地像是创建了一个帮助器类来处理WCF代理的生存期……你知道……就说……在这种情况下,我不会这么做……只是因为这种方法会将特定于WCF的代码泄漏到你的域中。

这种方法工作得很好。它返回表达式的参数类型和值>

    private static KeyValuePair<Type, object>[] ResolveArgs<T>(Expression<Func<T, object>> expression)
    {
        var body = (System.Linq.Expressions.MethodCallExpression)expression.Body;
        var values = new List<KeyValuePair<Type, object>>();

        foreach (var argument in body.Arguments)
        {
            var exp = ResolveMemberExpression(argument);
            var type = argument.Type;

            var value = GetValue(exp);

            values.Add(new KeyValuePair<Type, object>(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();
        }
    }
private static KeyValuePair[]解析参数(表达式)
{
var body=(System.Linq.Expressions.MethodCallExpression)expression.body;
var值=新列表();
foreach(body.Arguments中的var参数)
{
var exp=ResolveMemberExpression(参数);
变量类型=参数.type;
var值=GetValue(exp);
添加(新的KeyValuePair(类型、值));
}
返回值。ToArray();
}
公共静态MemberExpression ResolveMemberExpression(表达式)
{
if(表达式为MemberExpression)
{
返回(MemberExpression)表达式;
}
else if(表达式为一元表达式)
{
//如果涉及强制转换,则表达式不是x=>x.FieldName,而是x=>Convert(x.FieldName)
返回(MemberExpression)((一元表达式)表达式)。操作数;
}
其他的
{
抛出新的NotSupportedException(expression.ToString());
}
}
私有静态对象GetValue(MemberExpression)
{
//表达式为常量表达式或字段表达式
if(exp.Expression为常量表达式)
{
返回(((ConstantExpression)exp.Expression).Value)
.GetType()
.GetField(exp.Member.Name)
.GetValue(((ConstantExpression)exp.exp.Expression).Value);
}
else if(exp.Expression为MemberExpression)
{
返回GetValue((MemberExpression)exp.Expression);
}
其他的
{
抛出新的NotImplementedException();
}
}

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

var values = new List<object>();
foreach(var arg in body.Arguments)
{
    var value = Expression.Lambda(argument).Compile().DynamicInvoke();
    values.Add(value);
}
this.ArgValues = values.ToArray();
var值=新列表();
foreach(body.Arguments中的变量arg)
{
var value=Expression.Lambda(参数).Compile().DynamicInvoke();
增加(价值);
}
this.ArgValues=values.ToArray();

有什么原因不能编译表达式并执行它吗?或者这只是为了学习表达式树而胡闹?@rbev+1比我强。见鬼,为什么不直接调用
Delete(product)
?@Aron,这只是一个例子,助手将被实例化并传递。@rbev:他甚至不需要表达式。一个简单的委托就足够了。OP需要澄清他的约束条件,然后才能回答这一问题。这完全不是我想要的。参数应该是foreach中声明的arg。在文章中提到编译不是快速操作。我想你的意思是(添加了一个缺少的类型参数):
KeyValuePair[]ResolveArgs(Expression Expression)
@PatrickKoorevaar你是对的。在我的代码中,这个方法在一个带有类型参数的泛型类中,所以我错过了这一部分。我想我在这里显示了我的无知。为什么需要对
constantExpression.Value
的结果调用
GetType().GetField([name]).GetValue(constantExp.Value)
?是
var v=1的
.Value
属性表达式不是正在分配的1吗?是由
constantExpression.value
返回的表达式的对象表示形式上的字段中的文字1值吗?已经有一段时间了,但如果我没记错的话,表达式
x=>x.Delete(product)
由我从
获得的对象表示(constantExpression)exp.Expression).值
且此对象具有f
    private static KeyValuePair<Type, object>[] ResolveArgs<T>(Expression<Func<T, object>> expression)
    {
        var body = (System.Linq.Expressions.MethodCallExpression)expression.Body;
        var values = new List<KeyValuePair<Type, object>>();

        foreach (var argument in body.Arguments)
        {
            var exp = ResolveMemberExpression(argument);
            var type = argument.Type;

            var value = GetValue(exp);

            values.Add(new KeyValuePair<Type, object>(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<object>();
foreach(var arg in body.Arguments)
{
    var value = Expression.Lambda(argument).Compile().DynamicInvoke();
    values.Add(value);
}
this.ArgValues = values.ToArray();