Введение в ООП с примерами на C#. Часть вторая. Все, что нужно знать о наследовании


Оглавление (нажмите, чтобы открыть):

Введение в ООП с примерами на C#. Часть вторая. Все, что нужно знать о наследовании

Все, что необходимо начинающему и опытному программисту

Наследование.

Итак, настало время ознакомиться с таким вечнозеленым понятием объектно-ориентированного программирования как наследование. Данный термин вам уже известен из курса языка программирования С++.Теперь мы его рассмотрим в контексте С#. Начнем с синтаксиса применяемого для наследования:

Обратите внимание на следующие отличия в механизме наследования C# от С++:

  1. В качестве базового класса при наследовании может быть указан только один класс. Это означает, что в C# нет механизма множественного наследования. Если же необходимо всё-таки выполнить его то тогда можно воспользоваться понятием интерфейса, которое будет рассмотрено позже.
  2. При наследовании не указываются спецификаторы доступа как в C++.

Рассмотрим пример наследования:

Из примера видно, что принципы наследования работают достаточно схоже с С++.

Спецификатор доступа protected.

Спецификатор доступа protected вам уже известен из курса С++. В С# он работает точно также. Например:

Конструктора при наследовании.

В иерархии наследования допускается, чтобы базовые и дочерние классы имели свои собственные конструкторы. В этом случае при создании объекта от наследованного класса сначала вызываются конструктора базовых классов в порядке наследования и только потом конструктор потомка (фактически “сверху вниз”). Например:

В данном примере мы не передавали параметры в конструкторы базовых классов Base и Child, а если нам нужно передавать? Например:

Для того чтобы решить возникшую проблему нужно использовать следующий синтаксис:

Для вызова конструктора базового класса используется ключевое слово base. Например:

Как видно из примера приведенного выше с помощью ключевого слова base можно в дочернем классе обратиться к унаследованным членам базового. Его можно использовать только в классе-потомке. Вы обратили внимание на появившееся предупреждение при компиляции программы приведенной выше?

Оно сообщает о том, что метод Show() класса MobileTelephone перекрывает версию Show из базового класса Device. Для того чтобы убрать его необходимо указать ключевое слово new перед Show внутри класса MobileTelephone. Например (фрагмент из программы):

Ссылки на объекты базового и дочернего классов.

С# — это строго типизированный язык. Это правда, для вас не новость. Например, это ярко проявляется при операции присваивания. Рассмотрим пример ошибки связанной с преобразованиями типов:

В данной программе возникает ошибка на этапе компиляции, так как невозможно автоматическое преобразование по отношению к переменным ссылочного типа (оно распространяется только на переменные обычных типов). Отсюда можно сделать вывод, что ссылочная переменная одного класса не может ссылаться на объект другого класса. Однако из этого правила есть одно исключение. Ссылочной переменной базового класса можно присвоить ссылку на объект дочернего класса, в этом случае через неё можно будет обратиться к тем членам производного, которые были унаследованы из базового. Например:

Запрет наследования

Иногда при разработке класса нужно запретить возможность наследования от него. Для этого используется ключевое слово sealed, которое указывается при объявлении класса. Данный механизм может понадобиться при разработке какого-то служебного класса. Например, уже известный вам класс Console библиотеки .NET Framework объявлен с использованием sealed. Рассмотрим программу, демонстрирующую этот принцип:

Основы наследования

Наследование является одним из трех основополагающих принципов объектно-ориентированного программирования, поскольку оно допускает создание иерархических классификаций. Благодаря наследованию можно создать общий класс, в котором определяются характерные особенности, присущие множеству связанных элементов. От этого класса могут затем наследовать другие, более конкретные классы, добавляя в него свои индивидуальные особенности.

В языке C# класс, который наследуется, называется базовым, а класс, который наследует, — производным. Следовательно, производный класс представляет собой специализированный вариант базового класса. Он наследует все переменные, методы, свойства и индексаторы, определяемые в базовом классе, добавляя к ним свои собственные элементы.

Поддержка наследования в C# состоит в том, что в объявление одного класса разрешается вводить другой класс. Для этого при объявлении производного класса указывается базовый класс. При установке между классами отношения «является» строится зависимость между двумя или более типами классов. Базовая идея, лежащая в основе классического наследования, заключается в том, что новые классы могут создаваться с использованием существующих классов в качестве отправной точки:

