2014-08-29 21 views
5

Der Grund dieser Code ist aufgrund der Tatsache arbeitet, dass die Enumerator die Sammlung nicht ändern können:Warum ist Kovarianz mit ReadOnlyCollection nicht erlaubt?

var roList = new List<string>() { "One", "Two", "Three" }; 
IEnumerable<object> objEnum = roList; 

Wenn wir versuchen, die gleiche Sache mit einem List<object> zu tun, statt IEnumerable<object>, würden wir einen Compiler-Fehler erzählt bekommen uns, dass wir nicht implizit Typ List<string> zu List<object> konvertieren können. In Ordnung, das macht Sinn und es war eine gute Entscheidung von Microsoft, meiner Meinung nach eine Lektion aus Arrays.

Was ich jedoch nicht herausfinden kann ist, warum in der Welt ist diese Hardline-Regel auf etwas wie ReadOnlyCollection anwendbar? Wir sind nicht in der Lage, die Elemente in ReadOnlyCollection zu ändern. Was ist also das Sicherheitsrisiko, das dazu geführt hat, dass Microsoft uns daran gehindert hat, Kovarianz mit schreibgeschütztem Code zu verwenden? Gibt es eine Möglichkeit, diesen Sammlertyp zu ändern, für den Microsoft beispielsweise durch Zeiger verantwortlich ist?

+1

"eine Lektion gelernt von Arrays": in Bezug auf Array Kovarianz, das war eigentlich eine bewusste Design-Entscheidung, es zu erlauben, weil Java es hatte. Es stellte sich heraus, dass es eine ziemlich schlechte Idee war ... http://blogs.msdn.com/b/ericlippert/archive/2007/10/17/covariance-and-contravariance-in-c-part-two-array- covariance.aspx –

+0

@ThomasLevesque Gut gelesen, danke. –

Antwort

7

Varianz und Kontravarianz funktionieren nur an Schnittstellen und Delegaten, nicht an Klassen.

Allerdings können Sie das tun (mit .NET 4.5):

ReadOnlyCollection<string> roList = ... 
IReadOnlyCollection<object> objects = roList; 

(weil IReadOnlyCollection<T> kovariant)


Um Ihre Frage zu beantworten, warum Varianz auf Klassen nicht erlaubt ist , hier ist Jon Skeets Erklärung aus seinem Buch C# in Depth (zweite Ausgabe, §13.3.5, Seite 394).

NO VARIANCE für Typ Merkmale in Klassen

Nur Schnittstellen und Teilnehmer können Variantentyp Parameter. Even Wenn Sie eine Klasse haben, die nur den Typparameter für die Eingabe verwendet (oder nur für die Ausgabe verwendet), können Sie die in oder out Modifikatoren nicht angeben. Zum Beispiel Comparer<T>, die gemeinsame Implementierung von IComparer<T>, ist invariant - es gibt keine Konvertierung von Comparer<IShape> zu Comparer<Circle>.

Abgesehen von irgendwelchen Implementierungsschwierigkeiten, die entstehen könnte, würde ich sagen, dass es konzeptionell eine gewisse Menge an Sinn macht. Schnittstellen stellen eine Möglichkeit dar, ein Objekt aus einer bestimmten Perspektive zu betrachten, während Klassen eher im aktuellen -Objekt des Objekts verwurzelt sind. Dieses Argument wird durch die Vererbung ein wenig geschwächt, so dass Sie ein Objekt als eine Instanz einer der darin enthaltenen Klassen behandeln können Vererbung Hierarchie, zugegebenermaßen. In jedem Fall erlaubt die CLR es nicht.

+0

Sie haben Recht.Ich habe es getestet und es funktioniert mit den schreibgeschützten Schnittstellen 'IReadOnlyCollection ' und 'IReadOnlyList '. Gibt es einen bestimmten Grund, warum es nicht mit Klassen funktioniert? –

+0

@ B.K., Ich weiß es nicht, aber meine Vermutung ist, dass es einige CLR interne Implementierungsdetails verwandt ist ... –

+0

Es ist wahrscheinlich einfacher, Unterstützung für Schnittstellen zu implementieren. Wenn Sie von einem konkreten Typ in einen konkreten Basistyp umwandeln, muss nun der Basistyp entscheiden, ob er seine Methoden oder die Basistypen aufruft, die von virtuellen/Überschreibungs-/Verdeckungsszenarien abhängen, die nur in der realen Vererbung definiert sind. Eine ReadOnlyCollection erbt nicht von ReadOnlycollection , so dass es schwierig ist zu definieren, was passiert. Schnittstellen leiten Aufrufe immer an den konkreten Typ weiter, und es gibt keine Unklarheit darüber, ob base oder derived aufgerufen wird. Es ist viel klarer, was es bedeutet, auf eine kovariante Schnittstelle zuzugreifen. IMO – AaronLS