Назад Вперед Содержание

Отношение наследования

Классы А и В находятся в отношении наследования, если между объектами этих классов существует отношение «является».

Например, классы Квадрат и Прямоугольник.

Ясно, что эти классы связаны отношением наследования, так как «квадрат является прямоугольником». Причем, родительским классом является Прямоугольник, а дочерним Квадрат.

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

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

Встречаются классы, которые очень похожи, то есть имеют много общих полей и методов (например, класс автомобиль и класс спортивный автомобиль).

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

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

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

В результате, новый класс (он называется дочерним) будет обладать всеми свойствами родительского класса плюс будет иметь свои собственные отличительные особенности.

Описание дочернего класса

Класс в С# может иметь произвольное количество потомков и только одного предка.

При описании дочернего класса (класса потомка), в заголовке, после имени класса через двоеточие указывается родительский класс (класс предка).

В примере класс В дочерний, а класс А родительский.
  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, не похожее на его применение при создании экземпляра объекта:

Пример 1

Рассмотрим наследование классов на примере.

Ранее мы создали класс Triangle, описывающий треугольники.

Этот класс будет у нас родительским.

Каждый треугольник в классе Triangle однозначно определялся тремя закрытыми полями – длинами трех сторон. Чтобы эти поля были доступны из дочернего класса, опишем их со спецификатором protected.

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

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

Новый класс, описывающий цветные треугольники назовем ColorTriangle. Он будет содержать:

Все члены класса Triangle (поля и методы) наследуются классом ColorTriangle.

Описание дочернего класса ColorTriangle:

Метод Main класса Program:

В методе Main в цикле из двух витков:

Отработка:

Пример 2

Создать класс Armiya– армия, содержащий:

Создать класс Potomok дочерний классу Armiya, содержащий:

Для того чтобы из дочернего класса, вызвать метод Q родительского класса используем объект this.

В методе Main класса Program:

Отработка:

Пример 3

Создать класс Transport– транспорт, содержащий:

Создать класс Car (автомобиль) дочерний к классу Transport, содержащий:

В классе Program:

Для транспортного средства:

В классе Program для автомобиля:

Назад Вперед Содержание