2013-02-15 2 views
2

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

static void Main(string[] args) 
{ 
    Iterate iterate = new Iterate(); 
    foreach (int i in iterate.EnumerateList()) 
    { 
     Console.Write("{0}", i); 
    } 
    Console.ReadLine(); 
} 

class Iterate 
{ 
    public IEnumerable<int> EnumerateList() 
    { 
     List<int> lstNumbers = new List<int>(); 

     lstNumbers.Add(1); 
     lstNumbers.Add(2); 
     lstNumbers.Add(3); 
     lstNumbers.Add(4); 
     lstNumbers.Add(5); 

     foreach (int i in lstNumbers) 
     { 
      yield return i; 
     } 
    } 
} 

(1) Что делать, если я просто использовать return i вместо yield return i?

(2) В чем преимущества использования урожайности и когда нужно ее использовать?

Отредактировано **

В приведенном выше коде, я думаю, что это накладные расходы, чтобы использовать foreach два раза. Сначала в main function, а второй - в методе EnumerateList.

+2

'return i' приведет к ошибке компилятора; 'i' не' IEnumerable '. http://msdn.microsoft.com/en-us/library/vstudio/dscyy5s0.aspx – SLaks

+0

На всякий случай я удаляю IEnumerable и просто прохожу через коллекцию вместо использования IEnumerable с Yield. – RKh

+0

@RKh Тогда он вернет первое целое число и никогда не сможет вернуть кого-либо из других. – Servy

ответ

8

Использование yield return i делает этот метод iterator. Он создаст последовательность значений IEnumerable<int> из всего цикла.

Если вы использовали return i, он просто вернул бы одно значение int. В вашем случае это приведет к ошибке компилятора, так как возвращаемый тип вашего метода равен IEnumerable<int>, а не int.

В этом конкретном примере я лично просто верну lstNumbers вместо использования итератора. Вы можете переписать это без списка, хотя, как:

public IEnumerable<int> EnumerateList() 
{ 
    yield return 1; 
    yield return 2; 
    yield return 3; 
    yield return 4; 
    yield return 5; 
} 

Или даже:

public IEnumerable<int> EnumerateList() 
{ 
    for (int i=1;i<=5;++i) 
     yield return i; 
} 

Это очень удобно, когда вы делаете класс, который вы хотите, чтобы действовать как коллекции. Реализация IEnumerable<T> вручную для пользовательского типа часто требует создания пользовательского класса и т. Д. До итераторов это требовало большого количества кода, как показано в этом old sample for implementing a custom collection in C#.

+0

Является ли доходность еще предпочтительнее с .NET 4.0 и .NET 4.5? – RKh

+2

Или даже используйте 'return Enumerable.Range (1,5);' – Servy

+0

@RKh Это зависит от того, что вы делаете. Он по-прежнему используется, конечно, но это не то, что большинство людей должны использовать * много *. Это не устарело или что-то еще. – Servy

1

Ключевое слово yield заставит компилятор превратить функцию в объект перечислителя.

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

1

Вы не можете вернуть только i, поскольку i является int, а не IEnumerable. Он даже не компилируется. Вы могли бы вернуть lstNumbers, поскольку это реализует интерфейс IEnumerable.

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

2

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

Рассмотрите Directory.GetFiles из BCL. Он возвращает string[]. То есть он должен получить все файлы и поместить имена в массив, прежде чем он вернется. В противоположность Directory.EnumerateFiles возвращает IEnumerable<string>. То естьвызывающий отвечает за обработку набора результатов. Это означает, что вызывающий может отказаться от перечисления коллекции в любой точке.

+0

Что вы подразумеваете под: «не нужно ли материализовать весь результат, если это необходимо»? – RKh

+0

Если вы возвращаете массив, вам нужно заполнить весь массив, прежде чем вы сможете его вернуть. С помощью итераторов вы возвращаете один элемент за раз, и, таким образом, любая работа, которую вам нужно сделать, чтобы получить элементы, может быть выполнена по мере необходимости. –

Смежные вопросы