为什么 List.AsReadOnly return 是 ReadOnlyCollection 而 Dictionary.AsReadOnly return 是 IReadOnlyDictionary?
Why does List.AsReadOnly return a ReadOnlyCollection but Dictionary.AsReadOnly returns an IReadOnlyDictionary?
我 运行 遇到一个案例,其中 List.AsReadOnly()
returns 一个 ReadOnlyCollection
而不是 IReadOnlyCollection
的事实让我很难过。由于返回的集合是 Dictionary
的 Value
,因此无法自动向上转换为 IReadOnlyCollection
。这似乎是 st运行ge,在查看 .Net 源代码后,我确认 AsReadOnly()
方法对 List
的作用与对 Dictionary
的作用不同,即,返回具体 class 而不是接口。
谁能解释这是为什么?这种不一致似乎是一种伤害,尤其是因为我们希望尽可能使用接口,尤其是在 public.
时
在我的代码中,起初我在想,既然我的消费者只是一个私有方法,我可以将它的参数签名从 IReadOnlyDictionary<T, IReadOnlyCollection<T>>
更改为 IReadOnlyDictionary<T, ReadOnlyCollection<T>>
。但是,后来我意识到这使得私有方法看起来可能会修改集合值,所以我将一个烦人的显式转换放入早期代码中以便正确使用接口:
.ToDictionary(
item => item,
item => (IReadOnlyCollection<T>) relatedItemsSelector(item)
.ToList()
.AsReadOnly() // Didn't expect to need the direct cast
)
哦,因为我总是混淆协变和逆变,有人能告诉我哪个阻止了自动转换,并试着以一种明智的方式提醒我如何记住它们以备将来使用吗? (例如,对于 _____ [input/output] 参数,集合不是 ______variant [co/contra]。)我明白为什么这不可能,因为可以有很多实现接口,将字典的所有单个元素都转换为请求的类型是不安全的。除非我连这个简单的方面都搞砸了而且我不理解它,在这种情况下我希望你能帮助我纠正...
这是历史原因。 IReadOnly
* 接口是在 .NET 4.5 中添加的,而 List<T>.AsReadOnly()
是在 .NET 2.0 中重新添加的。更改其 return 类型将是一项重大更改。
显式转换还不错。它甚至不是运行时强制转换,因为编译器可以静态验证它(没有向 IL 发出强制转换)。顺便说一下,您可以将其转换为 IReadOnlyList<T>
,它也提供对列表的索引访问。您还可以编写一个 return 是您需要的类型的扩展方法(例如 AsReadOnlyList()
)。
关于{co,contra}方差,我发现使用 C# 关键字 in
(逆变)和 out
(协变)更容易记住。 in
类型参数只能作为 input 方法参数出现,而 out
类型参数只能作为 output (return 值)。接受参数的方法,例如类型 Base
可以安全地使用类型 Derived
调用,因此可以安全地向该方向转换 in
参数。 out
正好相反
例如:
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
非只读集合接口不能是 *-variant,因为它们必须同时是 in
和 out
,那是不安全的。编译器和 CLR 不允许这样做。 .NET 确实有一种不安全的数组变化形式:
var a = new[] { "s" };
var o = (object[])a;
o[0] = 1; // ArrayTypeMismatchException
你可以看到他们是如何避免泛型方差的混乱。据推测,他们可以添加允许 in
(协变)方向的只写接口,但我猜他们没有在其中找到很多价值。
我 运行 遇到一个案例,其中 List.AsReadOnly()
returns 一个 ReadOnlyCollection
而不是 IReadOnlyCollection
的事实让我很难过。由于返回的集合是 Dictionary
的 Value
,因此无法自动向上转换为 IReadOnlyCollection
。这似乎是 st运行ge,在查看 .Net 源代码后,我确认 AsReadOnly()
方法对 List
的作用与对 Dictionary
的作用不同,即,返回具体 class 而不是接口。
谁能解释这是为什么?这种不一致似乎是一种伤害,尤其是因为我们希望尽可能使用接口,尤其是在 public.
时在我的代码中,起初我在想,既然我的消费者只是一个私有方法,我可以将它的参数签名从 IReadOnlyDictionary<T, IReadOnlyCollection<T>>
更改为 IReadOnlyDictionary<T, ReadOnlyCollection<T>>
。但是,后来我意识到这使得私有方法看起来可能会修改集合值,所以我将一个烦人的显式转换放入早期代码中以便正确使用接口:
.ToDictionary(
item => item,
item => (IReadOnlyCollection<T>) relatedItemsSelector(item)
.ToList()
.AsReadOnly() // Didn't expect to need the direct cast
)
哦,因为我总是混淆协变和逆变,有人能告诉我哪个阻止了自动转换,并试着以一种明智的方式提醒我如何记住它们以备将来使用吗? (例如,对于 _____ [input/output] 参数,集合不是 ______variant [co/contra]。)我明白为什么这不可能,因为可以有很多实现接口,将字典的所有单个元素都转换为请求的类型是不安全的。除非我连这个简单的方面都搞砸了而且我不理解它,在这种情况下我希望你能帮助我纠正...
这是历史原因。 IReadOnly
* 接口是在 .NET 4.5 中添加的,而 List<T>.AsReadOnly()
是在 .NET 2.0 中重新添加的。更改其 return 类型将是一项重大更改。
显式转换还不错。它甚至不是运行时强制转换,因为编译器可以静态验证它(没有向 IL 发出强制转换)。顺便说一下,您可以将其转换为 IReadOnlyList<T>
,它也提供对列表的索引访问。您还可以编写一个 return 是您需要的类型的扩展方法(例如 AsReadOnlyList()
)。
关于{co,contra}方差,我发现使用 C# 关键字 in
(逆变)和 out
(协变)更容易记住。 in
类型参数只能作为 input 方法参数出现,而 out
类型参数只能作为 output (return 值)。接受参数的方法,例如类型 Base
可以安全地使用类型 Derived
调用,因此可以安全地向该方向转换 in
参数。 out
正好相反
例如:
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
非只读集合接口不能是 *-variant,因为它们必须同时是 in
和 out
,那是不安全的。编译器和 CLR 不允许这样做。 .NET 确实有一种不安全的数组变化形式:
var a = new[] { "s" };
var o = (object[])a;
o[0] = 1; // ArrayTypeMismatchException
你可以看到他们是如何避免泛型方差的混乱。据推测,他们可以添加允许 in
(协变)方向的只写接口,但我猜他们没有在其中找到很多价值。