<out T> vs <T> in Generics


189

Qual è la differenza tra <out T>e <T>? Per esempio:

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

vs.

public interface IExample<T>
{
    ...
}

1
Un buon esempio potrebbe essere IObservable <T> e IObserver <T>, definiti nel sistema ns in mscorlib. IObservable dell'interfaccia pubblica <out T> e IObserver dell'interfaccia pubblica <in T>. Allo stesso modo, IEnumerator <out T>, IEnumerable <out T>
VivekDev

2
La migliore spiegazione che ho incontrato: agirlamonggeeks.com/2019/05/29/… . (<In T> <- significa che T può essere passato solo come parametro a un metodo; <out T> <- significa che T può essere restituito solo come risultato del metodo)
Uladzimir Sharyi il

Risposte:


212

La outparola chiave in generici viene utilizzata per indicare che il tipo T nell'interfaccia è covariante. Vedi Covarianza e contraddizione per i dettagli.

L'esempio classico è IEnumerable<out T>. Poiché IEnumerable<out T>è covariante, è consentito eseguire le seguenti operazioni:

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

La seconda riga sopra fallirebbe se ciò non fosse covariante, anche se logicamente dovrebbe funzionare, poiché la stringa deriva dall'oggetto. Prima che la varianza delle interfacce generiche fosse aggiunta a C # e VB.NET (in .NET 4 con VS 2010), si trattava di un errore di compilazione.

Dopo .NET 4, è IEnumerable<T>stato contrassegnato come covariante ed è diventato IEnumerable<out T>. Dal momento IEnumerable<out T>che utilizza solo gli elementi al suo interno e non li aggiunge / modifica mai, è sicuro che tratti una collezione enumerabile di stringhe come una collezione enumerabile di oggetti, il che significa che è covariante .

Questo non funzionerebbe con un tipo come IList<T>, poiché IList<T>ha un Addmetodo. Supponiamo che ciò sia consentito:

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

È quindi possibile chiamare:

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

Ciò ovviamente fallirebbe, quindi IList<T>non può essere contrassegnato come covariante.

C'è anche, tra l'altro, un'opzione per in- che viene utilizzata da cose come interfacce di confronto. IComparer<in T>, ad esempio, funziona in modo opposto. Puoi usare un calcestruzzo IComparer<Foo>direttamente come IComparer<Bar>se Barè una sottoclasse di Foo, perché l' IComparer<in T>interfaccia è contraddittoria .


4
@ColeJohnson Perché Imageè una classe astratta;) Puoi fare new List<object>() { Image.FromFile("test.jpg") };senza problemi, oppure puoi fare new List<object>() { new Bitmap("test.jpg") };altrettanto. Il problema con il tuo è che new Image()non è permesso (neanche tu puoi farlo var img = new Image();)
Reed Copsey,

4
un generico IList<object>è un bizzarro esempio, se vuoi objects non hai bisogno di generici.
Jodrell,

5
@ReedCopsey Non stai contraddicendo la tua risposta nel tuo commento?
MarioDS,

64

Per ricordare facilmente l'uso ine la outparola chiave (anche covarianza e contraddizione), possiamo immaginare l'ereditarietà come wrapping:

String : Object
Bar : Foo

dentro fuori


11
Non è questo il modo sbagliato? Contravarianza = in = consente di utilizzare tipi meno derivati ​​al posto di più derivati. / Covariance = out = consente di utilizzare più tipi derivati ​​al posto di quelli meno derivati. Personalmente, guardando il tuo diagramma, l'ho letto come l'opposto di quello.
Sam Shiles,

Co u della variante (: per me
SNR

49

tener conto di,

class Fruit {}

class Banana : Fruit {}

interface ICovariantSkinned<out T> {}

interface ISkinned<T> {}

e le funzioni,

void Peel(ISkinned<Fruit> skinned) { }

void Peel(ICovariantSkinned<Fruit> skinned) { }

La funzione che accetta ICovariantSkinned<Fruit>sarà in grado di accettare ICovariantSkinned<Fruit>o ICovariantSkinned<Bananna>perchéICovariantSkinned<T> è un'interfaccia covariante ed Bananaè un tipo diFruit ,

la funzione che accetta ISkinned<Fruit>sarà solo in grado di accettare ISkinned<Fruit>.


38

" out T" significa che il tipo Tè "covariante". Ciò limita la Tvisualizzazione solo come valore restituito (in uscita) nei metodi della classe, interfaccia o metodo generici. L'implicazione è che puoi lanciare il tipo / interfaccia / metodo in un equivalente con un super-tipo di T.
Ad esempio ICovariant<out Dog>può essere lanciato ICovariant<Animal>.


14
Non mi rendevo conto che le outforze Tpossono essere restituite solo fino a quando non leggo questa risposta. L'intero concetto ha più senso ora!
MarioDS,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.