Всякий раз, когда один класс наследует от другого, после имени производного класса указывается имя базового класса, отделяемое двоеточием. В C# синтаксис наследования класса удивительно прост и удобен в использовании. Ниже представлено схематичное представление класса CSharp из вышеуказанного примера:

Как видите класс CSharp получает доступ к полям и методам класса ProfessorWeb. Всегда помните, что наследование предохраняет инкапсуляцию, а потому приватные члены никогда не могут быть доступны через ссылку на объект. Т.е. поле inf из примера не может быть доступно для вызова с помощью экземпляра класса obj.

Для любого производного класса можно указать только один базовый класс. В C# не предусмотрено наследование нескольких базовых классов в одном производном классе. (В этом отношении C# отличается от С++, где допускается наследование нескольких базовых классов. Данное обстоятельство следует принимать во внимание при переносе кода С++ в C#.) Тем не менее можно создать иерархию наследования, в которой производный класс становится базовым для другого производного класса. (Разумеется, ни один из классов не может быть базовым для самого себя как непосредственно, так и косвенно.) Но в любом случае производный класс наследует все члены своего базового класса, в том числе переменные экземпляра, методы, свойства и индексаторы.

Главное преимущество наследования заключается в следующем: как только будет создан базовый класс, в котором определены общие для множества объектов атрибуты, он может быть использован для создания любого числа более конкретных производных классов. А в каждом производном классе может быть точно выстроена своя собственная классификация.


Задачи на наследование классов, в которых данные описаны в качестве свойств

16.05.2013, 23:27

Задачи на использование классов и объектов, в которых данные описаны в качестве свойств
Круг на плоскости имеет координаты центра x0,y0 — вещественные свойства. Радиус круга r0 — также.

Наследование свойств и методов классов в Delphi
Не могу решить проблему с наследованием свойств и методов. Задача: Разработать консольное.

Наследование классов: почему производный класс не учитывает данные, введенные в базовом?
Почему при компилляции производный класс не учитывает данные, введенные в базовом? #include.

17.06.2014, 17:04 2

Напишу на Java, на С, если поймете исправите по аналогии.

Примерно в результате будет так, 2 товара:

Подушка: 99 рублей 99 копеек
Подушка: 100 рублей 0 копеек

Наследование классов в C++: что это и как он работает

Всем привет! Продолжаем изучать классы в C++. Сейчас поговорим об одном из свойств объектно ориентированного программирования — наследование.

Что такое наследование

Это принцип создание класса на базе уже существующего, при этом у нас есть возможность пользоваться функционалом (свойствами и методами) базового. Классы созданные таким образом называются производными или дочерними, а на базе которого создаются — родителем или базовым.

Этот механизм в объектно ориентированном программировании очень сильная фича. Она в несколько раз экономит время на создание проекта, а также не нагружает его повторяющимся кодом.

Производный класс мы можем усовершенствовать, добавляя:

И все это не изменяя базовый класс.

Например, на базе класса про животного можно создать потомка про собаку.

Модификатор доступа protected

Для начала нужно знать об доступе protected, который является неотъемлемой частью наследования. protected — это модификатор доступа, который работает как private, но распространяется также на свойства предка. На рисунки ниже можно увидеть какими доступами можно пользоваться.

Наследование C#

Наследование — один из трех фундаментальных принципов объектно-ориентиро­ванного программирования, поскольку именно благодаря ему возможно созда­ние иерархических классификаций. Используя наследование, можно создать общий класс, который определяет характеристики, присущие множеству связанных элемен­тов. Этот класс затем может быть унаследован другими, узкоспециализированными классами с добавлением в каждый из них своих, уникальных особенностей.

В языке С# класс, который наследуется, называется базовым. Класс, который на­следует базовый класс, называется производным. Следовательно, производный класс — это специализированная версия базового класса. В производный класс, наследующий все переменные, методы, свойства, операторы и индексаторы, определенные в базо­вом классе, могут быть добавлены уникальные элементы.

Мастер Йода рекомендует:  Как управлять развертыванием Windows-приложений

С# поддерживает наследование, позволяя в объявление класса встраивать другой класс. Это реализуется посредством задания базового класса при объявлении произ­водного. Лучше всего начать с примера. Рассмотрим класс TwoDShape, в котором оп­ределяются атрибуты «обобщенной» двумерной геометрической фигуры <например, квадрата, прямоугольника, треугольника и т.д.).

// Класс двумерных объектов, class TwoDShape <

public double width;

public double height;

public void showDiraO <

Console.WriteLine(«Ширина и высота равны » + width + » и » + height);


