为什么我不能从List <myclass>转换为List <object>?</object> </myclass>

时间:2011-05-04 10:02:18

标签: c# generics casting covariance

我有一个对象列表,它是我的类型QuoteHeader,我希望将此列表作为对象列表传递给能够接受List<object>的方法。

我的代码行是......

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

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

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

我是否需要对班级做任何事情才允许这样做?我以为所有类都继承自对象,所以我无法理解为什么这不起作用?

6 个答案:

答案 0 :(得分:39)

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

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<T>没有公开任何接收 T的方法。

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

  • 从旧列表中创建一个新的对象列表。
  • 使方法采用对象[]而不是List<object>,并使用不安全的数组协方差。
  • 使方法具有通用性,因此需要List<T>
  • 使方法采用IEnumerable
  • 使方法取IEnumerable<object>并使用C#4。

答案 1 :(得分:5)

您无法将List<OneType>转换为List<OtherType>,因为它实际上是您要投射的列表的实例,以及列表本身。

有一种扩展方法可以让你这样做(MSDN reference):

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

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

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

A objectOfTypeA;
B objectOfTypeB = (B) objectofTypeA;

为了能够进行强制转换,必须在可用类型之间进行隐式或显式转换,但没有(除非你提供了一个,你可以做到)。

您希望它能够正常运行,因为List<object>始终能够在其他列表中保留任何类型,但是当您按照这些条款考虑它时,您可以看到它为什么没有。

我确信有一个技术上更有能力的答案,但我认为这就是它的主旨。

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

This question也可能有用

答案 2 :(得分:3)

List<MyClass> x = someList.Select(f => (MyClass)f).ToList();

答案 3 :(得分:1)

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

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

答案 4 :(得分:0)

感谢您的回复。

我将解释我想做什么以及我作为解决方案提出的内容。

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

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

    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);

就像我说的那样,它可能不是最整洁的解决方案,但它在我的场景中运作良好。

答案 5 :(得分:-1)

问题在于,在编译时,编译器会发出2个单独的类,1代表List<MyClass>,1代表List<Object>。它们基本上是两种不同的类型。这就是Generic类型的工作方式,至少在.Net中。

假设这是.Net,你可以做

MyListOfQuoteHeaders.Cast<Object>()

基本上是

var objects = new List<Object>();

foreach(var item in MyListOfQuoteHeaders)
{
   objects.Add((object)item);
}

return objects;