2008-10-21 18 views
32

Esta podría ser una vieja pregunta: ¿Por qué IEnumerable<T> hereda de IEnumerable?¿Por qué IEnumerable <T> hereda de IEnumerable?

Así es como lo hace .NET, pero trae un pequeño problema. Cada vez que escribo una clase implementa IEumerable<T>, tengo que escribir dos funciones GetEnumerator(), una para IEnumerable<T> y la otra para IEnumerable.

Y, IList<T> no hereda de IList.

No sé por qué IEnumerable<T> está diseñado de otra manera.

Respuesta

48

Straight from the horse's mouth (Hejlsberg):

Idealmente todas las interfaces colección genérica (por ejemplo ICollection<T>, IList<T>) heredaría de sus homólogos no genéricos tales que las instancias de interfaz genéricos podrían utilizarse tanto con genérico y no código genérico Por ejemplo, sería conveniente si se pudiera pasar un IList<T> al código que espera un IList.

Como resultado, la única interfaz genérica para los que esto es posible es IEnumerable<T>, porque sólo IEnumerable<T> es contra-variante: En IEnumerable<T>, el parámetro de tipo T se utiliza sólo en posiciones de "salida" (valores de retorno) y no en posiciones de "entrada" (parámetros). ICollection<T> y IList<T> usan T en ambas posiciones de entrada y salida, y esas interfaces son por lo tanto invariables. (Dicho sea de paso, que habría sido contra-variante si se utilizó T sólo en posiciones de entrada, pero eso realmente no importa aquí.)

< ... tijeretazo ...>

Por lo tanto, para responder a su pregunta, IEnumerable<T> hereda de IEnumerable porque puede hacerlo. :-)

+1

Eso es interesante ... Me enfadó mucho que 'IList ' no heredó 'IList', pero tan pronto como se toma en cuenta la varianza comienza a tener sentido ... –

+1

Es una lástima que Microsoft nunca haya definido IReadableByIndex e IReadableByIndex (Of Out T), que podría haber sido heredado por IList e IList (Of T). IReadableByIndex podría ser heredado por IReadableByIndex (Of T) sin dificultad, y la varianza de las colecciones indexables de solo lectura habría sido muy buena. – supercat

+0

@supercat Microsoft puede haber escuchado sus oraciones, porque desde que escribió ese comentario, han introducido ['IReadOnlyList '] (http://msdn.microsoft.com/en-us/library/hh192385.aspx) donde el 'out' significa covarianza en' T'. –

16

La respuesta para IEnumerable es: "porque puede afectar la seguridad del tipo".

IEnumerable es una interfaz de "solo lectura", por lo que no importa que la forma genérica sea más específica que la forma no genérica. No se rompe nada implementando ambos. IEnumerator.Current devuelve object, mientras que IEnumerator<T>.Current devuelve T - está bien, ya que siempre se puede convertir legítimamente a object, aunque puede significar boxeo.

comparar esto con IList<T> y IList - puede llamar Add(object) en una IList, mientras que bien puede ser válida por cualquier particular, IList<T> (que no sea IList<object> de hecho cualquier cosa).

Brad Abram's blogged with Anders' answer sobre esta misma pregunta.

2

Esto es para que funcione con clases que no admiten genéricos. Además, los genéricos .NET no le permiten hacer cosas como lanzar IList < largo> como IList < int>, de modo que las versiones no genéricas de las interfaces pueden ser bastante útiles cuando necesita una clase base o interfaz fija.

+0

Este es un punto importante. IList no se puede convertir a IList . –

3

Es por compatibilidad con versiones anteriores. Si llamas a .Net 1.1 función que espera un IEnumerable de vainilla que puede pasar en su IEnumerable genérico.

Luckilly el IEnumerator genérica hereda del viejo estilo IEnumerator

que suelo poner en práctica un método privado que devuelve un empadronador y luego lo paso tanto para el método GetEnumerator estilo antiguo y nuevo.

private IEnumerator<string> Enumerator() { 
     // ... 
    } 

    public IEnumerator<string> GetEnumerator() { 
     return Enumerator(); 
    } 

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { 
     return Enumerator(); 
    } 
+0

Al implementar IEnumerable , también debe implementar el método privado IEnumerable.GetEnumerator(). Afortunadamente, el código anterior funciona bien, por lo que no está duplicando esfuerzos. – spoulson

+2

@spoulson: No es privado. Es parte de una interfaz, por lo que debe implementarse públicamente. El ejemplo de Mendelt no lo implementa en privado, lo implementa explícitamente, por lo que solo está disponible cuando el objeto se convierte en IEnumerable. –

+0

@spoulson: Los segundos dos métodos se generaron cuando hice clic en implementar interfaz en Visual Studio, realmente no lo miré. Solo puede agregar un uso para System.Collections y hacer que la tercera función sea explícitamente pública. – Mendelt

Cuestiones relacionadas