是Where(条件).Any()和Any(条件)等价物

时间:2011-09-23 18:09:51

标签: c# linq

我使用Any方法查看Linq查询的大约一半示例通过将其应用于Where()调用的结果来实现,另一半直接将其应用于集合。两种风格是否总是相同的,或者是否存在可以返回不同结果的情况?

我的测试支持前一个结论;但边缘情况并不总是很容易找到。

List<MyClass> stuff = GetStuff();
bool found1 = stuff.Where(m => m.parameter == 1).Any();
bool found2 = stuff.Any(m => m.parameter == 1);

6 个答案:

答案 0 :(得分:4)

逻辑上,没有区别,但后者表现明智:

stuff.Any(m => m.parameter == 1);

比以下更高效:

stuff.Where(m => m.parameter == 1).Any();

因为前者不使用迭代器(yield return)来产生它的结果。 Where()子句可以,迭代器很好,但它们确实增加了额外的处理开销。

这是巨大的吗?不,但通常我会在性能和可维护性方面采用最简洁,最易读的表达方式。

答案 1 :(得分:4)

归结为两个重要问题:

  • 是标准的“Where”/“Any”(例如Enumerable。*或Queryable。*),还是自定义的? (如果是后者,所有投注都已关闭)
  • 如果它是可查询的。*,提供商是什么?

后者非常重要。例如,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部分和正在运行的任何部分之间的可枚举,则会抛出异常,因此不会更改结果。