无法将 Dictionary<string, List<string>> 转换为 IReadOnlyDictionary<string, IReadOnlyCollection<string>>

3

有人能解释一下这个问题吗?

Dictionary<string, List<string>> x
  = new Dictionary<string, List<string>>();

IReadOnlyDictionary<string, IReadOnlyCollection<string>> y 
  = new Dictionary<string, IReadOnlyCollection<string>>();

y = x;  // CS0266: Cannot implicitly convert type...

3
C#中的方差(Variance) 方差是指子类型在委托和泛型接口成员方面的兼容性。协变允许返回派生类型,在泛型类型参数中使用,逆变允许传递基类类型作为参数。C#实现了方差,通过在委托和接口定义中使用in和out关键字来标记参数和返回类型。 - Guru Stron
1
@JonSkeet 我应该知道在献完血几分钟后不应该使用SO :) - DavidG
1
@DavidG:IReadOnlyCollection<T>并不意味着集合是只读的,只是接口被限制为只读。 - Jon Skeet
@JonSkeet 是的,甚至文档都说它表示一个强类型、只读元素集合 - DavidG
显示剩余3条评论
3个回答

0
你可以通过以下方式接近你的目标:
Dictionary<string, List<string>> x = new Dictionary<string, List<string>>()
  {
    { "Foo", new List<string> {"A", "B", "C"} }
  };

IReadOnlyDictionary<string, ReadOnlyCollection<string>> y = 
    new ReadOnlyDictionary<string, ReadOnlyCollection<string>>(x.ToDictionary(k => k.Key, v => new ReadOnlyCollection<string>(v.Value)));
    
IReadOnlyCollection<string> foo = y["Foo"];

请注意,ReadOnlyCollection<T> 包装了您的原始列表。它不会复制元素。 ReadOnlyDictionary<TKey, TValue> 同样如此。

0

IReadOnlyDictionary 对于其值而言不是协变的。这样可以在 TryGetValue 方法周围提供类型安全性。

考虑以下示例:

Dictionary<string, List<string>> x = new Dictionary<string, List<string>>
{
    { "Key", new List<string>() }
};

IReadOnlyDictionary<string, IReadOnlyCollection<string>> y = new Dictionary<string, IReadOnlyCollection<string>>
{
    { "Key", new System.ArraySegment<string>() } //This is allowed because an ArraySegment implements IReadOnlyCollection<string>
};

List<string> foo;
x.TryGetValue("Key", out foo);
foo.Add("Some string");

最后三行适用于变量 x,因为它的值是一个 List<string>。如果将变量 y 赋值给 x,则这些代码将无法工作,因为其值不是 List<string>,您不能在其上调用 Add 方法。以下代码将无法工作:

y.TryGetValue("Key", out foo); //Error

这也不行:

x = y;
x.TryGetValue("Key", out foo); //Error

因为它适用于x而不适用于y,它们不是类型兼容的,所以强制转换是不允许的。


-1

本文完美地描述了在你的代码中遇到的情况。

点击

Dictionary<TKey, TValue> 能够实现 IReadOnlyDictionary<TKey, TValue>,因为任何使用后者的代码都承诺不修改字典,这是前者提供的功能子集。

但是考虑另一个方向。你从一个 IReadOnlyDictionary<TKey, TValue> 开始,尝试将其转换为常规的 Dictionary<TKey, TValue>。现在,你已经拿走了之前承诺不会被修改的东西,并将其变成了可能被修改的东西。

显然,这行不通。

此外,你不能仅仅假设只读实现在被修改时会抛出异常或其他错误(例如运行时安全性而非编译时安全性),因为正如你所知,你总是可以从可变的实现中获取只读接口。

因此,为了安全起见,你有两个选择:

只需将整个字典内容复制到一个新对象中。 将只读对象包装在一个IDictionary<TKey,TValue>实现中,如果您尝试修改它,则会引发异常。 请注意,后者实际上并没有给您您特别要求的内容。它很接近,但它不是Dictionary<TKey,TValue>的实例。

在复制方面,您有许多选择。就我个人而言,我认为ToDictionary()本身就很好。但是,如果您真的不喜欢冗长,可以将其包装在扩展方法中:

public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(
    this IReadOnlyDictionary<TKey, TValue> dictionary)
{
    return dictionary.ToDictionary(x => x.Key, y => y.Value);
}

4
这不是问题所在。这是不可能的,因为IReadOnlyDictionary<TKey, TValue>中的TValue是不变的。 - Johnathan Barclay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接