C# - Why can I not cast a List to a clas

2019-08-21 08:48发布

问题:

I've got an object, which I'll call MyObject. It's a class that controls a particular data row.

I've then got a collection class, called MyObjectCollection:

public class MyObjectCollection : List<MyObject> {}

Why can I not do the following:

List<MyObject> list = this.DoSomethingHere();
MyObjectCollection collection = (MyObjectCollection)list;

Thanks in advance.

Edit: The error is InvalidCastException

回答1:

My guess is that DoSomethingHere doesn't return an instance of MyObjectCollection.

Let's get rid of all the generics etc here, as they're not relevant. Here's what I suspect you're trying to do:

public static object CreateAnObject()
{
    return new object();
}

object o = CreateAnObject();
string s = (string) o;

That will fail (at execution time) and quite rightly so.

To bring it back to your code, unless DoSomethingHere actually returns a MyObjectCollection at execution time, the cast will fail.



回答2:

Because a List<MyObject> is not a MyObjectCollection. The reverse is true: you could cast a MyObjectCollection to a List because MyObjectCollection inherits from List<MyObject> and thus, for all intents and purposes, IS A List<MyObject>.

The only thing you can do is to define a constructor on MyObjectCollection that takes an Ienumerable as a parameter and initalizes itself with the data in the other one, but that will make a new object containing the same data:

public class MyObjectCollection : List<MyObject> 
{
  public MyObjectCollection(IEnumerable<MyObject> items)
  {
    Addrange(items); 
  }
}

UPDATE: As noted in the comment, you COULD have the cast succeed at runtime, provided that DoSomething actually returns an instance of MyObjectCollection. If it does, the object effectively is a MyObjectCollection, and the cast is completely legal.

I'd have to say, it is bad practice in my view to upcast something like that. If the function returns a List, you should not rely on a specific implementation of List. Either modify the return type of DoSomething, if you own that function, and return a MyObjectCollection, or deal with it as a list.



回答3:

Without knowing what exactly is created inside DoSomething() we have to assume either:

You have a misunderstanding about the inheritence in .Net.

you have

A : B

B DoSomething() 
{
    return new B();
}
// then this is
B b = new B();
A a = (A)b;

Clearly b is a B but not an A. B might look much like A but it is not (if you traverse the parentage of b you won't find A anywhere)

This is true irrespective of the Generics involved (though that sometimes can cause situations where something that could work doesn't see the co-contra variance in c# 4.0)

or

A : B

B DoSomething() 
{
    return new A();
}
// then this is
B b = new A();
A a = (A)b;

Which in the absence of Generics will work.



回答4:

You can't do it because (I guessing) the list instance returned from DoSomethingHere isn't derived from MyObjectCollection



回答5:

You could create an implicit operator that would allow you to convert between your object and the list. You would need an constructor that takes a list and to property that returns the underlaying list.

public static implicit operator List<MyObject>(MyObjectCollection oCollection)
{
  //Convert here
  return MyObjectCollection.BaseList;
}

public static implicit operator MyObjectCollection(List<MyObject> oList)
{
  //Convert here
  return new MyObjectCollection(oList);
}