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

Posted

技术标签:

【中文标题】无法将 Dictionary<string, List<string>> 转换为 IReadOnlyDictionary<string, IReadOnlyCollection<string>>【英文标题】:Can't convert Dictionary<string, List<string>> to IReadOnlyDictionary<string, IReadOnlyCollection<string>> 【发布时间】:2021-10-13 07:00:39 【问题描述】:

谁能解释一下这个问题?

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...

【问题讨论】:

请始终显示您遇到的实际错误,这样更容易提供帮助。 Variance in C#. @JonSkeet 我应该知道最好不要在我献血几分钟后使用 SO :) @DavidG:IReadOnlyCollection&lt;T&gt; 并不意味着该集合是只读的,只是接口仅限于此。 这能回答你的问题吗? Cannot convert source type 'List<Person>' to IList<ISomething> 和 convert List<List<object>> to IList<IList<object>> 和 Convert IList<IList<object>> to List<List<object>> 和 still confused about covariance and contravariance & in/out 和 C# generic inheritance and covariance part 2 【参考方案1】:

您可以像这样接近您的目标:

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&lt;T&gt; 包含您的原始列表。它不复制元素。 ReadOnlyDictionary&lt;TKey, TValue&gt; 也一样。

【讨论】:

【参考方案2】:

IReadOnlyDictionary 的值不是covariant。这是为了围绕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&lt;string&gt;。如果将y 分配给x,它将不起作用,因为它的值不是List&lt;string&gt;,并且您不能在其上调用Add。这是行不通的:

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

这也不会:

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

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

【讨论】:

【参考方案3】:

这篇文章完美地描述了您在代码中遇到的情况。

Click?

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

但考虑另一个方向。您从 IReadOnlyDictionary 开始,然后尝试将其转换为常规 Dictionary。现在,你把之前承诺不会修改的东西,变成了可以修改的东西。

显然,那是行不通的。

此外,您不能只假设只读实现在修改时会抛出异常或某些东西(例如运行时安全,即使不是编译时安全),因为如您所知,您总是可以获得来自可变实现的只读接口。

为了安全起见,您有两种选择:

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

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

public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(
    this IReadOnlyDictionary<TKey, TValue> dictionary)

    return dictionary.ToDictionary(x => x.Key, y => y.Value);

【讨论】:

这不是问题。这是不可能的,因为TValueIReadOnlyDictionary&lt;TKey, TValue&gt; 中是不变的。

以上是关于无法将 Dictionary<string, List<string>> 转换为 IReadOnlyDictionary<string, IReadOnlyCollect的主要内容,如果未能解决你的问题,请参考以下文章

尝试保存数组,出现错误 - 无法将类型“[Data]”的值转换为预期的参数类型“[Dictionary<String, AnyObject>]”

无法将“Swift.Array<Any>”类型的值转换为“Swift.Dictionary<Swift.String, Any>”

WPF MVVM 将 Dictionary<String, List<String>> 绑定到数据网格

将 [Dictionary<String,Any?>] 转换为 [Dictionary<String,Any!>] ?迅速 3

如何将 List<Dictionary<string, byte[]> 对象添加到 Dictionary<string, byte[]> [重复]

无法使用类型为“(字符串,选项:nil,指标:nil,视图:Dictionary<String,UIView!>)”的参数列表调用“constraintsWithVisualFormat”