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