2016-11-28 4 views
3

Как наивный отзыв, вы часто слышите, что используете IEnumerable.Any(), потому что тогда все перечислимое необязательно нужно пройти.Будет ли компилятор оптимизировать сравнение с IEnumerable <T> .Count()?

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

if (reportInfo.OrebodyAndPits.SelectMany(ob => ob.Pits).Count() > 1) 
{ 
    ws.Cells[row, col++].Value = "Pits"; 
} 
else 
{ 
    ws.Cells[row, col++].Value = "Pit"; 
} 

Это заставило меня задаться вопросом, будет сравнение составляться в форме, достаточно умны, чтобы вернуться ложным, как только он перебирает мимо первого пункта?

Если нет, существует ли способ написать метод расширения linq, который бы это сделал?

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

+2

Нет, '.Count()' возвращает количество элементов, например. '123456789' и только тогда проверьте состояние'> 1'. Поместите 'Skip (1) .Any()' для умного поведения. В некоторых случаях (это одно исключено *) .Net видит, что 'IEnumerable ' на самом деле является * массивом * 'T []' или * list * 'List ' и вызывать 'Length' или' Count' вместо перемещения , но это все, чего мы можем ожидать –

ответ

4

Нет, это не будет. Ваш код будет считать все элементы в последовательности. Это связано с тем, что операторы LINQ не оптимизируются компилятором, то, что вы пишете, это то, что вы получаете.

equivelent, более эффективный способ проверки, содержит ли последовательность более чем на 1 пункт является:

reportInfo.OrebodyAndPits.SelectMany(ob => ob.Pits).Skip(1).Any(); 

Это будет проверять, после пропуска первого пункта, есть ли какие-либо предметы, оставленные.

3

Если вы хотите узнать, как что-то работает, почему вы не смотрите на исходный код?

Вот Any() метод: https://github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/Linq/AnyAll.cs#L20

Вот Count() метод: https://github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/Linq/Count.cs#L12

Компилятор не может сделать оптимизацию, как вы описали. Он запрашивает счет и получает число, затем сравнивает это число с тем, что находится в вашем условном выражении.

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

Если вы хотите написать метод LINQ (который является только методом расширения на IEnumerable<T>), который определяет, есть ли хотя бы два в IEnumerable, то это должно быть достаточно простым. Что-то вроде этого:

например.

public static bool AtLeastTwo<TSource>(this IEnumerable<TSource> source) 
    { 
     if (source == null) 
     { 
      throw Error.ArgumentNull(nameof(source)); 
     } 

     using (IEnumerator<TSource> e = source.GetEnumerator()) 
     { 
      e.MoveNext(); // Move past the first one 
      return e.MoveNext(); // true if there is at least a second element. 
     } 
    } 
+0

Skip + Any проще и не требует метода расширения. – Wazner

+0

Но это не то, что было задано: «Если нет, есть ли способ написать метод расширения linq, который бы сделал это?» –