¿Cuál es la diferencia entre <out T>
y <T>
? Por ejemplo:
public interface IExample<out T>
{
...
}
vs.
public interface IExample<T>
{
...
}
¿Cuál es la diferencia entre <out T>
y <T>
? Por ejemplo:
public interface IExample<out T>
{
...
}
vs.
public interface IExample<T>
{
...
}
Respuestas:
La out
palabra clave en genéricos se usa para denotar que el tipo T en la interfaz es covariante. Ver Covarianza y contravarianza para más detalles.
El ejemplo clásico es IEnumerable<out T>
. Como IEnumerable<out T>
es covariante, puede 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 agregaran variaciones en las interfaces genéricas a C # y VB.NET (en .NET 4 con VS 2010), esto fue un error de tiempo de compilación.
Después de .NET 4, IEnumerable<T>
se marcó covariante y se convirtió IEnumerable<out T>
. Dado que IEnumerable<out T>
solo usa los elementos que contiene y nunca los agrega / cambia, es seguro que trate 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 Add
método. Supongamos que esto se permitiría:
IList<string> strings = new List<string>();
IList<object> objects = strings; // NOTE: Fails at compile time
Entonces puedes llamar:
objects.Add(new Image()); // This should work, since IList<object> should let us add **any** object
Esto, por supuesto, fallaría, por IList<T>
lo que no se puede marcar como covariante.
También hay, por cierto, una opción para in
, que es utilizada por cosas como interfaces de comparación. IComparer<in T>
, por ejemplo, funciona de manera opuesta. Puede usar un concreto IComparer<Foo>
directamente como IComparer<Bar>
si Bar
es una subclase de Foo
, porque la IComparer<in T>
interfaz es contravariante .
Image
es una clase abstracta;) Puedes hacerlo new List<object>() { Image.FromFile("test.jpg") };
sin problemas, o también puedes hacerlo new List<object>() { new Bitmap("test.jpg") };
. El problema con el tuyo es que new Image()
no está permitido (tampoco puedes hacerlo var img = new Image();
)
IList<object>
es un ejemplo extraño, si quieres object
no necesitas genéricos.
Para recordar fácilmente el uso de in
y la out
palabra clave (también covarianza y contravarianza), podemos representar la herencia como envoltura:
String : Object
Bar : Foo
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<Bananna>
porque ICovariantSkinned<T>
es una interfaz covariante y Banana
es un tipo de Fruit
,
la función que acepta ISkinned<Fruit>
solo podrá aceptar ISkinned<Fruit>
.
" out T
" significa que el tipo T
es "covariante". Eso limita T
a aparecer solo como un valor devuelto (saliente) en los métodos de la clase genérica, interfaz o método. La implicación es que puede convertir el tipo / interfaz / método a un equivalente con un supertipo de T
.
Por ejemplo, ICovariant<out Dog>
se puede echar a ICovariant<Animal>
.
out
ejecuciones solo T
se pueden devolver, hasta que leí esta respuesta. ¡Todo el concepto tiene más sentido ahora!
Desde el enlace que publicaste ...
Para los parámetros de tipo genérico, la palabra clave out especifica que el parámetro de tipo es covariante .
EDITAR : nuevamente, desde el enlace que publicaste
Para obtener más información, vea Covarianza y contravarianza (C # y Visual Basic). http://msdn.microsoft.com/en-us/library/ee207183.aspx