C# 中的协方差

发布于 2024-09-29 15:07:06 字数 504 浏览 0 评论 0原文

是否可以在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

情归归情 2024-10-06 15:07:06

你不能这样做,因为这样不安全。考虑一下:

List<Joe> joes = GetJoes();    
List<Human> humanJoes = joes;
humanJoes.Clear();
humanJoes.Add(new Fred());
Joe joe = joes[0];

显然最后一行(如果不是前面的一行)必须失败 - 因为 Fred 不是 JoeList 的不变性可以在编译时(而不是执行时)防止出现此错误。

You can't do this, because it wouldn't be safe. Consider:

List<Joe> joes = GetJoes();    
List<Human> humanJoes = joes;
humanJoes.Clear();
humanJoes.Add(new Fred());
Joe joe = joes[0];

Clearly the last line (if not an earlier one) has to fail - as a Fred isn't a Joe. The invariance of List<T> prevents this mistake at compile time instead of execution time.

半夏半凉 2024-10-06 15:07:06

实例化一个新的人员列表,将 joes 作为输入:

List<human> humanJoes = new List<human>(joes);

Instantiate a new human-list that takes the joes as input:

List<human> humanJoes = new List<human>(joes);
像极了他 2024-10-06 15:07:06

不可以。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>.

春花秋月 2024-10-06 15:07:06

不。正如 Jared 所说,C# 4.0 的协/逆变功能仅支持接口和委托。但是,它也不适用于 IList,原因是 IList 包含添加和更改列表中项目的方法 - 正如 Jon斯基特的新答案说道。

能够将“joe”列表转换为“人类”的唯一方法是界面设计为纯粹只读,如下所示:

public interface IListReader<out T> : IEnumerable<T>
{
    T this[int index] { get; }
    int Count { get; }
}

即使是 Contains(T item) 方法也会不允许,因为当您将 IListReader 转换为 IListReader< human> 时,Contains( human item) 方法在 <代码>IListReader

您可以“强制”从 IList 强制转换为 IListReaderIListReader< human> 甚至 IListhuman> 使用 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 that IList<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:

public interface IListReader<out T> : IEnumerable<T>
{
    T this[int index] { get; }
    int Count { get; }
}

Even a Contains(T item) method would not be allowed, because when you cast IListReader<joe> to IListReader<human>, there is no Contains(human item) method in IListReader<joe>.

You could "force" a cast from IList<joe> to IListReader<joe>, IListReader<human> or even IList<human> using a GoInterface. But if the list is small enough to copy, a simpler solution is to just copy it into a new List<human>, as Paw pointed out.

萌能量女王 2024-10-06 15:07:06

如果我允许你的Listjoes 概括为...

  List<Human> humans = joes;

... 两个引用 humans 和 joes 现在指向完全相同的列表。上述赋值后的代码无法阻止将另一种类型的人类(例如管道工)的实例插入/添加到列表中。鉴于 class Plumber: Human {}

humans.Add(new Plumber()); // Add() now accepts any Human not just a Joe 

humans 引用的列表现在包含 joes 和 plumber。请注意,引用 joes 仍然引用同一个列表对象。 现在,如果我使用引用joes从列表对象中读取,我可能会弹出一个水管工而不是joe。管道工和乔不知道可以隐式互换......所以我从列表中得到管道工而不是乔破坏了类型安全。水管工肯定不会因为提及工作清单而受到欢迎。

然而,在 C# 的最新版本中,可以通过实现其类型参数具有 out 修饰符的泛型接口来解决泛型类/集合的此限制。假设我们现在有 ABag; :ICovariable。 out 修饰符将 T 限制为仅输出位置(例如方法返回类型)。您不能将任何 T 放入袋子中。你只能从中读出它们。 这使我们能够将 joes 概括为 ICovariable,而不必担心将管道工插入其中,因为接口不允许这样做。我们现在可以编写...

ICovariable<Human> humans = joes ; // now its good !
humans.Add(new Plumber()); // error 

If I allow your List<Joe> joes to be generalized as ...

  List<Human> humans = joes;

... the two references humans and joes 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 that class Plumber: Human {}

humans.Add(new Plumber()); // Add() now accepts any Human not just a Joe 

the list that humans refers to now contains both joes and plumbers. Note that the same list object is still referred to by the reference joes. Now if I use the reference joes 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 have ABag<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 an ICovariable<Human> without worrying about inserting a Plumber into it as the interface doesnt allow that. We can now write ...

ICovariable<Human> humans = joes ; // now its good !
humans.Add(new Plumber()); // error 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文