8

这个问题之后,我想知道是否应该将 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); 

所以相同的 lambda 参数实例 lparam 的声明对于 lambda1 和 lambda2 都是正确的。

只是这个共享的 lambda 参数实例强制 IQueryProvider 实现不关联基于纯 lambda 参数引用的其他全局含义,因为在处理不同的 lambda 期间可能需要以不同的方式解释相同的参数。此外,您不会通过使用从 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;

它还使表达式树更像是一个图形。

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

4

2 回答 2

8

我不会在两个不相交的 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);

因为那时你不需要重写任何东西。

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

于 2013-09-20T15:23:51.053 回答
3

您需要考虑您的用例。这些 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,那么您将需要这些参数不同。

于 2013-09-20T15:39:53.070 回答