完整性检查:这些嵌套的 .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()
.
可读性是一个更主观的问题,因为所提出的问题涉及几个如果-这个-那么-那个的步骤,这本身就很麻烦,因此会导致一个麻烦的表达。我赞成第一个,但不是太过分或教条化。
时间复杂度相同。性能可能会很大。乍一看,第二个的内部结构似乎更多地链接了枚举器,这可能会使它稍微慢一些,但我不会在这上面打赌很多钱;如果我非常关心这里的性能,我肯定会介绍两者。
我有以下代码:
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()
.
可读性是一个更主观的问题,因为所提出的问题涉及几个如果-这个-那么-那个的步骤,这本身就很麻烦,因此会导致一个麻烦的表达。我赞成第一个,但不是太过分或教条化。
时间复杂度相同。性能可能会很大。乍一看,第二个的内部结构似乎更多地链接了枚举器,这可能会使它稍微慢一些,但我不会在这上面打赌很多钱;如果我非常关心这里的性能,我肯定会介绍两者。