Классы А и В находятся в отношении наследования, если между объектами этих классов существует отношение «является».
Например, классы Квадрат и Прямоугольник.
Ясно, что эти классы связаны отношением наследования, так как «квадрат является прямоугольником». Причем, родительским классом является Прямоугольник, а дочерним Квадрат.
То есть, если один класс является подмножеством другого, то должно быть использовано наследование.
Встречаются классы, которые очень похожи, то есть имеют много общих полей и методов (например, класс автомобиль и класс спортивный автомобиль).
Если каждый класс описывать отдельно, то будет много повторяющегося кода.
Наследование, это механизм, посредством которого, один класс может использовать свойства и методы другого класса и добавлять к ним собственные черты.
Смысл наследования заключается в том, что не надо с нуля описывать новый класс, а можно указать родителя (базовый класс) и описать отличительные особенности нового класса.
В результате, новый класс (он называется дочерним) будет обладать всеми свойствами родительского класса плюс будет иметь свои собственные отличительные особенности.
Класс в С# может иметь произвольное количество потомков и только одного предка.
При описании дочернего класса (класса потомка), в заголовке, после имени класса через двоеточие указывается родительский класс (класс предка).
В примере класс В дочерний, а класс А родительский.
class В : А { } |
---|
Объекты дочернего класса, созданные в методе Main класса Program могут вызывать как методы родительского класса и так и собственные методы.
Если мы хотим прибавить к дочернему классу новое поведение:
Если мы хотим для потомка переопределить какой-нибудь метод, то в дочернем классе этот метод описывается с таким же именем, но со спецификатором new.
Важно различать перегрузку методов и переопределение методов.
Переопределяющий метод должен иметь то же самое имя и список формальных параметров, что и переопределяемый метод базового класса.
А у перегруженного метода то же имя, но другой список формальных параметров. Поэтому если включить в дочерний класс метод с тем же самым именем, что и метод в родительском классе, но с другим списком формальных параметров, метод родительского класса не будет переопределен методом дочернего. Дочерний унаследует метод родительского класса.
Из дочернего класса можно обратиться к полям родительского класса, но для этого в родительском классе поля должны иметь модификатор protected.
Из дочернего класса можно вызвать метод родительского класса, применяя его к объекту this.
Родитель не имеет возможности обратиться к полям и методам потомка.
Потомок наследует все поля и методы родителя, кроме конструкторов.
Конструкторы не наследуются от родителя, поэтому дочерний класс должен иметь собственные конструкторы.
В связи с этим возникает следующий резонный вопрос: какой конструктор отвечает за построение объекта производного класса: конструктор базового класса, конструктор производного класса или же оба? На этот вопрос можно ответить так: конструктор базового класса конструирует базовую часть объекта, а конструктор производного класса — производную часть этого объекта. И в этом есть своя логика, поскольку базовому классу неизвестны и недоступны любые элементы производного класса, а значит, их конструирование должно происходить раздельно.
Если конструктор определен только в производном классе, то все происходит очень просто: конструируется объект производного класса, а базовая часть объекта автоматически собирается его конструктором, используемым по умолчанию.
Итак, поскольку дочерний объект может пользоваться полями родительского класса эти поля должны каким-то образом получить значения.
Поэтому при создании объекта дочернего класса сначала нужно вызвать конструктор родительского класса, который инициализирует поля родительского класса и только потом конструктор дочернего класса для инициализации его полей.
Когда конструкторы определяются как в базовом, так и в производном классе, процесс построения объекта несколько усложняется, поскольку должны выполняться конструкторы обоих классов. В данном случае приходится обращаться к ключевому слову base, которое находит двоякое применение: во-первых, для вызова конструктора базового класса; и во-вторых, для доступа к члену базового класса, скрывающегося за членом производного класса.
С помощью формы расширенного объявления конструктора производного класса и ключевого слова base в производном классе может быть вызван конструктор, определенный в его базовом классе.
Для вызова конструктора родителя в заголовке дочернего конструктора используется ключевое слово base, именующее родительский класс. В base в скобках указываются параметры соответствующего конструктора родительского класса, причем конструктор с таким количеством параметров (с такой сигнатурой) должен обязательно присутствовать в родительском классе.
Любой конструктор в дочернем классе, который не вызывает инициализатор конструктора базового класса с помощью :base(Список параметров), получает инициализатор по умолчанию, присваиваемый компилятором.
В дочернем классе объект создается с большим количеством параметров, чем в родительском
//Родительский класс. С двумя полями class Found { //поля protected string name; protected int credit; //конструктор родительского класса public Found(string name, int credit) { this.name = name; this.credit = credit; } } //Дочерний класс. С еще одним полем public class Derived:Found { // новое поле в дочернем классе int debit; //конструктор дочернего класса с 3 параметрами public Derived (string x, int y, int z) : base(x, y) { this.debit=z; } } |
При создании объекта потомка первые два параметра будут передаваться конструктору родительского класса с двумя параметрами для инициализации его полей. Третий параметр инициализирует поле объекта потомка.
В дочернем классе объект создается с меньшим количеством параметров, чем в родительском
//Родительский класс. С тремя параметрами class Found { protected string name; protected int credit; protected int debit; public Found(string name, int credit, int debit) { this.name = name; this.credit = credit; this.debit = debit; } } //Дочерний класс. С двумя параметрами public class Derived:Found { int ost; public Derived(string x, int ost): base(x, ost) { this.ost = ost; } } |
При создании объекта потомка оба параметра будут передаваться конструктору родительского класса с двумя параметрами для инициализации его полей. Этот же параметр инициализирует поле объекта потомка.
Поскольку мы вызываем конструктор родительского класса с двумя параметрами, то он должен обязательно существовать.
То есть обязательно в родительском классе должен быть приблизительно такой конструктор:
public Found(string name, int ost) { this.name = name; this.credit = ost; this.debit = ost; } |
В базовый и производный класс добавим метод print().
В производном классе можно определить член с таким же именем, как и у члена его базового класса. В этом случае член базового класса скрывается в производном классе. И хотя формально в C# это не считается ошибкой, компилятор все же выдаст сообщение, предупреждающее о том, что имя скрывается. Если член базового класса требуется скрыть намеренно, то перед его именем следует указать ключевое слово new, чтобы избежать появления подобного предупреждающего сообщения. Следует, однако, иметь в виду, что это совершенно отдельное применение ключевого слова new, не похожее на его применение при создании экземпляра объекта:
Рассмотрим наследование классов на примере.
Ранее мы создали класс Triangle, описывающий треугольники.
Этот класс будет у нас родительским.
Каждый треугольник в классе Triangle однозначно определялся тремя закрытыми полями – длинами трех сторон. Чтобы эти поля были доступны из дочернего класса, опишем их со спецификатором protected.
В классе были методы вывода на экран значений полей, вычисления периметра, площади по формуле Герона, а также логический метод, определяющий существование треугольника.
Кроме него, у нас будет дочерний класс, описывающий цветные треугольники.
Новый класс, описывающий цветные треугольники назовем ColorTriangle. Он будет содержать:
Все члены класса Triangle (поля и методы) наследуются классом ColorTriangle.
Описание дочернего класса ColorTriangle:
Метод Main класса Program:
В методе Main в цикле из двух витков:
Отработка:
Создать класс Armiya– армия, содержащий:
Q =0,3* number +0,7* voorug |
Создать класс Potomok дочерний классу Armiya, содержащий:
Qp= Q*(p+1) |
Для того чтобы из дочернего класса, вызвать метод Q родительского класса используем объект this.
В методе Main класса Program:
Отработка:
Создать класс Transport– транспорт, содержащий:
Создать класс Car (автомобиль) дочерний к классу Transport, содержащий:
В классе Program:
Для транспортного средства:
В классе Program для автомобиля: