C# 中的协方差
是否可以在 C# 4.0 中将 List
转换为 List
?
沿着这样的思路:
class joe : human {}
List<joe> joes = GetJoes();
List<human> humanJoes = joes;
这不是协方差的用途吗?
如果你能做到:
human h = joe1 as human;
为什么你不能这样做,
List<human> humans = joes as List<human>;
因为做 (joe) humans[0] 是不合法的,因为该项目已被贬低.. 每个人都会很高兴。现在唯一的选择是创建一个新列表
Is it possible to cast a List<Subclass>
to List<Superclass>
in C# 4.0?
Something along these lines:
class joe : human {}
List<joe> joes = GetJoes();
List<human> humanJoes = joes;
Isn't this what covariance is for?
if you can do:
human h = joe1 as human;
why shouldn't you be able to do
List<human> humans = joes as List<human>;
than it wouldn't be legal to do (joe)humans[0] because that item has been down casted.. and everyone would be happy. Now the only alternative is to create a new List
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
你不能这样做,因为这样不安全。考虑一下:
显然最后一行(如果不是前面的一行)必须失败 - 因为
Fred
不是Joe
。List
的不变性可以在编译时(而不是执行时)防止出现此错误。You can't do this, because it wouldn't be safe. Consider:
Clearly the last line (if not an earlier one) has to fail - as a
Fred
isn't aJoe
. The invariance ofList<T>
prevents this mistake at compile time instead of execution time.实例化一个新的人员列表,将 joes 作为输入:
Instantiate a new human-list that takes the joes as input:
不可以。C# 4.0 的协/逆变功能仅支持接口和委托。不支持诸如
List
之类的具体类型。No. The co/contravariance features of C# 4.0 only support interfaces and delegates. The do not support concrete types like
List<T>
.不。正如 Jared 所说,C# 4.0 的协/逆变功能仅支持接口和委托。但是,它也不适用于
IList
,原因是IList
包含添加和更改列表中项目的方法 - 正如 Jon斯基特的新答案说道。能够将“joe”列表转换为“人类”的唯一方法是界面设计为纯粹只读,如下所示:
即使是。
Contains(T item)
方法也会不允许,因为当您将IListReader
转换为IListReader< human>
时,Contains( human item)
方法在 <代码>IListReader您可以“强制”从
IList
强制转换为IListReader
、IListReader< human>
甚至IList
human>
使用 GoInterface。但是,如果列表小到足以进行复制,则更简单的解决方案是将其复制到新的List
中,正如 Paw 指出的那样。No. As Jared said, the co/contravariance features of C# 4.0 only support interfaces and delegates. However it doesn't work with
IList<T>
either, and the reason is thatIList<T>
contains methods to add and change items in the list -- as Jon Skeet's new answer says.The only way to be able to cast a list of "joe" to "human" is if the interface is purely read-only by design, something like this:
Even a
Contains(T item)
method would not be allowed, because when you castIListReader<joe>
toIListReader<human>
, there is noContains(human item)
method inIListReader<joe>
.You could "force" a cast from
IList<joe>
toIListReader<joe>
,IListReader<human>
or evenIList<human>
using a GoInterface. But if the list is small enough to copy, a simpler solution is to just copy it into a newList<human>
, as Paw pointed out.如果我允许你的
Listjoes
概括为...... 两个引用
humans 和
joes 现在指向完全相同的列表。
上述赋值后的代码无法阻止将另一种类型的人类(例如管道工)的实例插入/添加到列表中。鉴于
class Plumber: Human {}
,humans
引用的列表现在包含 joes 和 plumber。请注意,引用joes
仍然引用同一个列表对象。 现在,如果我使用引用joes
从列表对象中读取,我可能会弹出一个水管工而不是joe。管道工和乔不知道可以隐式互换......所以我从列表中得到管道工而不是乔破坏了类型安全。水管工肯定不会因为提及工作清单而受到欢迎。然而,在 C# 的最新版本中,可以通过实现其类型参数具有
out
修饰符的泛型接口来解决泛型类/集合的此限制。假设我们现在有ABag; :ICovariable
。 out 修饰符将 T 限制为仅输出位置(例如方法返回类型)。您不能将任何 T 放入袋子中。你只能从中读出它们。 这使我们能够将 joes 概括为ICovariable
,而不必担心将管道工插入其中,因为接口不允许这样做。我们现在可以编写...If I allow your
List<Joe> joes
to be generalized as ...... the two references
humans
andjoes
are, now onward, pointing to the exact same list. The code following the above assignment has no way of preventing an insertion/addition of an instance of another type of human , say a Plumber, into the list. Given thatclass Plumber: Human {}
the list that
humans
refers to now contains both joes and plumbers. Note that the same list object is still referred to by the referencejoes
. Now if I use the referencejoes
to read from the list object I might pop out a plumber instead of a joe. Plumber and Joe are not known to be implicitly interconvertable... so my getting of a plumber instead of a joe from the list breaks down type safety. A plumber is certainly not welcome through a reference to a list of joes.However in the recent versions of C# , its kind of possible to work around this limitation for a generic class/collection by implementing a generic interface whose type parameter has an
out
modifier on it. Say we now haveABag<T> : ICovariable<out T>
. The out modifier restricts the T to ouput positions only (e.g. method return types). You cannot enter any T into the bag. You can only read them out of it. This allows us to generalize joes to anICovariable<Human>
without worrying about inserting a Plumber into it as the interface doesnt allow that. We can now write ...