2012-07-04 2 views
11

Просто интересно, почему Enumerable.Range реализует IDisposable.Почему Enumerable.Range реализует IDisposable?

Я понимаю, почему IEnumerator<T>., Но IEnumerable<T> не требует.


(я обнаружил это во время игры с моей .Memoise() реализации, который имеет заявление как

if (enumerable is IDisposable) 
    ((IDisposable)enumerable).Dispose(); 

в своем методе «источник закончил», что я поместил контрольную точку на из любопытства, и был вызван тестом.)

+1

http://csharpindepth.com/articles/chapter6/iteratorblockimplementation.aspx –

ответ

7

Enumerable.Range использует yield return в своем корпусе метода. yield return оператор производит анонимный тип, который реализует IDisposable под магией компилятора, например:

static IEnumerable<int> GetNumbers() 
{ 
    for (int i = 1; i < 10; i += 2) 
    { 
     yield return i; 
    } 
} 

После компиляции есть анонимный вложенный класс, как это:

[CompilerGenerated] 
private sealed class <GetNumbers>d__0 
    : IEnumerable<int>, IEnumerable, IEnumerator<int>, IEnumerator, IDisposable 
{ 
    //the implementation 
    //note the interface is implemented explicitly 
    void IDisposable.Dispose() { } 
} 

так is a IDisposable. В этом примере метод Dispose остается пустым. Я думаю, причина в том, что нет ничего, что нужно было бы утилизировать. Если вы используете yield return тип, который содержит неуправляемые ресурсы, вы можете получить другой результат компиляции. (НЕ УВЕРЕНЫ)

+0

Компилятор реализует как IEnumerable, так и IEnumerator с тем же классом? Интересно. Мне нужно подумать об этом. – Fowl

+0

О, я вижу это сейчас. Он использует свой конечный автомат, чтобы определить, был ли вызван метод GetEnumerator несколько раз, и возвращает себя, если он этого не сделал. – Fowl

+0

Это имеет смысл, но с фабричным методом возвращает тип, который реализует 'IDisposable', когда тип возврата фабрики не выглядит анти-шаблоном. Итераторы, сгенерированные с помощью компилятора, созданные с помощью 'yield return', выглядели бы типами, в которых отказ без утилизации на самом деле был бы безобидным, если бы' GetEnumerator() 'никогда не вызывался, но после вызова GetEnumerator()' обычно требуется удаление , Кстати, вызов «Dispose» на итераторе после вызова «GetEnumerable()» хотя бы один раз приведет к аннулированию только первого счетчика. – supercat

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