【问题标题】:Upcasting and generic lists向上转换和通用列表
【发布时间】:2010-10-18 08:10:53
【问题描述】:

我有以下类层次结构:

public abstract class BaseData
{
  //some properties
}

public class CoData : BaseData
{
  //some properties
}

我正在使用一种要求返回类型为List<BaseData> 的方法。在方法中,我可以访问List<CoData>

public List<BaseData> Save()
{
  List<CoData> listCoData = GetData();
  return listCoData;
}

如果我理解正确,我可以从 CoData 向上转换为 BaseData。但是,当我有一个列表时,即使我明确尝试进行类型转换,它也会出错。

错误:

Error   118 Cannot implicitly convert type 'System.Collections.Generic.List<CoData>' to System.Collections.Generic.List<BaseData>'

编辑:

mquander 的转换方法在 3.0 中似乎对我有用

向下转换的方式也一样吗?来自

即,我可以这样做吗 - List&lt;CoData&gt; listCoData = listBaseData.Cast&lt;BaseData&gt;().ToList();

【问题讨论】:

    标签: c# .net


    【解决方案1】:

    是的;欢迎变异。最终,它不是BaseData 的列表 - 例如,如果您有另一个子类,List&lt;BaseData&gt; 将(在编译时)让您 .Add 它...但是运行时类型不会让你。编译器会阻止你犯错。

    在某些情况下,泛型可以在这里提供帮助...我在this blog entry 的末尾讨论了这个问题。请注意,.NET 4.0 的差异不适用于列表。

    【讨论】:

      【解决方案2】:

      这称为集合参数的“协方差”,C# 目前不支持这种协方差。在 C# 4.0 中,用户定义类型和一些内置类型(如 IEnumerable&lt;T&gt;)将支持这一点。

      与此同时,您可以通过使用显式转换成员创建一个新枚举来解决此问题(您可能需要考虑将此处内容的返回类型更改为 IEnumerable&lt;T&gt;,这样您就不必实际制作一直有新的收藏:)

      public List<BaseData> Save()
      {
          List<CoData> listCoData = GetData();
          return listCoData.Cast<BaseData>().ToList();
      }
      

      根据 GetData 的工作方式,您还可以考虑这样的结构:

      public List<T> Save<T>() where T : BaseData
      {
          return listCoData = GetData<T>();
      }
      

      编辑:实际上,正如 Marc 和其他人无疑会指出的那样,List&lt;T&gt; 不能像这样是协变的,因为您可以以会破坏类型安全的方式添加成员。但是,如果您改为返回 IEnumerable&lt;T&gt;s,则可以在此处使用协方差。

      编辑 2:针对您的附加问题:

      我可以这样做吗? List<CoData> listCoData = listBaseData.Cast<BaseData>().ToList();

      没有。 listBaseData.Cast&lt;BaseData&gt;().ToList(); 返回一个 List&lt;BaseData&gt; 类型的对象,它不能直接转换为 List&lt;CoData&gt;。这就是为什么我们必须首先进行这项工作。

      【讨论】:

      • 我的第二个问题与我原来的问题不同。第二个问题与向下转换有关。我很想知道这种方法是否可以用于从基础到派生以及从派生到基础
      【解决方案3】:

      mquander 解决方案的另一个变体是这样实现 Save:

      public List<BaseData> Save()
      {
          List<CoData> listCoData = GetData();
          return listCoData.ConvertAll<BaseData>(
              delegate(CoData c) { return c as BaseData; });
      }
      

      这将允许您在匿名委托中进行一些额外的处理,而不仅仅是强制转换它。

      这里要记住的重要一点是,虽然 CoData 继承自 BaseData,但 List 不继承自 List。

      【讨论】:

      • 黑白 ConvertAll 和 Cast 的区别是什么
      • 在我发布的版本中,没有区别,因为委托所做的只是转换基本类型。如果你需要做一些类型检查或一些特殊的转换,你可以在委托中添加它。
      【解决方案4】:

      您必须等到 C# 4.0 才能使用generic covariance

      【讨论】:

      • 否; .NET 4.0 的差异至少有两个原因在这里不适用;列表既不是“输入”也不是“输出”,列表既不是接口也不是委托类型。 marcgravell.blogspot.com/2009/02/…
      猜你喜欢
      • 2012-03-22
      • 2017-03-16
      • 1970-01-01
      • 2010-10-15
      • 2013-08-24
      • 1970-01-01
      • 2019-03-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多