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

时间:2010-08-23 10:54:13

标签: c# lambda

我正在编写一个将传递lambda表达式的函数,我想将lambda带来的参数转换为对象数组。

我能够做到的唯一方法是使用here借来的代码,我的函数看起来像这样:

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

有更简洁的方法吗?当我想要的只是参数值时,似乎有必要编译和调用lambda。

3 个答案:

答案 0 :(得分:2)

嗯,使用lambda最自然的事情就是运行它。实际上,他们提供的一些便利是能够在我们不需要明确使用参数的地方使用它们,而不是使用方法。

获取参数不是“自然”的事情,它正在窥视表达式中正在发生的事情。真的,与反射所做的事情并没有那么遥远。

这是一种良好语言的标志,更自然的事情是最容易做的事情。显然,越容易做出其他事情,越好,但这对我来说似乎有些过分。

答案 1 :(得分:1)

嗯,在一般情况下,Compile()几乎就是你所能做的。想象一下,如果你打电话

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

你会怎么处理?您需要执行Math.Abs(a.SomeMethod())以找出它返回的值。这也向您展示了这种类型的内省是相当脆弱的(不能保证第二次调用a.SomeMethod()返回相同的值)。

但是,当传递的参数是常量(由 ConstantExpression 表示)时,确定,并且您不需要Compile():< / p>

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
}

检查上面的 ConstantExpression 可确保以下代码不会调用Compile():

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

就像我说的那样,编译在这里做起来并不是一件安全的事情,所以你不妨在这种情况下返回null或抛出错误。 (这就是我在这里放置...的原因;如果需要,可以将Compile放回到这里。)

答案 2 :(得分:1)

你的代码看起来更像这样吗?

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

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