У меня есть следующий код:
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; }
}
Я обнаружил, что второй метод (Bar
) с использованием .SelectMany
и .Where
был почти на 80% быстрее, чем первый метод (Foo
) с использованием вложенных вызовов .All
. Но это можно было доказать только на очень большом наборе данных, а реально затраченное время было очень небольшим. Это может иметь большее значение для небольших наборов данных, если каждый элемент вызывает запрос (например, к базе данных), который занимает больше времени, если действительно разница в производительности связана с количеством считываний элементов. Но если разница связана с накладными расходами при чтении элементов, а элементы считываются одинаковое количество раз для любого метода, то я предполагаю, что разница в производительности всегда будет незначительной, независимо от размера набора данных или времени чтения элемента.
Результаты ниже (из профилировщика производительности Visual Studio):
All
возвращаетtrue
для пустых наборов, они эквивалентны. Как насчет того, что лучше, это либо основано на мнении, либо зависит от реализации. - person Ivan Stoev   schedule 30.11.2016