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

Иерархия классов

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

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

Классы, расположенные ближе к началу иерархии, объединяют в себе общие черты для всех нижележащих классов. По мере продвижения вниз по иерархии классы приобретают все больше конкретных особенностей.

Итак, наследование применяется для следующих взаимосвязанных целей:

- исключения из программы повторяющихся фрагментов кода;

- упрощения модификации программы;

- упрощения создания новых программ на основе существующих.

Кроме того, наследование является единственной возможностью использовать объекты, исходный код которых недоступен, но в которые требуется внести из­менения.

 

Класс в С# может иметь произвольное количество потомков и только одного предка. При описании класса имя его предка записывается в заголовке класса после двоеточия. Если имя предка не указано, предком считается базовый класс всей иерархии System.Object.

 

[ атрибуты ] [ спецификаторы ] class имя_класса [ : предки ]

тело класса

 

 

Листинг 8.1 . Класс Student, потомок класса Person

using System;

namespace WindowsFormsApplication3

{

class Person

{

 

public string name;

public int age;

public string profession;

 

public Person(string name)

{

this.name = name;

}

 

public Person(string name, int age)

{

this.name = name;

this.age = age;

}

 

public Person(string name, string profession)

{

this.name = name;

this.profession = profession;

}

 

public Person(string name, int age, string profession)

{

this.name = name;

this.age = age;

this.profession = profession;

}

 

}

 

class Student : Person

{

string number_group;

 

public Student(string name, int age, string profession, string number_group)

: base(number_group)

{

this.name = name;

this.age = age;

this.profession = profession;

this.number_group = number_group;

}

 

public string GetInformation()

{

string information;

information = "Имя: " + this.name + "; Возраст: " + this.age.ToString() + "; Профессия: " + this.profession + "; № группы: " + this.number_group;

return information;

}

}

}

 

В классе Student введено поле number_group, определен собствен­ный конструктор. Все поля и свойства класса Person наследуются в классе Student.

Результат работы программы:

 

Имя: Миша; Возраст: 23; Профессия: Студент; № группы: 08-к-ПИ1

Имя: Иван; Возраст: 22; Профессия: Менеджер; № группы: нет

 

Конструкторы не наследуются, поэтому производный класс должен иметь соб­ственные конструкторы. Порядок вызова конструкторов определяется приведен­ными далее правилами:

  • Если в конструкторе производного класса явный вызов конструктора базового класса отсутствует, автоматически вызывается конструктор базового класса без параметров.
  • Для иерархии, состоящей из нескольких уровней, конструкторы базовых клас­сов вызываются, начиная с самого верхнего уровня. После этого выполняются конструкторы тех элементов класса, которые являются объектами, в порядке их объявления в классе, а затем исполняется конструктор класса. Таким об­разом, каждый конструктор инициализирует свою часть объекта.
  • Если конструктор базового класса требует указания параметров, он должен быть явным образом вызван в конструкторе производного класса в списке ини­циализации (это продемонстрировано в конструкторах, вызываемых в опера­торах 1 и 2). Вызов выполняется с помощью ключевого слова base. Вызывает­ся та версия конструктора, список параметров которой соответствует списку аргументов, указанных после слова base.

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

Элементы базового класса, определенные как public, в производном классе доступны.

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

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

  • объект, в который во время выполнения программы заносятся ссылки на объ­екты разных классов иерархии;
  • контейнер, в котором хранятся объекты разных классов, относящиеся к од­ной иерархии;
  • метод, в который могут передаваться объекты разных классов иерархии;
  • метод, из которого в зависимости от типа вызвавшего его объекта вызывают­ся соответствующие методы.

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

Абстрактный класс служит только для порождения потомков. Как правило, в нём задаётся набор методов, которые в каждом из потомков будут реализовываться по-своему. Абстрактный класс задаёт интерфейс всей иерархии. Абстрактный класс может содержать полностью определённые методы, помимо абстрактных.

Синтаксис:

abstract class AbsExample

{

public abstract void Print()

}

class Person:AbsExample

{

override public void Print()

{

C.W.(“Person {0} \ t “ , name);

}

}

class Student:AbsExample

{

override public void Print()

{

C.W.(“Group{0} \t”, group);

}

}

Если производный от абстрактного класс не переопределяет все абстрактные методы, то он также является абстрактным и описывается со спецификатором abstract.

 

Контрольные вопросы по теме «Иерархия классов»:

 

1. Дать определение наследованию.

2. Сколько предков может иметь класс в С#?

3. Сколько потомков может иметь класс в С#?

4. Синтаксис объявителя производного класса.

5. Наследуются ли конструкторы?

6. Наследуются ли поля, методы, свойства?

7. С помощью какого ключевого слова можно переопределить элемент базового класса?

8. Метод производного класса замещает метод базового класса. Как обратится к нему из метода производного класса?

9. Какой процесс называется ранним связыванием?

10. Какой процесс называется поздним связыванием?

11. Какие методы реализуют в С# процесс позднего связывании?

12. Синтаксис объявителя виртуальных методов.

13. При помощи какого ключевого слова переопределяется виртуальный метод в производном классе?

14. Синтаксис объявителя переопределенного виртуально метода в производном классе.

15. Какие ограничения накладываются на переопределённый виртуальный метод?

16. Нужно ли переопределять виртуальные методы в каждом производственном классе?

17. Какие классы называются абстрактными?

18. Может абстрактный класс содержать полностью определённые методы?