【问题标题】:How to cast List<ClassB> to List<ClassA> when ClassB inherits from ClassA?当 Class B 从 ClassA 继承时,如何将 List<Class> 转换为 List<Class>?
【发布时间】:2011-11-01 03:15:16
【问题描述】:

我将 json 字符串反序列化为 List&lt;ClassB&gt;,现在我想在返回之前将其转换为 List&lt;ClassA&gt; BindModel 方法。我需要强制转换,因为方法期望得到List&lt;ClassA&gt;

为什么我在投射时出错?毕竟,ClassB 继承自 ClassA。我该怎么办?

附:这个问题是从this post 扩展而来的。在行 new DataContractJsonSerializer(typeof(List&lt;ClassB&gt;)); 而不是 List&lt;ClassB&gt; 中,类型将在运行时构造。

    public override object BindModel(...)
    {
          var serializer = new DataContractJsonSerializer(typeof(List<ClassB>));
          MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes("[{\"id\":\"1\",\"name\":\"name\"}]"));
          var list = serializer.ReadObject(ms);

          return (List<ClassA>)list;
    }

    [KnownType(typeof(ClassA))]
    [DataContract]
    public class ClassA
    {
        public ClassA();
    }

    [KnownType(typeof(ClassB))]       
    [DataContract]
    public class ClassB : ClassA
    {
        [DataMember(Name = "id")]
        public int Id { get; set; }
        [DataMember(Name = "name")]
        public string CategoryName { get; set; }
    }

【问题讨论】:

  • 您确定您使用的是 .NET 4.0 吗?你想要的叫做covariance,应该已经支持了。
  • List 不支持协方差。

标签: c# c#-4.0 casting


【解决方案1】:

你可以使用

 Cast<T>()

例如:

 List<A> listOfA = new List<B>().Cast<A>();

这实际上是低于 Linq 并且是在 IEnumerable 而不是 IEnumerable&lt;T&gt; 上实现的,但仍然很有用。它效率不高,因为正如我所说,它试图投射它。

记住列表不允许协方差,这是一种麻烦。最好使用IEnumerable&lt;T&gt; 作为接口而不是列表。

你可以说:

 IEnumerable<B> listOfB = new List<B>();
 IEnumerable<A> listOfA = listOfB; // no casting required

【讨论】:

  • 成功了,我把List改成了IEnumerable,因为以后不需要添加元素了:)
  • 这个“不如Linq”怎么办?它 Linq!
  • 不是!正如我所说,它是由IEnumerable 实现的,它不是Linq。但Cast&lt;T&gt; 将其转为 Linq。
【解决方案2】:

您可以使用Cast Method

return list.Cast&lt;ClassB&gt;();

看看this question关于Co和Contra Variance的信息

【讨论】:

  • 我会阅读帖子,但serializer.ReadObject 返回object 所以我不能使用Cast&lt;T&gt;ClassB 只是我在Postscript(P.S.)中所说的示例,有什么建议吗?
  • Cast 返回 IEnumerable,但 OP 需要 List
  • @theateist 如果 serializer.ReadObject 返回一个运行时类型为 List 的对象,那么您可以这样做(但它返回一个可枚举的,而不是一个列表):((List&lt;ClassA&gt;)obj).Cast&lt;ClassB&gt;()。即使您不能将List&lt;ClassA&gt; 转换为List&lt;ClassB&gt;,您可以List&lt;ClassA&gt; 的引用从object 转换为List&lt;ClassA&gt;
【解决方案3】:

我会像这样投射列表:

var listB = GetListOfB();  // returns List<B>
var listA = listB.Select(q => (A)q).ToList();

这对你有用吗?

【讨论】:

  • +1,但为什么使用.Select(q =&gt; (A)q) 而不是.Cast&lt;A&gt;()
【解决方案4】:

您不能将 SubType 列表转换为 SuperType 列表。假设我有一个 Tortoises 列表,并且我能够将它转换为 Animals 列表。然后我可以将狮子添加到乌龟列表中,但狮子的类型不正确。

但是,使用可枚举,您可以做到这一点。之前的海报非常正确地说您可以将 SubType 的列表转换为 SuperType 的 IEnumerable。事实上,在 C# 4 中,IEnumerable SubTypeSuperType 的一个 IEnumerable。这是因为泛型参数被指定为输出参数。

【讨论】:

    【解决方案5】:

    要从List&lt;DerivedClass&gt; 创建List&lt;BaseClass&gt;,如其他答案所述,强制转换是不够的。您需要构建一个新列表。

    不过,我对其他答案所建议的代码并不特别满意,因此我提供了自己的解决方案。我会这样做:

    var baseClassList = new List<BaseClass>(derivedClassList.Cast<BaseClass>());
    

    有些人可能更喜欢这个:

    var baseClassList = derivedClassList.Cast<BaseClass>().ToList();
    

    我更喜欢第一个,因为它可以轻松地将类型从 List&lt;T&gt; 更改为任何其他集合,构造函数采用 IEnumerable&lt;T&gt;

    【讨论】:

      猜你喜欢
      • 2021-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-29
      相关资源
      最近更新 更多