.NET 4中的IDictionary <TKey,TValue>不是协变的

her*_*ter 58 .net dictionary .net-4.0 covariance

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

IDictionary<string, object> myDict = new Dictionary<string, string>();
Run Code Online (Sandbox Code Playgroud)

类似于我IEnumerable<T>现在可以做的事情.

可能归结为KeyValuePair<TKey, TValue>不协变.我觉得至少应该在字典中允许协方差值.

这是一个错误还是一个功能?它会不会来,也许在.NET 37.4中?

更新(2年后):

IReadOnlyDictionary<TKey, TValue>在.NET 4.5中会有一个,但它也不会是协变的:·/,因为它派生自IEnumerable<KeyValuePair<TKey, TValue>>,而KeyValuePair<TKey, TValue>不是一个接口,因此不能协变.

BCL团队将不得不重新设计出来并使用一些ICovariantPair<TKey, TValue>代替.this[TKey key]对于协变接口也不可能使用强类型索引器.类似的结束只能通过在GetValue<>(this IReadOnlyDictionary<TKey, TValue> self, TKey key)某个地方放置一个扩展方法来实现,这种方法在内部必须调用一个实际的实现,这可能看起来像一个非常混乱的方法.

Meh*_*ari 50

这是一个功能..NET 4.0仅支持安全协方差.你提到的演员是有潜在危险的,因为如果可能的话你可以在字典中添加一个非字符串元素:

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

另一方面,IEnumerable<T>是一个只读接口.该T类型参数仅在其输出位置(的返回类型Current属性),所以它是安全的治疗IEnumerable<string>作为IEnumerable<object>.

  • 从理论上来说,协方差是安全的,但是.Net 1.0的一个怪癖可能会在工作中引起轻微的影响.因为`Derived []`被认为是继承自`Base []`,所以`Derived []`将实现`IList <Base>`; 这样的`IList <Base>`可以正常读取,但写入时会抛出异常. (2认同)

jas*_*son 12

但是你可以说

myDict.Add("Hello, world!", new DateTime(2010, 1, 27));
Run Code Online (Sandbox Code Playgroud)

哪个会悲惨地失败.问题是TValuein IDictionary<TKey, TValue>用于输入和输出位置.以机智:

myDict.Add(key, value);   
Run Code Online (Sandbox Code Playgroud)

TValue value = myDict[key];
Run Code Online (Sandbox Code Playgroud)

这是一个错误还是一个功能?

这是设计的.

它会不会来,也许在.NET 37.4中?

不,它本质上是不安全的.


小智 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; }
}
}
Run Code Online (Sandbox Code Playgroud)