Урок № 21. Наследование
Наследование
Доброго времени суток! В этом уроке я затрону один из основных принципов ООП – наследование. Я доступными словами расскажу, как и зачем использовать этот механизм в ваших программах. И так к делу!
Для чего же все-таки нужен механизм наследования? Как правило, выделяют две основные функции, это расширение функциональности уже существующих классов, и вторая, более значимая, на мой взгляд, установка между классами особых «отношений». Более подробно, в этом уроке, я расскажу о первой функции.
Давайте сразу рассмотрим такой пример, допустим, у нас есть класс, описывающий точку на плоскости (в двухмерном пространстве), выглядит он примерно так:
//Класс, описывающий точку на плоскости class Point2D { //Конструктор, устанавливающий значения полям "x" и "y" public Point2D(int XValue, int YValue) { //Установка указанных значений x = XValue; y = YValue; } //Конструктор без аргументов, создает точку в начале координат public Point2D() { //Установка нулевых значений x = 0; y = 0; } //Свойство, для установки/получения значения поля "x" public int X { get { return x; } set { x = value; } } //Свойство, для установки/получения значения поля "y" public int Y { get { return y; } set { y = value; } } //Защищенные, ВНИМАНИЕ, не закрытые поля класса, а именно защищенные protected int x; //Координата х protected int y; //Координата у }
Обратите внимание, мы впервые используем атрибут доступа «protected», зачем он нужен, я объясню чуть позже, сейчас же скажу, что он больше похож на атрибут «private», т.е. напрямую (в «лоб») за пределами класса задавать значения полям «x» и «y» нельзя (кроме одного исключающего случая, но об этом чуть позже).
И все бы хорошо, но представим, что нам понадобилось описать точку в трехмерном пространстве. У и нас два выхода, можно «с нуля» написать новый класс, выглядеть он может примерно так:
//Класс, описывающий точку в трехмерном пространстве class Point3D { //Конструктор, устанавливающий значения полям "x", "y" и "z" public Point3D(int XValue, int YValue, int ZValue) { //Установка указанных значений x = XValue; y = YValue; z = ZValue; } //Конструктор без аргументов, создает точку в начале координат public Point3D() { //Установка нулевых значений x = 0; y = 0; z = 0; } //Свойство, для установки/получения значения поля "x" public int X { get { return x; } set { x = value; } } //Свойство, для установки/получения значения поля "y" public int Y { get { return y; } set { y = value; } } //Свойство, для установки/получения значения поля "z" public int Z { get { return z; } set { z = value; } } private int x; //Координата х private int y; //Координата у private int z; //Координата z }
Но можно использовать и другой вариант, можно создать новый класс на основе уже существующего, тут нам и понадобиться механизм наследования. В таком случае, мы возьмем класса «Point2D» за основу (это так называемый, базовый класс), и дополним его третьей координатой, добавим свойство для установки/получения её значения и доработаем конструкторы. А на практике, это выглядит так:
//Класс, описывающий точку в трехмерном пространстве class Point3D : Point2D //Указываем базовый класс { //Конструктор, устанавливающий значения полям "x", "y" и "z" public Point3D(int XValue, int YValue, int ZValue) { //Установка указанных значений x = XValue; y = YValue; z = ZValue; } //Конструктор без аргументов, создает точку в начале координат public Point3D() { //Установка нулевых значений x = 0; y = 0; z = 0; } //Добавим свойство, для установки/получения значения поля "z" public int Z { get { return z; } set { z = value; } } //Добавим координату по оси Z private int z; //Координата z }
Вот так, согласитесь, код класса получился значительно компактнее, да и процесс кодирования прошел значительно быстрее. А теперь, давайте подробнее разберем то, что мы сделали!
При объявлении класса «Point3D» (который является классом наследником), мы использовали следующий «трюк», после имени класса наследника, мы поставили двоеточие, т.е. символ «:», после которого, указали имя базового класса (в данном случае – «Point2D»). Т.е. правило выглядит так:
[Имя класса наследника] : [Имя базового класса] { //Тело класса наследника }
И класс наследник, автоматически получает все поля, методы и свойства базового класса. При этом, открытые (отмеченные как public) и защищенные (отмеченные как protected) поля, свойства и методы становятся «своими» (не только переходят в класс наследник, но и остаются доступными для методов и свойств класса наследника).
Вот для чего, нам понадобился новый атрибут доступа «protected», вот Вам и тот самый исключающий случай.
А закрытые (поля, методы и свойства), тоже переходят в класс наследник, но становятся недоступными для методов и свойств класса наследника.
А теперь, давайте рассмотрим пример использования объектов класса «Point3D» на практике:
//Создание точки в трехмерном пространстве Point3D tmpPoint = new Point3D(); //Зададим значения координат точки tmpPoint.X = 25; //Работает свойство класса Point2D (перешло по "наследству") tmpPoint.Y = 10; //Работает свойство класса Point2D (перешло по "наследству") tmpPoint.Z = 16; //Работает свойство класса Point3D
Все довольно просто! В следующем уроке я расскажу, как использовать вторую важную функцию наследования классов в C#, т.е. установку определенных «отношений» между классами.