【问题标题】:Should LINQ lambda expression parameters be reused in a second lambda?LINQ lambda 表达式参数是否应该在第二个 lambda 中重用?
【发布时间】:2013-09-20 07:35:15
【问题描述】:

关注this question我想知道lambda参数表达式实例的重用是好是坏?

我有时会得到一个完整的 LINQ 表达式树,其中相同的 lambda 参数实例在第二个非嵌套 lambda 中正确使用是本地的:

// class Person { public int AProp { get; set; } public bool BProp { get; set; }}
var lparam = Expression.Parameter(typeof(Person),"x");
var lambda1 = (Expression<Func<Person,int>>)Expression.Lambda(Expression.Property(lparam, "AProp"), lparam);
var lambda2 = (Expression<Func<Person,bool>>)Expression.Lambda(Expression.Property(lparam, "BProp"), lparam);
var source = (new Person[0]).AsQueryable();
var query = source.Where(lambda2).OrderBy(lambda1); 

因此,对于 lambda1 和 lambda2,同一个 lambda 参数实例 lparam 的声明都是正确的。

只是这个共享的 lambda 参数实例强制 IQueryProvider 实现不关联基于纯 lambda 参数引用的额外全局含义,因为在处理不同的 lambda 期间可能需要以不同的方式解释相同的参数。此外,您不会通过使用 LINQ 从 LINQ 获得这种表达式树(或者我应该说图形?)

Expression<Func<Person,int>> lambda3 = x => x.AProp;
Expression<Func<Person,bool>> lambda4 = x => x.BProp;

因为对于两个 lambda 表达式会有不同的 (Person x) 参数实例。

var query = source.Where(x => x.BProp).OrderBy(x => x.AProp);

var query = from x in source where x.BProp order by x.AProp select x;

它还使表达式树变成了一个图形。

这种重复使用参数实例的方式是好是坏?到目前为止,我还没有从当局那里找到明确的答案。

【问题讨论】:

  • 从逻辑上讲,我看不出参数重用有什么重要的原因。我知道通过重用 lambda 而不是制作一堆相同的 lambda,我发现了显着的性能提升,但不知道参数重用。我建议测试性能。
  • 如果你想办法让它们变成圆形,这可能是个问题。

标签: c# linq lambda


【解决方案1】:

我不会在两个不相交的 lambda 之间共享参数对象。

首先,我们不要在这里制造虚假经济。物品很便宜,你不会制造十万件。 (如果你是,你可能有更大的问题需要解决。)

其次,正如您所注意到的,在不相关的 lambda 之间共享引用相同的参数对象意味着分析这些 lambda 表达式树的代码需要了解参数对象在不同的​​上下文中具有不同的含义。这似乎是一个等待发生的错误。

第三,想象有一天你可能想要两个表达式树:

x => Foo(x);
y => Bar(y);

然后从他们那里构建第三个,比如说:

(x,y) => Foo(x) && Bar(y);

如果 x 和 y 实际上都是同一个参数对象,那么你就遇到了问题:

(x,x) => Foo(x) && Bar(x);  // Huh?

另一方面,StriplingWarrior 的回答指出,如果你有

x => Foo(x);
x => Bar(x);

然后将它们组合为更容易

x => Foo(x) && Bar(x);

因为你确实不需要需要重写任何东西。

基本上,这似乎是一个冒险的举动,没有真正令人信服的好处,那为什么要这样做呢?

【讨论】:

  • @Tom67:这就是为什么最好将问题作为问题提出而不是作为评论提出。我永远不会把它放在评论中。
  • 非常感谢!我曾希望/期待这个答案,现在我可以更好地与这种树的生产者争论。
【解决方案2】:

您需要考虑您的用例。这些 lambda 未来如何组合?

例如,您是否要使用 OR 运算组合两个 lambda?

Expression<Func<Person, bool>> lambda1 = p => !p.IsDeleted;
Expression<Func<Person, bool>> lambda2 = p => p.DomainId == 1;
// How do I get (p => !p.IsDeleted || p.DomainId == 1)?

如果是这样,像这样加入他们会更容易:

Expression.Lambda<Func<Person, bool>>(
    Expression.OrElse(lambda1.Body, lambda2.Body), 
    lambda1.Parameters[0]);

如果它们都有相同的参数表达式对象,上面的代码就可以正常工作。如果他们不这样做,突然间你必须遍历lambda2 的整个树,创建一个新的表达式树,用参数替换第一个表达式中的参数。可以做到,而且我已经编写了一些实用方法,以便在我必须做这样的事情时变得容易,但如果你能确定这是你将遇到的用例类型,你也可以这样做首先使用相同的参数,让自己的生活更简单。

另一方面,正如 Eric 指出的那样,如果您要以参数需要在更大的 lambda 表达式中扮演不同角色的方式组合 lambda,那么您将需要这些参数不同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-01
    • 1970-01-01
    • 2014-07-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多