Шаг 155.
Язык программирования C#. Начала. Абстрактные классы и интерфейсы. Использование абстрактных классов (окончание)

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

    Еще один пример, который мы рассмотрим далее, дает представление о том, как описывается и используется абстрактный класс, в котором есть абстрактные свойства и индексаторы.

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

namespace pr155_1
{
    // Абстрактный класс: 
    abstract class Base {
        // Абстрактное текстовое свойство: 
        public abstract string text { 
            get; set;
        }
        // Абстрактный индексатор с целочисленным индексом: 
        public abstract char this[int k] { 
            get;
        }
        // Абстрактное целочисленное свойство: 
        public abstract int length { 
            get;
        }
    }
    // Производный класс на основе абстрактного: 
    class Alpha: Base {
        // Закрытое поле, являющееся ссылкой на массив: 
        private char[] symbs;
        // Конструктор:
        public Alpha(string t): base() {
            // Текстовому свойству присваивается значение: 
            text = t;
        }
        // Переопределение текстового свойства: 
        public override string text {
            get {
                // Результатом является текстовая строка: 
                return new string(symbs);
            }
            set {
                // Создание символьного массива и присваивание 
                // значения полю: 
                symbs = value.ToCharArray();
            }
        }
        // Переопределение целочисленного свойства: 
        public override int length { 
            get {
                // Размер массива: 
                return symbs.Length;
            }
        }
        // Переопределение индексатора: 
        public override char this[int k] { 
            get {
                // Значение элемента символьного массива: 
                return symbs[k];
            }
        }
    }

    // Производный класс на основе абстрактного: 
    class Bravo: Base {
        // Закрытое текстовое поле: 
        private string txt;
        // Конструктор:
        public Bravo(string t): base() {
            // Текстовому свойству присваивается значение:
            text = t;
        }
        // Переопределение текстового свойства: 
        public override string text { 
            get {
                // Значение поля: 
                return txt;
            }
            set {
                // Присваивание значения полю: 
                txt = value;
            }
        }
        // Переопределение целочисленного свойства: 
        public override int length { 
            get {
                // Количество символов в тексте: 
                return txt.Length;
            }
        }
        // Переопределение индексатора: 
        public override char this[int k] { 
            get {
                // Символ в тексте: 
                return txt[k];
            }
        }
    }

    // Класс с главным методом: 
    class Program
    {
        // Главный метод:
        static void Main()
        {
            // Ссылка на объект производного класса записывается 
            // в объектную переменную базового класса:
            Base obj = new Alpha("Alpha");
            // Отображение значения текстового свойства:
            Console.WriteLine(obj.text);
            // Новое значение текстового свойства: 
            obj.text = "Base";
            // Индексирование объекта: 
            for(int k = 0;k < obj.length; k++) {
                Console.Write("|" + obj[k]);
            }
            Console.WriteLine("|");
            // Ссылка на объект производного класса записывается 
            // в объектную переменную базового класса: 
            obj = new Bravo("Bravo");
            // Индексирование объекта: 
            for(int k = 0;k < obj.length; k++) {
                Console.Write("|" + obj[k]);
            }
            Console.WriteLine("|");
            // Новое значение текстового свойства: 
            obj.text = "Base";
            // Отображение значения текстового свойства:
            Console.WriteLine(obj.text);
            // Задержка:
            Console.ReadLine();
        }
    }
}
Архив проекта можно взять здесь.

    Ниже показано, как выглядит результат выполнения программы:


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

    В программе описан абстрактный класс Base, в котором объявлены два абстрактных свойства (текстовое text и целочисленное length) и индексатор с целочисленным индексом. Все эти члены класса описаны с ключевым словом abstract. В теле абстрактного текстового свойства text указаны ключевые слова get и set (после каждого ключевого слова ставится точка с запятой). Это означает, что при переопределении (по факту при описании) свойства должен быть описан и get-аксессор, и set-аксессор. В теле свойства length и в теле индексатора указано только ключевое слово get. Поэтому при переопределении свойства и индексатора в производном классе описывается только get-аксессор.


В производных классах свойства и индексатор описываются с ключевым словом override, как при переопределении методов.

    На основе класса Base путем наследования создается класс Alpha и класс Bravo. Классы практически идентичны, но имеются отличия на "техническом" уровне. В классе Alpha используется закрытое поле symbs, являющееся ссылкой на символьный массив. В классе Bravo описано закрытое текстовое свойство txt. У каждого из классов есть конструктор с текстовым аргументом.


В базовом классе Base конструктор не описывался. В производных классах Alpha и Bravo описываются конструкторы. В них вызывается конструктор базового класса без аргументов (инструкция base()). Имеется в виду конструктор по умолчанию класса Base.

    В каждом из конструкторов переданное аргументом текстовое значение присваивается свойству text. Но в классе Alpha процедура присваивания значения свойству text описана так, что присваиваемое текстовое значение с помощью библиотечного метода ToCharArray() преобразуется в массив и ссылка на этот массив записывается в поле symbs. В классе Bravo текстовое значение при присваивании свойству text в действительности записывается в поле txt. Значение поля txt возвращается в виде значения свойства text для объекта класса Bravo.

    Для объекта класса Alpha в качестве значения свойства text возвращается текстовая строка, сформированная на основе символьного массива symbs. Чтобы сформировать текст на основе символьного массива, мы создаем анонимный объект класса String и передаем ему аргументом ссылку на символьный массив.


В команде
  return new string(symbs); 
в get-аксессоре свойства text класса Alpha мы использовали синоним string для инструкции System.String.

    Свойство length описано таким образом, что для объекта класса Alpha оно возвращает длину символьного массива symbs, а для объекта класса Bravo значение свойства определяется количеством символов в текстовом поле txt. Наконец, индексатор описан так, что результатом возвращается символьное значение элемента массива (класс Alpha) или символ из текста (класс Bravo).

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

  Base obj = new Alpha("Alpha");
создается объект класса Alpha, а ссылка на объект записывается в объектную переменную obj абстрактного класса Base. Мы проверяем значение свойства text (команда
  Console.WriteLine(obj.text);
), присваиваем свойству новое значение (команда
  obj.text = "Base";
) и с помощью циклической конструкции, индексируя объектную переменную obj, посимвольно отображаем содержимое символьного массива из объекта, на который ссылается переменная obj. После этого командой
  obj = new Bravo("Bravo");
создаем объект класса Bravo и записываем ссылку на него в переменную obj. В теле цикла выполняется индексирование объекта, благодаря чему мы посимвольно отображаем содержимое текстового поля объекта, на который теперь ссылается переменная obj. Командой
  obj.text = "Base";
текстовому свойству объекта присваивается новое значение, после чего мы проверяем значение этого свойства (команда
  Console.WriteLine(obj.text);
).

    Что мы получили в данном случае? На основе абстрактного класса мы создали два класса с одинаковым набором характеристик (имеются в виду свойства и индексатор), но при этом "механизм" реализации производных классов разный. Получается, что абстрактный класс задал некоторый шаблон, в соответствии с которым реализованы производные классы. Такой подход на практике нередко оказывается полезным.

    На следующем шаге мы начнем знакомиться с интерфейсами.




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