Шаг 144.
Язык программирования C#. Начала
Наследование. Замещение членов при наследовании

    На этом шаге мы рассмотрим особенности выполнения такого замещения.

    В производном классе некоторые поля и методы (и другие члены) наследуются из базового класса, а часть полей и методов описывается непосредственно в производном классе. Может статься, что член, описанный в производном классе, имеет такое же название, как и член, наследуемый из базового класса. Такая ситуация называется замещением члена класса. Она вполне законна и может использоваться. Единственное условие состоит в том, что при описании в производном классе поля или метода, название которого совпадает с названием поля или метода, наследуемого из базового класса, необходимо использовать инструкцию new.


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

    Из того, что поле или метод из базового класса замещаются в производном классе, вовсе не следует, что соответствующее поле или метод больше недоступны в производном классе. Замещаемый член класса существует одновременно с тем членом, который описан в производном классе. Просто если мы обращаемся к члену класса по его названию, то по умолчанию обращение выполняется к тому члену, который описан в производном классе. Если нам нужно получить доступ к замещенному члену класса, то перед его названием через точку указывается ключевое слово base.


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

    Пример, в котором используется замещение членов класса, представлен в примере ниже.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace pr144_1
{
    // Базовый класс: 
    class Base {
        // Целочисленное поле: 
        public int code;
        // Метод для отображения значения поля: 
        public void show(){
            Console.WriteLine("Класс Base: " + code);
        }
        // Конструктор с одним аргументом: 
        public Base(int n){ 
            code = n;
        }
    }

    // Производный класс: 
    class MyClass: Base {
        // Новое поле замещает одноименное поле,
        // унаследованное из базового класса: 
        new public int code;
        // Новый метод замещает одноименный метод,
        // унаследованный из базового класса: 
        new public void show(){
            // Вызов версии метода из базового класса: 
            base.show();
            // Обращение к полю производного класса:
            Console.WriteLine("Класс MyClass: " + code);
        }
        // Метод для присваивания значения полю, унаследованному
        // из базового класса и замещенному в производном 
        // классе:
        public void set(int n) {
            // Обращение к полю, унаследованному из базового 
            // класса и замещенному в производном классе: 
            base.code = n;
        }
        // Конструктор с двумя аргументами: 
        public MyClass(int m, int n): base(m) {
            // Присваивание значения полю производного класса: 
            code = n;
        }
    }

    // Класс с главным методом: 
    class Program
    {
        // Главный метод:
        static void Main()
        {
            // Создание объекта производного класса:
            MyClass obj = new MyClass(100, 200);
            // Отображение значений полей объекта: 
            obj.show();
            Console.WriteLine();
            // Присваивание значения замещенному полю: 
            obj.set(300);
            // Присваивание значения замещающему полю: 
            obj.code = 400;
            // Отображение значений полей объекта: 
            obj.show();
            // Задержка:
            Console.ReadLine();
        }
    }
}
Архив проекта можно взять здесь.

    Результат выполнения программы такой.


Рис.1. Результат выполнения программы

    Базовый класс Base содержит целочисленное поле code, метод show(), отображающий в консоли сообщение с названием класса и значением поля, а также конструктор с одним аргументом, определяющим значение целочисленного поля.


Конструктору производного класса MyClass передается два целочисленных аргумента. Первый из них становится аргументом конструктора базового класса Base. При вызове конструктора базового класса значение получает поле code, унаследованное из класса Base и замещенное в классе MyClass. Второй аргумент конструктора производного класса определяет значение поля code, описанного непосредственно в этом классе.

    В производном классе MyClass также описывается поле code и метод show(). И поле, и метод описаны с инструкцией new. Если мы в теле класса используем идентификатор code или вызываем метод show(), то имеются в виду именно те члены класса, которые описаны непосредственно в классе MyClass. Чтобы получить доступ к полю, унаследованному из базового класса и замещенному в производном, используем инструкцию base.code. Чтобы вызвать версию метода show() из базового класса, используем инструкцию base.show().

    Инструкцию base.show() мы используем в теле метода show(), который описывается в классе MyClass. Это означает, что при вызове метода show() из объекта класса MyClass первой командой в теле метода вызывается версия метода show(), описанная в классе Base. В результате в консольном окне отображается значение поля code, унаследованного из класса Base и замещенного в классе MyClass. Затем командой

            Console.WriteLine("Класс MyClass: " + code);
отображается значение поля code, описанного в классе MyClass.

    Инструкция base.code использована нами в теле метода set(), предназначенного для присваивания значения полю code, замененному в производном классе.

    В главном методе программы командой

            MyClass obj = new MyClass(100, 200);
создается объект obj, значения полей которого равны 100 (замещенное поле code) и 200 (поле code, описанное в производном классе). При выполнении команды
            obj.show();
из объекта obj вызывается версия метода show(), описанная в классе MyClass. В результате в консольном окне отображаются значения обоих полей.

    Командой obj.set(300); новое значение присваивается замещенному полю code, а командой

            obj.code = 400;
новое значение получает поле code, описанное в производном классе. Результат этих операций проверяется с помощью команды
            obj.show();     .

    На следующем шаге мы рассмотрим переопределение виртуальных методов.




Предыдущий шаг Содержание Следующий шаг