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

发布于 2024-09-15 18:08:55 字数 1126 浏览 3 评论 0原文

我正在编写一个将传递 lambda 表达式的函数,并且我想将 lambda 所采用的参数转换为对象数组。

我能够做到这一点的唯一方法是使用我从 这里,我的函数看起来像这样:

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 似乎有点矫枉过正。

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.

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

紫瑟鸿黎 2024-09-22 18:08:55

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

获取参数并不是“自然”的事情,而是要了解表达式中发生的情况。确实,这与通过反射完成的事情并没有那么遥远。

越自然的事情就越容易做,这是良好语言的标志。显然,做其他事情越容易越好,但这对我来说似乎并不过分。

Well, the most natural thing to do with a lambda, is to run it. 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.

莳間冲淡了誓言ζ 2024-09-22 18:08:55

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

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

您会如何处理?您需要执行 Math.Abs​​(a.SomeMethod()) 来找出它返回的值。这也表明这种类型的内省相当脆弱(不能保证第二次调用 a.SomeMethod() 返回相同的值)。

但是,当传递的参数是常量(由 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
}

:上面的constantExpression确保下面的代码不会调用Compile():

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

就像我说的,在这里编译并不是一件真正安全的事情,所以在这种情况下你最好返回null或抛出错误。 (这就是为什么我在这里放置了 ... ;如果需要,您可以将您的编译放回此处。)

Well, in the general case, a Compile() is pretty much all you can do. 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. 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).

However, when the parameter passed is a constant (represented by a ConstantExpression), you will be certain, and you don't need a 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():

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. (Which is why I've placed a ... here; you can put your Compile back here if you need to.)

送君千里 2024-09-22 18:08:55

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

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

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

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");
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文