UpCast/DownCast C#

117
16 января 2021, 10:50

В процессе изучения языка C# сталкиваюсь, как и все новички, с трудностями понимания. Изучаю язык на нескольких ресурсах, так как каждую тему собираю по понятным для меня кусочкам.

Очередной босс, которого я не могу пройти - это UpCast/DownCast, который приправлен обобщениями. Сначала всё казалось элементарным, темы проходные: Привёл к нужному типу, скрыл какую-то функциональность, засунул нужный тип в метод и всё отлично. Определил класс/метод/делегат/интерфейс с обобщением, в нужном месте подставил системный тип и готово - вы восхитительны ! На большинстве ресурсов на этих примерах повествование заканчивается, но не на ITVDN. Когда начали подставлять пользовательские типы я поплыл.

Объясните, пожалуйста, на примерах из реального мира, детских аналогиях или подобными методами, зачем это всё ??? Зачем мы приводим к типу интерфеса. а где-то присваиваем объекту производного класса ссылку на базовый ??? Когда закрываем обобщение пользовательским типом - это вообще к-к-комбо для моего мозга. Я получил понимание как это делается, но не знаю для чего и в каких случаях ( буду очень благодарен простым аналогиям ) я должен реализовывать эти возможности языка ??? Я почитал темы на форуме, но так и не смог понять для себя глобальный смысл этого мероприятия.

 public abstract class Shape { }
public class Circle: Shape { }
public interface IContainer<T>
{
    T Figure { get; set; }
}
public class Container<T> : IContainer<T>
{
    public T Figure { get; set; }
    public Container(T figure)
    {
        this.Figure = figure;
    }
}
class Program
{
    static void Main(string[] args)
    {
        Circle circle = new Circle();
        IContainer<Circle> container = new Container<Circle>(circle);
        Console.WriteLine(container.Figure.ToString());
    }
}

Вопросы:

1.С какой целью в данном примере мы присваиваем ссылку типа IContainer объекту Container ?

2.Закрывая Т типом Circle, что мы ожидаем получить ?

Я не могу понять какой смысл в " public Circle Figure {get;set;}". Как идёт моя логическая цепочка:"Объекту типа Container присваиваем ссылку типа IContainer( Зачем ?? IConteiner и Container оба содержат только T Figure ). При этом мы закрываем типом Circle ( переходим в класс Container и видим что это отражается на типе свойства Figure и конструкторе. Тут начинается основной затык: public Circle Figure {get;set;} - как свойство Figure может быть типа Circle и что мы можем ему присваивать ??? Вероятно, нет понимания из-за отсутствия наполнения классов какими-либо свойствами, методами чтобы стало ясно какая функциональность добавляется/скрывается при выполненных приведениях и закрытии обобщений тем или иным типом ( на примере указанного выше кода ).

Answer 1

1.С какой целью в данном примере мы присваиваем ссылку типа IContainer объекту Container ?

Например, для того чтобы работать с объектом только как с контейнером. Приведу пример:

Представьте, что ваш Container<T> - более сложный класс, который предоставляет дополнительный функционал:

  • сохранение значение контейнера в файл;
  • сравнение двух контейнеров между собой.

Тогда Container<T> будет примерно таким:

public class Container<T> : IContainer<T>, IStorable<T>, IComparable<T>
{
    public T Figure { get; set; }
    public Container(T figure)
    {
        this.Figure = figure;
    }
    public void Store(T item, string filePath)
    { 
        // Реализация IStorable<T>
    }
    public int CompareTo(T other)
    {
        // Реализация IComparable<T>
    }        
}

И чтобы можно было работать с объектом Container<T> как с объектом, который можно сохранять в файловой системе, мы сделаем следующее:

IStorable<Circle> shapeToSave = new Container<Circle>(circle);
shapeToSave.Store(...);

2.Закрывая Т типом Circle, что мы ожидаем получить ?

Объекту типа Container присваиваем ссылку типа IContainer...

Наоборот. Переменной типа IContainer<Circle> мы присваиваем ссылку на объект Container<T>.

По поводу обобщения. Вот у нас есть свойство:

public T Figure { get; set; }

Компилятор, когда видит строчку

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

понимает, что нужно создать класс, подобный Container<T>, где вместо типа T будет наш Circle. Поэтому свойство "магическим образом" становится типа Circle. После этой строчки мы этому свойству можем присваивать только объекты типа Circle.

Если что-то дополнительно нужно пояснить,отпишите об этом ниже.

READ ALSO
Как скопировать каталоги в потоке с входными параметрами?

Как скопировать каталоги в потоке с входными параметрами?

Учусь запускать методы в потокеПодскажите как запустить метод в потоке, если в методе есть входные параметры?

134
Использовать ли оператор return , если функция ничего не возвращает?

Использовать ли оператор return , если функция ничего не возвращает?

Нужно ли использовать оператор return в функции, тип возвращаемого значения которой указан как void, или же его просто опускать? В чем разница...

133
Бинарная сериализация COM-объектов

Бинарная сериализация COM-объектов

Как правильно перевести в массив байт COM-объект и/или объекты, к классам которым нельзя получить доступ и поставить им атрибут Serializable (например,...

143