2010-01-27 64 views
50

IDictionary<TKey, TValue>在.NET 4/Silverlight的4不支持協方差,即我不能做的IDictionary <TKEY的,TValue>中在.NET 4不是協變

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

模擬到我可以用做IEnumerable<T>現在。

大概歸結爲KeyValuePair<TKey, TValue>也不是協變。我認爲字典中至少應該允許協變量的值。

那麼,這是一個錯誤或功能?它會不會來,也許在.NET 37.4?

UPDATE(2年後):

會有在.NET 4.5的IReadOnlyDictionary<TKey, TValue>,但它不會被協變要麼:·/,因爲它是從IEnumerable<KeyValuePair<TKey, TValue>>派生,並且KeyValuePair<TKey, TValue>不是接口,因此不能是協變的。

BCL團隊將不得不重新設計很多來使用而不是使用ICovariantPair<TKey, TValue>。對於協變接口,強類型索引器也是不可能的。類似的結局只能通過在某個地方放置一個擴展方法GetValue<>(this IReadOnlyDictionary<TKey, TValue> self, TKey key)來實現,這個方法在某種程度上必須在內部調用一個實際的實現,這可能看起來像一個相當混亂的方法。

+7

感謝您提供.NET 4.5的更新。恕我直言,在只讀字典上使用協變會很有用,所以它看起來不像支持它。 – dcstraw 2012-03-21 20:12:53

回答

46

這是一個功能。 .NET 4.0僅支持安全協方差。你提到中投潛在危險的,因爲你可以在非字符串元素添加到字典中,如果這是可能的:

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

在另一方面,IEnumerable<T>是一個只讀接口。 T類型參數僅在其輸出位置(返回類型爲Current屬性),因此將IEnumerable<string>視爲IEnumerable<object>是安全的。

+0

呵呵,當然,我確實打算只讀使用。 .NET庫肯定會錯過只讀字典類型。有人應該在這些日子之後發表關於這個問題的另一個問題。 ;-) – herzmeister 2010-01-27 19:11:50

+1

理論上協變是安全的,但.Net 1.0的一個怪癖可能會在作品中拋出一個小小的扳手。由於'Derived []'被認爲是從'Base []'繼承的,'Derived []'將實現'IList ';這樣的'IList '可以正常讀取,但寫入時會拋出異常。 「協方差」中的「 – supercat 2012-12-03 18:01:23

11

但你可以說

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

這將慘遭失敗。問題是IDictionary<TKey, TValue>中的TValue用於輸入和輸出位置。即:

myDict.Add(key, value); 

TValue value = myDict[key]; 

所以是錯誤或功能?

這是設計。

它會不會來,也許在.NET 37.4?

不,它本質上是不安全的。

2

.NET 4僅支持out協方差不是in。它適用於IEnumerable,因爲IEnumerable是隻讀的。

+10

」是一個用詞不當。這將是矛盾的,它在.NET 4中得到了支持,並且在某些情況下很有用。 – dcstraw 2012-03-21 20:08:51

0

一個解決特定類型有用的協方差對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

我也有類似的問題,但更多的專業派生類型(而不是它的一切派生自對象)

訣竅是使該方法通用,並將where子句放入相關限制。 假設您正在處理基類型和派生類型,以下工作:

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

這是一個很好的解決方法!它使我能夠完成我需要做的代碼重用,並且完全避免了協方差會引入的問題。 – jltrem 2014-11-11 20:11:12

相關問題