Are Where(condition).Any() and Any(condition) equivalent
About half of the examples I see for Linq queries using the Any method do so by applying it to the results of a Where() call, the other half apply it directly to the collection. Are the two styles always equivalent, or are there cases wheres that they could return different results?
My testing supports the former conclusion; but edge cases aren't always easy to find.
List<MyClass> st开发者_Python百科uff = GetStuff();
bool found1 = stuff.Where(m => m.parameter == 1).Any();
bool found2 = stuff.Any(m => m.parameter == 1);
It comes down to 2 important questions:
- is it a standard "Where"/"Any" (such as Enumerable.* or Queryable.*), or is it custom? (if the latter, all bets are off)
- if it is Queryable.*, what is the provider?
The latter matters hugely. For example, LINQ-to-SQL and LINQ-to-EF behave differently re Single, so I would not assume that they behave identically for Any. A more esoteric provider could do anything. But more: LINQ-to-SQL does different things (re the identity-manager) for Single(predicate) vs Where(predicate).Single (and for First too). In fact, there are 3 different behaviours available in LINQ-to-SQL there depending on 3.5, 3.5SP1, or 4.0.
Additionally, IIRC LINQ-to-ADO.NET-Data-Services had different (opposite, from memory) support to EF - so (again from memory) while one provider only supported Single(predicate), the other only supported Where(predicate).Single(); it is not a great leap to suggest that Any() could be similarly affected by different providers.
So: while Any(predicate) and Where(predicate).Any() are semantically equivalent - it is impossible to say if they are actually the same without very detailed information to the context.
Logically, no difference, but performance wise the latter:
stuff.Any(m => m.parameter == 1);
is more performant than:
stuff.Where(m => m.parameter == 1).Any();
because the former does not use an iterator (yield return
) to produce it's result. The Where()
clause does, iterators are nice, but they do add extra processing overhead.
Is it huge? No, but typically I'd go with the most concise and readable expression for both performance and maintainability.
Using standard LINQ functions, there is no difference, since where
is deferred operation -- only an additional function call.
Here is little code in 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);
}
}
If you check the IL code you notice a little difference between the two :
.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
While the UserWhere method is :
.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
From what I understand, the the use of Where and Any cause more overhead with an additional Enumeration.
While they are semantically equivalent in most cases, it is possible for an object to provide its own Where
method that could cause stuff.Where(foo).Any()
to be very different from stuff.Any(foo)
.
There is a subtle difference.
Calling Any()
checks the enumerable for emptiness and returns false if it is.
Calling Any(Func<TSource, bool> predicate)
checks if any of the items in the enumerable match the predicate and returns false if they don't.
However I can't think of a way that this difference would effect execution, since Any
isn't called until the enumerable is enumerated.
If another thread changes the enumerable between the where part being run and the any part being run there will be an exception thrown, so that won't change the results.
精彩评论