【问题标题】:How to call a lambda using LINQ expression trees in C# / .NET如何在 C# / .NET 中使用 LINQ 表达式树调用 lambda
【发布时间】:2015-06-18 13:16:34
【问题描述】:

我想使用表达式树来动态创建调用 lambda 的方法。以下代码在第一次调用 ComposeLambda 函数时运行良好,但第二次调用失败并显示以下错误消息。

为调用方法“Int32”提供的参数数量不正确 lambda_method(System.Runtime.CompilerServices.Closure, Int32)'

{
    Func<int, int> innerLambda = i => i + 1;    
    var composedLambda = ComposeLambda(innerLambda);
    Console.WriteLine(composedLambda.DynamicInvoke(0));
    var composedLambda2 = ComposeLambda(composedLambda);
    Console.WriteLine(composedLambda2.DynamicInvoke(0));
}

private static Delegate ComposeLambda(Delegate innerLambda)
{
    Func<int, int> outerLambda = i => i + 2;
    var parameter = Expression.Parameter(typeof (int));
    var callInner = Expression.Call(innerLambda.GetMethodInfo(), parameter);
    var callOuter = Expression.Call(outerLambda.GetMethodInfo(), callInner);
    var composedLambdaType = typeof (Func<,>).MakeGenericType(typeof (int), typeof (int));
    var composedLambdaExpression = Expression.Lambda(composedLambdaType, callOuter, parameter);
    var composedLambda = composedLambdaExpression.Compile();
    return composedLambda;
}

如何获取和传递这个闭包对象?

【问题讨论】:

  • @jp2code 在 4.5 中:GetMethodInfo
  • 不要使用Expression.Call(innerLambda.GetMethodInfo(), ...),那只是自找麻烦。改为调用委托——你没有任何事情搞乱委托的“方法”——你不仅失去了目标(在实例方法中非常重要),而且还侵犯了隐私(例如,匿名方法是内部的或私有的) )。在这种情况下,您没有将闭包参数传​​递给方法:)
  • @Luaan 表达式树用来处理低级的东西——这就是它们的用途
  • 好吧,显然你不知道你的“低级”东西:) 看看我的回答。

标签: c# .net linq lambda expression-trees


【解决方案1】:

不要使用Expression.Call(innerLambda.GetMethodInfo(), ...),那只是自找麻烦。改为调用委托——你没有任何事情搞乱委托的“方法”——你不仅失去了目标(在实例方法中非常重要),而且还侵犯了隐私(例如,匿名方法是内部的或私有的) )。

在这种情况下,您没有将闭包参数传​​递给方法 :) 这从错误消息中应该很明显 - 它向您显示了实际方法的签名(包括闭包)。

如果你使用Expression.Invoke(你应该使用代表),它会按预期工作:

void Main()
{
    Func<int, int> innerLambda = i => i + 1;    
    var composedLambda = ComposeLambda(innerLambda);
    Console.WriteLine(composedLambda.DynamicInvoke(0));
    var composedLambda2 = ComposeLambda(composedLambda);
    Console.WriteLine(composedLambda2.DynamicInvoke(0));
}

private static Delegate ComposeLambda(Delegate innerLambda)
{
    Func<int, int> outerLambda = i => i + 2;
    var parameter = Expression.Parameter(typeof (int));

    var callInner = Expression.Invoke(Expression.Constant(innerLambda), parameter);
    var callOuter = Expression.Invoke(Expression.Constant(outerLambda), callInner);
    var composedLambdaType = typeof (Func<,>).MakeGenericType(typeof (int), typeof (int));
    var composedLambdaExpression = Expression.Lambda(composedLambdaType, callOuter, parameter);
    var composedLambda = composedLambdaExpression.Compile();
    return composedLambda;
}

除此之外,如果您在编译时知道正确的委托类型,请不要使用Delegate。在这种情况下,使用Func&lt;int, int&gt; 非常简单,然后您可以将其调用为composedLambda2(0),例如:

private static Func<int, int> ComposeLambda(Func<int, int> innerLambda)
{
  Func<int, int> outerLambda = i => i + 2;
  var parameter = Expression.Parameter(typeof (int));

  var callInner = Expression.Invoke(Expression.Constant(innerLambda), parameter);
  var callOuter = Expression.Invoke(Expression.Constant(outerLambda), callInner);

  var composedLambdaExpression = Expression.Lambda<Func<int, int>>(callOuter, parameter);
  return composedLambdaExpression.Compile();
}

【讨论】:

  • 哪里是个好去处……了解它的作用、用途和名称?这里简直把我吹走了。
  • @jp2code 我不知道有什么好的解释或教程。这些被称为“表达式树”——MSDN 上的基本介绍位于msdn.microsoft.com/en-us/library/bb397951.aspx。也可以试试blogs.msdn.com/b/charlie/archive/2008/01/31/…,以获得一些解释和示例。
  • @Luaan 谢谢,这是一个很大的帮助。当然,如果我不被强迫,我不会使用表达式树;当然,我总是尽可能具体地了解这些类型。但是,如果一个人编写了非常通用的代码,那是最坏的情况。
  • @EmperorOrionii Expression.Constant 适用于任何表现为常量值的东西——也就是说,不依赖于表达式的其他部分,理想情况下是不可变的。它确实也适用于可变值,但请确保您知道自己在做什么 - 这些功能中的大多数都是以函数式思维方式设计的,不变性是默认设置。当然,所有这一切都只有在编译 Expression 时才是正确的——如果你将 Expression 用于其他东西(例如实体框架),你需要查找消费者的具体限制等。
  • @EmperorOrionii 这不是硬性规定;即使表达式被命名为Constant,它真正的意思是“这是通过引用传递的”。引用被烘焙到表达式中(如果你编译表达式树,则是委托)。当然,值类型会被复制(除非您重用装箱的值)。如今,我只是倾向于以健康的怀疑态度对待函数范围之外的任何可变事物:)
猜你喜欢
  • 2017-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-13
  • 2016-10-05
  • 2013-06-18
  • 2014-02-04
  • 1970-01-01
相关资源
最近更新 更多