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
  • Вопрос задан
  • 427 просмотров
Решения вопроса 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
Прочитайте про ооп с примерами кода.
На практике необходимость и профит очевидны.
Для динамических языков и слаьотипизипованных условия менее строгие, но преимущества те же.
Лень разжевывать, может другому не будет.
Ответ написан
Комментировать
Ваш ответ на вопрос

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

Войти через центр авторизации
Похожие вопросы