2015-12-09 4 views
3

В Visual Studio, ReSharper предупреждает: «Возможно многократное перечисление IEnumerable» для следующего кода:Как доказать метод, возвращающий IEnumerable, вызывается дважды?

static void Main(string[] args) 
{ 
    IEnumerable<string> items = Test2(); 
    foreach (var item in items) 
    { 
     Console.WriteLine(item); 
    } 
    var newitems = new StringBuilder(); 
    foreach (var item in items) 
    { 
     newitems.Append(item); 
    } 
} 

private static IEnumerable<string> Test2() 
{ 
    string[] array1 = { "1", "2", "3" }; 
    return array1; 
} 

Я ожидаю, что метод Test2 будет вызван дважды, но это называется один раз.

Что мне не хватает?

+6

Метод вызывается один раз, но массив повторяется дважды. Это не проблема в этом случае, но иногда это (например, если перечислены ленивые данные с веб-сайта) – Rob

+0

Как увидеть, что массив был повторен дважды? – tesicg

+4

вы дважды перебираете один и тот же массив. почему бы просто не заполнить свой 'StringBuilder' в первом цикле foreach? – user1666620

ответ

6

Это вызывается только один раз, потому что на самом деле возвращает Test2()string [] который также является IEnumerable<string>. Этот string [] массив остается ссылается items так каждый раз, когда вы используете items вы просто повторно использовать массив.

Случай вы ожидаете является реализация Test2() с iterator block:

private static IEnumerable<string> Test2() 
{ 
    string[] array1 = { "1", "2", "3" }; 

    foreach (var str in array1) 
    { 
     yield return str; 
    } 
} 
+3

[Вот документы] (https://confluence.jetbrains.com/display/ReSharper/Possible+multiple+enumeration+of+IEnumerable) для этого предупреждения, подтверждая, что это больше связано с тем, что он может перейти к источнику больше, чем один раз. –

0

IEnumerable<T> является интерфейсом, который имеет нумератор, который будет вызываться каждый раз, когда вы хотите получить доступ к вашей коллекции данных (foreach). Resharper предупреждает вас, что если ваши данные не упорядочены, и вы вызываете этот перечислитель в наборе данных несколько раз, то время выполнения, вероятно, придется проходить через вашу коллекцию несколько раз, что может привести к нагрузке и замедлить время выполнения.

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

5

Взгляните на этот пример:

void Main() 
{ 
    IEnumerable<int> items = Test2(); 
    foreach (var item in items) 
    { 
     Console.WriteLine(item); 
    } 
    var newitems = new StringBuilder(); 
    foreach (var item in items) 
    { 
     newitems.Append(item); 
    } 
} 

IEnumerable<int> Test2() 
{ 
    Console.WriteLine("Test2 called"); 
    return GetEnum(); 
} 

IEnumerable<int> GetEnum() 
{ 
    for(var i = 0; i < 5; i ++) 
    { 
     Console.WriteLine("Doing work..."); 
     Thread.Sleep(50); //Download some information from a website, or from a database 
     yield return i; 
    } 
} 

Представьте себе, что return GetEnum(); был return new int[] { 1, 2, 3 }

Теперь, с массивами, итерация их несколько раз, не обязательно плохо. В вашем случае вы можете выполнять работу в одном цикле, но это не причина, по которой вас предупреждает resharper. Он предупреждает вас из-за возможности, что Test2() возвращает ленивый перечислимый, который действительно работает каждый раз, когда он повторяется.

Если запустить приведенный выше код, вы получите этот результат:

Test2 called 
Doing work... 
0 
Doing work... 
1 
Doing work... 
2 
Doing work... 
3 
Doing work... 
4 
Doing work... 
Doing work... 
Doing work... 
Doing work... 
Doing work... 

Обратите внимание, что Test2 сам вызывается только один раз, но перечислимы повторяется дважды (и работа выполняется в два раза!).

Вы можете избежать этого, написав:

var items = Test2().ToList(); 

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

+1

Удивительный ответ !! –

+2

_ «В этом случае работа выполняется только один раз». _ С оговоркой, что вся обработка должна выполняться сразу и все, что хранится в памяти, а не обрабатывать элементы по одному за раз, поскольку они доступно. +1, тем не менее ... –

0

Как указывалось выше, целью этого предупреждения является указание на то, что дорогостоящая операция может происходить более одного раза. Это происходит из-за того, что ReSharper видит, что ваш метод возвращает IEnumerable, который может привести к ленивой оценке, если вы используете возвращаемые доходности или большинство методов LINQ.

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

  1. Изменить тип возвращаемого Test2 к IList<string>
  2. Перед первым foreach добавить System.Diagnostics.Debug.Assert(items is IList<string>);

Если вы используете ToList() над возвращаемой IEnumerable<string> ReSharper будет также знать, что вы итерацию по коллекции, но вы также создадите ненужный временный список (у вас уже был массив), уплатив затраты времени и памяти, чтобы построить этот новый список.