Урок № 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#, т.е. установку определенных «отношений» между классами.

Перейти к следующему уроку