2012-06-08 22 views
121

Was ist der Unterschied zwischen <out T> und <T>? Zum Beispiel:<out T> vs <T> in Generics

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

gegen

public interface IExample<T> 
{ 
    ... 
} 

Die einzige Info, die ich von MSDN bekommen haben war, dass

Sie das Schlüsselwort out in generischen Schnittstellen und Delegaten verwenden kann.

+1

gutes Beispiel wäre IObservable und IObserver , definiert in System ns in mscorlib. Öffentliche Schnittstelle IObservable und öffentliche Schnittstelle IObserver . In ähnlicher Weise IEnumerator , IEnumerable VivekDev

Antwort

158

out Das Schlüsselwort in generics wird verwendet, um anzuzeigen, dass der Typ T in der Schnittstelle kovarianten ist. Details siehe Covariance and contravariance.

Das klassische Beispiel ist IEnumerable<out T>. Da IEnumerable<out T> covariant ist, sind Sie erlaubt die folgenden Funktionen ausführen:

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

Die zweite Zeile oben würde fehlschlagen, wenn dies nicht war kovariant, obwohl logisch sollte es funktionieren, da String aus Objekt ableitet. Bevor variance in generic interfaces zu C# und VB.NET hinzugefügt wurde (in .NET 4 mit VS 2010), war dies ein Fehler bei der Kompilierung.

Nach .NET 4, IEnumerable<T> wurde markiert kovariant, und wurde IEnumerable<out T>. Da IEnumerable<out T> nur die darin enthaltenen Elemente verwendet und sie niemals hinzufügt/ändert, ist es sicher, eine aufzählbare Sammlung von Strings als eine aufzählbare Sammlung von Objekten zu behandeln, was bedeutet, dass sie kovariant ist.

Dies würde mit einem Typ wie IList<T> nicht funktionieren, da IList<T> eine Add Methode hat. Angenommen, dies würde erlaubt sein:

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

Sie könnten dann rufen:

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

Dies würde natürlich nicht - so IList<T> nicht markiert covariant werden.

Es gibt auch, btw, eine Option für in - die von Dingen wie Vergleichsschnittstellen verwendet wird.IComparer<in T>, zum Beispiel funktioniert der umgekehrte Weg. Sie können eine konkrete IComparer<Foo> direkt als IComparer<Bar> verwenden, wenn Bar eine Unterklasse von Foo ist, weil die IComparer<in T> Schnittstelle contravariant ist.

+3

@ColeJohnson Da 'Image' ist eine abstrakte Klasse;) Sie können' neue Liste () {Image.FromFile ("test.jpg")}; 'ohne Probleme, oder Sie können do 'new Liste () {new Bitmap (" test.jpg ")};' auch. Das Problem mit deiner ist, dass 'new Image()' nicht erlaubt ist (du kannst nicht 'var img = new Image();' entweder) –

+3

ein generisches 'IList ' ist ein bizarres Beispiel, wenn du willst ' Objekt's brauchst du keine Generika. – Jodrell

+3

@ReedCopsey Widersprichst du nicht deiner eigenen Antwort in deinem Kommentar? – MarioDS

5

Vom Link .... posted

Für generische Typparameter, das Schlüsselwort out gibt an, dass der Typ Parameter covariant ist.

EDIT: Wieder aus dem Link gepostet Sie

Weitere Informationen finden Sie Kovarianz und Kontra (C# und Visual Basic). http://msdn.microsoft.com/en-us/library/ee207183.aspx

23

"out T" bedeutet, dass der Typ T "kovariant" ist. Das beschränkt T nur als zurückgegebenen (ausgehenden) Wert in Methoden der generischen Klasse, Schnittstelle oder Methode. Die Implikation ist, dass Sie den Typ/Schnittstelle/Methode zu einem Äquivalent mit einem Super-Typ T umwandeln können.
Zum Beispiel ICovariant<out Dog> kann in ICovariant<Animal> umgewandelt werden.

+4

Ich habe nicht erkannt, dass "out" erzwingt, dass "T" nur zurückgegeben werden kann, bis ich diese Antwort gelesen habe. Das ganze Konzept macht jetzt mehr Sinn! – MarioDS

35

betrachten,

class Fruit {} 

class Banana : Fruit {} 

interface ICovariantSkinned<out T> {} 

interface ISkinned<T> {} 

und die Funktionen,

void Peel(ISkinned<Fruit> skinned) { } 

void Peel(ICovariantSkinned<Fruit> skinned) { } 

Die Funktion, die ICovariantSkinned<Fruit> können akzeptiert werden ICovariantSkinned<Fruit> oder ICovariantSkinned<Bananna> akzeptieren, weil ICovariantSkinned<T> eine kovariante Schnittstelle ist und Banana ist eine Art von Fruit ,

die Funktion, die akzeptieren s ISkinned<Fruit> kann nur ISkinned<Fruit> akzeptieren.

36

Für leicht die Verwendung von in und out Schlüsselwort (auch Kovarianz und Kontra) erinnern, können wir Bild Erbe als Verpackung:

String : Object 
Bar : Foo 

in/out

+0

Das macht es so klar. – antiduh

+4

Ist das nicht falsch herum? Contravariance = in = erlaubt es, weniger abgeleitete Typen anstelle von abgeleiteten zu verwenden. /Covariance = out = ermöglicht die Verwendung von mehr abgeleiteten Typen anstelle von weniger abgeleiteten Typen. Persönlich, schaue auf Ihr Diagramm, ich lese es als das Gegenteil von dem. –