我使用Any方法查看Linq查询的大约一半示例通过将其应用于Where()调用的结果来实现,另一半直接将其应用于集合。两种风格是否总是相同的,或者是否存在可以返回不同结果的情况?
我的测试支持前一个结论;但边缘情况并不总是很容易找到。
List<MyClass> stuff = GetStuff();
bool found1 = stuff.Where(m => m.parameter == 1).Any();
bool found2 = stuff.Any(m => m.parameter == 1);
答案 0 :(得分:4)
逻辑上,没有区别,但后者表现明智:
stuff.Any(m => m.parameter == 1);
比以下更高效:
stuff.Where(m => m.parameter == 1).Any();
因为前者不使用迭代器(yield return
)来产生它的结果。 Where()
子句可以,迭代器很好,但它们确实增加了额外的处理开销。
这是巨大的吗?不,但通常我会在性能和可维护性方面采用最简洁,最易读的表达方式。
答案 1 :(得分:4)
归结为两个重要问题:
后者非常重要。例如,LINQ-to-SQL和LINQ-to-EF的行为方式与Single不同,因此我不会假设它们对Any的行为相同。一个更深奥的提供者可以做任何事情。但更多:LINQ-to-SQL为Single(谓词)与Where(谓词).Single(以及First也是如此)做不同的事情(重新识别身份管理器)。实际上,LINQ-to-SQL中有3种不同的行为,具体取决于3.5,3.5SP1或4.0。
此外,IIRC LINQ-to-ADO.NET-Data-Services对EF具有不同的(相反,从内存中)支持 - 所以(再次从内存中),而一个提供程序仅支持Single(谓词) ),另一个只支持Where(谓词).Single();提出Any()可能同样受到不同提供者的影响并不是一个很大的飞跃。
所以:当Any(谓词)和Where(谓词).Any()在语义上等价时 - 如果不是实际上是那么它是不可能的em>非常详细的信息到上下文。
答案 2 :(得分:3)
使用标准LINQ函数,没有区别,因为where
是延迟操作 - 只是一个额外的函数调用。
答案 3 :(得分:2)
以下是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 的使用会导致更多的额外枚举开销。
答案 4 :(得分:0)
虽然在大多数情况下它们在语义上是等效的,但可能对象提供自己的 Where
方法,可能导致stuff.Where(foo).Any()
与stuff.Any(foo)
非常不同。
答案 5 :(得分:0)
有一个微妙的区别。
调用Any()
检查枚举是否为空,如果是,则返回false。
调用Any(Func<TSource, bool> predicate)
检查枚举中的任何项是否与谓词匹配,如果不匹配则返回false。
但是我无法想到这种差异会影响执行的方式,因为在枚举枚举之前不会调用Any
。
如果另一个线程更改了正在运行的where部分和正在运行的任何部分之间的可枚举,则会抛出异常,因此不会更改结果。