c# - generic - Covarianza genérica y contravarianza
in generic c# (2)
La clave aquí es si la colección es modificable. IEnumerable<T>
es una colección de solo lectura de T
s, mientras que ICollection<T>
admite Add
. Una colección modificable no puede ser covariante, porque:
IList<String> obj = new List<String>();
ICollection<Object> obj1 = obj;
obj1.Add(new Elephant());
Esto haría una comprobación de tipo, ya que (presumiblemente) Elephant
es una subclase de Object
. Pero ahora obj
, que es una List<string>
tiene un Elephant
como su último elemento, que claramente es algo malo.
Esta pregunta ya tiene una respuesta aquí:
- Pregunta sobre C # covarianza 4 respuestas
Considere el fragmento de código.
IList<String> obj=new List<string>();
IEnumerable<Object> obj1 = obj;
Pero si escribo ICollection<Object> obj2 = obj;
me arroja un error de compilación.
No se puede convertir implícitamente el tipo ''
System.Collections.Generic.IList<string>
'' a ''System.Collections.Generic.ICollection<object>
''.
¿Por qué este comportamiento se debe a que List<T>
implementa tanto IEnumerable<T>
como ICollection<T>
e IList<T>
se define como
public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable
{
T this[int index] { get; set; }
int IndexOf(T item);
void Insert(int index, T item);
void RemoveAt(int index);
}
ICollection<T>
no es covariante en el parámetro de tipo, mientras que IEnumerable<T>
es. Si observa sus declaraciones ( ICollection , IEnumerable ) puede ver que IEnumerable<T>
usa la palabra clave out
en T
, mientras que ICollection<T>
no.
Esto tiene sentido si lo piensa, ya que (en términos generales) la covarianza es segura cuando la interfaz solo se usará para leer objetos (y, por lo tanto, la palabra clave de out
). IEnumerable<T>
cumple claramente con ese criterio, mientras que ICollection<T>
es todo lo contrario.
Como ejemplo de lo que podría salir mal (usando tu ejemplo):
IList<String> obj = new List<string>(); // Legal, of course
ICollection<Object> obj1 = obj; // Illegal, but let''s see what happens
obj1.Add(new NonStringObject()); // That''s not a string being stored in a List<string>
Recuerde: la covarianza no es lo mismo que la herencia. El hecho de que dos clases o interfaces compartan una relación de herencia no significa que sus parámetros de tipo compartan las mismas características de varianza.