Класс TwoDShape можно использовать в качестве базового (т.е. как стартовую площадку) для классов, которые описывают специфические типы двумерных объек­тов. Например, в следующей программе класс TwoDShape используется для выведения класса Triangle. Обратите внимание на то, как объявляется класс Triangle. // Простая иерархия классов.

// Класс двумерных объектов. class TwoDShape <

public double width;

public double height;

public void showDimO <

Console.WriteLine(«Ширина и высота равны » + width + » и » + height);

II Класс Triangle выводится из класса TwoDShape. class Triangle : TwoDShape \

public string style; // Тип треугольника.

// Метод возвращает площадь треугольника, public double area() <

return width * height / 2;

// Отображаем тип треугольника, public void showStyleO <

public static vo >

Console.WriteLine(«Информация о tl: «); tl.showStyleO ; tl.showDimt);

Console.WriteLine(«Площадь равна » + tl.area<)); Console.WriteLine();

Console.WriteLine(«Информация о t2: «); 12.showStyleO ; t2.showDira();

Console.WriteLine(«Площадь равна » + t2.area());

Вот результаты работы этой программы. Информация о t1: Треугольник равнобедренный Ширина и высота равны 4 и 4 Площадь равна 8

Информация о t2: Треугольник прямоугольный Ширина и высота равны 8 и 12 Площадь равна 48

В классе Triangle создается специфический тип объекта класса TwoDShape, в данном случае треугольник. Класс Triangle содержит все элементы класса TwoDShape и, кроме того, поле style, метод area () и метод showStyle О. В пере­менной style хранится описание типа треугольника, метод area о вычисляет и воз­вращает его площадь, а метод showStyle () отображает данные о типе треугольника.

Поскольку класс Triangle включает все члены базового класса, TwoDShape, он может обращаться к членам width и height внутри метода агеа(). Кроме того, внутри метода Main () объекты tl и t2 могут прямо ссылаться на члены width и height, как если бы они были частью класса Triangle. Несмотря на то что класс TwoDShape является базовым для класса Triangle, это совершенно независимый и автономный класс. То, что его использует в качестве ба­зового производный класс (классы), не означает невозможность использования его самого. Например, следующий фрагмент кода абсолютно легален:

TwoDShape shape = new TwoDShape();

Безусловно, объект класса TwoDShape «ничего не знает» и не имеет права доступа К классу, производному от TwoDShape.

Общая форма объявления класса, который наследует базовый класс, имеет такой вид:

class имя_производного_класса : имя_базо&ого_класса < // тело класса

Для создаваемого производного класса можно указать только один базовый класс. В С# (в отличие от С++) не поддерживается наследование нескольких базовых клас­сов в одном производном классе. Этот факт необходимо учитывать при переводе С++-кода на С#. Однако можно создать иерархию наследования, в которой один производный класс становится базовым для другого производного класса. И конечно же, ни один класс не может быть базовым (ни прямо, ни косвенно) для самого себя.

Основное достоинство наследования состоит в том, что, создав базовый класс, ко­торый определяет общие атрибуты для множества объектов, его можно использовать для создания любого числа более специализированных производных классов. В каж­дом производном классе можно затем точно «настроить» собственную классифика­цию. Вот, например, как из базового класса TwoDShape можно вывести производный класс, который инкапсулирует прямоугольники:

// Класс прямоугольников Rectangle, производный //от класса TwoDShape. class Rectangle : TwoDShape <

// Метод возвращает значение true, если // прямоугольник является квадратом, public bool isSquare О <


