Реализация интерфейсов

Итак, если интерфейс определен, один или несколько классов могут его реализо­вать. Чтобы реализовать интерфейс, нужно указать его имя после имени класса по­добно тому, как при создании производного указывается базовый класс. Формат запи­си класса, который реализует интерфейс, таков:

class имя_класса : имя_интерфейса { // тело класса

}

Нетрудно догадаться, что имя реализуемого интерфейса задается с помощью эле­мента имя_интерфейса.

Если класс реализует интерфейс, он должен это сделать в полном объеме, т.е. реа­лизация интерфейса не может быть выполнена частично.

Классы могут реализовать несколько интерфейсов. В этом случае имена интерфей­сов отделяются запятыми. Класс может наследовать базовый класс и реализовать один или несколько интерфейсов. В этом случае список интерфейсов должно возглавлять имя базового класса.

Методы, которые реализуют интерфейс, должны быть объявлены открытыми. Дело в том, что методы внутри интерфейса неявно объявляются открытыми, поэтому их реализации также должны быть открытыми. Кроме того, сигнатура типа в реализации метода должна в точности совпадать с сигнатурой типа, заданной в определении ин­терфейса.

Рассмотрим пример реализации интерфейса ISeries, объявление которого приве­дено выше. Здесь создается класс с именем ByTwos, генерирующий ряд чисел, в кото­ром каждое следующее число больше предыдущего на два.

// Реализация интерфейса ISeries, class ByTwos : ISeries {

int start;

int val;

public ByTwos() { start = 0; val = 0;

}

public int getNext() {

val +=2; return val;

}

public void reset() { val = start;

}

public void setStart(int x) { start = x; val = start;

}

}

Как видите, класс ByTwos реализует все три метода, определенные интерфейсом ISeries. Иначе и быть не может, поскольку классу не разрешается создавать частич­ную реализацию интерфейса.

Рассмотрим пример, демонстрирующий использование класса ByTwos, Вот его код:

// Демонстрация использования интерфейса,

// реализованного классом ByTwos.

using System;

class SeriesDemo (

public static void MainO { ByTwos ob = new ByTwos();

for(int i=0; i < 5; i++)

Console.WriteLine("Следующее значение равно " + ob.getNext() ) ;

Console.WriteLine("nПереход в исходное состояние."); ob.reset();

for(int i-0; i < 5; i++)

Console.WriteLine("Следующее значение равно " +

Iob.getNext()) ; Console.WriteLine("n Начинаем с числа 100."); ob.setStart(100); for(int i=0; i < 5; 1++)

Console.WriteLine("Следующее значение равно " +' ob.getNext());

}

}

Чтобы скомпилировать программу SeriesDemo, необходимо включить в процесс компиляции файлы, которые содержат классы ISeries, ByTwos и SeriesDemo. Для создания выполняемой программы компилятор автоматически скомпилирует все три файла. Если эти файлы называются, например, ISeries, cs, ByTwos. cs и SeriesDemo.es, то программа скомпилируется посредством выполнения такой ко­мандной строки:

>csc SeriesDemo.cs ISeries.cs ByTwos.cs

Если вы используете интегрированную среду (IDE) Visual Studio, добавьте все эти три файла в свой С#-проект. Вполне допустимо также поместить их в один файл.

В классах, которые реализуют интерфейсы, можно определять дополнительные члены. Например, в представленную ниже версию класса ByTwos добавлен метод getPrevious (), который возвращает предыдущее значение ряда.

// Реализация интерфейса ISeries с дополнительно // определяемым методом getPrevious().

class ByTwos : ISeries {

int start;

int val;

int prev;

public ByTwos() {

start = 0;

val = 0;

prev = -2;

}

public int getNext(){

prev = val;

val +- 2;

return val;

}

public void reset() {

val = start;

prev = start - 2;

)

public void setStart(int x) { start = x; val - start;

Iprev = val - 2; // Метод, не объявленный в интерфейсе ISeries. public int getPrevious<) { return prev;

Обратите внимание на то, что добавление метода getPrevious () потребовало внесения изменений в реализацию методов, определенных интерфейсом iSeries. Но поскольку интерфейс для этих методов остается прежним, при изменении не разру­шается код, написанный ранее. В этом и заключается одно из достоинств использова­ния интерфейсов.

Возможно, вы будете несколько удивлены, узнав, что можно объявить ссылочную переменную интерфейсного типа. Другими словами, можно создать переменную-ссылку на интерфейс. Такая переменная может ссылаться на любой объект, который реализует ее интерфейс. При вызове метода для объекта посредством интерфейсной ссылки будет выполнена та версия указанного метода, которая реализована этим объ­ектом. Этот процесс аналогичен использованию ссылки на базовый класс для доступа к объекту производного класса.

Использование интерфейсной ссылки демонстрируется в следующем примере. Здесь используется одна и та же интерфейсная переменная-ссылка, чтобы вызывать методы для объектов как класса ByTwos, так и класса Primes.

// Демонстрация использования интерфейсных ссылок.

using System;

// Определение интерфейса, public interface ISeries {

int getNextO; // Возвращает следующее число ряда.

void reset О; // Выполняет перезапуск.

void setStart(int х); // Устанавливает начальное

// значение.

}

// Используем интерфейс ISeries для генерирования // последовательности четных чисел, class ByTwos : ISeries {

int start;

int val;

public ByTwosО {

start = 0; val = 0;

}

public int getWext{) {

val +=2; return val;

}

public void reset{) {

val = start;

}

public void setStart(int x) {

start = x; val = start;

}

}

// Используем интерфейс ISeries для построения

// ряда простых чисел,

class Primes : ISeries {

int start;

int val;

public Primes() {

start = 2; val = 2;

}

public int getNext() { int I, j; bool isprime;

val++;

for(i - val; i < 1000000; i++) { isprime = true;

for(j - 2; j < (i/j + 1); {

if<(i%j)--0) { isprime = false; break;

}

}

if(isprime) {

val = i; break;

}

}

return val;

}

public void reset() {

val = start;

}

public void setStart(int x) {

start = x;

val = start;

)

}

class SeriesDemo2 {

public static void Main() {

ByTwos twoOb - new ByTwos();

Primes primeOb = new Primes();

ISeries ob;

for(int i=0; i < 5; i++) {

ob = twoOb; Console.WriteLine(

"Следующее четное число равно

ob.getNext()) ; ob = primeOb; Console.WriteLine(

"Следующее простое число равно

ob.getNext());

Вот результаты выполнения этой программы:

(Следующее четное число равно 2

Следующее простое число равно 3

Следующее четное число равно А

Следующее простое число равно 5

Следующее четное число равно 6

Следующее простое число равно 7

Следующее четное число равно 8

Следующее простое число равно 11

Следующее четное число равно 10

Следующее простое число равно 13

В методе Main() объявляется переменная ob как ссылка на интерфейс iSeries. Это означает, что ее можно использовать для хранения ссылок на любой объект, ко­торый реализует интерфейс ISeries. В данном случае она служит для ссылки на объ­екты twoOb и primeOb, которые являются экземплярами классов ByTwos и Primes, соответственно, причем оба класса реализуют один и тот же интерфейс, ISeries.

Важно понимать, что интерфейсная ссылочная переменная "осведомлена" только о методах, объявленных "под сенью" ключевого слова interface. Следовательно, ин­терфейсную ссылочную переменную нельзя использовать для доступа к другим пере­менным или методам, которые может определить объект, реализующий этот интер­фейс.