IDictionary <TKey, TValue> в .NET 4 не ковариант
IDictionary<TKey, TValue>
в .NET 4/Silverlight 4 не поддерживает ковариацию, т.е. я не могу сделать
IDictionary<string, object> myDict = new Dictionary<string, string>();
аналог того, что я могу сделать с IEnumerable<T>
сейчас.
Вероятно, сводится к тому, что KeyValuePair<TKey, TValue>
не является ковариантным. Я считаю, что ковариация должна допускаться в словарях, по крайней мере, для значений.
Так что это ошибка или функция? Придет ли это когда-нибудь, возможно, в .NET 37.4?
ОБНОВЛЕНИЕ (2 года спустя):
В .NET 4.5 будет IReadOnlyDictionary<TKey, TValue>
, но он не будет ковариантным либо :·/
, потому что он происходит из IEnumerable<KeyValuePair<TKey, TValue>>
, а KeyValuePair<TKey, TValue>
не является интерфейсом и, следовательно, не может быть ковариантным.
Команда BCL должна будет перепроектировать много, чтобы подойти и использовать вместо нее ICovariantPair<TKey, TValue>
. Кроме того, ковариантные интерфейсы не могут быть введены строго типизированными индексаторами á la this[TKey key]
. Подобный конец может быть достигнут только путем размещения метода расширения GetValue<>(this IReadOnlyDictionary<TKey, TValue> self, TKey key)
где-то, что бы как-то внутренне требовало фактической реализации, которая, по-видимому, выглядит довольно беспорядочным.
Ответы
Ответ 1
Это особенность..NET 4.0 поддерживает только безопасную ковариацию. Вы упомянули о потенциально опасном, поскольку вы могли бы добавить в словарь нестроковый элемент, если это возможно:
IDictionary<string, object> myDict = new Dictionary<string, string>();
myDict["hello"] = 5; // not an string
С другой стороны, IEnumerable<T>
является интерфейсом только для чтения. Параметр T
type находится только в его выходных позициях (тип возврата свойства Current
), поэтому безопасно обрабатывать IEnumerable<string>
как IEnumerable<object>
.
Ответ 2
Но тогда вы могли бы сказать
myDict.Add("Hello, world!", new DateTime(2010, 1, 27));
который потерпел бы неудачу. Проблема заключается в том, что TValue
in IDictionary<TKey, TValue>
используется как в позиции ввода, так и вывода. К остроумию:
myDict.Add(key, value);
и
TValue value = myDict[key];
Итак, это ошибка или функция?
Это по дизайну.
Придет ли он когда-нибудь, возможно, в .NET 37.4?
Нет, это по своей сути небезопасно.
Ответ 3
У меня была аналогичная проблема, но с более специализированными производными типами (а не с объектом, из которого все происходит)
Трюк заключается в том, чтобы сделать метод общим и поставить предложение 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; }
}
}
Ответ 4
.NET 4 поддерживает только ковариацию. Она работает с IEnumerable, потому что IEnumerable доступен только для чтения.
Ответ 5
Работает для определенного типа полезной ковариации на 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;
}
}