2010-01-27 13 views
50

Il IDictionary<TKey, TValue> NET 4/Silverlight 4 non supporta covarianza, cioè che non può fare un analogoIDictionary <TKey, TValue> NET 4 non covariante

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

di quanto posso fare con IEnumerable<T> s ora.

Probabilmente si riduce allo KeyValuePair<TKey, TValue> non essendo covariante neanche. Sento che la covarianza dovrebbe essere permessa nei dizionari almeno per i valori.

Quindi è un bug o una funzionalità? Arriverà mai, forse in .NET 37.4?

UPDATE (2 anni più tardi):

Ci sarà un IReadOnlyDictionary<TKey, TValue> NET 4.5, ma non sarà covariante sia :·/, perché deriva dalla IEnumerable<KeyValuePair<TKey, TValue>>, e KeyValuePair<TKey, TValue> non è un'interfaccia e quindi non può essere covariante.

Il team BCL dovrebbe riprogettare molto per venire e utilizzare un po 'di ICovariantPair<TKey, TValue>. Anche gli indicizzatori fortemente tipizzati alla this[TKey key] non sono possibili per le interfacce covarianti. Un fine simile può essere raggiunto solo posizionando un metodo di estensione GetValue<>(this IReadOnlyDictionary<TKey, TValue> self, TKey key) da qualche parte che in qualche modo dovrebbe internamente chiamare un'attuazione reale, che probabilmente sembra un approccio piuttosto caotico.

+7

Grazie per la fornitura l'aggiornamento su .NET 4.5. IMHO sarebbe utile avere covarianza su un dizionario di sola lettura, quindi è un peccato che non sia come se fosse supportato. – dcstraw

risposta

46

È una funzionalità. .NET 4.0 supporta solo sicuro covarianza. Il cast lei ha citato è potenzialmente pericoloso come si potrebbe aggiungere un elemento non stringa al dizionario se possibile:

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

D'altra parte, IEnumerable<T> è un'interfaccia di sola lettura. Il parametro di tipo T è solo nelle sue posizioni di uscita (tipo di ritorno della proprietà Current) quindi è sicuro trattare IEnumerable<string> come IEnumerable<object>.

+0

Ahh ok, certo, in effetti avevo intenzione di usare solo la lettura. La libreria .NET sicuramente manca un tipo di dizionario di sola lettura. Qualcuno dovrebbe pubblicare un'altra domanda su quel problema uno di questi giorni. ;-) – herzmeister

+1

In teoria la covarianza è sicura, ma una stranezza da .Net 1.0 può gettare una leggera chiave nelle opere. Poiché "Derivato []" è considerato come ereditato da "Base []", un "Derivato []" implementerà "IList "; un simile 'IList ' funzionerà correttamente per la lettura, ma genererà un'eccezione quando scritto. – supercat

11

Ma allora si potrebbe dire

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

che fallire miseramente. Il problema è che il TValue in IDictionary<TKey, TValue> viene utilizzato in entrambe le posizioni di input e di output. Vale a dire:

myDict.Add(key, value); 

e

TValue value = myDict[key]; 

Così è che un bug o una funzione?

È di progettazione.

Arriverà mai, forse in .NET 37.4?

No, è intrinsecamente pericoloso.

2

.NET 4 supporta solo out covarianza non in. Funziona con IEnumerable perché IEnumerable è di sola lettura.

+10

"in covarianza" è un termine improprio. Ciò sarebbe controverso, ed è supportato in .NET 4 ed è utile in alcuni scenari. – dcstraw

0

un lavoro in giro per un tipo specifico di covarianza utili su 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

Ho avuto un problema simile, ma con tipi derivati ​​più specializzati (anziché oggetto di cui tutto deriva da)

Il trucco è quello di rendere il metodo generico e mettere una clausola where mettendo la restrizione relativa. Supponendo che hai a che fare con i tipi di base e tipi derivati, i seguenti lavori:

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

questa è un'ottima soluzione! Mi consente di fare esattamente ciò che devo fare per riutilizzare il codice e di evitare completamente il problema che la covarianza introdurrebbe. – jltrem

Problemi correlati