<out T> vs <T> en genéricos

Resuelto Cole Tobin asked hace 12 años • 8 respuestas

¿ Cuál es la diferencia entre <out T>y <T>? Por ejemplo:

public interface IExample<out T>
{
    ...
}

vs.

public interface IExample<T>
{
    ...
}
Cole Tobin avatar Jun 09 '12 06:06 Cole Tobin
Aceptado

La outpalabra clave en genéricos se utiliza para indicar que el tipo T en la interfaz es covariante. Consulte Covarianza y contravarianza para obtener más detalles.

El ejemplo clásico es IEnumerable<out T>. Como IEnumerable<out T>es covariante, puedes hacer lo siguiente:

IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings;

La segunda línea anterior fallaría si esto no fuera covariante, aunque lógicamente debería funcionar, ya que la cadena deriva del objeto. Antes de que se agregara variación en las interfaces genéricas a C# y VB.NET (en .NET 4 con VS 2010), esto era un error de tiempo de compilación.

Después de .NET 4, IEnumerable<T>se marcó como covariante y se convirtió en IEnumerable<out T>. Dado que IEnumerable<out T>solo usa los elementos que contiene y nunca los agrega o cambia, es seguro tratar una colección enumerable de cadenas como una colección enumerable de objetos, lo que significa que es covariante .

Esto no funcionaría con un tipo como IList<T>, ya que IList<T>tiene un Addmétodo. Supongamos que esto estaría permitido:

IList<string> strings = new List<string>();
IList<object> objects = strings;  // NOTE: Fails at compile time

Entonces podrías llamar:

objects.Add(7); // This should work, since IList<object> should let us add **any** object

Por supuesto, esto fallaría, por lo que IList<T>no se puede marcar como covariante.

Por cierto, también existe una opción para inque se utiliza en cosas como interfaces de comparación. IComparer<in T>, por ejemplo, funciona al revés. Puede usar un concreto IComparer<Foo>directamente como IComparer<Bar>si Bares una subclase de Foo, porque la IComparer<in T>interfaz es contravariante .

Reed Copsey avatar Jun 08 '2012 23:06 Reed Copsey

Para recordar fácilmente el uso de inuna outpalabra clave (también covarianza y contravarianza), podemos imaginar la herencia como una envoltura:

String : Object
Bar : Foo

En fuera

o0omycomputero0o avatar Jan 09 '2015 09:01 o0omycomputero0o

considerar,

class Fruit {}

class Banana : Fruit {}

interface ICovariantSkinned<out T> {}

interface ISkinned<T> {}

y las funciones,

void Peel(ISkinned<Fruit> skinned) { }

void Peel(ICovariantSkinned<Fruit> skinned) { }

La función que acepta ICovariantSkinned<Fruit>podrá aceptar ICovariantSkinned<Fruit>o ICovariantSkinned<Banana>porque ICovariantSkinned<T>es una interfaz covariante y Bananaes un tipo de Fruit,

la función que acepta ISkinned<Fruit>sólo podrá aceptar ISkinned<Fruit>.

Jodrell avatar Dec 18 '2013 14:12 Jodrell