开发者

Why can't I cast from a List<MyClass> to List<object>?

I have a List of objects, which are of my type QuoteHeader and I want to pass this list as a list of objects to a method which is able to accept a List<object>.

My line of code reads...

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

But I get the following error at design time...

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

Do I need to do anything to my class to allow th开发者_JAVA百科is? I thought that all classes inherit from object so I can't understand why this wouldn't work?


The reason this is not legal is because it is not safe. Suppose it were legal:

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

But "animals" is actually a list of giraffes; you can't put a tiger in a list of giraffes.

In C# this is, unfortunately, legal with arrays of reference type:

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

In C# 4 this is legal on IEnumerable but not 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

It is safe because IEnumerable<T> does not expose any method that takes in a T.

To solve your problem you can:

  • Create a new list of objects out of the old list.
  • Make the method take an object[] rather than a List<object>, and use unsafe array covariance.
  • Make the method generic, so it takes a List<T>
  • Make the method take IEnumerable
  • Make the method take IEnumerable<object> and use C# 4.


You can't cast List<OneType> to List<OtherType> as it is actually the instances of the list you want to cast, as well as the List itself.

there is an extension method which will allow you to do this (MSDN reference):

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

This method will attempt to cast each instance of the list of one type to the other type and add them to a new enumerable. it will throw an exception if any instance can't be cast.

As far as the system is concerned the two types for your lists are just different types, so it is like saying:

A objectOfTypeA;
B objectOfTypeB = (B) objectofTypeA;

To be able to do the cast there would have to be an implicit or explicit conversion between the types available, which there isn't (unless you provided one, which you might be able to do).

you expect it to work because List<object> will always be able to hold any type in another list, but when you think about it in those terms you can see why it doesn't.

I'm sure there is a more technically competent answer, but that is the gist of it I think.

you might be interested in reading Eric Lippert's series on Covariance and Contravariance as this may be helpful to you.

This question may also be useful


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


I'm presuming that you mean that the lists are of types which inherit from each other or can otherwise be cast from one type to another - in that case, try this:

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


Thanks for the many responses.

I'll explain what I wanted to do and what I've come up with as a solution.

I needed a method that I could call by passing in a List of objects of any type and then output that list to XML. Also passed to the method would be a string which would be a system file structure path location which points to the location the XML file would be saved to. As I have an ever growing number of classes and types, I wanted to avoid writing multiple methods to cater for each type of class. I'm not sure if I've even gone about this the right way, but it's a lightweight solution to my problem and works. If there are any issues with it, or if anyone has any comments please feel free...

So... my method now looks like this...

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

... and my calling line reads...

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

Like I said, it may not be the tidiest solution, but it works well in my scenario.


The problem is that at Compile time, the compiler emits 2 separate classes, 1 that represents List<MyClass> and one that represents List<Object>. They are essentially 2 separate types. That's how Generic types work, in .Net at least.

Assuming this is .Net, you could do

MyListOfQuoteHeaders.Cast<Object>()

which basically does

var objects = new List<Object>();

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

return objects;
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