2016-11-30 1 views
1

У меня есть следующий код:Проверка работоспособности. Вкладывают ли эти вложенные. Все вызовы приравниваются к соответствующим .Where и .SelectMany звонят в 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) 

Я думаю, что это, но мой коллега бросил вызов мне, что это изменение может быть функционально отличается в некоторых (например, если какая-либо из коллекций пуста, например).

Хотя ответ да или нет, любые мнения по этому вопросу также будут оценены в отношении того, что лучше с точки зрения удобочитаемости, цикличности, временной сложности и производительности.

UPDATE:

Итак, я профилированного код, используя следующие:

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 Performance Profiler): enter image description here

+1

Поскольку 'All' возвращает' true' для пустых множеств, они эквивалентны. Что относительно того, что лучше, это зависит от мнения или реализации. –

ответ

4
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 

Так что да, они имеют тот же смысл. В частности, в любом случае пустой список на любом этапе означает true из метода All(), передается ли true, исходящий из пустоты, до вызова All() или пустоты, переданной в окончательный All().

Считываемость является более субъективным вопросом, так как сам заданный вопрос включает в себя несколько шагов, если-это-то-то, что по своей сути громоздко и, следовательно, приведет к громоздкому выражению. Я одобряю первое, но не сильно или догматически.

Временная сложность такая же. Спектакль, скорее всего, будет многого. На внутреннем уровне второго на первый взгляд, похоже, больше цепочки перечислителей, которые могли бы сделать его несколько медленнее, но я бы не стал делать ставки на это много; если бы я сильно заботился о производительности здесь, я бы определенно профилировал оба.

+0

Спасибо. С точки зрения удобочитаемости я только что немного изменил код, добавив возврат каретки и отступы, чтобы сделать его более реалистичным. Теперь, что вы думаете? – Neo

+1

Это, безусловно, помогает разбивать шаги в каждом, но делает это примерно одинаково в каждом, поэтому с ними оба улучшаются в равной степени. Я все еще очень немного одобряю первое, но только немного и не ожидаю, что все согласятся на это. –

+0

Я просто профилировал два метода, и я вставлял результаты в вопрос как обновление. Похоже, второй способ работает лучше. Но я думаю, что это настолько незначительно, независимо от типа или размера набора данных, что это не стоит рассматривать в реальности. Но я могу ошибаться. – Neo