BadCats
@BadCats

Generic Типы и UpCast к базовому интерфейсному типу(ковариантность обобщений)?

Всем привет, имеется следующий код:
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()
        {
            Circle circle = new Circle();

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

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

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

Итак, вкратце - что я понял в данном примере.

В данном примере мы сперва создаем абстрактный класс с именем Shape{}, затем мы создаем конкретный класс с именем Circle - который наследуется от абстрактного класса shape.

далее мы создаем открытый интерфейс с именем IContainer - параметризированный - указателем места заполнения типом Т , и в теле данного интерфейса, мы создаем абстрактное авто-свойство с именем Figure - типа указателя места заполнения типа Т.

Далее vss создаем класс с именем Container - который реализует интерфейс IContainer, который в свою очередь(интерфейс) параметризирован типом места заполнения типом Т.

В теле класса Container мы реализуем абстрактное свойство Figure, и после этого мы создаем конструктор, который принимает один аргумент типа указателя места заполнения типом Т, и в теле этого конструктора мы авто свойству Figure присваиваем значение аргумента данного конструктора нашему свойству.

Далее, в классе Program, в теле метода Main(), мы создаем экземпляр класса Circle - который как мы помним наследуется от абстрактного класса Shape.

На строке:
IContainer<Circle> container = new Container<Circle>(circle);

- мы создаем переменную с именем container - типа базового интерфейсного типа IContainer, а параметр типа места заполнения типом Т у этого базового интерфейсного типа мы "закрываем" нашим типом Circle.

Условно разделим приведенную ниже строку на две части:

IContainer container = new Container(circle);

( курсив- первая часть , жирная - вторая часть)

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

Вот мой вопрос: с абстрактным классом Shape{} и конкретным классом Circle{} - наследующим его - все понятно.

Затем, кто-нибудь объясните толком и по человечески что такое интерфейс и для чего он нужен (нет, я конечно его изучал, но толком не понял; вот например про делегаты мне простым языком объяснили так - "Делегаты — это способ сделать следующее: взять произвольный кусок кода, упаковать его в объект (экземпляр делегата) и вызвать этот кусок кода в любом месте, куда получится передать ссылку на наш экземпляр делегата." - может это и не совсем правильно, но это понятно!)

С авто свойством, его реализацией и конструктором - ине тоже все понятно.

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

И мой самый главный вопрос также вытекает из "что такое интерфейсы - толком" - непонятно зачем мы передаем конструктору уже готовый экземпляр класса Circle, - т.е неужели мы его(этот экземпляр передаваемый как аргумент конструктора) приводим к типу этого интерфейса.

напоследок вот картинка, на которой я пытался разобраться в ситуации:
7d54967f17164d939117237127bc7925.jpg
  • Вопрос задан
  • 440 просмотров
Решения вопроса 1
WarFollowsMe
@WarFollowsMe
В C# при помощи интерфейсов реализуют важное свойство ООП - полиморфизм. Вкратце, это выделение одинаковых свойств и методов объектов, не привязываясь к их типу. Если простыми словами, то например у нас есть ряд объектов: стул, стол, чашка, грустный мопс и комета Чурюмова-Герасименко. И например стоит задача отслеживать их передвижение. Все объекты разных типов и привести их к одну общему не получается. В этом случае на помощь как раз и приходят интерфейсы. Описываем нужные нам свойства
public interface INotifyCoordinateChanged
{
    string Name{get;set;}
    double X{get;set;}
    double Y{get;set;}
    double Z{get;set;}
    event Action<INotifyCoordinateChanged> CoordinateChanged;
}

и если мы реализуем у всех объектов этот интерфейс, то теперь нам достаточно отслеживать изменения в списке
List<INotifyCoordinateChanged> Objects{get;set;}
P.S. А ваш пример очень странный. Интересно для объяснения какой темы его использовали. Просто это абсолютно бессмысленная солянка, пмсм.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
Прочитайте про ооп с примерами кода.
На практике необходимость и профит очевидны.
Для динамических языков и слаьотипизипованных условия менее строгие, но преимущества те же.
Лень разжевывать, может другому не будет.
Ответ написан
Комментировать
Ваш ответ на вопрос

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

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