Опубликован: 25.03.2010 | Доступ: свободный | Студентов: 1447 / 158 | Оценка: 4.31 / 4.00 | Длительность: 25:42:00
Лекция 9:

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

Абстрактные функции

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

Например, разрабатывая расширение Circle, можно предположить, что в дальнейшем на базе этого кода будет построено новое расширение типа Cylinder. А там может потребоваться функция вычисления объема Volume(). Чтобы не забыть об этом факте, можно вставить в текущее расширение только объявление этой будущей виртуальной функции, без ее реализации, надеясь реализовать ее в следующих расширениях, например в Cylinder. Такие виртуальные функции ( методы с отложенной реализацией ) называются чисто виртуальными или просто абстрактными.

Абстрактный метод объявляется в классе с помощью ключевого слова abstract. Он автоматически является виртуальным, поэтому употребление еще и модификатора virtual считается синтаксической ошибкой. Объявление в базовом классе абстрактного метода без тела только определяет его шаблон (заглушку, интерфейс, сигнатуру). В далнейшем требуется обязательная его реализация в виде переопределения (используется override, как и в случае с виртуальными методами) в ближайшем или отдаленных расширениях - до первого вызова (использования) метода.

Как и в ряде других случаев, при объявлении абстрактных классов введена некоторая синтаксическая избыточность ( контролирующее обременение ) для усиления контроля компилятора за действиями программиста: если в классе объявлен хоть один абстрактный метод, то и сам класс нужно помечать модификатором abstract. Объявлять абстрактным очередное расширение нужно до тех пор, пока все наследуемые абстрактные методы не будут реализованы. Класс, который объявляет или не реализует унаследованный абстрактный метод, не может иметь экземпляры.

Вот модификация предыдущего примера, иллюстрирующая сказанное

using System;
    
namespace Test
{
    abstract class A
    {
        string name = "Слой A";
        public abstract void Show();// Отложили реализацию
    }
    
    // Реализации наследуемого абстрактного метода еще нет,
    // значит слой тоже нужно обозначить абстрактным
    abstract class B : A
    {
        string name = "Слой B";
    }
    
    // Есть первая реализация, абстрактные слои закончились
    class C : B
    {
        string name = "Слой C";
        public override void Show()
        {
            Console.WriteLine(name);
        }
    }
    
    class D : C
    {
        string name = "Слой D";
    }
    
    class E : D
    {
        string name = "Слой E";
        public override void Show()
        {
            Console.WriteLine(name);
        }
    }
    
    class F : E
    {
        string name = "Слой F";
        public override void Show()
        {
            Console.WriteLine(name);
        }
    }
    
    // Вызывающая сторона
    class MyClass
    {
        public MyClass()
        {
            // Создаем объекты для всех неабстрактных 
            // типов иерархической цепочки наследования,
            // применяя ссылку абстрактного базового типа
            A[] a = {
                // new A(), // Ошибка компиляции!!!
                // new B(), // Ошибка компиляции!!!
                new C(),
                new D(),
                new E(),
                new F()
            };
    
            // Распечатываем
            Console.WriteLine("Реализация абстрактных методов");
            for (int i = 0; i < a.Length; i++)
            {
                Console.Write("Объект " + 
      a[i].GetType().Name + "\t");
                a[i].Show();
            }
        }
    }
    
    // Запуск
    class Program
    {
        static void Main()
        {
            // Настройка консоли
            Console.Title = "Абстрактные методы и классы";
            Console.ForegroundColor = ConsoleColor.White;
            Console.CursorVisible = false;
            Console.WindowWidth = 60;
            Console.WindowHeight = 10;
    
            new MyClass();// Чтобы сработал конструктор
    
            Console.ReadLine();
        }
    }
}
Листинг 9.18 . Абстрактные методы и классы

Результат будет таким


Обратите внимание, что создавать экземпляры абстрактных классов компилятор не дает, но в тоже время объявлять и использовать ссылки абстрактных типов можно (что мы и сделали для базового абстрактного типа A ). Он же следит и затем, чтобы класс, наследующий абстрактный метод и не переопределивший его, был явно помечен как абстрактный. А далее, после первого определения абстрактного метода, механизм адресации виртуальных функций в последующих расширениях работает обычным образом.

Запрещение классу иметь экземпляры

Способ 1. Класс с защищенным конструктором не может иметь экземпляров, но может участвовать в наследовании

using System;
    
namespace Test
{
    class A
    {
        string name = "Класс A";
    
        protected A()
        {
        }
    
        public virtual void Show()
        {
            Console.WriteLine(name);
        }
    }
    
    class B : A
    {
        string name = "Класс B";
        public override void Show()
        {
            Console.WriteLine(name);
        }
    }
    
    class C : B
    {
        string name = "Класс C";
        public override void Show()
        {
            Console.WriteLine(name);
        }
    }
    
    // Вызывающая сторона
    class MyClass
    {
        public MyClass()
        {
            // Создаем и распечатываем объекты
            //A a = new A(); a.Show();  // Ошибка компиляции
            B b = new B(); b.Show();
            C c = new C(); c.Show();
        }
    }
    
    // Запуск
    class Program
    {
        static void Main()
        {
            // Настройка консоли
            Console.Title = "Защищенный конструктор класса";
            Console.ForegroundColor = ConsoleColor.White;
            Console.CursorVisible = false;
            Console.WindowWidth = 60;
            Console.WindowHeight = 10;
    
            new MyClass();// Чтобы сработал конструктор
    
            Console.ReadLine();
        }
    }
}

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

Способ 3. Класс нужно обязательно объявлять абстрактным, если он содержит или наследует, а сам не реализует, хотя-бы один абстрактный метод. Этого требует синтаксис языка C#, потому что класс сырой и не готов к производству объектов. Но любой класс, даже не содержащий абстрактных или виртуальных методов, мы можем объявить абстрактным, если не хотим, чтобы кто-то или мы сами использовали его для создания экземпляров. Обычно это промежуточные вспомогательные классы-заготовки, планируемые для дальнейшего развития с помощью механизма наследования.

Максим Филатов
Максим Филатов

Прошел курс. Получил код Dreamspark. Ввожу код на сайте, пишет:

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

 

Как активировать код?