Г. Шилдт - Полный справочник по C# (1160789), страница 59
Текст из файла (страница 59)
Язык С#Полныйсправочник поИнтерфейсы, структурыи перечисления™1 та глава посвящена одному из самых важных средств языка С#: интерфейсу.^ ^ Интерфейс определяет набор методов, которые будут реализованы классом. Саминтерфейс не реализует методы. Таким образом, интерфейс — это логическая конструкция, которая описывает методы, не устанавливая жестко способ их реализации. Вэтой главе рассматриваются еще два типа данных С#: структуры и перечисления.Структуры подобны классам, за исключением того, что они обрабатываются как типызначений, а не как ссылочные типы.
Перечисления — это списки именованных целочисленных констант. Структуры и перечисления вносят существенный вклад в общуюкопилку средств и инструментов, составляющих среду программирования С#.ИнтерфейсыВ объектно-ориентированном программировании иногда требуется определить,что класс должен делать, а не как он будет это делать. Вы уже видели такой подход напримере абстрактного метода. Абстрактный метод определяет сигнатуру для метода,но не обеспечивает его реализации. В производном классе каждый абстрактный метод, определенный базовым классом, реализуется по-своему. Таким образом, абстрактный метод задает интерфейс для метода, но не способ его реализации.
Несмотряна всю полезность абстрактных классов и методов, эту идею можно развить. В С#предусмотрена возможность полностью отделить интерфейс класса от его реализациис помощью ключевого слова i n t e r f a c e .Интерфейсы синтаксически подобны абстрактным классам. Однако в интерфейсени один метод не может включать тело, т.е.
интерфейс в принципе не предусматривает какой бы то ни было реализации. Он определяет, что должно быть сделано, но неуточняет, как. Коль скоро интерфейс определен, его может реализовать любое количество классов. При этом один класс может реализовать любое число интерфейсов.Для реализации интерфейса класс должен обеспечить теля (способы реализации)методов, описанных в интерфейсе. Каждый класс может определить собственную реализацию. Таким образом, два класса могут реализовать один и тот же интерфейс различными способами, но все классы поддерживают одинаковый набор методов. Следовательно, код, "осведомленный" о наличии интерфейса, может использовать объектылюбого класса, поскольку интерфейс для всех объектов одинаков. Предоставляя программистам возможность применения такого средства программирования, как интерфейс, С# позволяет в полной мере использовать аспект полиморфизма, выражаемыйкак "один интерфейс — много методов".Интерфейсы объявляются с помощью ключевого слова i n t e r f a c e .
Вот как выглядит упрощенная форма объявления интерфейса:i n t e r f a c e имя{тип_возврата имя_метода1 {список_параметров) ;тип_возврата имя_метода2 {список_параметров) ;// . . .тип_возврата имя_методаЫ(список_параметров) ;}Имя интерфейса задается элементом имя.
Методы объявляются с использованиемлишь типа возвращаемого ими значения и сигнатуры. Все эти методы, по сути, — абстрактные. Как упоминалось выше, для методов в интерфейсе не предусмотрены способы реализации. Следовательно, каждый класс, который включает интерфейс, должен реализовать все его методы.
В интерфейсе методы неявно являются открытыми(public-методами), при этом не разрешается явным образом указывать спецификатордоступа.320Часть I. Язык С#Рассмотрим пример интерфейса для класса, который генерирует ряд чисел.public interface ISeries {int getNext(); // Возвращает следующее число ряда,void reset (); // Выполняет перезапуск,void setStart(int x) ; // Устанавливает начальное// значение.Этот интерфейс имеет имя I S e r i e s .
Хотя префикс " I " необязателен, • многиепрограммисты его используют, чтобы отличать интерфейсы от классов. ИнтерфейсI S e r i e s объявлен открытым, поэтому он может быть реализован любым классом влюбой программе.Помимо сигнатур методов интерфейсы могут объявлять сигнатуры свойств, индексаторов и событий. События рассматриваются в главе 15, поэтому здесь мы остановимся на методах, индексаторах и свойствах. Интерфейсы не могут иметь членов данных. Они не могут определять конструкторы, деструкторы или операторные методы.Кроме того, ни один член интерфейса не может быть объявлен статическим.Реализация интерфейсовИтак, если интерфейс определен, один или несколько классов могут его реализовать. Чтобы реализовать интерфейс, нужно указать его имя после имени класса подобно тому, как при создании производного указывается базовый класс. Формат записи класса, который реализует интерфейс, таков:c l a s s имя_класса : имя__интерфейса {// тело классаНетрудно догадаться, что имя реализуемого интерфейса задается с помощью элемента имя_интерфейса.Если класс реализует интерфейс, он должен это сделать в полном объеме, т.е.
реализация интерфейса не может быть выполнена частично.Классы могут реализовать несколько интерфейсов. В этом случае имена интерфейсов отделяются запятыми. Класс может наследовать базовый класс и реализовать одинили несколько интерфейсов. В этом случае список интерфейсов должно возглавлятьимя базового класса.Методы, которые реализуют интерфейс, должны быть объявлены открытыми. Делов том, что методы внутри интерфейса неявно объявляются открытыми, поэтому ихреализации также должны быть открытыми. Кроме того, сигнатура типа в реализацииметода должна в точности совпадать с сигнатурой типа, заданной в определении интерфейса.Рассмотрим пример реализации интерфейса I S e r i e s , объявление которого приведено выше. Здесь создается класс с именем ByTwos, генерирующий ряд чисел, в котоэом каждое следующее число больше предыдущего на два.// Реализация интерфейса ISeries,class ByTwos : ISeries {int s t a r t ;int val;public ByTwos() {start = 0;val = 0;public i n t getNext() {Глава 12.
Интерфейсы, структуры и перечисления321val += 2;return val;public void reset() {val = start;public void setStart(int x) {start = x;val = start;Как видите, класс ByTwos реализует все три метода, определенные интерфейсомI S e r i e s . Иначе и быть не может, поскольку классу не разрешается создавать частичную реализацию интерфейса.Рассмотрим пример, демонстрирующий использование класса ByTwos. Вот его код:// Демонстрация использования интерфейса,// реализованного классом ByTwos.using System;class SeriesDemo {public static void Main() {ByTwos ob = new ByTwos() ;f o r ( i n t i=0; i < 5;Console.WriteLine("Следующее значение равно " +ob.getNext());Console.WriteLine("ХпПереход в исходное состояние.")ob.reset ( ) ;f o r ( i n t i=0; i < 5; i++)Console.WriteLine("Следующее значение равно " +ob.getNext());Console.WriteLine("ХпНачинаем с числа 100.");o b . s e t S t a r t (100);f o r ( i n t i=0; i < 5; i++)Console.WriteLine("Следующее значение равно " +"ob.getNext());Чтобы скомпилировать программу SeriesDemo, необходимо включить в процесскомпиляции файлы, которые содержат классы I S e r i e s , ByTwos и SeriesDemo.
Длясоздания выполняемой программы компилятор автоматически скомпилирует все трифайла. Если эти файлы называются, например, I S e r i e s , cs, ByTwos. cs иSeriesDemo.es, то программа скомпилируется посредством выполнения такой командной строки:I >csc SeriesDemo.cs I S e r i e s . c s ByTwos.csЕсли вы используете интефированную среду (IDE) Visual Studio, добавьте все этитри файла в свой С#-проект. Вполне допустимо также поместить их в один файл.322Часть I.
Язык С#Вот результаты выполненияСледующее значение равноСледующее значение равноСледующее значение равноСледующее значение равноСледующее значение равноПереход вСледующееСледующееСледующееСледующееСледующееисходноезначениезначениезначениезначениезначениеэтой программы: ф24б810состояние.равно 2равно 4равно бравно 8равно 10Начинаем с числа 100.Следующее значение равноСледующее значение равноСледующее значение равноСледующее значение равноСледующее значение равно102104106108110В классах, которые реализуют интерфейсы, можно определять дополнительныечлены. Например, в представленную ниже версию класса ByTwos добавлен методgetPrevious (), который возвращает предыдущее значение ряда.// Реализация интерфейса ISeries с дополнительно// определяемым методом getPrevious().class ByTwos : ISeries {int start;int val;int prev;public ByTwos() {start = 0;val = 0;prev = -2;}public int getNextO {prev = val;val += 2;return val;}public void reset() {val = start;prev = start - 2;}public void setStart(int x) {start = x;val = start;prev = val - 2;}// Метод, не объявленный в интерфейсе ISeries,public int getPrevious() {return prev;Глава 12.
Интерфейсы, структуры и перечисления323Обратите внимание на то, что добавление метода g e t P r e v i o u s () потребоваловнесения изменений в реализацию методов, определенных интерфейсом I S e r i e s . Нопоскольку интерфейс для этих методов остается прежним, при изменении не разрушается код, написанный ранее. В этом и заключается одно из достоинств использования интерфейсов.Как упоминалось выше, интерфейс может реализовать любое количество классов.Рассмотрим, например, класс Primes, который генерирует ряд простых чисел. Обратите внимание на то, что его способ реализации интерфейса I S e r i e s в корне отличается от используемого классом ByTwos.// Использование интерфейса I S e r i e s для реализации// ряда простых чисел.class Primes : ISeries {int start;int val;public Primes () {start = 2;val = 2;}public int getNext() {int i, j;bool isprime;for(i = val; i < 1000000;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;Здесь важно понимать, что, хотя классы Primes и ByTwos генерируют разные рядычисел, оба они реализуют один и тот же интерфейс I S e r i e s .