0: Приветствую вас, друзья. Меня зовут Сергей. Мы с вами продолжаем изучать тему полиморфизма в объектно ориентированном программировании в языке программирования сишарп. И сегодня мы с вами будем говорить о том, что такое абстрактный класс. Абстрактный метод, абстрактное свойство, как это все работает, для чего оно нужно.
1: И причём здесь вообще полиморфизм, не забудьте поддержать это видео Лайком это поможет развитию канала. Канал развивается, вы получаете новые уроки. Ну и теперь приступим непосредственно к делу. Для начала, если вы не смотрели прошлый урок, где мы начали разбираться с тем, что же такое вообще.
2: Полиморфизм и что такое виртуальные методы? Как они переопределяются, то рекомендую вам это сделать. В таком случае этот урок вам будет более просто понять ссылку найдёте в описании теперь к тому, что же такое абстрактный класс и как вообще сделать класс абстрактным
3: В данном случае у нас здесь имеется класс плеер, который мы будем использовать в этом уроке. Давайте сразу же попробуем сделать из него абстрактный класс и посмотрим, что из этого получится. Для того, чтобы сделать класс абстрактным, нам нужно использовать ключевое слово abstract. Как только мы сделали наш класс абстрактным
4: Мы сразу же получили ошибку на этапе компиляции, которая говорит нам о том, что мы не можем создать экземпляр абстрактного класса. Это запрещено. Возникает логичный вопрос, зачем нам нужен такой класс, объект которого мы не можем создать, как мы вообще его можем?
5: Пользовать. Дело в том, что абстрактный класс это, по сути, некая идея, описание некоего контракта, возможность какой-то частичной реализации поведения этого объекта, но все это мы можем использовать только в наследниках нашего
6: Абстрактного класса для того, чтобы понять смысл того, что я сейчас сказал, приведу немножко другой пример. Допустим, мы с вами хотим написать некую игру, в которой у нас будет игрок, и этот игрок может стрелять из разных видов оружия. Это может быть
7: Абсолютно любой вид оружия, но в нашем конкретном случае нам важно, чтобы в этом оружии была реализована какая-то логика, которая позволяет из него выстрелить, и благодаря тому, что ссылка базового класса может хранить экземпляры.
8: Наследников, то в нашем базовом классе мы можем описать, какой метод мы будем использовать для того, чтобы выстрелить. При этом нам даже не обязательно его реализовывать. А в наследниках мы уже напишем конкретную реализацию. И в таком случае, если
9: Вот этот базовый абстрактный класс мы будем использовать в нашем классе плеер, то наш игрок, наш плеер, сможет стрелять абсолютно с любого вида оружия, которое было унаследовано от этого базового класса. В принципе, нечто подобное мы рассматривали в прошлом.
10: Давайте теперь реализуем эту идею, посмотрим, как это все будет выглядеть и почему нам удобно и логично здесь будет использовать именно абстрактный класс. Допустим, мы хотим научить наш класс плеер стрелять из любого вида оружия. Создадим
11: Класс weapon. И давайте с вами подумаем. Мы хотим научить наш класс плеер стрелять из оружия, вообще, из любого оружия, какого угодно вида. Нам неважно, что это будет пистолет, автомат, луки, арбалет.
12: Баллисты. В общем, главное, чтобы оно могло стрелять. И для всего этого нам понадобится базовый класс. Это, в принципе, какое-то абстрактное оружие. Ну, вепон не какое-то конкретное. То есть это не автомат, не пулемёт, не лук, это
13: Просто нечто абстрактное, которое должно стрелять. Логично, что в этом случае нам не нужно иметь возможность создавать экземпляр этого нечто, потому что сейчас мы с вами просто описываем идею и контракт.
14: Взаимодействие. И нам нужно сказать, что если какой-то класс вот эту идею в себе содержит, реализовывает, то значит мы можем его использовать. Ну и в нашем случае это уже будет какая-то конкретная реализация какого-то оружия мы можем из него выстрелить. В таком случае мы делаем наш класс
15: По сути это точно такой же класс, как и все остальные, и мы можем создавать в нём конструкторы, указывать какие-то параметры, которые будут принимать эти конструкторы, перегружать эти конструкторы. Также мы можем создавать в этих классах какие-то методы, писать какую-то
16: Реализацию, но в данном конкретном случае мы хотим использовать наш класс weapon как некий контракт, который будут реализовывать его наследники. Что я подразумеваю под контрактом. Если это оружие, мы должны иметь возможность из него как-то стрелять.
17: Соответственно, все наследники класса вепон тоже должны иметь возможность стрелять. Соответственно, у нас в нашем классе вепон должен быть метод фаер, но наш класс weapon это абстрактный класс, это не какое-то конкретное оружие, поэтому мы
18: Вписываем лишь идею, что вот это нечто должно иметь возможность стрелять. Соответственно, раз мы ещё не знаем, что это такое, мы и не можем написать реализацию для метода фаер. И более того, она нам и не нужна, потому что конкретную реализацию мы будем делать в классах наследии.
19: Поэтому и метод фаер мы тоже можем сделать абстрактным, но раз этот метод абстрактный, то в нём в принципе не может быть реализации. Поэтому мы убираем фигурные скобки, где должна быть реализация. И просто вот указываем вот такую сигнатуру, то есть что
20: Это паблик метод возвращает он Войт, у него вот такое название принимает он вот такие параметры. Сразу же хочу обратить ваше внимание на то, что абстрактные методы могут находиться только внутри абстрактных классов. Поэтому если у нас класс не абстрактный, мы попытаемся
21: Там описать абстрактный метод, то такой код не скомпилируется. Теперь вернёмся к нашему классу плеер его метод фаер. Мы будем передавать объект типа weapon и у этого объекта типа weapon. Вызывать метод фаер. Теперь наш класс плеер.
22: Может стрелять из всего чего угодно, что будет унаследовано от абстрактного класса вепон это полиморфизм в чистом виде. Давайте опишем какой-то класс и унаследуем его от класса вепон. Это будет уже какой-то класс, экземпляр которого мы можем создать
23: Пусть это будет класс Ган, и мы унаследуем я от Вепа. Видите, после того, как мы написали синтаксис наследования, у нас появилась ошибка и среда разработки нам сообщает о том, что класс Ган должен реализовывать метод фаер.
24: Но мы его не реализовали. Если вы помните, я несколько раз упоминал слово контракт, когда говорил о наследниках класса веп. Так вот, если у нас есть абстрактный класс и в нём есть какой-то абстрактный метод, то наследники абстрактного класса должны реализовать его.
25: Тактные методы. В этом и заключается контракт, что мы обязываем Наследников эти методы реализовать. Если метод реализован не будет, такой код просто не скомпилируется. Этим самым мы гарантируем то, что все наследники класса вепон будут иметь метод фаер и
26: Соответственно, наш плеер из любого класса, который унаследован от ветен, может этот метод использовать, и, соответственно, из этого нечто может стрелять у среды разработки visual studio есть полезный функционал, который позволяет ускорить написание кода.
27: Для того, чтобы реализовать вот такие абстрактные методы в наследниках, для того, чтобы это сделать, мы просто наведём на название класса Ган жмакнет на вот эту жёлтую лампочку и выберем имплемент абстракт класс, как видите, у нас здесь был реализован
28: Метод фаер обратите внимание на ключевое слово override в этом методе в прошлом уроке мы уже говорили об этом ключевом слове и оно позволяет переопределить работу метода, но в том уроке мы его использовали в паре с
29: Ключевым словом virtual в базовом классе в общем то write работает и с virtual и с abstract, ну и соответственно так же как мы ключевое слово override использовали для переопределения виртуальных методов в прошлом уроке, в этом уроке мы исполь.
30: Ключевое слово override для переопределения абстрактных методов, ну и так как этот код был сгенерирован вижуал студио, здесь по умолчанию, когда метод генерируется, выбрасывается на имплемент эксепшен, тему эксепшенов мы будем поднимать в следующих уроках.
31: Сути. Вот эта строчка кода при попытке вызвать метод фаер у класса Ган выбросит ошибку, что мы не реализовали этот метод, мы можем этот код просто убрать и ошибки уже не будет, но и реализации ещё нет. Реализацию мы должны написать сами. Ну и собственно реализ
32: У нас будет такая на консоль мы будем выводить сообщение пыш, ну и, соответственно, также, как это было у нас и в прошлом уроке. Теперь мы можем создать объект класса Ган.
33: И передать этот объект класса Ган в метод фаер нашему объекту, типа player наш плеер умеет стрелять из всего, что унаследовано от класса вепон, соответственно, из объекта класса Ган наш плеер сможет выстрелить, и мы
34: Получим сообщение, пыш. Если мы посмотрим в дебаге, как это работает, поставим брейкпоинт, сделаем шаг за ходом, попадаем в метод фаер класса плеер. Здесь вызывается метод фаер у того, что мы передали в качестве параметра
35: Переменную вепон там у нас объект класса Ган, который может храниться в ссылке типа weapon, потому что gone унаследован от вепон, ну и так как метод у нас переопределён, я имею ввиду метод фаер в классе Ган, то мы вызываем реализацию
36: Именно класса Ган и видим сообщение пущ на консоль для примера я добавил ещё пару классов это laser gun и bo, то есть лазерная пушка и лук оба этих класса унаследованы от нашего класса, веп у них переопределён метод фаер, соответственно наш плеер.
37: Сможет выстрелить из лазер Гана, из лука. Ему, в общем, все равно, из чего стрелять, лишь бы это нечто было унаследовано от абстрактной идеи оружия и имело метод фаер. Давайте немножечко отредактируем код, который у нас в методе main, и представим, что
38: У нас в нашей игре есть некий инвентарь, в котором есть разные виды оружия выглядеть это будет примерно вот так. То есть у нас есть некий массив типа weapon, и в качестве элементов этого массива мы поместили объекты.
39: Разных классов Ган, лазер, Ган боу. Мы можем поместить все эти элементы разных классов, ну, якобы разных классов в 1 массив типа weapon по той причине, что все эти классы унаследованы от типа weapon.
40: Соответственно, хоть мы и не можем создать экземпляр абстрактного класса, но мы его можем использовать в качестве ссылки как тип данных для того, чтобы хранить в нём его Наследников. Ну и в данном случае мы используем массив. Далее мы можем просто перебрать этот массив.
41: Можем циклом foreach, можем циклом for и нашему классу плеер в метод фаер мы можем передавать все элементы этого массива, а так как в нашем массиве любой элемент унаследован от класса вепон и должен реализовать по сути вот этот контракт.
42: То есть абстрактный метод фаер из всего того, что находится в этом массиве, наш класс плеер с помощью метода фаер может выстрелить. Добавлю здесь дополнительный пробел и запущу этот код. Наш плеер выстрелил из всех элементов, из всех видов.
43: Оружия, которые были у нас в массиве. А это и есть полиморфизм в чистом виде. При этом всем каждый раз, когда мы захотим реализовать новый вид оружия, нам не нужно будет переделывать наш класс плеер, нам нужно лишь будет унаследовать это новое оружие от нашего
44: Базового абстрактного класса вепон. И в этом случае наш плеер из нашего нового оружия нового класса, который мы реализуем, все равно сможет выстрелить. Я уже упоминал, что абстрактный класс может содержать в себе не только какие-то абстрактные
45: Методы, но и конкретную реализацию. Давайте сюда добавим метод, назовём его шоу инфо, с помощью которого мы сможем проверять информацию о том, какое оружие мы сейчас используем. И в данном случае выглядеть это будет так. Console. Мы будем выводить имя
46: Типа данных, у которого мы вызвали метод show инфо, обратите внимание на метод get tape откуда он вообще здесь взялся? Как я уже упоминал, в нескольких прошлых уроках все типы данных в языке программирования сишарп унаследованы от типа object метод get.
47: Это метод типа object. Соответственно, у любого типа данных этот метод есть. Так вот, мы этот самый метод будем вызывать и обращаться к полю name. Таким образом, выводить информацию о типе данных. Метод show инфо у нас реализован в абстрактном классе веп.
48: Все остальные классы у нас от этого класса вепон унаследованы, соответственно, они все содержат этот метод и могут его использовать. Давайте модифицируем наш класс плеер таким образом, чтобы он мог проверять информацию об оружии, которое
49: Собирается использовать. Назовём этот метод чек инфо сюда мы тоже будем передавать параметр типа weapon, но в данном случае здесь мы уже будем вызывать метод show инфо. Таким образом наш игрок будет проверять информацию о
50: Об оружии, а само конкретное оружие будет выводить информацию себе на консоль. Соответственно, вот здесь, прежде чем стрелять, мы можем вызывать метод чек инфо и передавать туда айтем из нашего массива, который
51: Представляет у нас инвентарь в виде разных типов оружия, если мы запустим этот код теперь
52: То мы получим информацию о том, из какого же оружия стрелял наш игрок. При этом всем метод show инфо у нас реализован только в нашем базовом абстрактном классе, и все его наследники теперь получат возможность выводить информацию о себе. Следующий момент, который нам
53: Нужно обсудить это абстрактное свойство. У нас на канале есть отдельный урок о том, что такое свойство сишарп. Рекомендую его посмотреть, если не смотрели. Но если вы смотрели, то, наверное, вы помните, что я там упоминал, что, по сути, свойство это тот же самый метод.
54: Ну вот, например, как вот этот, но который маскируется под поле, пытается себя вести как переменная. Сделано это для упрощения работы с гетерами и сеттера. Так вот, раз наше свойство это, по сути, метод, то оно также может быть
55: Абстрактным и может быть виртуальным. И его можно переопределить в наследниках. Давайте добавим в наш класс weapon абстрактное свойство, которое будет отвечать за то, какой урон наносит наше оружие, какое-то уже конкретное, которое мы потом реализуем в наследии.
56: Допустим, я не хочу, чтобы у нас была возможность извне, на уровне экземпляра домаг нашего оружия изменять, поэтому я уберу отсюда.
57: Оставлю только getter. Обратите внимание на то, что как только мы добавили абстрактное свойство наш класс weapon, у нас сразу же появилось 3 ошибки. Это опять-таки та же самая ошибка, которая была с методом фаера до того, как мы не написали реализацию конкретных класс.
58: Наследника. И ошибка говорит нам о том, что для нашего свойства, Демич, в наследниках не выполнена реализация. Ну и, соответственно, такой код не скомпилируется. Раз свойство у нас абстрактное, мы обязаны реализовать его в наследнике. Давайте поступим следующим образом.
59: Так как мы это делали, и с методом фаер с помощью средств вижуал студии у класса Ган выберем имплемент абстракт класс и у нас добавится реализация для поля дём. Видите, здесь у нас некий
60: Синтаксис, который мы ещё с вами ранее не видели. Здесь используется лямбда выражения. Мы с вами будем проходить их в будущем. Здесь я его просто для примера хочу показать, но, по сути, мы можем реализовать это так же, как делали и до этого с обычным
61: Геттерами и сеттерами. И нам нужно здесь указать, какое значение будет просто возвращать наше свойство. Демедж. Теперь перейдём к классу боу и в принципе, здесь мы должны сделать тоже самое. И посмотрите для того, чтобы
62: Не писать вот такой синтаксис, а воспользоваться лямбдой, хоть мы их ещё и не учили, давайте все равно это сделаем. Давайте здесь троечку укажем. Вот этот синтаксис, по сути, будет равнозначен вот Такому синтаксису. Просто он, короче, работать будет одинаково.
63: Ну и у нас ещё остался класс лазерган. Можем вот это вот скопировать, добавить сюда. Ну, пускай лазерган у нас там побольше урона наносит. Теперь у любого наследника класса вепон есть ещё и свойство, Демич, это все, конечно, хорошо. Мы можем обращаться к
64: Полю на уровне объектов, но помимо этого мы прямо в нашем абстрактном классе можем написать логику, которая будет использовать это абстрактное свойство и в том числе мы можем использовать в абстрактном классе, в методах, у которых есть какая
65: Это конкретная реализация, как в нашем методе шоу инфо какие-то другие абстрактные члены. То есть, по сути, мы могли бы здесь вызвать вот этот fire и во всех наследниках. Каждый раз, когда бы мы вызывали метод show инфо, вызывались бы конкретные реалии.
66: Method. Ну, в нашем случае это нам и не интересно, и нелогично, но вот абстрактное свойство, демыч, мы вполне можем использовать, и мы модифицируем метод show инфо таким образом, чтобы помимо названия класса у нас выводился ещё и урон для этого
67: Класс. Вот таким образом, теперь, когда у нас будет вызываться метод show инфо, то у каждого класса, который унаследован от абстрактного класса вепон, будет выводиться информация о его уроне. Давайте попробуем теперь это все запустить и посмотреть, что.
68: Ну и, собственно, мы теперь получаем информацию о типе оружия, его уроне. Ну и собственно реализацию самого метода фаер полиморфизм в чистом виде. Исходный код урока, как всегда, найдёте на патрион. Буду рад, если поделитесь этим уроком, это поможет развитию.
69: Канала. Ну и конечно, если вы ещё не подписаны на канал, то обязательно подписывайтесь. Здесь уже есть и будет ещё много полезных интересных уроков для вас. Всем спасибо за внимани.