2010-01-27 11 views
50

Die IDictionary<TKey, TValue> in .NET 4/Silverlight 4 nicht Kovarianz nicht unterstützt, dh ichIDictionary <TKey, TValue> in .NET 4 nicht kovariant

nicht tun kann
IDictionary<string, object> myDict = new Dictionary<string, string>(); 

analog zu dem, was ich tun kann, mit IEnumerable<T> s jetzt.

Wahrscheinlich kocht die KeyValuePair<TKey, TValue> auch nicht kovariant. Ich denke, dass die Kovarianz zumindest für die Werte in Wörterbüchern erlaubt sein sollte.

Also ist das ein Fehler oder eine Funktion? Wird es jemals kommen, vielleicht in .NET 37.4?

UPDATE (2 Jahre später):

Es wird eine IReadOnlyDictionary<TKey, TValue> in .NET 4.5, aber es wird auch nicht :·/ kovariant werden, weil sie von IEnumerable<KeyValuePair<TKey, TValue>> ableitet, und KeyValuePair<TKey, TValue> ist keine Schnittstelle und kann somit nicht kovariant sein.

Das BCL-Team würde viel umgestalten müssen, um zu kommen und stattdessen ICovariantPair<TKey, TValue> zu verwenden. Auch stark typisierte Indexer á la this[TKey key] sind für kovariante Schnittstellen nicht möglich. Ein ähnliches Ende kann nur erreicht werden, indem irgendwo eine Erweiterungsmethode platziert wird, die intern eine tatsächliche Implementierung nennen müsste, was wohl ein ziemlich chaotischer Ansatz ist.

+7

Dank für die Bereitstellung der Aktualisierung auf .NET 4.5. IMHO wäre es nützlich, Kovarianz in einem schreibgeschützten Wörterbuch zu haben, also ist es zu schade, dass es nicht so aussieht, als ob es unterstützt wird. – dcstraw

Antwort

46

Es ist eine Funktion. .NET 4.0 unterstützt nur sichere Kovarianz. Die Besetzung Sie potenziell gefährliche erwähnt ist, wie Sie ein nicht-String-Element zum Wörterbuch hinzufügen könnten, wenn das möglich ist:

IDictionary<string, object> myDict = new Dictionary<string, string>(); 
myDict["hello"] = 5; // not an string 

Auf der anderen Seite ist IEnumerable<T> eine Read-Only-Schnittstelle. Der T Parameter ist nur in seinen Ausgabepositionen (Rückgabetyp der Current Eigenschaft), so dass IEnumerable<string> als IEnumerable<object> behandelt werden kann.

+0

Ahh ok, natürlich wollte ich ja nur lesen. Die .NET-Bibliothek verfehlt sicherlich einen schreibgeschützten Dictionary-Typ. In diesen Tagen sollte jemand eine weitere Frage zu diesem Thema stellen. ;-) – herzmeister

+1

Theoretisch ist die Kovarianz sicher, aber eine Eigenart von .Net 1.0 könnte einen kleinen Haken in den Arbeiten werfen. Da 'Derived []' von 'Base []' geerbt wird, implementiert 'Derived []' 'IList 10'; Solch ein 'IList ' wird zum Lesen korrekt funktionieren, aber beim Schreiben wird eine Ausnahme ausgelöst. – supercat

11

Aber dann könnte man

myDict.Add("Hello, world!", new DateTime(2010, 1, 27)); 

sagen, die kläglich scheitern würde. Das Problem ist, dass die in IDictionary<TKey, TValue> in beiden Eingabe- und Ausgabepositionen verwendet wird. Nämlich:

myDict.Add(key, value); 

und

TValue value = myDict[key]; 

ist, so dass ein Bug oder ein Feature?

Es ist von Entwurf.

Wird es jemals kommen, vielleicht in .NET 37.4?

Nein, es ist von Natur aus unsicher.

2

.NET 4 unterstützt nur aus Kovarianz nicht in. Es funktioniert mit IEnumerable, da IEnumerable schreibgeschützt ist.

+10

"in Kovarianz" ist eine falsche Bezeichnung. Das wäre eine Kontravarianz, die in .NET 4 unterstützt wird und in bestimmten Szenarien nützlich ist. – dcstraw

0

Eine Arbeit um für eine bestimmte Art von nützlicher Kovarianz auf IDictionary

public static class DictionaryExtensions 
{ 
    public static IReadOnlyDictionary<TKey, IEnumerable<TValue>> ToReadOnlyDictionary<TKey, TValue>(
     this IDictionary<TKey, List<TValue>> toWrap) 
    { 
     var intermediate = toWrap.ToDictionary(a => a.Key, a => a.Value!=null ? 
             a.Value.ToArray().AsEnumerable() : null); 
     var wrapper = new ReadOnlyDictionary<TKey, IEnumerable<TValue>>(intermediate); 
     return wrapper; 
    } 
} 
5

Ich hatte ein ähnliches Problem, aber mit mehr spezialisiert abgeleiteten Typen (und nicht Objekt, das alles von ableitet)

Der Trick ist, die Methode generisch zu machen und eine Where-Klausel zu setzen, die die relevante Einschränkung setzt. Unter der Annahme, dass Sie mit Basistypen und abgeleitete Typen zu tun haben, die folgenden Werken:

using System; 
using System.Collections.Generic; 

namespace GenericsTest 
{ 
class Program 
{ 
    static void Main(string[] args) 
    { 
     Program p = new Program(); 

     p.Run(); 
    } 

    private void Run() 
    { 

     Dictionary<long, SpecialType1> a = new Dictionary<long, SpecialType1> { 
     { 1, new SpecialType1 { BaseData = "hello", Special1 = 1 } }, 
     { 2, new SpecialType1 { BaseData = "goodbye", Special1 = 2 } } }; 

     Test(a); 
    } 

    void Test<Y>(Dictionary<long, Y> data) where Y : BaseType 
    { 
     foreach (BaseType x in data.Values) 
     { 
      Console.Out.WriteLine(x.BaseData); 
     } 
    } 
} 

public class BaseType 
{ 
    public string BaseData { get; set; } 
} 

public class SpecialType1 : BaseType 
{ 
    public int Special1 { get; set; } 
} 
} 
+0

Dies ist ein guter Workaround! Es erlaubt mir, genau das zu tun, was ich für die Code-Wiederverwendung tun muss, und vermeidet vollständig das Problem, das die Kovarianz einführen würde. – jltrem

Verwandte Themen