简体   繁体   English

获取包含Lambda表达式中参数值的对象数组

[英]Getting object array containing the values of parameters from a Lambda Expression

I'm writing a function that will be passed a lambda expression, and I want to convert the parameters that the lambda takes to an object array. 我正在编写一个将传递lambda表达式的函数,我想将lambda带来的参数转换为对象数组。

The only way I've been able to do it is with code I borrowed from here , and my function looks something like this: 我能够做到的唯一方法就是从这里借来的代码,我的函数看起来像这样:

public class MyClassBase<T> where T : class 
{
    protected void DoStuff(Expression<Action<T>> selector)
    {
        ReadOnlyCollection<Expression> methodArgumentsCollection = (selector.Body as MethodCallExpression).Arguments;
        object[] methodArguments = methodArgumentsCollection.Select(c => Expression.Lambda(c is UnaryExpression ?
                        ((UnaryExpression)c).Operand : c)
                        .Compile()
                        .DynamicInvoke())
                        .ToArray();
        // do more stuff with methodArguments
    }       
}

interface IMyInterface
{
    void MethodSingleParam(string param1);
}

class MyClass : MyClassBase<IMyInterface>
{
    void MakeCall()
    {
        DoStuff(x => x.MethodSingleParam("abc"));
    }
}

Is there a neater way of doing this? 这样做有一个更简洁的方法吗? It seems like overkill having to compile and invoke the lambda when all I want is the parameter values. 当我想要的只是参数值时,似乎有必要编译和调用lambda。

Well, the most natural thing to do with a lambda, is to run it. 嗯,使用lambda最自然的事情就是运行它。 Indeed, some of the convenience they offer is from being able to use them in places where we don't need to be as explicit about the parameters used than if we used a method instead. 实际上,他们提供的一些便利是因为能够在我们不需要明确使用参数的地方使用它们,而不是使用方法。

Obtaining the parameters, isn't the "natural" thing to do, it's peeking into what's going on in the expression. 获取参数不是“自然”的事情,它正在窥视表达式中正在发生的事情。 Really, it's not that distant from the sort of thing that's done with reflection. 真的,与反射所做的事情并没有那么遥远。

It's a sign of a good language that the more natural things are the easiest things to do. 这是一种良好语言的标志,更自然的东西是最容易做的事情。 Obviously, the easier one can make anything else, the better, but this doesn't seem like overkill to me. 显然,越容易做出其他事情,越好,但这对我来说似乎并不过分。

Well, in the general case, a Compile() is pretty much all you can do. 好吧,在一般情况下,Compile()几乎就是你所能做的。 Imagine if you'd call 想象一下,如果你打电话

DoStuff(x => x.MethodSingleParam(Math.Abs(a.SomeMethod())));

How would you handle that? 你会怎么处理? You'd need to execute Math.Abs(a.SomeMethod()) to find out what value it returns. 您需要执行Math.Abs(a.SomeMethod())以找出它返回的值。 This also shows you that this type of introspection is rather brittle (no guarantees that a second call to a.SomeMethod() returns the same value). 这也向你展示了这种类型的内省是相当脆弱的(不能保证第二次调用a.SomeMethod()返回相同的值)。

However, when the parameter passed is a constant (represented by a ConstantExpression ), you will be certain, and you don't need a Compile(): 但是,当传递的参数是常量(由ConstantExpression表示)时,您确定,并且您不需要Compile():

protected void DoStuff(Expression<Action<T>> selector)
{
    ReadOnlyCollection<Expression> methodArgumentsCollection = 
                  (selector.Body as MethodCallExpression).Arguments;
    object[] methodArguments = methodArgumentsCollection.Select(c =>
              c is ConstantExpression 
              ? ((ConstantExpression) c).Value 
              : ... ).ToArray();
    // do more stuff with methodArguments
}

The check for ConstantExpression above ensures that the following code will not call Compile(): 检查上面的ConstantExpression可确保以下代码不会调用Compile():

DoStuff(x => x.MethodSingleParam("abc"));

Like I said, compiling is not really a safe thing to do here, so you might as well return null or throw an error in such cases. 就像我说的那样,编译在这里做起来并不是一件安全的事情,所以你不妨在这种情况下返回null或抛出错误。 (Which is why I've placed a ... here; you can put your Compile back here if you need to.) (这就是为什么我放置了...这里;如果你需要,可以把你的编译放回到这里。)

Could your code look more like this? 您的代码可能看起来更像这样吗?

public class MyClassBase<T>
{
    protected void DoStuff(params T[] arguments)
    {
        // do more stuff with arguments
    }
}

class MyClass : MyClassBase<string>
{
    void MakeCall()
    {
        DoStuff("abc");
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM