2010-02-12 2 views
32

Мне здесь нечего пропустить.Использование IEnumerable без петли foreach

Возьмем следующий код:

public IEnumerable<int> getInt(){ 
    for(int i = 0; i < 10; i++){ 
    yield return i; 
    } 
} 

Я могу назвать это с:

foreach (int j in obj.getInt()){ 
    //do something with j 
} 

Как я могу использовать метод GetInt без цикла Еогеасп:

IEnumerable<int> iter = obj.getInt(); 
// do something with iter ?? 

Спасибо.

редактирует

Для тех, кому интересно, почему я хочу этого. Я повторяю две вещи:

IEnumerator<int> iter = obj.getInt().GetEnumerator(); 
foreach(object x in xs){ 
    if (x.someCondition) continue; 
    iter.MoveNext(); 
    int n = iter.current(); 
    x.someProp = n; 
    etc... 
} 
+0

Что именно вы пытаетесь сделать? Весь смысл в 'IEnumerable ' состоит в том, чтобы разрешить итерацию, а цикл 'foreach' - наиболее эффективный способ сделать это. –

+0

@ Zakalwe, я хочу повторить два перечислимых момента одновременно. Предпросмотр проходит через первый, если он удовлетворяет условию, мне нужен следующий int (просто пример, а не int). – Mark

ответ

52

Вы можете получить ссылку на Enumerator, используя метод GetEnumerator, то вы можете использовать метод MoveNext() двигаться дальше, и использовать Current свойство для доступа к элементам:

var enumerator = getInt().GetEnumerator(); 
while(enumerator.MoveNext()) 
{ 
    int n = enumerator.Current; 
    Console.WriteLine(n); 
} 
+1

Справа. Но какая польза от этого вы используете вместо foreach? –

+0

Это позволяет выполнять специальную обработку, скажем, первых нескольких элементов, или полностью останавливаться после обработки, хотя и многих, и т. Д. В основном это дает вам немного больше универсальности. –

+3

@ Нельзя: вы можете сделать то же самое с 'foreach'. –

3

Как насчет этого?

IEnumerator<int> iter = obj.getInt(); 
using(iter) { 
    while(iter.MoveNext()) { 
     DoSomethingWith(iter.Current) 
    } 
} 
+0

@itowlson, это было именно то, чего мне не хватало! – Mark

29

Мои совет: не возитесь с перечисляющими вообще. Охарактеризуйте вашу проблему как последовательность операций над последовательностями. Напишите код, чтобы выразить эти операции. Пусть операторы последовательности заботятся об управлении счетчиками.

Итак, давайте посмотрим, есть ли у меня это прямо. У вас две последовательности. Скажем, {2, 3, 5, 7, 12} и {"лягушка", "жаба"}. Логическая операция, которую вы хотите выполнить, - это сказать «пройти первую последовательность. Каждый раз, когда вы находите число, делящееся на три, получите следующий элемент во второй последовательности. Сделайте что-нибудь с результирующей (число, амфибия)».

Легко сделано. Во-первых, фильтр первой последовательности:

var filtered = firstSequence.Where(x=>x%3 == 0); 

Далее, почтовый индекс фильтрованной последовательности со второй последовательностью:

var zipped = filtered.Zip(
      secondSequence, 
      (y, z)=> new {Number = x, Amphibian = y}); 

И теперь вы можете перебрать архивный последовательности и делать все, что вы хотите с парами:

foreach(var pair in zipped) 
    Console.WriteLine("{0} : {1}", pair.Number, pair.Amphibian); 

Легкий peasy, не возиться с счетчиками.

+0

@ Эрик, спасибо, мне это нравится, это очень близко к тому, что мне нужно. Я трачу больше времени на программирование на питоне, и он очень похож на то, как я это сделаю (мне нужно изучить возможности C#). Мне любопытно, каковы потенциальные ловушки счетчиков? – Mark

+5

@Mark: (1) люди забывают распоряжаться ими. (2) перечисления подчеркивают * как * код работает, а не * что он для * - чтение кода с помощью счетчиков - это как просмотр описания дрели, когда то, что вы хотите описать, - это образовавшееся отверстие.Они, безусловно, иногда необходимы, но я бы скорее выразил свои действия на более высоком уровне абстракции, чем тот, где я должен явно отслеживать несколько «курсоров» в нескольких последовательностях. –

+3

@ Эрик, один из подходов к вашему подходу состоит в том, что если это действует на большой объем данных в памяти, может быть увеличение производительности при одновременном ручном повторении обоих списков ... Если это небольшой объем данных или производительности не вызывает беспокойства, тогда я выбираю элегантный код (т. е. ваш) по коду nitty gritty. –

0

Хотя принятые ответы верны, обратите внимание, что IEnumerator.Current не определен до первого вызова MoveNext().

Если вы итерация вторичный массив, вы хотите что-то вроде:

IEnumerable<Foo> Foo() { ... } 

int AssignValues(List<TakesFoo> data) { 
    var count = 0; 
    var foo = Foo().GetEnumerator(); 

    // Step into the first element of the array; 
    // doing this does not discard a value from the IEnumerator 
    if (foo.MoveNext()) { 
    foreach (var x in data) { 
     x.SetFoo(foo.Current); 
     count += 1; 
     if (!foo.MoveNext()) { 
     break; 
     } 
    } 

    // Return count of assigned values 
    return count; 
} 
2

используя цикл:

for (var enumerator = getInt().GetEnumerator(); enumerator.MoveNext();) 
{ 
    Console.WriteLine(enumerator.Current); 
} 
+0

Вы должны вывести счетчик в конце – CharlesB

0

Важно отметить, что обязанность цикла foreach является для удаления перечислителя, если экземпляр реализует IDisposable. Другими словами, foreach следует заменить примерно так:

var enumerator = enumerable.GetEnumerator(); 

try 
{ 
    while (enumerator.MoveNext()) 
    { 
     var item = enumerator.Current; 
     // do stuff 
    } 
} 
finally 
{ 
    var disposable = enumerator as IDisposable; 
    if (disposable != null) 
    { 
     disposable.Dispose(); 
    } 
}