Where(condition).Any()和Any(condition)是等价的
本文关键字:Any condition Where | 更新日期: 2023-09-27 18:09:26
在我看到的使用Any方法的Linq查询示例中,大约有一半是通过将其应用于Where()调用的结果来实现的,另一半则直接将其应用于集合。这两种样式是否总是相等的,或者是否存在它们可能返回不同结果的情况?
我的测试支持前者的结论;但边缘情况并不总是那么容易找到。
List<MyClass> stuff = GetStuff();
bool found1 = stuff.Where(m => m.parameter == 1).Any();
bool found2 = stuff.Any(m => m.parameter == 1);
这可以归结为两个重要的问题:
- 是标准的"Where"/"Any"(如Enumerable)吗?*或可查询的。*),还是自定义?(如果是后者,所有的赌注都是无效的)
- 如果它是可查询的。*,提供者是什么?
后者非常重要。例如,LINQ-to-SQL和LINQ-to-EF在Single下的行为不同,所以我不会假设对于Any它们的行为相同。一个更深奥的提供者可以做任何事情。但更多的是:LINQ-to-SQL对Single(谓词)和Where(谓词)做不同的事情(身份管理器)。单身(也是第一次)。实际上,LINQ-to-SQL中有3种不同的行为,这取决于3.5、3.5 sp1或4.0。
此外,IIRC LINQ-to-ADO。NET-Data-Services具有不同的支持EF - so(同样来自内存),而一个提供者只支持Single(谓词),另一个只支持Where(谓词).Single();建议Any()可能受到不同提供者的类似影响并不是一个大的飞跃。
所以:虽然Any(predicate)和Where(predicate).Any()在语义上是等价的,但是如果没有非常详细的上下文的信息,就不可能说它们实际上是相同的。逻辑上没有区别,但性能方面,后者:
stuff.Any(m => m.parameter == 1);
比
性能更好。stuff.Where(m => m.parameter == 1).Any();
,因为前者不使用迭代器(yield return
)来生成结果。Where()
子句可以,迭代器很好,但它们确实增加了额外的处理开销。
它很大吗?不,但通常情况下,为了性能和可维护性,我会选择最简洁易读的表达式。
使用标准的LINQ函数,没有区别,因为where
是延迟的操作——只是一个额外的函数调用。
下面是一小段c#代码:
class Program
{
List<string> data = new List<string>(){ "ABC", "DEF", "H" };
static void Main(string[] args)
{
var p = new Program();
}
private Program()
{
UseWhereAndAny();
UseAny();
}
private void UseWhereAndAny()
{
var moreThan2 = data.Where(m => m.Length > 2).Any();
}
private void UseAny()
{
var moreThan2 = data.Any(m => m.Length > 2);
}
}
如果你检查IL代码,你会发现两者之间有一点不同:
.method private hidebysig
instance void UseAny () cil managed
{
// Method begins at RVA 0x2134
// Code size 45 (0x2d)
.maxstack 4
.locals init (
[0] bool moreThan2
)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldfld class [mscorlib]System.Collections.Generic.List`1<string> AnyWhere.Program::data
IL_0007: ldsfld class [mscorlib]System.Func`2<string, bool> AnyWhere.Program::'CS$<>9__CachedAnonymousMethodDelegate4'
IL_000c: brtrue.s IL_0021
IL_000e: ldnull
IL_000f: ldftn bool AnyWhere.Program::'<UseAny>b__3'(string)
IL_0015: newobj instance void class [mscorlib]System.Func`2<string, bool>::.ctor(object, native int)
IL_001a: stsfld class [mscorlib]System.Func`2<string, bool> AnyWhere.Program::'CS$<>9__CachedAnonymousMethodDelegate4'
IL_001f: br.s IL_0021
IL_0021: ldsfld class [mscorlib]System.Func`2<string, bool> AnyWhere.Program::'CS$<>9__CachedAnonymousMethodDelegate4'
IL_0026: call bool [System.Core]System.Linq.Enumerable::Any<string>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, class [mscorlib]System.Func`2<!!0, bool>)
IL_002b: stloc.0
IL_002c: ret
} // end of method Program::UseAny
而UserWhere方法是:
.method private hidebysig
instance void UseWhereAndAny () cil managed
{
// Method begins at RVA 0x20d8
// Code size 50 (0x32)
.maxstack 4
.locals init (
[0] bool moreThan2
)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldfld class [mscorlib]System.Collections.Generic.List`1<string> AnyWhere.Program::data
IL_0007: ldsfld class [mscorlib]System.Func`2<string, bool> AnyWhere.Program::'CS$<>9__CachedAnonymousMethodDelegate2'
IL_000c: brtrue.s IL_0021
IL_000e: ldnull
IL_000f: ldftn bool AnyWhere.Program::'<UseWhereAndAny>b__1'(string)
IL_0015: newobj instance void class [mscorlib]System.Func`2<string, bool>::.ctor(object, native int)
IL_001a: stsfld class [mscorlib]System.Func`2<string, bool> AnyWhere.Program::'CS$<>9__CachedAnonymousMethodDelegate2'
IL_001f: br.s IL_0021
IL_0021: ldsfld class [mscorlib]System.Func`2<string, bool> AnyWhere.Program::'CS$<>9__CachedAnonymousMethodDelegate2'
IL_0026: call class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0> [System.Core]System.Linq.Enumerable::Where<string>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, class [mscorlib]System.Func`2<!!0, bool>)
IL_002b: call bool [System.Core]System.Linq.Enumerable::Any<string>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)
IL_0030: stloc.0
IL_0031: ret
} // end of method Program::UseWhereAndAny
据我所知,使用Where和Any会导致额外的枚举带来更多的开销。
虽然在大多数情况下它们在语义上是等价的,但是有可能为对象提供自己的 Where
方法,这可能导致stuff.Where(foo).Any()
与stuff.Any(foo)
非常不同。
有一个微妙的区别。
调用Any()
检查可枚举对象是否为空,如果为空则返回false。
调用Any(Func<TSource, bool> predicate)
检查可枚举对象中是否有匹配谓词的项,如果不匹配则返回false。
然而,我不认为这种差异会影响执行,因为Any
在枚举对象被枚举之前不会被调用。
如果另一个线程改变了where部分和任何部分之间的可枚举数,将会抛出异常,因此不会改变结果。