开发者

C# polymorphism simple question

开发者 https://www.devze.com 2022-12-19 12:20 出处:网络
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 :

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,开发者_JAVA技巧 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


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.


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.


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 ...


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


Try this:


using System.Collections.Generic;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            List myList;

            myList = GetMyList(MyListTypeEnum.MyList1);
            myList = GetMyList(MyListTypeEnum.MyList2);
        }

        public static List GetMyList(MyListTypeEnum tipo)
        {
            List result;
            result = new List();

            switch (tipo)
            {
                case MyListTypeEnum.MyList1:
                    List myList1 = GetMyList1();
                    foreach (var item in myList1)
                    {
                        result.Add((IMyList) item);
                    }
                    break;
                case MyListTypeEnum.MyList2:
                    List myList2 = GetMyList2();
                    foreach (var item in myList2)
                    {
                        result.Add((IMyList) item);
                    }
                    break;
            }

            return result;
        }

        public static List GetMyList1()
        {
            List myList1 = new List();
            myList1.Add(new MyList1 { Code = 1 });
            myList1.Add(new MyList1 { Code = 2 });
            myList1.Add(new MyList1 { Code = 3 });
            return myList1;
        }

        public static List GetMyList2()
        {
            List myList2 = new List();
            myList2.Add(new MyList2 { Name = "1" });
            myList2.Add(new MyList2 { Name = "2" });
            myList2.Add(new MyList2 { Name = "3" });
            return myList2;
        }
    }

    public interface IMyList
    {
    }

    public class MyList1 : IMyList
    {
        public int Code { get; set; }
    }

    public class MyList2 : IMyList
    {
        public string Name { get; set; }
    }

    public enum MyListTypeEnum
    {
        MyList1,
        MyList2
    }
}


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.


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...
}
0

精彩评论

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

关注公众号