C# 为什么List.AsReadOnly返回ReadOnlyCollection而Dictionary.AsReadOnly返回IReadOnlyDictionary?
我遇到了这样一个例子,C# 为什么List.AsReadOnly返回ReadOnlyCollection而Dictionary.AsReadOnly返回IReadOnlyDictionary?,c#,.net,readonly-collection,C#,.net,Readonly Collection,我遇到了这样一个例子,List.AsReadOnly()返回一个ReadOnlyCollection而不是IReadOnlyCollection这一事实让我很为难。由于返回的集合是字典的值,因此无法自动向上转换为IReadOnlyCollection。这似乎很奇怪,在查看.Net源代码后,我确认AsReadOnly()方法对List的作用与对Dictionary的不同,即返回具体类而不是接口 有人能解释为什么会这样吗?这种不一致性似乎是一种伤害,特别是因为我们希望在可能的情况下使用接口,尤其是在
List.AsReadOnly()
返回一个ReadOnlyCollection
而不是IReadOnlyCollection
这一事实让我很为难。由于返回的集合是字典
的值
,因此无法自动向上转换为IReadOnlyCollection
。这似乎很奇怪,在查看.Net源代码后,我确认AsReadOnly()
方法对List
的作用与对Dictionary
的不同,即返回具体类而不是接口
有人能解释为什么会这样吗?这种不一致性似乎是一种伤害,特别是因为我们希望在可能的情况下使用接口,尤其是在公开的情况下
在我的代码中,我一开始认为,由于我的使用者只是一个私有方法,我可以将其参数签名从IReadOnlyDictionary
更改为IReadOnlyDictionary
。但是,后来我意识到这使得私有方法看起来可能会修改集合值,因此我在前面的代码中加入了一个恼人的显式强制转换,以便正确使用接口:
.ToDictionary(
item => item,
item => (IReadOnlyCollection<T>) relatedItemsSelector(item)
.ToList()
.AsReadOnly() // Didn't expect to need the direct cast
)
.ToDictionary(
项目=>项目,
item=>(IReadOnlyCollection)相关Items选择器(item)
托利斯先生()
.AsReadOnly()//不希望需要直接强制转换
)
哦,既然我总是把协变和逆变弄糊涂了,有人能告诉我是哪一个阻止了自动转换,并试着用一种明智的方式提醒我如何在将来记住它们吗?(例如,集合不是用于输入/输出参数的变量[co/contra])我理解为什么不能这样做,因为接口可以有许多实现,并且将字典的所有单个元素强制转换为请求的类型是不安全的。除非我连这个简单的方面都搞砸了,我不明白,在这种情况下,我希望你能帮我纠正一下…原因是历史性的。
IReadOnly
*接口是在.NET 4.5中添加的,而List.AsReadOnly()
是在.NET 2.0中重新添加的。改变它的返回类型将是一个突破性的改变
显式演员阵容并不坏。它甚至不是运行时强制转换,因为编译器可以静态地验证它(没有向IL发出强制转换)。顺便说一下,您可以将其强制转换为IReadOnlyList
,它还提供对列表的索引访问。您还可以编写一个扩展方法来返回所需的类型(例如,AsReadOnlyList()
)
关于{co,contra}方差,我发现使用C#关键字in
(逆变)和out
(协变)更容易记住in
类型参数只能显示为input方法参数,而out
类型参数只能显示为output(返回值)。接受参数(例如Base
类型)的方法可以安全地与派生的类型一起调用,因此在该方向上对参数进行强制转换是安全的<代码>输出
正好相反
例如:
interface IIn<in T> { Set(T value); }
IIn<Base> b = ...
IIn<Derived> d = b;
d.Set(derived); // safe since any method accepting Base can handle Derived
interface IOut<out T> { T Get(); }
IOut<Derived> d = ...
IOut<Base> b = d;
b.Get(); // safe since any Derived is Base
你可以看到他们是如何避免通用变异带来的混乱。大概他们可以添加只写的接口,允许(协变量)方向的,但我猜他们没有发现这方面的很多价值。原因是历史的。IReadOnly
*接口是在.NET 4.5中添加的,而List.AsReadOnly()
是在.NET 2.0中重新添加的。改变它的返回类型将是一个突破性的改变
显式演员阵容并不坏。它甚至不是运行时强制转换,因为编译器可以静态地验证它(没有向IL发出强制转换)。顺便说一下,您可以将其强制转换为IReadOnlyList
,它还提供对列表的索引访问。您还可以编写一个扩展方法来返回所需的类型(例如,AsReadOnlyList()
)
关于{co,contra}方差,我发现使用C#关键字in
(逆变)和out
(协变)更容易记住in
类型参数只能显示为input方法参数,而out
类型参数只能显示为output(返回值)。接受参数(例如Base
类型)的方法可以安全地与派生的类型一起调用,因此在该方向上对参数进行强制转换是安全的<代码>输出
正好相反
例如:
interface IIn<in T> { Set(T value); }
IIn<Base> b = ...
IIn<Derived> d = b;
d.Set(derived); // safe since any method accepting Base can handle Derived
interface IOut<out T> { T Get(); }
IOut<Derived> d = ...
IOut<Base> b = d;
b.Get(); // safe since any Derived is Base
你可以看到他们是如何避免通用变异带来的混乱。大概他们可以添加只写的接口,允许(协变量)方向的,但我猜他们没有发现这方面的很多价值。Oh!我假设它不会自动强制转换,因为它不是协变的,但现在我看到正是因为IReadOnlyCollection
是只读的,编译器可以看到从派生类型强制转换到基类型是安全的。如果是这样的话,为什么我必须进行显式转换呢?协变和逆变只会在两个泛型接口之间进行转换并更改类型参数(从base到derived,反之亦然)时发生。您使用的强制转换只是强制转换-这是允许的,因为ReadOnlyCollection
实现了IReadOnlyList
。哦,糟糕,这很有意义。越来越清楚了。为什么不能隐式强制转换?强制转换是隐式的,例如,您可以编写IReadOnlyList x=list.AsReadOnly()
,但在您的例子中,编译器只是推断lambda的类型,并将其用于ToDictionary
方法。同样,与协方差无关-您不会更改任何类型参数。哦!我以为不会