在编写具有多个“和”条件的LINQ查询时,我应该编写一个包含where
或多个&&
子句的where
子句,每个条件一个吗?
static void Main(string[] args)
{
var ints = new List<int>(Enumerable.Range(-10, 20));
var positiveEvensA = from i in ints
where (i > 0) && ((i % 2) == 0)
select i;
var positiveEvensB = from i in ints
where i > 0
where (i % 2) == 0
select i;
System.Diagnostics.Debug.Assert(positiveEvensA.Count() ==
positiveEvensB.Count());
}
positiveEvensA 和 positiveEvensB 之间是否存在个人偏好或编码风格(长线,可读性等)之外的差异?
我想到的一个可能的区别是,不同的LINQ提供程序可能能够更好地处理多个where
而不是更复杂的表达;这是真的吗?
答案 0 :(得分:46)
我个人总是会选择&amp;&amp;只要不使声明无法理解,就可以使用两个条款。
在你的情况下,它可能根本不会引起注意,但如果你有一个大型集合,并且你使用了这个查询的所有结果,那么有2个where子句肯定会对性能产生影响。例如,如果在结果上调用.Count(),或者遍历整个列表,则会运行第一个where子句,创建一个将再次完全枚举的新IEnumerable,并使用第二个委托。
将2个子句链接在一起会导致查询形成一个在枚举集合时运行的委托。这导致一次枚举通过集合,每次返回结果时调用一次委托。
如果你拆分它们,情况会发生变化。当你的第一个where子句枚举原始集合时,第二个where子句枚举它的结果。这可能(最坏的情况)导致通过集合的2个完整枚举和每个成员调用的2个委托,这可能意味着这个声明(理论上)可能需要2倍的运行时速度。
如果您决定使用2 where子句,那么首先放置更具限制性的子句将有很大帮助,因为第二个where子句仅在传递第一个子句的元素上运行。
现在,在你的情况下,这无关紧要。在大型系列上,它可以。作为一般经验法则,我选择:
1)可读性和可维护性
2)表现
在这种情况下,我认为这两个选项都是同样可维护的,所以我会选择性能更高的选项。
答案 1 :(得分:20)
这主要是个人风格问题。就个人而言,只要where
子句适合一行,我就对这些子句进行分组。
使用多个where
s往往性能较差,因为它需要对每个元素进行额外的委托调用。然而,它可能是一个微不足道的问题,只有在剖析器显示它是一个问题时才应该考虑。
答案 2 :(得分:4)
性能问题仅适用于基于内存的集合... Linq to SQL生成延迟执行的表达式树。更多详情:
答案 3 :(得分:1)
与其他人一样,这更像是个人偏好。我喜欢使用&amp;&amp;因为它更具可读性并且模仿其他主流语言的语法。
答案 4 :(得分:0)
正如 Jared Par 已经说过的:这取决于您的个人喜好、可读性和用例。例如,如果您的方法有一些可选参数,并且您想过滤给定的集合,那么 Where
是完美的:
IEnumerable<SomeClass> matchingItems = allItems;
if(!string.IsNullOrWhiteSpace(name))
matchingItems = matchingItems
.Where(c => c.Name == name);
if(date.HasValue)
matchingItems = matchingItems
.Where(c => c.Date == date.Value);
if(typeId.HasValue)
matchingItems = matchingItems
.Where(c => c.TypeId == typeId.Value);
return matchingItems;
如果你想用 &&
做到这一点,玩得开心;)
我不同意 Jared 和 Reed 的地方是多个 Where
应该具有的性能问题。实际上,Where
的优化方式是将多个谓词组合为一个,如您所见 here。
但我想知道如果集合很大并且有多个 Where
都必须评估,它是否真的没有太大影响。我很惊讶以下 benchmark 显示即使是多 Where
方法也稍微更有效。总结:
方法 | 平均 | 错误 | StdDev |
---|---|---|---|
MultipleWhere | 1.555 秒 | 0.0310 秒 | 0.0392 s |
MultipleAnd | 1.571 秒 | 0.0308 s | 0.0649 s |
这是基准代码,我认为它足以进行此测试:
#LINQPad optimize+
void Main()
{
var summary = BenchmarkRunner.Run<WhereBenchmark>();
}
public class WhereBenchmark
{
string[] fruits = new string[] { "apple", "mango", "papaya", "banana", "guava", "pineapple" };
private IList<string> longFruitList;
[GlobalSetup]
public void Setup()
{
Random rnd = new Random();
int size = 1_000_000;
longFruitList = new List<string>(size);
for (int i = 1; i < size; i++)
longFruitList.Add(GetRandomFruit());
string GetRandomFruit()
{
return fruits[rnd.Next(0, fruits.Length)];
}
}
[Benchmark]
public void MultipleWhere()
{
int count = longFruitList
.Where(f => f.EndsWith("le"))
.Where(f => f.Contains("app"))
.Where(f => f.StartsWith("pine"))
.Count(); // counting pineapples
}
[Benchmark]
public void MultipleAnd()
{
int count = longFruitList
.Where(f => f.EndsWith("le") && f.Contains("app") && f.StartsWith("pine"))
.Count(); // counting pineapples
}
}