63

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

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

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

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

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

更新(2年后):

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

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

4

6 回答 6

57

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

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

另一方面,IEnumerable<T>是一个只读接口。Ttype 参数仅在其输出位置(属性的返回类型)Current,因此可以安全地IEnumerable<string>视为IEnumerable<object>.

于 2010-01-27T19:07:09.987 回答
14

但是你可以说

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

这会惨败。问题是TValueinIDictionary<TKey, TValue>用于输入和输出位置。以机智:

myDict.Add(key, value);   

TValue value = myDict[key];

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

这是设计使然。

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

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

于 2010-01-27T19:07:34.120 回答
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; }
}
}
于 2014-10-31T10:12:10.553 回答
2

.NET 4 仅支持协方差,而不支持. 它适用于 IEnumerable,因为 IEnumerable 是只读的。

于 2010-01-27T19:10:33.863 回答
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;
    }   
}
于 2014-04-02T15:21:11.780 回答
0

假设您只需要字典中的特定操作,您可以创建一个包装器:

class Container
{
    private IDictionary<string, string> myDict = new Dictionary<string, string>();

    public object GetByKey(string key) => myDict[key];
}

甚至在Container中实现你需要的接口。

于 2021-10-25T16:22:36.153 回答