IDictionary<TKey, TValue> in .NET 4 not covariant

早过忘川 提交于 2019-11-26 13:22:35

It's a feature. .NET 4.0 only supports safe covariance. The cast you mentioned is potentially dangerous as you could add a non-string element to the dictionary if that was possible:

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

On the other hand, IEnumerable<T> is a read-only interface. The T type parameter is only in its output positions (return type of the Current property) so it's safe to treat IEnumerable<string> as an IEnumerable<object>.

But then you could say

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

which would fail miserably. The issue is that the TValue in IDictionary<TKey, TValue> is used in both input and output positions. To wit:

myDict.Add(key, value);   

and

TValue value = myDict[key];

So is that a bug or a feature?

It's by design.

Will it ever come, maybe in .NET 37.4?

No, it's inherently unsafe.

I had a similar problem, but with more specialised derived types (rather than object which everything derives from)

The trick is to make the method generic and put a where clause putting the relevant restriction. Assuming that you're dealing with base types and derived types, the following works:

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; }
}
}

.NET 4 only supports out covariance not in. It works with IEnumerable because IEnumerable is read only.

Maslow

A work around for a specific type of useful covariance on 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;
    }   
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!