2012-06-08 14 views
121

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

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

vs

public interface IExample<T> 
{ 
    ... 
} 

La única información que he recibido de MSDN fue que

Usted puede utilizar la palabra clave en interfaces genéricas y delegados.

+1

buen ejemplo sería IObservable y IObserver , definido en el sistema ns en mscorlib. la interfaz pública IObservable , y la interfaz pública IObserver . Del mismo modo, IEnumerator , IEnumerable VivekDev

Respuesta

158

La palabra clave out en genéricos se utiliza para denotar que el tipo T en la interfaz es covariante. Vea Covariance and contravariance para más detalles.

El ejemplo clásico es IEnumerable<out T>. Desde IEnumerable<out T> es covariante, se le permite hacer lo siguiente:

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

La segunda línea anterior fracasaría si esto no se covariante, a pesar de que lógicamente debería funcionar, ya que la cadena se deriva del objeto. Antes de agregar variance in generic interfaces 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>. Como IEnumerable<out T> solo usa los elementos que contiene, y nunca los agrega ni los 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 método Add. Supongamos que esto le permitiría:

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

entonces se podría llamar:

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

Esto, por supuesto, un error - por lo IList<T> puede no estar marcado covariante.

También hay, por cierto, una opción para in - que es utilizado por cosas como interfaces de comparación.IComparer<in T>, por ejemplo, funciona de manera opuesta. Puede usar un IComparer<Foo> concreto directamente como IComparer<Bar> si Bar es una subclase de Foo, porque la interfaz IComparer<in T> es contravariante.

+3

@ColeJohnson Porque 'Image' es una clase abstracta;) Puede hacer' new List () {Image.FromFile ("test.jpg")}; 'sin problemas, o puede do 'new List () {nuevo Bitmap (" test.jpg ")};' también. El problema con el tuyo es que 'nueva imagen()' no está permitida (no puedes hacer 'var img = new Image();' tampoco) –

+3

un 'IList ' genérico es un ejemplo extraño, si quieres ' objeto's no necesitas genéricos. – Jodrell

+3

@ReedCopsey ¿No estás contradiciendo tu propia respuesta en tu comentario? – MarioDS

5

Desde el enlace informados ....

Para los parámetros de tipo genérico, la palabra clave out especifica que el parámetro de tipo es covariante.

EDITAR: Una vez más, desde el enlace que publicó

Para obtener más información, consulte la covarianza y contravarianza (C# y Visual Basic). http://msdn.microsoft.com/en-us/library/ee207183.aspx

23

"out T" significa que el tipo T es "covariante". Eso restringe T para que aparezca solo como un valor devuelto (saliente) en los métodos de la clase, interfaz o método genérico. La implicación es que puede convertir el tipo/interfaz/método a un equivalente con un super-tipo de T.
P. ej. ICovariant<out Dog> se puede convertir a ICovariant<Animal>.

+4

No me di cuenta de que 'out' exige que' T' solo se devuelva, hasta que lea esta respuesta. ¡Todo el concepto tiene más sentido ahora! – MarioDS

35

consideran,

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> será capaz de 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 s ISkinned<Fruit> solo será capaz de aceptar ISkinned<Fruit>.

36

Para recordar fácilmente el uso de in y out palabra clave (también covarianza y contravarianza), podemos herencia imagen como envoltura:

String : Object 
Bar : Foo 

in/out

+0

Esto lo hace tan claro. – antiduh

+4

¿No es este el camino equivocado? Contravarianza = in = permite que se usen menos tipos derivados en lugar de más derivados. /Covarianza = out = permite que se usen más tipos derivados en lugar de menos derivados. Personalmente, mirando su diagrama, lo leo como lo contrario de eso. –