2012-07-04 22 views
11

Me preguntaba por qué Enumerable.Range implementa IDisposable.¿Por qué Enumerable.Range Implement IDisposable?

Entiendo por qué IEnumerator<T> lo hace, pero IEnumerable<T> no lo requiere.


(descubrí esto mientras jugando con mi .Memoise (aplicación), que tiene declaración como

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

en su método "fuente de acabado" que había colocado un punto de interrupción en por curiosidad, y fue desencadenada por una prueba.)

+1

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

Respuesta

7

Enumerable.Range usa yield return en su cuerpo de método. La declaración yield return produce un tipo anónimo que implementa IDisposable, bajo la magia del compilador, de esta manera:

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

Después de ser compilado, hay una clase anidada anónima como esto:

[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() { } 
} 

por lo que la resultado is a IDisposable. En este ejemplo, el método Dispose deja vacío. Creo que la razón es que no hay nada que desechar. Si yield return es un tipo que contiene recursos no administrados, puede obtener un resultado de compilación diferente. (NO ESTÁ SEGURO al respecto)

+0

El compilador implementa tanto IEnumerable como IEnumerator con la misma clase? Interesante. Tendré que pensar acerca de eso funciona. – Fowl

+0

Oh, lo veo ahora. Utiliza su máquina de estado para detectar si GetEnumerator ha sido llamado varias veces y se devuelve si no lo ha hecho. – Fowl

+0

Eso tiene sentido, pero tener un método de fábrica devuelve un tipo que implementa 'IDisposable' cuando el tipo de devolución de la fábrica no parece ser un antipatrón. Los iteradores generados por el compilador producidos a través de 'yield return' parecerían ser tipos en los que el abandono sin eliminación sería inofensivo siempre que' GetEnumerator() 'nunca haya sido invocado, pero una vez llamado' GetEnumerator() ', la eliminación es generalmente necesaria . Por cierto, al invocar 'Dispose' en un iterador después de haber llamado' GetEnumerable() 'al menos una vez invalidará el primer enumerador solamente. – supercat

Cuestiones relacionadas