2009-12-21 4 views
7

Рассмотрите следующий код, первый показывает, что «очистка» выполняется, когда мы закончили итерацию над IEnumerable строк. Второй проход - это то, что вызывает у меня горе. Мне нужно отказаться от IEnumerable до достижения цели, а затем выполнить очистку кода. Но если вы запустите это, вы увидите, что во втором проходе очистка никогда не срабатывает.Как я могу отказаться от IEnumerator без итерации до конца?

Каков предпочтительный способ отказаться от IEnumerable, как это?

static void Main(string[] args) 
{ 
    // first pass 
    foreach (String color in readColors()) 
     Console.WriteLine(color); 

    // second pass 
    IEnumerator<string> reader = readColors().GetEnumerator(); 
    if (reader.MoveNext()) 
    { 
     Console.WriteLine(reader.Current); 
     reader.Dispose(); 
    } 
} 
static IEnumerable<string> readColors() 
{ 
    string[] colors = { "red", "green", "blue" }; 
    for (int i = 0; i < colors.Length; i++) 
     yield return colors[i]; 

    Console.WriteLine("Cleanup goes here"); 
} 
+1

Я может быть что-то отсутствует, или ваш пример чрезмерно упрощен ... но то, что очистка вы ней- запустить в конце Перечисления в readColors? Похоже, что GC должен иметь возможность обрабатывать вещи просто отлично. –

+0

Если у вас есть пользовательская коллекция, вы также можете реализовать IDisposable?!. – serhio

ответ

7

Вы должны поместить основную часть вашего метода итератора в try..finally, с код очистки в конце концов:

public IEnumerable<string> readColors() 
    { 
     try 
     { 
      string[] colors = { "red", "green", "blue" }; 
      for (int i = 0; i < colors.Length; i++) 
       yield return colors[i]; 
     } 
     finally 
     { 
      Console.WriteLine("Cleanup goes here"); 
     } 
    } 

Помните, что под капотом метод итератора вызывает отдельный класс, который будет создан, который реализует IEnumerable и IEnumerator. Поместив вашу очистку в блок finally, он попадает в метод класса Dispose.

[Edit: (как указано в других ответах) предпочитают using заявление в связи с вашим подходом вызова Dispose вручную. Я предполагал, что вы сделали это именно так, чтобы подчеркнуть обсуждаемый вопрос, но все равно стоит отметить]

+0

@ Лука: Да. Это строка 'reader.Dispose();'. – jason

+0

Это правильный ответ. После этого вы можете упростить свою вторую строку, чтобы использовать «foreach», а не более сложный код, который у вас есть сейчас. Просто выполните: foreach (цвет строки в readColors()) {Console.WriteLine (цвет); ломать; } – StarPacker

4

Это один из способов отказаться от него. Причина, по которой вы не видите

Cleanup goes here 

печатается на консоли, потому что петля for (int i = 0; i < colors.Length; i++) никогда не выполняется до завершения. См. Ниже, как заставить код очистки выполнить.

Вот еще один способ. Это предпочтительный шаблон для использования объектов IDisposable в C#. Это является предпочтительным, потому что это вызовет вызов IEnumerator.Dispose, даже если возникает исключение.

using (IEnumerator<string> reader = readColors().GetEnumerator()) { 
    reader.MoveNext(); 
    Console.WriteLine(reader.Current); 
} 

Как для выгонки очищающего кода вы должны выполнить вы можете сделать следующее:

static IEnumerable<string> readColors() { 
    string[] colors = { "red", "green", "blue" }; 
    try { 
     for (int i = 0; i < colors.Length; i++) { 
      yield return colors[i]; 
     } 
    } 
    finally { 
     Console.WriteLine("Cleanup goes here"); 
    } 
} 
+0

@Downvoter: Дайте причину. – jason

+0

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

+0

@Rob Levine: Я щелкнул представить немного преждевременно настолько справедливо. Спасибо, что вернулся к комментарию. – jason

1

Я думаю, что предпочтительный способ очистки - это использовать IDisposable. В этом случае вам лучше реализовать свой собственный IEnumerable<string> с конкретным IEnumerator<string> и с помощью обычного метода Dispose. Вы получаете для при использовании foreach.

class MyEnumerator : IEnumerator<string> 
    { 
     // ... 
     #region IDisposable Members 

     public void Dispose() 
     { 
      // do your cleanup here 
      throw new NotImplementedException(); 
     } 

     #endregion 
     // ... 
    } 
+0

Почему downvote? –

+1

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

+0

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

0
try { 

    string[] colors = { "red", "green", "blue" }; 
    for (int i = 0; i < colors.Length; i++) { 
    if(condition == true) 
     break; 
    yield return colors[i]; 
    } 
} 
finally { 
    Console.WriteLine("Cleanup goes here"); 
} 
+0

@ Лука: Да. Это строка 'reader.Dispose();'. – jason