1629295407-c61bfe4caba98380ea3e7cdae6295416 (846200), страница 60
Текст из файла (страница 60)
Интерфейсы, структуры и перечисления329Console.WriteLine("\nНaчинaeм с числа 21");ob.next = 21;for(int i=0; i < 5; i++)Console.WriteLine("Следующее значение равно " +ob.next);Console.WriteLine("\nпереход в исходное состояние.");ob.next = 0;}}// Получаем доступ к ряду посредством индексатора.for(int i=0; i < 5; i++)Console.WriteLine("Следующее значение равно " +ob[i]);Вот результаты, сгенерированные этой программой:Следующее значение равно 2Следующее значение равно 4Следующее значение равно 6Следующее значение равно 8Следующее значение равно 10Начинаем с числа 21Следующее значение равно 23Следующее значение равно 25Следующее значение равно 27Следующее значение равно 29Следующее значение равно 31Переход в исходное состояние.Следующее значение равно 0Следующее значение равно 2Следующее значение равно 4Следующее значение равно 6Следующее значение равно 8Наследование интерфейсовОдин интерфейс может унаследовать “богатство” другого.
Синтаксис этогомеханизма аналогичен синтаксису, используемому для наследования классов. Если классреализует интерфейс, который наследует другой интерфейс, этот класс должен обеспечитьспособы реализации для всех членов, определенных внутри цепочки наследованияинтерфейсов. Рассмотрим такой пример:// Один интерфейс может наследовать другой.using System;public interface A {void meth1();void meth2();}// Интерфейс В теперь включает методы meth1() и meth2(),//а также добавляет метод meth3().330Часть I. Язык C#public interface В : A {void meth3();}// Этот класс должен реализовать все методы// интерфейсов А и В.class MyClass : В {public void meth1() {Console.WriteLine("Реализация метода meth1().");}public void meth2() {Console.WriteLine("Реализация метода meth2().");}}public void meth3() {Console.WriteLine("Реализация метода meth3().");}class IFExtend {public static void Main() {MyClass ob = new MyClass();}}ob.meth1();ob.meth2();ob.meth3();Если бы в качестве эксперимента вы попытались удалить метод meth1(),реализованный в классе MyClass, то сразу же получили бы от компилятора сообщение обошибке.
Как упоминалось выше, любой класс, который реализует интерфейс, долженреализовать все методы, определенные этим интерфейсом, включая методы, которыеунаследованы от других интерфейсов.Сокрытие имен с помощью наследованияинтерфейсовВ производном интерфейсе можно объявить член, который скрывает член,определенный в базовом интерфейсе. Это происходит при совпадении их сигнатур. Такоесовпадение вызовет предупреждающее сообщение, если член производного интерфейса немодифицировать с помощью ключевого слова new.Явная реализация членов интерфейсаПри реализации члена интерфейса можно квалифицировать его имя сиспользованием имени интерфейса. В этом случае говорят, что член интерфейсареализуется явным образом, или имеет место его явная реализация. Например, приопределении интерфейсаГлава 12.
Интерфейсы, структуры и перечисления331interface IMyIF {int myMeth(int x);}вполне допустимо реализовать интерфейс IMyIF следующим образом:class MyClass : IMyIF{int IMyIF.myMeth(int x) {return x / 3;}}Как видите, при реализации метода myMeth() члена интерфейса IMyIF указываетсяего полное имя, включающее имя интерфейса.Явная реализация членов интерфейса может понадобиться по двум причинам.
Вопервых, реализуя метод с использованием полностью квалифицированного имени, вы темсамым обозначаете части закрытой реализации, которые не “видны” коду, определенномувне класса. Во-вторых, класс может реализовать два интерфейса, которые объявляютметоды с одинаковыми именами и типами. Полная квалификация имен позволяет избежатьнеопределенности ситуации.
Рассмотрим примеры.Закрытая реализацияСледующая программа содержит интерфейс с именем IEven, который определяетдва метода isEven() и isOdd(), устанавливающие факт четности и нечетности числа,соответственно. Класс MyClass реализует интерфейс IEven, причем его член isOdd()реализуется явным образом.// Явная реализация члена интерфейса.using System;interface IEven {bool isOdd(int x);bool isEven(int x);}class MyClass : IEven {// Явная реализация.bool IEven.isOdd(int x) {if((x%2) != 0) return true;else return false;}}// Обычная реализация.public bool isEven(int x) {IEven o = this; // Ссылка на вызывающий объект.return !o.isOdd(x);}class Demo {public static void Main() {MyClass ob = new MyClass();bool result;result = ob.isEven(4);if(result) Console.WriteLine("4 - четное число.");332Часть I.
Язык C#}}else Console.WriteLine("3 - нечетное число.");// result = ob.isOdd(); // Ошибка, член не виден.Поскольку метод isOdd() реализован в явном виде, он недоступен вне классаMyClass. Такой способ реализации делает его надежно закрытым. Внутри классаMyClass к методу isOdd() можно получить доступ только через ссылку на интерфейс.Вот почему он прекрасно вызывается для объекта о в реализации метода isEven().Как избежать неопределенности с помощью явной реализацииРассмотрим пример, в котором реализовано два интерфейса, причем оба объявляютметод с именем meth().
В этой ситуации явная реализация используется для того, чтобыизбежать неопределенности.// Использование явной реализации для того, чтобы избежать// неоднозначности.using System;interface IMyIF_A {int meth(int x);}interface IMyIF_B {int meth(int x);}// В классе MyClass реализованы оба интерфейса.class MyClass : IMyIF_A, IMyIF_B {// Явным образом реализуем два метода meth().int IMyIF_A.meth(int x) {return x + x;}int IMyIF_B.meth(int x) {return x * x;}// Вызываем метод meth() посредством ссылки на интерфейс.public int methA(int x){IMyIF_A a_ob;a_ob = this;return a_ob.meth(x);// Имеется в виду// интерфейс IMyIF_A.}public int methB(int x){IMyIF_B b_ob;b_ob = this;return b_ob.meth(x);}}// Имеется в виду// интерфейс IMyIF_BГлава 12.
Интерфейсы, структуры и перечисления333class FQIFNames {public static void Main() {MyClass ob = new MyClass();Console.Write("Вызов метода IMyIF_A.meth(): ");Console.WriteLine(ob.methA(3));}}Console.Write("Вызов метода IMyIF_B.meth(): ");Console.WriteLine(ob.methB(3));Вот результаты выполнения этой программы:Вызов метода IMyIF_A.meth(): 6Вызов метода IMyIF_B.meth(): 9Обратите внимание на то, что метод meth() имеет одинаковую сигнатуру винтерфейсах IMyIF_A и IMyIF_B. Следовательно, если класс MyClass реализует оба этиинтерфейса, он должен реализовать каждый метод в отдельности, полностью указав его имя(с использованием имени соответствующего интерфейса).
Поскольку единственный способвызова явно заданного метода состоит в использовании интерфейсной ссылки, методmeth(), объявленный в интерфейсе IMyIF_A, создает ссылку на интерфейс IMyIF_A, аметод meth(), объявленный в интерфейсе IMyIF_B, создает ссылку на интерфейсIMyIF_B. Созданные ссылки затем используются при вызове этих методов, благодаря чемуможно избежать неоднозначности.Выбор между интерфейсом и абстрактнымклассомВ программировании на C# при необходимости описать функции, а не способ ихреализации, важно знать, когда следует использовать интерфейс, а когда — абстрактныйкласс. Общее правило таково.
Если вы полностью описываете действия класса и не нужноуточнять, как он это делает, следует использовать интерфейс. Если же требуется включить вописание детали реализации, имеет смысл представить концепцию программы (или еечасти) в виде абстрактного класса.Стандартные интерфейсы среды .NET FrameworkВ среде .NET Framework определено множество интерфейсов, которые могутиспользовать C#-программы.
Например, интерфейс System.IComparable определяетметод CompareTo(), который позволяет сравнивать объекты. Интерфейсы такжеобразуют важную часть коллекции классов, которая обеспечивает различные типы дляхранения групп объектов (например, стеки и очереди). Так, например, интерфейсSystem.Collections.ICollection определяет функциональность, общую для всехколлекций. Интерфейс System.Collections.IEnumerator предлагает способ опросаэлементов в коллекции. Эти и другие интерфейсы мы рассмотрим в части II.334Часть I. Язык C#Учебный проект: создание интерфейсаПрежде чем продолжать изучение других средств программирования на C#, было быполезно рассмотреть пример использования интерфейса. В этом разделе мы создадиминтерфейс ICipher, который определяет методы поддержки шифрования строк.
Для этойзадачи использование интерфейса вполне оправданно, поскольку здесь можно полностьюотделить код с описанием “что” от кода, где указано, “как”.Интерфейс ICipher имеет такой вид:// Интерфейс шифрования и дешифрирования строк.public interface ICipher {string encode(string str);string decode(string str);}В интерфейсе ICipher объявляются два метода: encode() и decode(), которыеиспользуются для шифрования и дешифрирования строк, соответственно.
При этом другиедетали не уточняются. Это значит, что классы, которые будут реализовывать эти методы,могут выбирать любой метод шифрования. Например, один класс мог бы шифровать строкуна основе ключа, определенного пользователем. Другой мог бы использовать защиту спомощью системы паролей. У третьего механизм действия шифра мог бы опираться напобитовую обработку, а у четвертого — на простую перестановку кода (реализацияперестановочного шифра).