c# - clase - system collections generic list 1
Diferencia entre IEnumerable e IEnumerable<T>? (4)
¿Cuál es la diferencia entre IEnumerable
e IEnumerable<T>
?
He visto muchas clases de framework que implementan estas dos interfaces, por lo tanto, me gustaría saber qué ventajas se obtienen implementando ambas.
Por favor, eche un vistazo cómo han sido definidos:
public interface IEnumerable
{
[DispId(-4)]
IEnumerator GetEnumerator();
}
public interface IEnumerable<T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}
Como vemos, IEnumerable<T>
deriva de IEnumerable
, eso significa que cualquier cosa que IEnumerable
tenga, IEnumerable<T>
hereda, entonces ¿por qué implementamos ambos en lugar de solo IEnumerable<T>
? ¿Implementar IEnumerable<T>
no es suficiente?
Del mismo modo, hay otros pares similares:
-
IList
eIList<T>
-
ICollection
eICollection<T>
Me gustaría saber sobre estos también.
Al iterar a través del bucle, IEnumerator mantiene el estado. Recuerda la posición del cursor y IEnumerable no.
Básicamente, las interfaces no genéricas fueron lo primero, en .NET 1.0 y 1.1. Luego, cuando salió .NET 2.0, salieron los equivalentes genéricos. La vida hubiera sido mucho más simple si los genéricos hubieran logrado convertirse en .NET 1.0 :)
En términos de implementar "solo" IEnumerable<T>
lugar de ambos, básicamente debe implementar ambos, y también debe usar la implementación explícita de la interfaz, dado que ambos definen un método GetEnumerator
parámetros. Como IEnumerator<T>
extiende IEnumerator
, normalmente es algo como esto:
public IEnumerator<T> GetEnumerator()
{
// Return real iterator
}
// Explicit implementation of nongeneric interface
IEnumerator IEnumerable.GetEnumerator()
{
// Delegate to the generic implementation
return GetEnumerator();
}
Por otro lado, con los bloques iteradores introducidos en C # 2 (con yield return
etc.) rara vez es necesario implementar estas cosas completamente a mano, afortunadamente. Es posible que deba escribir algo como el anterior, y luego usar yield return
en el método GetEnumerator
.
Tenga en cuenta que IList<T>
no extiende IList
, e ICollection<T>
no extiende ICollection
. Esto se debe a que es menos seguro para los tipos ... mientras que cualquier iterador genérico puede verse como un iterador no genérico debido a la conversión (potencialmente de boxeo) de cualquier valor al object
, IList
e ICollection
permiten que los valores se agreguen a la colección; y no tiene sentido agregar (decir) una cadena a un IList<int>
.
EDITAR: La razón por la que necesitamos IEnumerable<T>
es para que podamos iterar de forma segura y propagar esa información. Si le devuelvo una IEnumerable<string>
, sabrá que puede asumir con seguridad que todo lo devuelto será una referencia de cadena o nulo. Con IEnumerable
, tuvimos que emitir con eficacia (a menudo implícitamente en una instrucción foreach
) cada elemento que se devolvió de la secuencia, porque la propiedad Current
de IEnumerator
es solo del tipo object
. En cuanto a por qué todavía necesitamos IEnumerable
, porque las interfaces antiguas nunca desaparecen, básicamente. Hay demasiado código existente usándolo.
Hubiera sido posible que IEnumerable<T>
no ampliara IEnumerable
, pero cualquier código que desee hacer uso de un IEnumerable<T>
no podría llamar a un método que acepte IEnumerable
, y había muchos métodos como este. NET 1.1 y 1.0.
En cuanto a por qué ves clases que definen ambos, suficiente aunque IEnumerable <T> implementa IEnumerable, no es necesario, pero es bueno en una autoedición para enumerar las subinterfaces a veces. Considerar
interface IA { }
interface IB : IA { }
class A : IB {} // legal, IB implements IA
class B : IA, IB {} // also legal, possible more clear on intent
Uno devuelve un objeto de tipo Objeto, el otro devuelve un objeto de tipo T.