在编写具有多个"和"条件的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());
}
Run Code Online (Sandbox Code Playgroud)
是否有任何差别比之间的个人喜好或编码风格(排长队,可读性等)等positiveEvensA和positiveEvensB?
想到的一个可能的区别是,不同的LINQ提供者可能能够更好地处理多个wheres而不是更复杂的表达; 这是真的?
Ree*_*sey 48
我个人总是会使用&&与两个where子句一起使用,只要它不会使语句难以理解.
在你的情况下,它可能不会是都明显可言,但有2 where子句肯定会影响性能,如果你有一个大集合,如果你用所有的结果从该查询.例如,如果在结果上调用.Count(),或者遍历整个列表,则会运行第一个where子句,创建一个将再次完全枚举的新IEnumerable,并使用第二个委托.
将2个子句链接在一起会导致查询形成单个委托,该委托在枚举集合时运行.这导致通过集合进行一次枚举,并在每次返回结果时调用一次委托.
如果你拆分它们,情况会发生变化.当你的第一个where子句枚举原始集合时,第二个where子句枚举它的结果.这可能(最坏的情况)导致通过集合的2个完整枚举和每个成员调用2个委托,这可能意味着此声明(理论上)可能需要2倍的运行时速度.
如果您决定使用2 where子句,则首先放置更具限制性的子句将有很大帮助,因为第二个where子句仅在传递第一个子句的元素上运行.
现在,在你的情况下,这没关系.在大型系列上,它可以.作为一般经验法则,我会去:
1)可读性和可维护性
2)表现
在这种情况下,我认为这两个选项都是同样可维护的,所以我会选择性能更高的选项.
Jar*_*Par 20
这主要是个人风格问题.就个人而言,只要该where条款适用于一行,我就对这些条款进行分组.
使用多个wheres往往性能较差,因为它需要对每个元素进行额外的委托调用才能实现这一目标.然而,它可能是一个微不足道的问题,只有在剖析器显示它是一个问题时才应该考虑.
Tim*_*ter 15
正如 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;
Run Code Online (Sandbox Code Playgroud)
如果你想这样做&&,那就玩得开心;)
我不同意Jared和Reed 的Where观点是多个应该存在的性能问题。实际上,它以一种将多个谓词组合为一个谓词的方式进行了优化,如您在此处(在)Where中看到的那样。CombinePredicates
但我想知道如果集合很大并且有多个都Where必须进行评估,它是否真的没有太大影响。令我惊讶的是,以下基准测试表明,即使是多种Where方法也稍微更有效。摘要:
| 方法 | 意思是 | 错误 | 标准差 |
|---|---|---|---|
| 多重地点 | 1.555秒 | 0.0310秒 | 0.0392秒 |
| 多重与 | 1.571秒 | 0.0308秒 | 0.0649秒 |
这是基准代码,我认为对于这个测试来说已经足够了:
#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
}
}
Run Code Online (Sandbox Code Playgroud)