C # Kovarianz und Kontravarianz bei der Implementierung von Schnittstellen

8

Ich habe kürzlich beschlossen, meinen Speicher bezüglich der C # Grundlagen aufzufrischen, also könnte das trivial sein, aber ich bin auf das folgende Problem gestoßen:

StringCollection wurde in .NET v1.0 verwendet, um eine stark typisierte Sammlung für Strings zu erstellen, im Gegensatz zu object based ArrayList (dies wurde später durch die Aufnahme generischer Sammlungen erweitert):

Wenn Sie einen kurzen Blick auf StringCollection definition werfen, können Sie Folgendes sehen:

%Vor%

Sie sehen es implementiert IList , die die folgende Deklaration enthält (unter einigen anderen Deklarationen):

%Vor%

Aber nicht:

%Vor%

Meine erste Annahme war, dass dies aufgrund der .NET-Framework-Kovarianzregeln möglich ist.

Um sicherzugehen, habe ich versucht, meine eigene Klasse zu schreiben, die IList implementiert und

geändert hat %Vor%

, um einen Stringtyp anstelle eines Objekttyps abzurufen, aber zu meiner Überraschung habe ich beim Kompilieren des Projekts einen Fehler bei der Kompilierung bekommen:

%Vor%

Irgendwelche Ideen, was das verursacht?

Danke!

    
Mikey S. 14.08.2011, 01:53
quelle

4 Antworten

7

Das Verhalten wird durch die explizite Implementierung von IList.Add(object) anstelle von co / contravariance verursacht. In der MSDN-Dokumentation implementiert StringCollection explizit IList.Add(object) ; Die Methode Add(string) ist nicht verwandt. Die Implementierung ähnelt etwa so:

%Vor%

Diese Unterscheidung kann beobachtet werden:

%Vor%

Zusatz

Das oben Gesagte behandelt nicht, warum dieses Muster implementiert wird. Die C # -Sprachspezifikation besagt, dass [§13.4.1, Hervorhebung hinzugefügt]:

  

In einigen Fällen ist der Name eines Schnittstellenelements möglicherweise nicht angemessen   für die implementierende Klasse, in welchem ​​Fall das Schnittstellenelement sein kann   implementiert unter Verwendung einer expliziten Schnittstellenmember-Implementierung. [...]

     

Es ist nicht möglich, über einen vollständig qualifizierten Namen in einem Methodenaufruf, Eigenschaftenzugriff oder Indexerzugriff auf eine explizite Schnittstellenmemberimplementierung zuzugreifen. Auf eine explizite Schnittstellenmemberimplementierung kann nur über eine Schnittstelle zugegriffen werden Instanz, und wird in diesem Fall einfach durch seinen Mitgliedsnamen referenziert.

StringCollection hält sich an das erforderliche IList-Verhalten - IList gibt keine Garantie dafür, dass ein beliebiges Objekt hinzugefügt werden kann. StringCollection gibt stärkere Garantien - in erster Linie, dass es nur Zeichenfolgen enthält. Die Klasse enthält eigene stark typisierte Methoden für Add , Contains , Item und andere für den Standard-Anwendungsfall, in dem auf sie als StringCollection und nicht als IList zugegriffen wird. Aber es funktioniert immer noch perfekt als IList , akzeptiert und gibt Objekte zurück, gibt aber einen Fehlercode zurück (wie IList erlaubt), wenn versucht wird, ein Element hinzuzufügen, das keine Zeichenkette ist.

Letztendlich liegt es im Ermessen des Klassenautors, ob eine Schnittstelle in der Klasse auftaucht (d. h. explizit implementiert wird). Im Fall von Framework-Klassen sind explizite Implementierungen in der MSDN-Dokumentation enthalten, aber nicht als Klassenmitglieder verfügbar (z. B. in Autokomplettierungskontexten).

    
drf 14.08.2011, 02:09
quelle
0

Wenn Sie .net 2.0+ verwenden, würde ich nur Generika verwenden:

%Vor%

Das sollte dir alles geben, was du willst.

    
Leslie Hanks 14.08.2011 01:56
quelle
0

IList.Add(object) kann andere Parameter als Strings akzeptieren - es kann jeden beliebigen Typ annehmen. Wenn Sie also Ihre Implementierung der Schnittstelle so deklarieren, dass sie nur Zeichenfolgen akzeptiert, stimmt sie nicht mehr mit der Schnittstellenspezifikation überein, weil ich zum Beispiel kein Stream übergeben konnte.

Die Varianz kann die andere -Weise verwenden: Wenn die Interface-Methode zum Akzeptieren von Strings deklariert wurde, dann wäre das Akzeptieren von Objekten in Ordnung, da Strings ebenfalls Objekte sind und somit auch jede Eingabe für die Methode der Schnittstelle akzeptabler Input für Ihre Implementierung sein. (Sie müssen jedoch immer noch eine explizite Schnittstellenimplementierung mit einer Methode bereitstellen, die einen String akzeptiert, da in C # eine Schnittstellenmethodenimplementierung genau der Deklaration der Schnittstellenmethode entspricht.)

    
cdhowie 14.08.2011 01:57
quelle
0

Was IList im Grunde angibt, ist, dass Sie Add aufrufen und jedes Objekt als Parameter übergeben können, von einem eingerahmten Int zu einem anderen IList zu einem System.DivideByZeroException . Wenn Sie nur eine Add( string ) -Methode angeben, haben Sie diese Anforderung nicht erfüllt, da Sie nur Zeichenfolgen hinzufügen können.

Mit anderen Worten, Sie könnten StringCollection.Add( new Object() ); nicht aufrufen, was perfekt möglich wäre, wenn die Schnittstelle korrekt implementiert wäre. : D

    
Gordon Gustafson 14.08.2011 01:58
quelle