【问题标题】:Upcasting with a generic type parameter使用泛型类型参数向上转换
【发布时间】:2010-02-09 18:49:28
【问题描述】:

是否可以从基于 T 的泛型类“向上转换”为基于比 T 更通用的泛型类?

例如,假设我有一个名为 Derived 的类,它继承自一个名为 Base 的类。我可以做这样的事情吗:

List<Derived> der = new List<Derived>();
List<Base> bas = (List<Base>) der;

或者,使用接口,有没有可能做这样的事情:

List<MyClonableType> specific = new List<MyClonableType>();
List<IClonable> general = (List<IClonable>)specific;

如此处所述,这些示例中的每一个都以InvalidCastException 失败。我们争论的问题是它实际上是不可能的,或者只是一个语法错误,如果我们知道如何解决的话。

【问题讨论】:

  • Java 有 &lt;? extends X&gt; 用于这种类型的构造,我会很惊讶 C# 没有等价物。
  • C# 具有与扩展相同的通用约束(where 关键字),但我不肯定这就是 Auraseer 所追求的。他想从外观上实际使用该系列的差异。
  • @Romain,该转换在 C# 中无效,但您可以定义类似 void Process&lt;T&gt;(List&lt;T&gt; list) where T: Base {...} 的方法
  • @finnw:我明白了……我猜where 约束涵盖了典型的用例。那里的语法有点重,不过(但比 Java 更灵活)

标签: c# generics casting


【解决方案1】:

C# 4.0 will have that feature.

它将在遵守某些规则的接口和委托上启用。 List&lt;T&gt; 将不受支持,因为它是一个具体的类。也不支持IList&lt;T&gt;,因为它的方法在inout 位置都使用T

【讨论】:

  • 该链接还明确告诉我,在 4.0 之前不允许这种演员。这正是我想知道的。
  • 即使在 .NET 4.0 中,它也不适用于 List
  • 好点。仅接口和委托支持方差。
  • C# 4.0 将没有 List&lt;T&gt; 的此功能。它本质上是不安全的。
  • 界面上也不会有这个功能IList&lt;T&gt;
【解决方案2】:

这个

List<Derived> der = new List<Derived>(); 
List<Base> bas = (List<Base>)der;

是不可能的,也不应该是不可能的。这里发生了什么:

Base b = new Base();
bas.Add(b);

咔嚓!会发生什么。如果上述内容是合法的,bas 只会引用der,而der 不能添加Base 的实例,这就是Add 会尝试做的事情。 (具体而言,将Base 视为Animal,将Derived 视为Cat;您不能将List&lt;Cat&gt; 转换为List&lt;Animal&gt;,因为这样您就可以将Dog 的实例添加到已转换列表中这会失败,因为它真的是List&lt;Cat&gt;。)

出于类似的原因

List<MyClonableType> specific = new List<MyClonableType>();  
List<IClonable> general = (List<IClonable>)specific;  

永远不可能。

C# 4.0 中,其中一些问题将通过covariance and contravariance 的概念得到解决。例如,在 C# 4.0 中这是合法的:

List<Derived> der = new List<Derived>();
IEnumerable<Base> bas = der;

这是因为IEnumerable&lt;Base&gt; 只吐出Base 的实例,并且因为所有Deriveds 都是Bases 这没关系。换句话说,IEnumerable&lt;Base&gt; 说“我知道如何向你抛出 Base 的实例”,而 List&lt;Derived&gt; 说“我知道如何向你抛出 Derived 的实例”。但是由于所有Deriveds 都是Bases,这意味着List&lt;Derived&gt; 也知道如何向您抛出Base 的实例。因此,它应该可以分配给IEnumerable&lt;Base&gt; 的实例。这在 C# 4.0 中是可能的。这是协方差的一个例子。

我必须强调,对于像List&lt;T&gt; 这样的类型,它们的方法既吃Ts 又吐出Ts,这是不可能的。

【讨论】:

    【解决方案3】:

    有不错的 LINQ 扩展:

    der.ToList<Base>();
    

    【讨论】:

    • 我相信这种方法会创建一个全新的集合。假设您有 Linq,它可能很有用,但它与演员表不同。
    • 确实可以通过这种方式专门处理 IEnumberable 类型,但这并不是问题所要求的实际转换。在这种情况下,ToList() 方法会创建一个新的 列表,并通过从原始列表中转换每个项目来填充它。
    • 如果您只是要阅读列表(这是演员表有意义的唯一情况),大概您正在阅读副本或原件。
    • finnw,这很重要。如果我复制一个对象并保存对副本的引用,那么原始代码会被其他一些代码更改,我永远不会知道更改。此外,仅进行转换可能会非常昂贵,而演员则非常接近免费。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多