На этом шаге мы рассмотрим назначение, создание и использование таких переменных.
Хотя интерфейс напоминает класс, но на основе интерфейса объект создать нельзя (хочется верить, что не нужно объяснять, почему этого нельзя сделать). Но существует такое понятие, как интерфейсная переменная.
Интерфейсная переменная - это переменная, типом которой указано название интерфейса. Особенность и "сила" интерфейсной переменной в том, что интерфейсная переменная может ссылаться на объект любого класса, реализующего данный интерфейс (указанный типом интерфейсной переменной). То есть если имеется класс, который реализует некоторый интерфейс, и мы создали объект такого класса, то ссылку на данный объект можно записать не только в объектную переменную, типом которой указан интерфейс. Правда здесь имеется важное ограничение: через интерфейсную переменную можно получить доступ только к тем методам, свойствам и индексаторам (через индексирование объекта), которые объявлены в интерфейсе.
Рассмотрим пример, в котором используется указанная особенность интерфейсных переменных. Проанализируем программу, представленную ниже.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace pr160_1 { // Интерфейс: interface MyInterface { // Объявление метода: char getChar(int n); // Объявление индексатора: char this[int k] { get; } } // Первый класс, реализующий интерфейс: class Alpha: MyInterface { // Закрытое символьное поле: private char symb; // Конструктор с символьньм аргументом: public Alpha(char s) { // Полю присваивается значение: symb = s; } // Описание метода: public char getChar(int n) { // Результат: return (char)(symb + n); } // Описание индексатора: public char this[int k] { // Аксессор для считывания значения: get { // Результат: return getChar(k); } } } // Второй класс, реализующий интерфейс: class Bravo: MyInterface { // Закрытое текстовое поле: private string text; // Конструктор с текстовым аргументом: public Bravo(string t) { // Полю присваивается значение: text = t; } // Описание метода: public char getChar(int k) { return text[k % text.Length]; } // Описание индексатора: public char this[int k] { // Аксессор для считывания значения: get { // Результат: return getChar(k); } } } // Класс с главным методом: class Program { // Главный метод: static void Main() { // Целочисленная переменная: int m = 5; // Интерфейсная переменная: MyInterface R; // Создается объект класса Alpha и ссылка на него // записывается в интерфейсную переменную: R = new Alpha('F'); // Вызов метода через интерфейсную переменную: Console.WriteLine("Символы от \'{0}\' до \'{1}\':", R.getChar(-m), R.getChar(m)); // Индексирование объекта через // интерфейсную переменную: for(int i = -m; i <= m; i++) { Console.Write("|" + R[i]); } Console.WriteLine("|"); // Создается объект класса Bravo и ссылка на него // записывается в интерфейсную переменную: R = new Bravo("bravo"); // Вызов метода через интерфейсную переменную: Console.WriteLine("Символы от \'{0}\' до \'{1}\':", R.getChar(0), R.getChar(2 * m + 1)); // Индексирование объекта через // интерфейсную переменную: for(int i = 0; i <= 2 * m + 1; i++) { Console.Write("|" + R[i]); } Console.WriteLine("|"); // Задержка: Console.ReadLine(); } } }
Результат выполнения программы следующий:
Рис.1. Результат выполнения программы
В программе описан интерфейс MyInterface. В этом интерфейсе объявляется метод getChar() с целочисленным аргументом и символьным результатом, а также объявлен индексатор с целочисленным индексом и символьным значением. Интерфейс MyInterface реализуется в классах Alpha и Bravo. В классе Alpha имеется закрытое символьное поле symb и конструктор с одним аргументом (определяет значение символьного поля). Метод getChar() описан в классе так, что результатом возвращается значение выражения (char)(symb+n). Это значение вычисляется следующим образом: к коду символа из поля symb прибавляется целочисленное значение аргумента метода n, и полученное целое число преобразуется к символьному типу. Индексатор определен так, что при заданном индексе k результатом возвращается значение выражения getChar(k). То есть при индексировании объекта с индексом k и вызове метода getChar() с аргументом k по определению получаем один и тот же результат. Стоит сразу отметить, что в классе Bravo индексатор определен так же - на основе метода getChar(). Этот класс тоже реализует интерфейс MyInterface. В классе есть закрытое текстовое поле text и конструктор с текстовым аргументом, определяющим значение текстового поля. Метод getChar() определен таким образом, что при аргументе k в качестве результата возвращается значение выражения text[k%text.Length]. Это символ из текстового поля text с индексом k (с учетом циклической перестановки индекса, если он выходит за верхнюю допустимую границу).
Как отмечалось выше, для индексатора с индексом k результатом возвращается значение getChar(k).
В главном методе командой
MyInterface R;
R = new Alpha('F');
Похожие операции выполняются с объектом класса Bravo. Этот объект создается командой
R = new Bravo("bravo"); ,
На следующем шаге мы рассмотрим явную реализацию членов интерфейса.