完整性检查:这些嵌套的 .All 调用是否等同于 LINQ 中相应的 .Where 和 .SelectMany 调用?

Sanity check: Do these nested .All calls equate to the corresponding .Where and .SelectMany calls in LINQ?

我有以下代码:

bool b = myList
    .All(x => x.MyList
        .Where(y => y.MyBool)
        .All(y => y.MyList
            .All(z => z.MyBool)))

这在功能上等同于:

bool b = myList
    .SelectMany(x => x.MyList)
    .Where(x => x.MyBool)
    .SelectMany(x => x.MyList)
    .All(x => x.MyBool)

我认为是的,但我的同事向我提出质疑,这种变化 可能 在某些情况下在功能上有所不同(例如,如果任何集合为空)。

虽然答案不是肯定的就是否定的,但是对于在可读性、圈复杂度、时间复杂度和性能方面哪个更好的任何意见也将不胜感激。

更新:

因此,我使用以下代码分析了代码:

static void Main(string[] args)
{
    var myList = new List<A>();

    for (var j = 0; j < 1000; j++)
    {
        var a = new A();

        for (var k = 0; k < 1000; k++)
        {
            var b = new B {MyBool = true};

            for (var l = 0; l < 1000; l++)
            {
                var c = new C {MyBool = true};
                b.MyList.Add(c);
            }

            a.MyList.Add(b);
        }

        myList.Add(a);
    }

    for (var x = 0; x < 10000; x++)
    {
        bool b1 = Foo(myList);
    }

    for (var x = 0; x < 10000; x++)
    {
        bool b2 = Bar(myList);
    }
}

private static bool Foo(List<A> myList)
{
    return myList
        .All(x => x.MyList
            .Where(y => y.MyBool)
            .All(y => y.MyList
                .All(z => z.MyBool)));
}

private static bool Bar(List<A> myList)
{
    return myList
        .SelectMany(x => x.MyList)
        .Where(x => x.MyBool)
        .SelectMany(x => x.MyList)
        .All(x => x.MyBool);
}

private class A
{
    public List<B> MyList => new List<B>();
}

private class B
{
    public bool MyBool { get; set; }

    public List<C> MyList => new List<C>();
}

private class C
{
    public bool MyBool { get; set; }
}

我发现使用 .SelectMany.Where 的第二种方法 (Bar) 比使用嵌套的第一种方法 (Foo) 快将近 80% .All 来电。但这只能在非常大的数据集上证明,实际花费的时间非常少。如果每个元素调用一个需要较长时间的查询(例如,对数据库),如果性能差异确实是由于元素被读取的次数引起的,那么这在较小的数据集上可能更重要。但是,如果差异是由于读取元素之间的开销造成的,并且两种方法读取元素的次数相同,那么我想无论数据集大小或元素读取时间如何,性能差异总是可以忽略不计。

以下结果(来自 Visual Studio Performance Profiler):

myList.All // is it true for all elements in myList that…
(x => x.MyList //in their MyList property
.Where(y => y.MyBool) // those elements that have MyBool returning true
.All( // have it true for all elements in that list that…
y => y.MyList //in their MyList property
.All(z => z.MyBool) // all elements have MyBool returning true


myList.SelectMany( // for all the elements in myList
x => x.MyList)  // for all elements in their MyList property…
.Where(x => x.MyBool) // that have MyBool returning true
.SelectMany( // for all those elements
x => x.MyList) // for all elements in their MyList property
.All(x => x.MyBool) // is it true that all elements have MyBool returning true

所以是的,它们具有相同的含义。特别是对于任何一种情况,任何阶段的空列表都意味着来自 All() 方法的 true,无论是来自空虚的 true 传递给调用 All() 还是空虚传递进入决赛 All().

可读性是一个更主观的问题,因为所提出的问题涉及几个如果-这个-那么-那个的步骤,这本身就很麻烦,因此会导致一个麻烦的表达。我赞成第一个,但不是太过分或教条化。

时间复杂度相同。性能可能会很大。乍一看,第二个的内部结构似乎更多地链接了枚举器,这可能会使它稍微慢一些,但我不会在这上面打赌很多钱;如果我非常关心这里的性能,我肯定会介绍两者。