【问题标题】:Why can't I cast from a List<MyClass> to List<object>?为什么我不能从 List<MyClass> 转换为 List<object>?
【发布时间】:2011-05-04 10:02:18
【问题描述】:

我有一个对象列表,我的类型为 QuoteHeader,我想将此列表作为对象列表传递给能够接受 List&lt;object&gt; 的方法。

我的代码行是...

Tools.MyMethod((List<object>)MyListOfQuoteHeaders);

但我在设计时收到以下错误...

Cannot convert type 'System.Collections.Generic.List<MyNameSpace.QuoteHeader>' 
to 'System.Collections.Generic.List<object>'

我需要对我的班级做任何事情来允许这样做吗?我认为所有类都继承自 object 所以我不明白为什么这不起作用?

【问题讨论】:

标签: c# generics casting covariance


【解决方案1】:

这不合法的原因是因为它不安全。假设它是合法的:

List<Giraffe> giraffes = new List<Giraffe>();
List<Animal> animals = giraffes; // this is not legal; suppose it were.
animals.Add(new Tiger());  // it is always legal to put a tiger in a list of animals

但“动物”实际上是长颈鹿的列表;你不能把老虎放在长颈鹿名单中。

不幸的是,在 C# 中,这对于引用类型的数组是合法的:

Giraffe[] giraffes = new Giraffe[10];
Animal[] animals = giraffes; // legal! But dangerous because...
animals[0] = new Tiger(); // ...this fails at runtime!

在 C# 4 中,这在 IEnumerable 上是合法的,但在 IList 上是合法的:

List<Giraffe> giraffes = new List<Giraffe>();
IEnumerable<Animal> animals = giraffes; // Legal in C# 4
foreach(Animal animal in animals) { } // Every giraffe is an animal, so this is safe

这是安全的,因为IEnumerable&lt;T&gt; 不公开任何采用 T 的方法。

要解决您的问题,您可以:

  • 从旧列表中创建一个新的对象列表。
  • 使该方法采用 object[] 而不是 List&lt;object&gt;,并使用不安全的数组协方差。
  • 使方法通用,所以它需要一个List&lt;T&gt;
  • 使方法采用 IEnumerable
  • 使方法采用IEnumerable&lt;object&gt; 并使用C# 4。

【讨论】:

    【解决方案2】:

    您不能将List&lt;OneType&gt; 转换为List&lt;OtherType&gt;,因为它实际上是您要转换的列表的实例,以及列表本身。

    有一个扩展方法可以让你做到这一点(MSDN reference):

    IEnumerable<Object> myNewEnumerable = myEnumerable.Cast<Object>();
    

    此方法将尝试将一种类型列表的每个实例强制转换为另一种类型,并将它们添加到新的可枚举中。如果无法强制转换任何实例,它将抛出异常。

    就系统而言,您的列表的两种类型只是不同的类型,所以就像说:

    A objectOfTypeA;
    B objectOfTypeB = (B) objectofTypeA;
    

    为了能够进行强制转换,必须在可用类型之间进行隐式或显式转换,而这是不存在的(除非您提供了一个,否则您可能能够做到)。

    您希望它能够工作,因为 List&lt;object&gt; 将始终能够在另一个列表中保存任何类型,但是当您以这些术语考虑它时,您会明白为什么它不能。

    我确信有一个技术上更胜任的答案,但我认为这就是它的要点。

    您可能有兴趣阅读Eric Lippert's series on Covariance and Contravariance,因为这可能对您有所帮助。

    This question 也可能有用

    【讨论】:

      【解决方案3】:
      List<MyClass> x = someList.Select(f => (MyClass)f).ToList();
      

      【讨论】:

      • 欢迎您,在这里,解释为什么要使用您的解决方案而不只是如何使用是一个很好的做法。这将使您的答案更有价值,并帮助进一步的读者更好地理解您是如何做到的。我还建议您查看我们的常见问题解答:stackoverflow.com/faq
      • 这是一个非常好的解决方案。正是我在寻找的东西。
      【解决方案4】:

      我假设您的意思是列表是相互继承的类型,或者可以从一种类型转换为另一种类型 - 在这种情况下,试试这个:

      Tools.MyMethod(MyListOfQuoteHeaders.Cast<OtherType>());
      

      【讨论】:

        【解决方案5】:

        感谢您的众多回复。

        我将解释我想要做什么以及我想出的解决方案。

        我需要一个方法,我可以通过传入任何类型的对象列表来调用该方法,然后将该列表输出到 XML。传递给该方法的还有一个字符串,它是一个系统文件结构路径位置,指向 XML 文件将保存到的位置。由于我有越来越多的类和类型,我想避免编写多种方法来满足每种类型的类。我不确定我是否以正确的方式解决了这个问题,但它是解决我的问题并且有效的轻量级解决方案。如果有任何问题,或者如果有人有任何 cmet,请随时...

        所以...我的方法现在看起来像这样...

            public static Enums.Status WriteListToXML<T>(string outputPath, List<T> inboundList)
            {
                try
                {
                    XmlSerializer xmlSerializer = new XmlSerializer(inboundList.GetType());
                    using (StreamWriter streamWriter = System.IO.File.CreateText(outputPath))
                    {
                        xmlSerializer.Serialize(streamWriter, inboundList);
                    }
                    return Enums.Status.Success;
                }
                catch (Exception ex)
                {
                    return Enums.Status.Failure;
                }
            }
        

        ...而我的呼叫线路显示...

        WriteListToXML<QuoteHeader>(@"C:\XMLDocuments\output\QuoteHeaders.xml", quoteHeadersList);
        

        就像我说的,它可能不是最整洁的解决方案,但在我的场景中效果很好。

        【讨论】:

          【解决方案6】:

          问题在于,在编译时,编译器会发出 2 个单独的类,1 个代表 List&lt;MyClass&gt;,一个代表 List&lt;Object&gt;。它们本质上是两种不同的类型。这就是泛型类型的工作方式,至少在 .Net 中是这样。

          假设这是.Net,你可以这样做

          MyListOfQuoteHeaders.Cast<Object>()
          

          基本上是这样的

          var objects = new List<Object>();
          
          foreach(var item in MyListOfQuoteHeaders)
          {
             objects.Add((object)item);
          }
          
          return objects;
          

          【讨论】:

          • 这根本不是泛型在 .NET 中的工作方式。您正在描述模板如何在 C++ 中工作。在 .NET 中,泛型作为单一类型发出,然后在运行时抖动为每个具有值类型的实例构造新代码,并为所有引用类型共享一个实例。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-09-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多