BadCats
@BadCats

Generic Типы и двойной UpCast?

всем привет, имеется следующий пример:
public abstract class Shape { }
    public class Circle : Shape { }

    public interface IContainer<out T>
    {
        T Figure { get; }
    }

    public class Container<T> : IContainer<T>
    {
        private T figure;

        public Container(T figure)
        {
            this.figure = figure;
        }

        public T Figure
        {
            get { return figure; }
        }
    }

    class Program
    {
        static void Main()
        {
            Circle circle = new Circle();

            IContainer<Shape> container = new Container<Circle>(circle);

            Console.WriteLine(container.Figure.ToString());

            // Delay.
            Console.ReadKey();
        }
    }

меня интересует вот эта строка
IContainer<Shape> container = new Container<Circle>(circle);

-экземпляр circle (передаваемый в качестве аргумента конструктора) ни приводится ни к какому типу в данном случае - т.к тип Т (я знаю, что правильно говорить тип места заполнения типом Т, но просто "тип Т" - будет короче) у класса Container мы закрыли тем же типом, что и данный экземпляр (и я имею ввиду, что т.к типы у них одинаковые, то и приводится не к чему.)

Итак, у нас сперва тип Container приводим к типу IContainer, а поле figure типа Circle приводится к типу Shape.

Почему поле figure внутри класса Container приводится к типу Shape?

итак мои предположения:

1) передаваемый в качестве аргумента экземпляр тут ни причем - т.к как уже было сказанно выше сам экземпляр никчему не приводится.

2)Из-за того, что тип Container мы привели к типу IContainer - тип Т у которого мы закрыли типом Shape

- и отсюда возникает еще один вопрос: у нас одновременно происходит как бы два UpCast -а : Container - IContainer;

Circle-Shape; - какой из них так скажем влияем на передаваемый аргумент конструктора - наш экземпляр circle (да-да, я знаю, что выше писал, что UpCast -a экземпляра circle не происходит - просто в предыдущем примере экземпляр приводится к типу Shape )
IContainer<Shape> container = new Container<Shape>(circle);

- но, раз поле figure меняет тип, а ссылка на это поле хранится в этом экземпляре, который мы передаем в качестве аргумента конструктора.

Моя догадка заключается в том, что раз мы тип Т у Container (Circle) - тот тип которого и аргумент конструктора circle - приводим к типу Т у IContainer(Shape), то и этот аргумент конструктора типа Circle также неявно UpCast - ится "следуя" изменениям своего типа - и соответственно затем произойдет UpCast типов внутри этого экземпляра в том числе и поля figure - которое изменит свой тип Circle на Shape.
  • Вопрос задан
  • 231 просмотр
Решения вопроса 1
Nipheris
@Nipheris Куратор тега C#
Вы не совсем верно понимаете ковариантность в generic-интерфейсах и напутали в кучу много всего.

Во-первых, тип поля figure ни на какой другой не меняется. Он не может измениться "на ходу", т.к. конкретный объект конкретного класса уже создан.

Тип возвращаемого значения свойства Figure также не меняется.
Еще раз повторюсь - у класса и у его экземпляра не меняются никакие типы ни полей, ни свойств.

Вы почему-то воспринимаете приведение типа класса к интерфейсу как конвертацию значения объекта - как будто создастся другой Circle, у которого будут другие типы полей. Совершенно точно это не так.

Когда речь идёт о ссылочных типах (а полиморфизм с использованием интерфейсов работает только с такими типами), то вам следует четко понимать, что вы работаете со ссылкой, и приведение типа для ссылки никак не может влиять на сам объект. Иными словами, вы просто меняете "окно", через которое смотрите на объект.

Таким образом, ковариантность в случае интерфейсов - это вопрос исключительно типизации. Когда в C# добавили ко- и контрвариантность, фактически вам разрешили считать, что IContainer это частный случай IContainer, а раз так - то ВСЯКИЙ IContainer можно трактовать как IContainer. В ранних версиях языка этого нельзя было сделать - можно было только Container трактовать как IContainer.

Если в ответе что-то непонятно, спрашивайте, т.к. по вашему вопросу создаётся ощущение, что у вас где-то большой пробел в понимании языка. Вы закапываетесь в детали, делая совершенно неверные предположения о происходящем.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы