Страницы

Поиск по вопросам

вторник, 18 июня 2019 г.

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

всем привет, имеется следующий пример:
public abstract class Shape { } public class Circle : Shape { }
public interface IContainer { T Figure { get; } }
public class Container : IContainer { 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 container = new Container(circle);
Console.WriteLine(container.Figure.ToString());
// Delay. Console.ReadKey(); } }
меня интересует вот эта строка
IContainer container = new Container(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 container = new Container(circle);
но, раз поле figure меняет тип, а ссылка на это поле хранится в этом экземпляре, который мы передаем в качестве аргумента конструктора.
Моя догадка заключается в том, что раз мы тип Т у Container (Circle) - тот тип которого и аргумент конструктора circle - приводим к типу Т у IContainer(Shape), то и этот аргумент конструктора типа Circle также неявно UpCast - ится "следуя" изменениям своего типа - и соответственно затем произойдет UpCast типов внутри этого экземпляра в том числе и поля figure - которое изменит свой тип Circle на Shape.


Ответ

Смотрите.
У вас в строке IContainer container = new Container(circle); есть лишь одно приведение типов: левая часть имеет тип IContainer, а правая — Container
Почему такое приведение возможно? Дело в out, которое у вас в interface IContainer. Смысл этого самого out таков: если Т1 — подтип T2, то IContainer считать подтипом IContainer
Таким образом, у вас Container приводится к IContainer, а вследствие out IContainer приводится к IContainer
То есть при этом реальный тип объекта как был, так и остаётся Container. То, что к этому объекту можно получить доступ по ссылке типа IContainer, не заставляет тип поля самого объекта поменяться.

Почему вообще приведение из IContainer к IContainer возможно? Это контролируется компилятором. Дело в том, что тип T у вас находится в интерфейсе позиции возвращаемого значения. Это значит, что если у вас реально в руках есть IContainer, то он удовлетворяет требованиям на IContainer: его свойство Figure возвращает объект типа Circle, а значит, его можно трактовать как объект типа Shape
(Если бы T находилось в позиции аргумента, а не возвращаемого значения, компилятор не дал бы вам скомпилировать интерфейс с out.)

Комментариев нет:

Отправить комментарий