II Метод возвращает значение площади прямоугольника, public double area О ( return width * height;

Класс Rectangle включает класс TwoDShape и добавляет метод isSquareO, ко­торый определяет, является ли прямоугольник квадратом, и метод агеа(), вычис­ляющий площадь прямоугольника.

Полиморфизм C#

Полиморфизм (от греческого слова polymorphism, означающего «много форм») — это качество, которое позволяет одному интерфейсу получать доступ к целому классу дейст­вий. Простым примером полиморфизма может послужить руль автомобиля. Руль (интерфейс) остается рулем независимо от того, какой тип рулевого механизма исполь­зуется в автомобиле. Другими словами, руль работает одинаково в любом случае: осна­щен ли ваш автомобиль рулевым управлением прямого действия, рулевым управлением с усилителем или реечным управлением. Таким образом, поворот руля влево заставит автомобиль поехать влево независимо от типа используемого в нем рулевого управле­ния. Достоинство такого единообразного интерфейса состоит, безусловно, в том, что, если вы знаете, как обращаться с рулем, вы сможете водить автомобиль любого типа.

Тот же принцип можно применить и к программированию. Рассмотрим, напри­мер, стек (stack), т.е. область памяти, функционирующую по принципу «последним пришел — первым обслужен». Предположим, вы пишете программу, для которой нужно организовать три различных типа стека. Один стек предназначен для целочис­ленных значений, второй — для значений с плавающей точкой, а третий — для сим­волов. В этом случае для реализации каждого стека используется один и тот же алго­ритм, несмотря на различие в типах сохраняемых данных. В случае не объектно-ориентированного языка вам пришлось бы создать три набора «стековых» подпро­грамм, имеющих различные имена. Но благодаря полиморфизму в среде С# можно создать один общий набор «стековых» подпрограмм, который обрабатывает все три типа стека. Иными словами, зная, как использовать один стек, можно использовать все остальные.

Концепцию полиморфизма часто выражают такой фразой: «один интерфейс — много методов». Это означает, что для выполнения группы подобных действий можно разработать общий интерфейс. Полиморфизм позволяет понизить степень сложности программы, предоставляя программисту возможность использовать один и тот же ин­терфейс для задания общего класса действий. Конкретное (нужное в том или ином случае) действие (метод) выбирается компилятором. Программисту нет необходимо­сти делать это вручную. Его задача — правильно использовать общий интерфейс.

ООП в примерах. Часть 2. Наследование

Продолжаем осваивать ООП.
Первая часть

Разберёмся с наследованием.
Наследование позволяет взять доступные свойства родительского (базового) класса и использовать их в производных (потомках).
Сразу пример.
Вернём наш класс пункта меню в первоначальный вид:

Первое, что бросается в глаза это ключевое слово extends . Оно означает, что новый класс будет унаследован от класса MenuItem, то есть будет брать все его свойства.
Второе нововведение это super(name). super означает обращение к родительскому классу (то есть классу MenuItem). В данном случае мы обращаемся к конструктору родительского класса: public MenuItem(String name).
При вызове конструктора public ColorMenuItem(String name, int color) строка name передастся конструктору public MenuItem(String name), затем (уже внутри класса MenuItem) произойдёт присваивание переменной члену класса: this.name = name; После этого выполнение конструктора базового класса выполнится и начнут выполняться следующие операторы уже в нашем классе ColorMenuItem: this.color = color;
Ещё раз, что будет выполнено:
super(name) класса ColorMenuItem
—-this.name = name; класса MenuItem
this.color = color; класса ColorMenuItem

Теперь давайте сделаем ещё один производный класс, на этот раз с иконкой:

Теперь вызов будет таков:
super(name) класса IconMenuItem
—-this.name = name; класса MenuItem
createIcon(iconName) класса IconMenuItem
—icon = Image.createImage(«/» + iconName); класса IconMenuItem
—//если такой картинки нет, то будет вызван icon = Image.createImage(18, 18);

Ещё раз напомню, во всех этих трёх классах метод getName() будет доступен, так как он не объявлен как private, а значит распространяется на все производные классы.

Если указать методу или члену класса модификатор private, то он не будет доступен из классов-потомков. Но это не значит, что его вовсе нет. В данном примере мы не можем из производных классов (ColorMenuItem и IconMenuItem) обратиться напрямую к name так: name = «Name» или super.name = name; но мы можем получить значение этого поля: super.getName(); потому что getName объявлен как public. К тому же, мы можем в конструкторе класса обратиться к базовому: super(«Name»), потому что конструктор класса MenuItem тоже объявлен как public.
А есть ещё модификатор protected. Это значит, что поле или метод (или конструктор) доступен только производным классам (и самому классу в котором он объявлен) и никому больше.
Если в MenuItem написать protected String name; тогда мы сможем в ColorMenuItem и IconMenuItem обращаться к этому полю так: super.name = «Name».
А вот наоборот нельзя. Класс MenuItem ничего не знает про своих потомков. Он не может получить доступ к полю color, icon и методам getIcon(), getColor().

Перепишем сам класс меню OopMenu3.java
Обратите внимание, мы можем в массив MenuItem класть элементы производных классов.
Так тоже можно: MenuItem item = new ColorMenuItem(«Name», 0x00);
Но мы не сможем получить доступ к методу getColor, надо конкретно указывать тип: ((ColorMenuItem) item).getColor();
А вот так написать мы можем, но работать не будет: ((IconMenuItem) item).getIcon()
Это потому что у нас инициализируется объект ColorMenuItem и мы не можем привести его к другому наследуемому типу.
Получить доступ к методу getName() мы можем из любого класса:

Мастер Йода рекомендует:  11 лучших плагинов jQuery-каруселей для увеличения привлекательности сайта Javascript

Наследование в C#

Всем доброго времени суток. На связи Алексей Гулынин. В прошлой статье вы узнали немного о том, что такое делегаты в C#. В данной статье я бы хотел рассказать про наследование в C#. Простыми словами, «Наследование» означает то, что мы создаём класс на основе другого. Получается, у нас есть родительский класс и дочерний класс, который наследует все поля и методы родительского класса. Для того, чтобы понять, что такое наследование и для чего его нужно использовать, вернёмся к понятию объекта.

Объект представляет собой некую абстрактную сущность. Допустим мы хотим создать класс «Animal» (Животное). Но животных же существует великое множество и вряд ли мы одним классом сможем описать их всех. В классе «Animal» мы можем создать поля и методы, присущие всем животным. Например, полями общими для всех животных могут быть «Вес», «Средняя продолжительность жизни», «Имеется хвост или нет». Методом может быть «eat()» (кушать), ведь все же животные питаются. От такого общего класса мы можем создать дочерний класс, который расширяет родительский класс. Например, класс «Dog» (собака) может расширять класс «Animal» уже конкретными полями и методами, которые соответствуют именно собакам.

Отношение наследования — это отношение перехода от более общей абстракции к более конкретной.

В C# наследование является одиночным, то есть нельзя наследоваться от двух и более классов. Наследование определяется через «:».

Интересный момент: если вы пишете обычный класс, который не имеет родителя, то, по умолчанию, этот класс является наследником класса «Object». Класс «Object» является родителем абсолютно для всех классов в .NET.

Немного поговорим про понятия, так или иначе связанные с наследованием.

Абстрактный класс — это класс, объекты которого нельзя создавать, т.е. нельзя будет использовать ключевое слово «new». Абстрактные классы используются при наследовании и используются как прародители к другим классам (реальным, не абстрактным). Т.е. в нашем примере можно класс «Animal» пометить как «abstract». Абстрактным может быть также и метод (это метод без реализации). Если в классе присутствует хотя бы один абстрактный метод, то и сам класс обязан быть абстрактным. В обратную сторону правило не действует.

С помощью ключевого слова «sealed» можно запретить создавать наследников от класса. Пример: класс «String». От этого класса мы не сможем создать наследников. Применение ключевого слова «sealed» к методу означает, что мы запрещаем переопределение этого метода в классах-наследниках.

Ключевое слово «virtual» применяется только к методам, и используется для того, чтобы превратить метод в виртуальный. Это делается для того, чтобы метод можно было переопределить в классах-наследниках. Переопределение метода означает, что мы внутри класса-наследника создаём метод, у которого заголовок полностью совпадает с заголовком метода класса-родителя. При этом в классе-наследнике нужно указать ключевое слово «override», чтобы явно указать, что мы переопределяем метод.

Давайте на примере разберем всё то, что мы только что узнали. Для простоты все поля и методы сделаем публичными (в реальных программах так, конечно, делать не нужно):

Конструкторы при наследовании.

Конструкторы не наследуются. Если в родительском классе определены различные конструкторы, то при наследовании эти конструкторы будут недоступны у класса-наследника. Несмотря на то, что конструкторы не наследуются — они автоматически вызываются. Когда вызывается конструктор класса-наследника автоматом вызывается конструктор класса-родителя. По умолчанию будет вызываться дефолтный конструктор класса-наследника без параметров. Обращаю ваше внимание на то, что если такого конструктора без параметров не будет, то будет получена ошибка при компиляции.


Если мы хотим, чтобы вызывался другой родительский конструктор, то это можно указать с помощью ключевого слова «base» .

С помощью «base» также можно вызывать родительский метод.

Разберем на эту тему вот такой пример: пусть имеется родительский класс «Degree», имеющий одной поле «degrees» и один метод, который возвращает значение данного поля. Создадим дочерний класс «Radiance», который, используя метод родительского класса, возвращает градусы, переведенные в радианы:

Напоследок, обобщу особенности наследования:

  • Ключевые слова «sealed» и «static» (статический класс, про него поговорим в отдельной статье) запрещают наследование
  • Если в базовом классе определен какой-то метод «abstract», то базовый класс тоже должен быть абстрактным. В классе-наследнике такой абстрактный метод нужно переопределить. Абстрактный метод, по умолчанию, является виртуальным.
  • При проектировании программы важным является понимание того, что от чего можно унаследовать, а что нельзя. Для проверки условия наследования используется слово «является». В нашем примере: «Собака является животным? — является», «Питбуль является собакой? — является». А вот наоборот лучше не делать (технически конечно можно, но программы лучше сразу проектировать правильно), «Животное является собакой? — не является». Поэтому класс «Animal» от класса «Dog» наследовать нельзя.

В данной статье вы узнали про механизм наследования в C# и о том, как его лучше использовать.

На связи был Алексей Гулынин, оставляйте свои комментарии, увидимся в следующих статьях.

Что такое ООП на примерах. Для чайников

Наверное, в половине вакансий(если не больше), требуется знание и понимание ООП. Да, эта методология, однозначно, покорила многих программистов! Обычно понимание ООП приходит с опытом, поскольку годных и доступно изложенных материалов на данный счет практически нет. А если даже и есть, то далеко не факт, что на них наткнутся читатели. Надеюсь, у меня получится объяснить принципы этой замечательной методологии, как говорится, на пальцах.

Итак, уже в начале статьи я уже упомянул такой термин «методология». Применительно к программированию этот термин подразумевает наличие какого-либо набора способов организации кода, методов его написания, придерживаясь которых, программист сможет писать вполне годные программы.

ООП (или объектно-ориентированное программирование) представляет собой способ организации кода программы, когда основными строительными блоками программы являются объекты и классы, а логика работы программы построена на их взаимодействии.

Об объектах и классах

Класс — это такая структура данных, которую может формировать сам программист. В терминах ООП, класс состоит из полей (по-простому — переменных) и методов (по-простому — функций). И, как выяснилось, сочетание данных и функций работы над ними в одной структуре дает невообразимую мощь. Объект — это конкретный экземпляр класса. Придерживаясь аналогии класса со структурой данных, объект — это конкретная структура данных, у которой полям присвоены какие-то значения. Поясню на примере:

Допустим, нам нужно написать программу, рассчитывающую периметр и площадь треугольника, который задан двумя сторонами и углом между ними. Для написания такой программы используя ООП, нам необходимо будет создать класс (то есть структуру) Треугольник. Класс Треугольник будет хранить три поля (три переменные): сторона А, сторона Б, угол между ними; и два метода (две функции): посчитать периметр, посчитать площадь. Данным классом мы можем описать любой треугольник и вычислить периметр и площадь. Так вот, конкретный треугольник с конкретными сторонами и углом между ними будет называться экземпляром класса Треугольник. Таким образом класс — это шаблон, а экземпляр — конкретная реализация шаблона. А вот уже экземпляры являются объектами, то есть конкретными элементами, хранящими конкретные значения.

Одним из самых распространенных объектно-ориентированных языков программирования является язык java. Там без использования объектов просто не обойтись. Вот как будет выглядеть код класса, описывающего треугольник на этом языке:

Если мы внутрь класса добавим следующий код:

то программу уже можно будет запускать на выполнение. Это особенность языка java. Если в классе есть такой метод

то этот класс можно выполнять. Разберем код подробнее. Начнем со строки

Здесь мы создаем экземпляр triangle1 класса Triangle и тут же задаем ему параметры сторон и угла между ними. При этом, вызывается специальный метод, называемый конструктор и заполняет поля объекта переданными значениями в конструктор. Ну, а строки

выводят рассчитанные площадь треугольника и его периметр в консоль.

Аналогично все происходит и для второго экземпляра класса Triangle .

Понимание сути классов и конструирования конкретных объектов — это уверенный первый шаг к пониманию методологии ООП.

Еще раз, самое важное:

ООП — это способ организации кода программы;

Класс — это пользовательская структура данных, которая воедино объединяет данные и функции для работы с ними(поля класса и методы класса);

Объект — это конкретный экземпляр класса, полям которого заданы конкретные значения.

Три волшебных слова


ООП включает три ключевых подхода: наследование, инкапсуляцию и полиморфизм. Для начала, приведу определения из wikipedia:

Инкапсуляция — свойство системы, позволяющее объединить данные и методы, работающие с ними, в классе. Некоторые языки (например, С++) отождествляют инкапсуляцию с сокрытием, но большинство (Smalltalk, Eiffel, OCaml) различают эти понятия.

Наследование — свойство системы, позволяющее описать новый класс на основе уже существующего с частично или полностью заимствующейся функциональностью. Класс, от которого производится наследование, называется базовым, родительским или суперклассом. Новый класс — потомком, наследником, дочерним или производным классом.

Полиморфизм — свойство системы, позволяющее использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта.

Понять, что же все эти определения означают на деле достаточно сложно. В специализированных книгах, раскрывающих данную тему на каждое определение, зачастую, отводится целая глава, но, как минимум, абзац. Хотя, сути того, что нужно понять и отпечатать навсегда в своем мозге программиста совсем немного.
А примером для разбора нам будут служить фигуры на плоскости. Из школьной геометрии мы знаем, что у всех фигур, описанных на плоскости, можно рассчитать периметр и площадь. Например, для точки оба параметра равны нулю. Для отрезка мы можем вычислить лишь периметр. А для квадрата, прямоугольника или треугольника — и то, и другое. Сейчас же мы опишем эту задачу в терминах ООП. Также не лишним будет уловить цепь рассуждений, которые выливаются в иерархию классов, которая , в свою очередь, воплощается в работающий код. Поехали:

Мастер Йода рекомендует:  Как удалить, очистить, восстановить, разблокировать или сбросить пароль безопасности BIOS

Итак, точка — это самая малая геометрическая фигура, которая является основой всех прочих построений (фигур). Поэтому именно точка выбрана в качестве базового родительского класса. Напишем класс точки на java:

У получившегося класса Point пустой конструктор, поскольку в данном примере мы работаем без конкретных координат, а оперируем только параметрами значениями сторон. Так как у точки нет никаких сторон, то и передавать ей никаких параметров не надо. Также заметим, что класс имеет методы Point::getSquare() и Point::getPerimeter() для расчета площади и периметра, оба возвращают 0. Для точки оно и логично.

Поскольку у нас точка является основой всех прочих фигур, то и классы этих прочих фигур мы наследуем от класса Point . Опишем класс отрезка, наследуемого от класса точки:

означает, что класс LineSegment наследуется от класса Point . Методы LineSegment::getSquare() и LineSegment::getPerimeter() переопределяют соответствующие методы базового класса. Площадь отрезка всегда равняется нулю, а площадь периметра равняется длине этого отрезка.

Теперь, подобно классу отрезка, опишем класс треугольника(который также наследуется от класса точки):

Тут нет ничего нового. Также, методы Triangle::getSquare() и Triangle::getPerimeter() переопределяют соответствующие методы базового класса.
Ну а теперь, собственно, тот самый код, который показывает волшебство полиморифзма и раскрывает мощь ООП:

Мы создали массив объектов класса Point , а поскольку классы LineSegment и Triangle наследуются от класса Point , то и их мы можем помещать в этот массив. Получается, каждую фигуру, которая есть в массиве figures мы можем рассматривать как объект класса Point . В этом и заключается полиморфизм: неизвестно, к какому именно классу принадлежат находящиеся в массиве figures объекты, но поскольку все объекты внутри этого массива принадлежат одному базовому классу Point , то все методы, которые применимы к классу Point также и применимы к его классам-наследникам.

Теперь о инкапсуляции. То, что мы поместили в одном классе параметры фигуры и методы расчета площади и периметра — это и есть инкапсуляция, мы инкапсулировали фигуры в отдельные классы. То, что у нас для расчета периметра используется специальный метод в классе — это и есть инкапсуляцию, мы инкапсулировали расчет периметра в метод getPerimiter() . Иначе говоря, инкапсуляция — это сокрытие реализции (пожалуй, самое короткое, и в то же время емкое определением инкапсуляции).

Введение в ООП с примерами на C#. Часть вторая. Все, что нужно знать о наследовании

Все, что необходимо начинающему и опытному программисту

Наследование.

Итак, настало время ознакомиться с таким вечнозеленым понятием объектно-ориентированного программирования как наследование. Данный термин вам уже известен из курса языка программирования С++.Теперь мы его рассмотрим в контексте С#. Начнем с синтаксиса применяемого для наследования:

Обратите внимание на следующие отличия в механизме наследования C# от С++:

  1. В качестве базового класса при наследовании может быть указан только один класс. Это означает, что в C# нет механизма множественного наследования. Если же необходимо всё-таки выполнить его то тогда можно воспользоваться понятием интерфейса, которое будет рассмотрено позже.
  2. При наследовании не указываются спецификаторы доступа как в C++.

Рассмотрим пример наследования:

Из примера видно, что принципы наследования работают достаточно схоже с С++.

Спецификатор доступа protected.

Спецификатор доступа protected вам уже известен из курса С++. В С# он работает точно также. Например:

Конструктора при наследовании.

В иерархии наследования допускается, чтобы базовые и дочерние классы имели свои собственные конструкторы. В этом случае при создании объекта от наследованного класса сначала вызываются конструктора базовых классов в порядке наследования и только потом конструктор потомка (фактически “сверху вниз”). Например:

В данном примере мы не передавали параметры в конструкторы базовых классов Base и Child, а если нам нужно передавать? Например:

Для того чтобы решить возникшую проблему нужно использовать следующий синтаксис:

Для вызова конструктора базового класса используется ключевое слово base. Например:

Как видно из примера приведенного выше с помощью ключевого слова base можно в дочернем классе обратиться к унаследованным членам базового. Его можно использовать только в классе-потомке. Вы обратили внимание на появившееся предупреждение при компиляции программы приведенной выше?


Оно сообщает о том, что метод Show() класса MobileTelephone перекрывает версию Show из базового класса Device. Для того чтобы убрать его необходимо указать ключевое слово new перед Show внутри класса MobileTelephone. Например (фрагмент из программы):

Ссылки на объекты базового и дочернего классов.

С# — это строго типизированный язык. Это правда, для вас не новость. Например, это ярко проявляется при операции присваивания. Рассмотрим пример ошибки связанной с преобразованиями типов:

В данной программе возникает ошибка на этапе компиляции, так как невозможно автоматическое преобразование по отношению к переменным ссылочного типа (оно распространяется только на переменные обычных типов). Отсюда можно сделать вывод, что ссылочная переменная одного класса не может ссылаться на объект другого класса. Однако из этого правила есть одно исключение. Ссылочной переменной базового класса можно присвоить ссылку на объект дочернего класса, в этом случае через неё можно будет обратиться к тем членам производного, которые были унаследованы из базового. Например:

Запрет наследования

Иногда при разработке класса нужно запретить возможность наследования от него. Для этого используется ключевое слово sealed, которое указывается при объявлении класса. Данный механизм может понадобиться при разработке какого-то служебного класса. Например, уже известный вам класс Console библиотеки .NET Framework объявлен с использованием sealed. Рассмотрим программу, демонстрирующую этот принцип:

Введение в ООП с примерами на C#. Часть вторая. Все, что нужно знать о наследовании

Наследование — создание нового класса на базе уже имеющегося, или базового класса. Принцип наследования состоит в том, что элементы данных и методы базового класса автоматически становятся элементами данных нового класса. Класс в С# может иметь только одного предка.

1. Механизм наследования позволяет реализовать принцип эффективного программирования: написанный код можно многократно использовать. Методы базового или родительского класса могут использоваться объектами всех классов потомков.

2. Наследование реализует принцип программирования от «простого к сложному» или от общего к частному. При этом базовый класс реализует только самые общие и простые методы, описывающие поведение всех объектов, а каждый производный класс лишь добавляет специфические особенности.

class имя_класса : имя_класса_родителя

При создании конструктора производного класса:

  • сначала вызывается конструктор базового класса, а затем вызывается конструктор текущего класса
  • Для вызова конструктора базового класса используется ключевое слово base
  • если базовый класс имеет несколько конструкторов, то необходимый указывается при определении конструктора производного класса;
  • если конструктор базового класса имеет параметры, то они включаются в список параметров производного класса.
  • Используя ключевое слово base можно вызвать метод или поле родительского класса, если оно имеет уровень доступа public

ПРИМЕР

Создайте базовый класс Person. Класс должен содержать следующие поля:

  • имя служащего (fam)
  • номер служащего (tab)
  • оклад служащего (salary) — должен быть в диапазоне: от 13000 до 17000.

В классе необходимо реализовать:

  • Конструктор
  • Свойства: Fam, Tab, Salary.

Создайте производный класс Manager, который добавляет следующие поля в класс Person

  • наименование компании, в которой работает менеджер (company)
  • объем продаж менеджера (sale)

Класс должен содержать конструктор и следующие методы:

  • cвойства: Company, Sale
  • свойство — вычисление размер премии — 5% от объема продаж
  • метод, обеспечивающий вывод значений полей двух классов на экран монитора

Напишите программу, демонстрирующую работу с этими классами.

Добавить комментарий