views:

394

answers:

5

Hi All,

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

+5  A: 

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.

Jon Skeet
DoSomethingHere()... Or FindByAssetId(assetTypeId) in the real case, returns a List<MyObject> or a List<Asset> in the real case. So, even those MyObjectCollection pretty much is List<MyObject> I cannot use it to cast? Just seems like it should work!
GenericTypeTea
Nope - you need to read up on the inheritance concepts some more sorry
ShuggyCoUk
No, you can't cast an object to a different type. You could write your own converter, but that doesn't sound like a great idea to me. What's the point of MyObjectCollection in the first place?
Jon Skeet
MyObjectCollection was intended to just be a neat way of using List<MyObject>. However I'll just use the List or find another way around it.
GenericTypeTea
If you are going to use your shorthand, you would just need to have DoSomething construct and return your specialized class instead of the generic. Is there a reason that the DoSomething method is not using your shorthand class?
Steve Mitcham
+2  A: 

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.

ShuggyCoUk
Thanks for this, although it wasn't the answer I selected, it made me understand why it didn't work. I marked Jon's correct as he posted first.
GenericTypeTea
That's ok http://stackoverflow.com/questions/305223/jon-skeet-facts "Users don't mark Jon Skeet's answers as accepted. The universe accepts them out of a sense of truth and justice." ;)
ShuggyCoUk
+1  A: 

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

Sean
+5  A: 

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.

Denis Troller
Would have loved to do this, however I can only have one instance of each class as all the data they contain are management in one place.
GenericTypeTea
Your statement, while true in a general sense (i.e. every instance of List<MyObject> is not a MyObjectCollection) does not apply to every specific case of a cast. If, in fact, the value returned by 'DoSomething' is a MyObjectCollection, then the cast will succeed becuase the underlying type is a MyObjectCollection.
Steve Mitcham
quite true, Steve. I guess I was focused on why he was seeing an error. I'll update my answer since it has been upvoted, don't want false/partial answer to surface. Thanks.
Denis Troller
A: 

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);
}
Stevo3000
Implicit operators are bad, bad, bad. They make code really tough to read, can introduce bugs that are hard to find, and really suck to maitain for anyone who wasn't the author.
ctacke
@ctacke - If used correctly like in this case then I can see no reason not to use them. You wouldn't complain about int to double implicit conversions would you? They are visible in the source code just like any other operator.
Stevo3000
int to double is *guaranteed* to work in all cases without dataloss, is commonly done in lanagues which don't use bigint/bigrational by default... there's a world of a difference
ShuggyCoUk