2010-01-27 9 views
50

.NET 4/Silverlight 4'teki IDictionary<TKey, TValue> kovaryansı desteklemiyor, yani IEnumerable<T> s ile yapabileceğim şeye.NET'te IDictionary <TKey, TValue> eşdeğeri değil

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

analog yapamıyorum.

Muhtemelen, KeyValuePair<TKey, TValue>'e eşdeğerde kovaryant olmadığından aşağı doğru kaymaktadır. En azından değerler için sözlükte kovaryansa izin verilmesi gerektiğini hissediyorum.

Bu bir hata mı yoksa bir özellik mi? Hiç gelebilir mi, belki .NET 37.4?

UPDATE (2 yıl sonra):

.NET 4.5 IReadOnlyDictionary<TKey, TValue> olacaktır, ancak :·/'den türetilmediği için :·/ kovaryantı olmayacaktır, çünkü IEnumerable<KeyValuePair<TKey, TValue>>'ten türetilmiştir ve KeyValuePair<TKey, TValue> bir arabirim değildir ve kovaryant olamaz.

BCL ekibi, gelmek için bir çok şeyi yeniden tasarlamak ve yerine ICovariantPair<TKey, TValue> kullanmak zorunda kalacaktı. Ayrıca, güçlü yazılan dizinleyiciler á la this[TKey key], kovaryant arayüzler için mümkün değildir. Benzer bir son sadece, bir şekilde içsel olarak gerçek bir uygulamayı çağırmak zorunda kalacak bir uzatma metodu GetValue<>(this IReadOnlyDictionary<TKey, TValue> self, TKey key) yerleştirilerek elde edilebilir, ki bu oldukça karmaşık bir yaklaşıma benzemektedir.

+7

Teşekkür .NET 4.5 üzerinde güncelleştirme sağlanması için: Eğer baz türleri ve türetilen türleri, aşağıdaki eserleri ile uğraşıyoruz varsayarsak. IMHO, salt okunur bir sözlükte kovaryansa sahip olmanın yararı olacaktır, bu yüzden desteklenecek gibi görünmeyecek kadar kötüdür. – dcstraw

cevap

46

Bu bir özelliktir. .NET 4.0 yalnızca güvenli kovaryansını destekler. Bu mümkün olsaydı Sözlüğe olmayan bir dize öğesi ekleyebilir olarak Bahsettiğiniz dökme tehlikeli olup:

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

Öte yandan, IEnumerable<T> salt okunur bir arayüz. T tip parametre yalnızca çıkış konumlarında olduğu (Current özelliğinin dönüş tipi) bu yüzden bir IEnumerable<object> olarak IEnumerable<string> tedavi etmek güvenlidir.

+0

Ahh tamam, elbette, salt okunur kullanım amaçlıydı. .NET kitaplığı kesinlikle bir salt okunur Sözlük türünü özlüyor. Birisi, bu konulardan biri hakkında bir başka soru yayınlamalı. ;-) – herzmeister

+1

Teoride kovaryans güvenlidir, ancak .Net 1.0'dan bir quirk, eserlerde hafif bir anahtar atar. Türetilmiş [] 'nin [Base] [] türetilmiş olarak kabul edildiğinden, Türetilmiş []' IList '; böyle bir 'IList ', okuma için doğru şekilde çalışır, ancak yazıldığında bir istisna atar. – supercat

11

Ama sonra sefil başarısız olur

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

söyleyebiliriz. Sorun, IDictionary<TKey, TValue>'daki TValue'un hem giriş hem de çıkış konumlarında kullanılmasıdır. zekâ için:

myDict.Add(key, value); 

ve

TValue value = myDict[key]; 

Yani bir hata veya bir özellik bu?

Bu tasarım gereği bu.

Hiç gelebilir mi, belki .NET 37.4?

Hayır, doğal olarak güvensiz bu.

2

.NET 4 sadece değil kovaryans üzerinden destekler. IEnumerable ile çalışır çünkü IEnumerable salt okunur.

+10

"kovaryansta" bir yanlış isimdir. Bu, contravariance olur ve .NET 4'te desteklenir ve belirli senaryolarda kullanışlıdır. – dcstraw

0

I ama daha özel türetilmiş türde

numara (tercihan her türeyen nesne daha az), benzer bir sorunu vardı 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

yararlı kovaryans belirli bir türü için yaklaşık bir çalışma Yöntemi jenerik yapmak ve ilgili kısıtlamayı koyarak bir fıkra koymaktır.

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

Bu harika bir çözüm! Bu kod yeniden kullanım için tam olarak ne yapmamı sağlar ve kovaryansın getireceği problemi tamamen ortadan kaldırır. – jltrem