IDictionary<TKey, TValue> en.NET 4 no covariante
.net-4.0 covariance (5)
IDictionary<TKey, TValue>
en .NET 4 / Silverlight 4 no es compatible con la covarianza, es decir, no puedo hacer una
IDictionary<string, object> myDict = new Dictionary<string, string>();
análogo a lo que puedo hacer con IEnumerable<T>
s ahora.
Probablemente se reduce a KeyValuePair<TKey, TValue>
tampoco es covariante. Creo que la covarianza debe permitirse en los diccionarios, al menos para los valores.
Entonces, ¿es eso un error o una característica? ¿Alguna vez vendrá, tal vez en .NET 37.4?
ACTUALIZACIÓN (2 años después):
Habrá un IReadOnlyDictionary<TKey, TValue>
en .NET 4.5, pero no será covariante tampoco :·/
, porque deriva de IEnumerable<KeyValuePair<TKey, TValue>>
, y KeyValuePair<TKey, TValue>
no es una interfaz y por lo tanto no puede ser covariante.
El equipo de BCL debería rediseñar mucho para poder utilizar ICovariantPair<TKey, TValue>
. Tampoco son posibles los indexadores fuertemente tipados a la this[TKey key]
para las interfaces covariantes. Un fin similar solo se puede lograr colocando un método de extensión GetValue<>(this IReadOnlyDictionary<TKey, TValue> self, TKey key)
algún lugar que de alguna manera internamente tendría que llamar a una implementación real, lo que podría parecer un enfoque bastante desordenado.
.NET 4 solo admite la covarianza no en . Funciona con IEnumerable porque IEnumerable es de solo lectura.
Es una característica. .NET 4.0 solo admite una covarianza segura . El reparto que mencionas es potencialmente peligroso, ya que podrías agregar un elemento que no sea de cuerda al diccionario si fuera posible:
IDictionary<string, object> myDict = new Dictionary<string, string>();
myDict["hello"] = 5; // not an string
Por otro lado, IEnumerable<T>
es una interfaz de solo lectura. El parámetro de tipo T
está solo en sus posiciones de salida (tipo de retorno de la propiedad Current
) por lo que es seguro tratar IEnumerable<string>
como IEnumerable<object>
.
Pero entonces podrías decir
myDict.Add("Hello, world!", new DateTime(2010, 1, 27));
que fallaría miserablemente El problema es que TValue
en IDictionary<TKey, TValue>
se usa tanto en las posiciones de entrada como de salida. Esto es:
myDict.Add(key, value);
y
TValue value = myDict[key];
Entonces, ¿es eso un error o una característica?
Es por diseño.
¿Alguna vez vendrá, tal vez en .NET 37.4?
No, es intrínsecamente inseguro.
Tuve un problema similar, pero con tipos derivados más especializados (en lugar de objetos de los que todo se deriva)
El truco es hacer que el método sea genérico y poner una cláusula where poniendo la restricción relevante. Suponiendo que se trata de tipos básicos y tipos derivados, lo siguiente funciona:
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; }
}
}
Una IDictionary
para un tipo específico de covarianza útil en 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;
}
}