views:

295

answers:

6

Hi there,

I got a class X and a class Y, the latter which derives from X :

class x {}
class y : x {}

Then somewhere I am using a list of X :

List<X> lstX;
...

Then I'd like to use a new list of Y, from the data in my other list...something along those lines :

List<Y> lstY = lstX;

I would believe that the items in the list of X would get converted automatically into Y, but thats not the case.

Also, how could I initialize a new instance of Y, from a certain X? I would like to do :

var newX = new X();
var newY = new Y(X);

but it does not seem to work like that.

Thanks for your help! and sorry for formatting, trying my best

+1  A: 

No, since you can not be sure that all items in listX, which are of type 'X', are also of type Y.
The inheritance relationship is the other way around: an item of type Y can be casted to X, because Y is-a X.

In C#, there are also no 'copy constructors' available like in C++, so I fear that you'll have to implement that logic , in order to be able to initialize a new instance of Y, from a certain X, yourselves. Also, keep in mind that classes are reference types ...

Frederik Gheysels
+3  A: 

That is never going to work; after all List<Y> lstY = lstX; just copies the reference (unless you add your own implicit static conversion operator to your own list type) - so it is still a list of X and could contain things other than Y instances.

Even in 4.0, co/contra variance doesn't extend to a: lists (both in and out), or b: concrete types (like List<T>).

Interestingly though, it will (and always has) work for arrays of reference types, but only in the direction X[] arrX = arrY;. It doesn't convert anything; if you try and put the wrong data in it'll throw an exception.

Marc Gravell
Even that won't work, since Y is an X, but X is not an Y.
Frederik Gheysels
@Frederik - Ah right, yes. My bad. Fixed.
Marc Gravell
A: 

Your scenario is sort of backwards from the usual polymorphic use cases. Y is an X, but X is not a Y.

With that in mind you could force it work the way you say by placing cloning code in constructors and whatnot.

Sailing Judo
A: 

The problem you are going to have with your first example is that Y derives from X, so it "is an X", but "X is not a Y", also C# currently does not support type casting in this method. You could try using extension methods for Cast, such as lstX.ConvertAll as a helper to accomplish this.

For the second question you want to look into a Copy constructor, such as:

public Y(X baseObject) {
   //copy all the data you need here...
}
GrayWizardx
+1  A: 

It can't automatically "widen" the objects' type from x to y, because X is not Y, but Y is X.

You could try casting from X to Y, but that'll fail with an InvalidCastException unless your object was originally a Y masquerading as an X all along. You need to initialise and populate a new List<Y> manually:

IList<Y> yList = new List<Y>();
foreach (var x in xList)
{
    var y = new Y(x); // Copies/clones inherited properties from x to a new Y
    // TODO: Set other properties of y not present on x
    yList.Add(y);
}
Neil Barnwell
+14  A: 

There are a couple of questions here.

First: "I can assign an object of type Tiger to a variable of type Animal. Why can I not assign an object of type List of Tiger to a variable of type List of Animal?"

Because then this happens:

List<Tiger> tigers = new List<Tiger>();
List<Animal> animals = tigers; // this is illegal because if we allow it...
animals.Add(new Giraffe()); // ... then you just put a giraffe into a list of tigers.

In C# 4 it will be legal to do this:

IEnumerable<Tiger> tigers = new List<Tiger>();
IEnumerable<Animal> animals = tigers;

This is legal because IEnumerable<T> has no "Add" method and therefore this is guaranteed to be safe.

See my series of articles on covariance for details about this new feature of C# 4.

http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx

Second question: "how could I initialize a new instance of Tiger, from a certain Animal?"

You cannot. The Animal in question could be a Ladybug. How are you going to initialize a new Tiger from an instance of Ladybug? That doesn't make any sense, so we don't let you do it. If you want to write your own special method that knows how to turn arbitrary animals into tigers, you are free to do so. But we don't know how to do that for you.

Eric Lippert
Dagnammit that's a good answer, I couldn't have put it better. In fact, to prove it, I didn't. Darn you Eric Lippert.
Neil Barnwell
excellent answer indeed, thank you very much.
ibiza
Although to me, instantiating a List of Animals with a bunch of Tigers, and then adding some Giraffes seems legit to me, since you got a List of Animals there. It just happens that you initialized it with some Tigers.It must be my logic then that I must change.
ibiza
@ibiza: well, continue along with that logic then. Suppose it worked your way. Now you take the original list of tigers and enumerate it: foreach(Tiger tiger in tigers) -- what happens when you encounter the giraffe in the list? Remember, these conversions are *reference* conversions. You're not making a new list of animals; rather, you've got two references to the same list.
Eric Lippert