2009-07-22 21 views
15

Kann mir jemand erklären, das Konzept der Kovarianz und Kontravarianz in Programmiersprachen Theorie?Kovarianz und Kontravarianz in Programmiersprachen

+5

Ich rieche eine Hausaufgabenfrage. –

+0

[Kovarianz vs Kontravarianz] (http://izlooite.blogspot.com/2011/04/covariance-and-contravariance.html) –

+0

möglich Duplikat von [C#: Ist Varianz (Kovarianz/Kontravarianz) ein anderes Wort für Polymorphismus?] (http://stackoverflow.com/questions/1078423/c-sharp-is-variance-covariance-contraviance-another-word-for-polymorphis) – nawfal

Antwort

13

Kovarianzstrukturen ist ziemlich einfach und beste Gedanke aus der Sicht einiger Kollektionsklasse List. Wir können die List Klasse mit einigen Typ Parameter T parametrieren. Das heißt, unsere Liste enthält Elemente vom Typ T für einige T. Liste würde kovariant, wenn

S ein Subtyp von T iff Liste ist [S] ist ein Subtyp der Liste [T]

(Wo ich bin mit der mathematischen Definition iff bedeuten , wenn und nur wenn.)

Das heißt, ein List[Apple]ist einList[Fruit]. Wenn es eine Routine gibt, die einen List[Fruit] als Parameter akzeptiert, und ich habe eine List[Apple], dann kann ich dies als gültigen Parameter übergeben.

def something(l: List[Fruit]) { 
    l.add(new Pear()) 
} 

Wenn unsere Kollektionsklasse List wandelbar ist, dann macht Kovarianz keinen Sinn, weil wir vielleicht davon ausgehen, dass unsere Routine eine andere Frucht hinzufügen könnte, wie oben (die kein Apfel war). Daher sollten wir nur unveränderliche Sammlungsklassen als kovariant bezeichnen!

+0

Gute Definition, aber es vermisst die Tatsache, dass nicht nur Typen als co behandelt werden können/kontravariant. Zum Beispiel ist Java 'List ' nicht entweder, aber Java-Platzhalter können Sie entweder kovariante oder kontravariante Mode am Ort der Verwendung behandeln (anstatt Deklaration) - natürlich, die Reihe von Operationen auf den Typ zu beschränken, die sind eigentlich kovariant und kontravariant dafür. –

+1

Ich glaube, dass 'List 'ist eine Art * existentieller Typ *: d. h.' List [T] forSome T <: Fruit' - das * forSome T <: Fruit * ist in diesem Fall selbst ein Typ. Java ist in diesem Typ jedoch immer noch nicht kovariant. Zum Beispiel eine Methode, die eine 'List 'würde' List ' –

+0

Ich meine" würde eine 'Liste 'nicht akzeptieren? erweitert Apple>' natürlich –

14
+1

Das ist so eine gute Erklärung, nicht Bookish. Danke. –

+1

Heck - ich genoss sogar das Lesen. Mehr als Wikipedia's :) – xtofl

+0

Nun, FWIW, die Erklärung stammt aus einer Diskussion, die ich und meine Kollegen mit einem bedeutenden Mitglied (früher HP) geführt haben, als die Diskussion von Bertrand Meyers auf OOSC abzielte, in der ich denke, Meyers hebt die Bedeutung der Kontravarianz in OOP hervor von B.Meyers ist ein Muss Buch lesen, wenn man OOP praktisch verstehen soll – Abhay

6

Hier sind meine Artikel auf, wie wir neue Varianz in C# 4.0-Funktionen hinzugefügt haben. Beginne von unten.

http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx

+0

OT: Eric, du hast ein Rätsel zu einer der Fragen früher gestellt. Ich konnte es nicht lösen und stellte eine Frage darüber. Könntest du es bitte sehen? http://stackoverflow.com/questions/1167666/c-puzzle-reachable-goto-pointing-to-an-reachable-label – SolutionYogi

+0

OMG Eric Lippert !!!! – Janie

+1

Beobachten Sie es mit diesen Ausrufezeichen dort. Du kannst jemanden mit einem dieser Dinge aus der Ruhe bringen. –

0

Bart De Smet hat einen großen Blog-Eintrag über Kovarianz & Kontra here.

4
4

Es gibt einen Unterschied zwischen Kovarianz und Kontra gemacht.
Sehr grob, eine Operation ist kovariant, wenn es die Reihenfolge der Typen erhält, und kontravariant, wenn es diese Reihenfolge umkehrt.

Die Reihenfolge selbst soll allgemeinere Typen darstellen, die größer als spezifischere Typen sind.
Hier ist ein Beispiel für eine Situation, in der C# die Kovarianz unterstützt. Erstens ist dies ein Array von Objekten:

object[] objects=new object[3]; 
objects[0]=new object(); 
objects[1]="Just a string"; 
objects[2]=10; 

Natürlich ist es möglich, unterschiedliche Werte in das Array eingefügt werden, da am Ende alles, was sie von System.Object in .NET Framework abzuleiten. Mit anderen Worten, System.Object ist ein sehr allgemeiner oder großer Typ. Jetzt ist hier eine Stelle, wo Kovarianz unterstützt:
einen Wert eines kleineren Typ einer Variablen eines größeren Typs zuweisen

string[] strings=new string[] { "one", "two", "three" }; 
objects=strings; 

Die variablen Objekte, die vom Typ object[] kann einen Wert speichern das ist in der Tat vom Typ string[].

Denken Sie darüber nach - bis zu einem Punkt, es ist, was Sie erwarten, aber dann wieder ist es nicht. Nach alledem, während sich string von object, string[]ableitet, ergibt sich NICHT von object[]. Die Sprachunterstützung für die Kovarianz in diesem Beispiel macht die Zuweisung ohnehin möglich, was Sie in vielen Fällen finden werden. Varianz ist eine Funktion, die die Sprache intuitiver arbeiten lässt.

Die Überlegungen zu diesen Themen sind sehr kompliziert. Auf der Grundlage des vorhergehenden Codes gibt es hier zwei Szenarien, die zu Fehlern führen.

// Runtime exception here - the array is still of type string[], 
// ints can't be inserted 
objects[2]=10; 

// Compiler error here - covariance support in this scenario only 
// covers reference types, and int is a value type 
int[] ints=new int[] { 1, 2, 3 }; 
objects=ints; 

Ein Beispiel für die Funktionsweise der Kontravarianz ist ein bisschen komplizierter.Stellen Sie sich diese zwei Klassen:

public partial class Person: IPerson { 
    public Person() { 
    } 
} 

public partial class Woman: Person { 
    public Woman() { 
    } 
} 

Woman von Person abgeleitet ist, offensichtlich. Betrachten wir nun haben Sie diese beiden Funktionen:

static void WorkWithPerson(Person person) { 
} 

static void WorkWithWoman(Woman woman) { 
} 

Eine der Funktionen etwas tut (es spielt keine Rolle, was) mit einem Woman, der andere ist allgemeiner und kann mit jeder Art von Person abgeleitet arbeiten. Auf der Woman Seite der Dinge, die Sie jetzt haben auch diese:

delegate void AcceptWomanDelegate(Woman person); 

static void DoWork(Woman woman, AcceptWomanDelegate acceptWoman) { 
    acceptWoman(woman); 
} 

DoWork ist eine Funktion, die eine Woman und einen Verweis auf eine Funktion übernehmen kann, die auch eine Woman nimmt, und dann geht es um die Instanz von Woman zu der Delegierte. Betrachten Sie den Polymorphismus der Elemente, die Sie hier haben. Person ist größer als Woman und WorkWithPerson ist größer als WorkWithWoman. WorkWithPerson gilt auch als größer als AcceptWomanDelegate für den Zweck der Varianz.

Schließlich haben Sie diese drei Zeilen Code:

Woman woman=new Woman(); 
DoWork(woman, WorkWithWoman); 
DoWork(woman, WorkWithPerson); 

A Woman Instanz erstellt wird. Dann wird DoWork aufgerufen, wobei die Woman Instanz sowie ein Verweis auf die WorkWithWoman-Methode übergeben werden. Letzteres ist offensichtlich kompatibel mit dem Delegattyp AcceptWomanDelegate - ein Parameter vom Typ Woman, kein Rückgabetyp. Die dritte Zeile ist ein bisschen seltsam. Die Methode WorkWithPerson benötigt eine Person als Parameter, keine Woman, wie von AcceptWomanDelegate gefordert. Trotzdem ist WorkWithPerson mit dem Delegattyp kompatibel. Contravarianz macht es möglich, so im Fall von Delegierten der größere Typ WorkWithPerson kann in einer Variablen des kleineren Typs AcceptWomanDelegate gespeichert werden. Noch einmal ist es die intuitive Sache: wenn WorkWithPerson kann mit jeder Person arbeiten, in einer Woman kann nicht falsch sein, richtig?

Jetzt fragen Sie sich vielleicht, wie sich das alles auf Generika bezieht. Die Antwort ist, dass die Varianz auch auf Generika angewendet werden kann. Das vorhergehende Beispiel verwendete object und string Arrays. Hier ist der Code verwendet allgemeine Listen anstelle der Arrays:

List<object> objectList=new List<object>(); 
List<string> stringList=new List<string>(); 
objectList=stringList; 

Wenn Sie dies ausprobieren, werden Sie feststellen, dass dies nicht unterstütztes Szenario in C#. In C# Version 4.0 sowie .NET Framework 4.0, Varianz Unterstützung in den Bereichen Generika gereinigt wurde, und es ist nun möglich, die neuen Keywords in und aus mit generischen Typparametern zu verwenden. Sie können die Richtung des Datenflusses für einen bestimmten Typparameter definieren und einschränken, so dass die Varianz funktioniert.Aber im Fall von List<T>, die Daten des Typs T fließt in beide Richtungen - es gibt Methoden auf den Typ List<T>, die T Werte zurückgeben, und andere, die solche Werte erhalten.

Der Punkt dieser Richtungsbeschränkungen ist Varianz zu ermöglichen, wo es Sinn macht, aber zu verhindern, dass Probleme wie die Laufzeit-Fehler in einem der vorhergehenden Array Beispiele erwähnt. Bei Typ-Parameter korrekt mit in oder aus eingerichtet sind, kann der Compiler überprüfen und zulassen bzw. nicht zulassen, deren Varianz bei Kompilierung. Microsoft hat der Zugabe dieser Schlüsselwörter zu vielen Standard-Schnittstellen in .NET Framework, wie IEnumerable<T> zu den Bemühungen fort:

public interface IEnumerable<out T>: IEnumerable { 
    // ... 
} 

Für diese Schnittstelle ist der Datenfluss vom Typ T Objekte klar: sie immer nur sein kann abgerufen von Methoden, die von dieser Schnittstelle unterstützt werden, nicht an sie übergeben. Als Ergebnis ist es möglich, ein Beispiel ähnlich den List<T> Versuch zuvor beschrieben zu konstruieren, aber mit IEnumerable<T>:

IEnumerable<object> objectSequence=new List<object>(); 
IEnumerable<string> stringSequence=new List<string>(); 
objectSequence=stringSequence; 

Dieser Code ist akzeptabel 4.0 auf den C# -Compiler seit Version weil IEnumerable<T> covariant zurückzuführen ist, um die Spezifizierer auf den Typ Parameter T.

Bei der Arbeit mit generischen Typen ist es wichtig, sich der Varianz und der Art und Weise bewusst zu sein, in der der Compiler verschiedene Tricks anwendet, damit Ihr Code so funktioniert, wie Sie es erwarten.

Es gibt mehr über Varianz als in diesem Kapitel behandelt, aber dies reicht aus, um alle weiteren Codes verständlich zu machen.

Ref:

0

C# und das CLR für Kovarianz und Kontra Varianz von Referenztypen wenn Bindung, ein Verfahren zu einem Delegierten ermöglichen. Kovarianz bedeutet, dass eine Methode einen Typ zurückgeben kann, der ist, der vom Rückgabetyp des Stellvertreters abgeleitet wurde. Contravarianz bedeutet, dass eine Methode einen Parameter annehmen kann, der eine Basis des Parametertyps des Delegate ist. Wenn beispielsweise ein Delegat definiert wie folgt definiert wird:

Delegate-Objekt MyCallback (FileStream s);

es möglich ist, eine Instanz dieser Delegattyp ein Verfahren gebunden zu konstruieren, die

wie diese prototypisiert wird:

String Somemethod (Stream s);

Hier ist der Rückgabetyp von SomeMethod (String) ein Typ, der vom Rückgabetyp des Delegaten (Object) abgeleitet ist; Diese Kovarianz ist erlaubt.SomeMethods Parametertyp (Stream) ist ein Typ, der eine Basisklasse des Parametertyps des Delegate (FileStream) ist. diese Kontravarianz ist erlaubt.

Beachten Sie, dass Kovarianz und Kontravarianz nur für Referenztypen, nicht für Werttypen oder für void unterstützt werden. So kann ich zum Beispiel die folgende Methode nicht an den MyCallback-Delegaten binden:

Int32 SomeOtherMethod (Stream s);

Obwohl SomeOtherMethod Rückkehr Typ (Int32) von MyCallback Rückkehr Typ (Objekt), diese Form der Kovarianz ist nicht erlaubt, weil Int32 ein Wert abgeleiteten Typ ist.

Offensichtlich ist der Grund, warum Werttypen und nichtig können nicht für Kovarianz verwendet werden und Kontravarianz ist, weil die Speicherstruktur für diese Dinge ändert, während die Speicherstruktur für Referenztypen immer ein Zeiger ist. Glücklicherweise erzeugt der C# -Compiler einen Fehler, wenn Sie versuchen, etwas zu tun, das nicht unterstützt wird.