Qual è la differenza tra <out T>
e <T>
? Per esempio:
public interface IExample<out T>
{
...
}
vs.
public interface IExample<T>
{
...
}
Qual è la differenza tra <out T>
e <T>
? Per esempio:
public interface IExample<out T>
{
...
}
vs.
public interface IExample<T>
{
...
}
Risposte:
La out
parola 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 Add
metodo. 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 .
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();
)
IList<object>
è un bizzarro esempio, se vuoi object
s non hai bisogno di generici.
Per ricordare facilmente l'uso in
e la out
parola chiave (anche covarianza e contraddizione), possiamo immaginare l'ereditarietà come wrapping:
String : Object
Bar : Foo
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>
.
" out T
" significa che il tipo T
è "covariante". Ciò limita la T
visualizzazione 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>
.
out
forze T
possono essere restituite solo fino a quando non leggo questa risposta. L'intero concetto ha più senso ora!
Dal link che hai pubblicato ....
Per i parametri di tipo generico, la parola chiave out specifica che il parametro type è covariante .
EDIT : Ancora una volta, dal link che hai pubblicato
Per ulteriori informazioni, vedere Covarianza e contraddizione (C # e Visual Basic). http://msdn.microsoft.com/en-us/library/ee207183.aspx