На этом шаге мы рассмотрим особенности явной реализации членов интерфейса.
Но у нас есть и альтернатива реализации предыдущего шага. Мы можем воспользоваться явной реализацией методов, свойств и индексаторов интерфейса. При явной реализации, когда соответствующий член описывается в классе, перед его именем (через точку) указывается название интерфейса, а спецификатор уровня доступа не указывается. Доступ к такому члену класса можно получить через переменную соответствующего интерфейса. Дальше обратимся к примеру. Программа представлена ниже.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace pr162_1 { // Базовый класс: abstract class Base { // Абстрактное свойство: public abstract char symbol { get; } // Абстрактный индексатор: public abstract int this[int k] { get; } // Абстрактный метод: public abstract void show(); } // Первый интерфейс: interface First { // Свойство: char symbol { get; } // Индексатор: int this[int k] { get; } // Метод: void show(); } // Второй интерфейс: interface Second { // Свойство: char symbol { get; } // Индексатор: int this[int k] { get; } // Метод: void show(); } // Производный класс наследует абстрактный базовый класс // и реализует интерфейсы: class MyClass: Base, First, Second { // Закрытое символьное поле: private char smb; // Конструктор с символьньм аргументом: public MyClass(char s): base() { smb = s; } // Описание свойства из абстрактного класса: public override char symbol { get { return smb; } } // Явная реализация свойства из интерфейса First: char First.symbol { get { return (char)(smb + 1); } } // Описание индексатора из базового класса: public override int this[int k] { get { return smb + k; } } // Явная реализация индексатора из интерфейса Second: int Second.this[int k] { get { return smb - k; } } // Описание метода из базового класса: public override void show() { Console.WriteLine("Базовый класс Base:\t\'{0}\'", symbol); } // Явная реализация метода из интерфейса First: void First.show() { Console.WriteLine("Интерфейс First:\t\'{0}\'", symbol); } // Явная реализация метода из интерфейса Second: void Second.show() { Console.WriteLine("Интерфейс Second:\t\'{0}\'", symbol); } } // Класс с главным методом: class Program { // Главный метод: static void Main() { // Создание объекта: MyClass obj = new MyClass('A'); // Интерфейсные переменные: First A = obj; Second = obj; // Вызов метода через переменные: obj.show(); A.show(); B.show(); // Считывание значения свойства: Console.WriteLine("obj.symbol = \'{0}\'", obj.symbol); Console.WriteLine(" A.symbol = \'{0}\'", A.symbol); Console.WriteLine(" B.symbol = \'{0}\'", B.symbol); // Индексирование объекта: Console.WriteLine("obj[10] = {0}", obj[10]); Console.WriteLine(" A[10] = {0}", A[10]); Console.WriteLine(" B[10] = {0}", B[10]); // Задержка: Console.ReadLine(); } } }
Результат выполнения программы следующий:
Рис.1. Результат выполнения программы
Интерфейсы First и Second описаны одинаково. Отличает их только название. В каждом из интерфейсов объявлено символьное свойство symbol с get-аксессором, индексатор с целочисленным индексом и get-аксессором и метод show() - без аргументов и не возвращающий результат. Такая же "начинка" и у абстрактного класса Base, но только с поправкой на использование в описании соответствующих членов ключевых слов public и abstract. Класс MyClass наследует класс Base и реализует интерфейсы First и Second. В этом классе появляется закрытое символьное поле smb и конструктор с одним символьным аргументом, который определяет значение поля. Но нас, конечно, в первую очередь интересует тот способ, которым описывается метод show(), свойство symbol и индексатор.
Свойство symbol описано дважды. Одно из описаний свойства - это обычное описание унаследованного из абстрактного класса свойства. При запросе значения свойства результатом возвращается значение поля smb. Также в классе есть явная специализация для свойства из интерфейса First. Данная версия описана без ключевого слова public, а название свойства указано в виде First.symbol. Значение свойства вычисляется как (char)(smb+1). Это следующий символ после символа, записанного в поле smb (но значение самого поля smb при этом не меняется). Такая версия свойства будет задействована, если мы будем обращаться к объекту класса MyClass через интерфейсную переменную типа First. Если мы будем получать доступ к объекту через объектную переменную класса MyClass или интерфейсную переменную типа Second, то используется первая версия свойства.
У индексатора также две версии. Одна описана как свойство, переопределяемое в производном классе. При запросе значения выражения с проиндексированным объектом при заданном индексе k результатом возвращается значение smb+k (сумма кода символа из поля smb и индекса k). Явная реализация индексатора выполняется для интерфейса Second. Версия описывается без ключевого слова public, а вместо ключевого слова this используется конструкция Second.this. Запрос значения выражения с проиндексированным объектом вычисляется как разность кода символа из поля smb и индекса k (выражение smb-k). Явная реализация индексатора будет задействована, если мы станем индексировать объект через интерфейсную переменную типа Second. При индексировании объекта через объектную переменную класса MyClass или через интерфейсную переменную типа First в игру вступает первая версия индексатора.
У метода show() в классе MyClass есть три версии. Версия с ключевым словом override вызывается через объектную переменную базового класса. Явная реализация метода из интерфейса First задействуется при обращении к объекту через переменную интерфейсного типа First. При обращении к объекту через интерфейсную переменную типа Second используется явная реализация метода для интерфейса Second. Каждая версия метода отображает значение свойства symbol объекта и название класса или интерфейса, для которого выполнена реализация метода.
В главном методе командой
MyClass obj = new MyClass('A');
First A = obj;
Second = obj;
obj.show();
A.show();
B.show();
На следующем шаге мы резюмируем все ранее сказанное по данной теме на предыдущих шагах.