C # полиморфизм простой вопрос - PullRequest
9 голосов
/ 09 февраля 2010

Я получил класс X и класс Y, последний из которых является производным от X:

class x {}
class y : x {}

Тогда где-то я использую список X:

List<X> lstX;
...

Тогда я бы хотел использовать новый список Y из данных в моем другом списке ... что-то вроде этого:

List<Y> lstY = lstX;

Я полагаю, что элементы в списке X будут автоматически преобразованы в Y, но это не так.

Кроме того, как я мог инициализировать новый экземпляр Y из определенного X? Я хотел бы сделать:

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

но, похоже, это не так.

Спасибо за вашу помощь! и извините за форматирование, стараюсь изо всех сил

Ответы [ 7 ]

27 голосов
/ 09 февраля 2010

Здесь есть пара вопросов.

Сначала: «Я могу назначить объект типа Tiger переменной типа Animal. Почему я не могу назначить объект типа List Tiger переменной типа List of Animal?»

Потому что тогда это происходит:

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.

В C # 4 это будет законно:

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

Это допустимо, потому что IEnumerable<T> не имеет метода "Добавить", и поэтому это гарантированно безопасно.

См. Мою серию статей о ковариации для получения подробной информации об этой новой функции C # 4.

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

Второй вопрос: «Как я мог инициализировать новый экземпляр Тигра от определенного Животного?»

Вы не можете. Это животное может быть Божья коровка. Как вы собираетесь инициализировать нового тигра из экземпляра Божья коровка? Это не имеет никакого смысла, поэтому мы не позволяем вам сделать это. Если вы хотите написать свой собственный специальный метод, который знает, как превратить произвольных животных в тигров, вы можете это сделать. Но мы не знаем, как это сделать для вас.

3 голосов
/ 09 февраля 2010

Это никогда не сработает; в конце концов List<Y> lstY = lstX; просто копирует ссылку (если вы не добавите свой собственный оператор неявного статического преобразования в свой собственный тип списка) - так что это все еще список X и может содержать вещи другие чем Y экземпляров.

Даже в 4.0 дисперсия co / contra не распространяется на a: списки (как in и out), так и b: конкретные типы (например, List<T>).

Интересно, однако, что будет (и всегда имеет) работать для массивов ссылочных типов, но только в направлении X[] arrX = arrY;. Это ничего не конвертирует; если вы попытаетесь ввести неверные данные, произойдет исключение.

1 голос
/ 28 апреля 2012

Попробуйте это:


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

1 голос
/ 09 февраля 2010

Он не может автоматически "расширять" тип объектов от x до y, потому что X - это не Y, а Y - X.

Вы можете попробовать привести из X в Y, но это не удастся с InvalidCastException, если ваш объект изначально не был Y, маскирующимся под X все время. Вам нужно инициализировать и заполнить новый List<Y> вручную:

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);
}
1 голос
/ 09 февраля 2010

Нет, поскольку вы не можете быть уверены, что все элементы в listX, которые относятся к типу 'X', также относятся к типу Y.
Отношение наследования происходит наоборот: элемент типа Y может быть приведен к X, потому что Y is-a X.

В C # также нет доступных «конструкторов копирования», как в C ++, поэтому я боюсь, что вам придется реализовать эту логику, чтобы иметь возможность инициализировать новый экземпляр Y из определенного X, сами. Также имейте в виду, что классы являются ссылочными типами ...

0 голосов
/ 09 февраля 2010

Проблема, с которой вы столкнетесь в своем первом примере, состоит в том, что Y является производным от X, поэтому он «является X», но «X не является Y», также C # в настоящее время не поддерживает приведение типов в этом методе. Вы можете попробовать использовать методы расширения для Cast, такие как lstX.ConvertAll в качестве помощника для достижения этой цели.

По второму вопросу вы хотите обратиться к конструктору копирования, например:

public Y(X baseObject) {
   //copy all the data you need here...
}
0 голосов
/ 09 февраля 2010

Ваш сценарий отстает от обычных полиморфных сценариев использования. Y является X, но X не является Y.

Имея это в виду, вы можете заставить работать так, как вы говорите, помещая клонирующий код в конструкторы и все такое.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...