Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Интерфейсы и структурные типы

Поиск

Синтаксис интерфейса

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

Каждый класс может определять элементы интерфейса по-своему. Так достигается полиморфизм: объекты разных классов по-разному реагируют на вызовы одного и того же метода.

Синтаксис интерфейса аналогичен синтаксису класса:

[ атрибуты ] [ спецификаторы ] interface имя_интерфейса [: предки ]

телоинтерфейса [; ]

Для интерфейса могут быть указаны спецификаторы, new, publ i с, protected, internal и private. Спецификатор new применяется для вложенных интерфейсов и имеет такой же смысл, как и соответствующий модификатор метода класса. Остальные спецификаторы управляют видимостью интерфейса. В разных контекстах определения интерфейса допускаются разные спецификаторы. По умолчанию интерфейс доступен только из сборки, в которой он описан (internal).

Интерфейс может наследовать свойства нескольких интерфейсов, в этом случае предки перечисляются через запятую. Тело интерфейса составляют абстрактные методы, шаблоны свойств и индексаторов, а также события.

В интерфейсе имеет смысл задавать заголовки тех методов и свойств, которые будут по-разному реализованы различными классами разных иерархий.

Отличия интерфейса от абстрактного класса:

• элементы интерфейса по умолчанию имеют спецификатор доступа publ i с и не могут иметь спецификаторов, заданных явным образом;

• интерфейс не может содержать полей и обычных методов — все элементы интерфейса должны быть абстрактными;

• класс, в списке предков которого задается интерфейс, должен определять все его элементы, в то время как потомок абстрактного класса может не переопределять часть абстрактных методов предка (в этом случае производный класс также будет абстрактным);

• класс может иметь в списке предков несколько интерфейсов, при этом он должен определять все их методы.

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

В списке предков класса сначала указывается его базовый класс, если он есть, а затем через запятую — интерфейсы, которые реализует этот класс. Таким образом, в С# поддерживается одиночное наследование для классов и множественное — для интерфейсов. Это позволяет придать производному классу свойства нескольких базовых интерфейсов, реализуя их по своему усмотрению.

Таким образом, при явном задании имени реализуемого интерфейса соответствующий метод не входит в интерфейс класса. Это позволяет упростить его в том случае, если какие-то элементы интерфейса не требуются конечному пользователю класса.

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

Оба интерфейса содержат метод Draw с одной и той же сигнатурой. Различать их помогает явное указание имени интерфейса. Обращаться к этим методам можно,используя операцию приведения типа.

К методу Draw, описанному таким образом, можно обращаться любым способом: через объект класса Monster, через интерфейс IAction или ITest.

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

Работа с объектам и через интерфейсы. Операции is и as.

При работе с объектом через объект типа интерфейса бывает необходимо убедиться, что объект поддерживает данный интерфейс. Проверка выполняется с помощью бинарной операции is. Эта операция определяет, совместим ли текущий тип объекта, находящегося слева от ключевого слова is, с типом, заданным справа. Результат операции равен true, если объект можно преобразовать к заданному типу, и false в противном случае. Операция обычно используется в следующем контексте:

1f (объект is тип)

{

// выполнит ь преобразование "объекта " к " т и п у "

/'/ выполнит ь действия с преобразованным объектом

Допустим, мы оформили какие-то действия с объектами в виде метода с параметром типа object. Прежде чем использовать этот параметр внутри метода для обращения к методам, описанным в производных классах, требуется выполнить преобразование к производному классу. Для безопасного преобразования следует проверить, возможно ли оно.

static vo'd Act(object A)

{

if (A is IAction)

{

IAction Actor = (IAction) A;

Actor.Draw(); 1

}

В метод Act можно передавать любы е объекты, но на экран будут выведены только те, которые поддерживают интерфейс IAction. Недостатком использования операции is является то, что преобразование фактически выполняется дважды: при проверке и при собственно преобразовании. Более эффективной является другая операция — as. Она выполняет преобразование к заданному типу, а если это невозможно, формирует результат null, например:

static void Act (object A)

I

IAction Actor = A as IAction;

if (Actor!= null) Actor.Draw();

Обе рассмотренные операции применяются как к интерфейсам, так и к классам.

Интерфейсы и наследование

Интерфейс может не иметь или иметь сколько угодно интерфейсов-предков, в последнем случае он наследует все элементы всех своих базовых интерфейсов,начиная с самого верхнего уровня. Базовые интерфейсы должны быть доступны в не меньшей степени, чем их потомки. Например, нельзя использовать интерфейс, описанный со спецификатором private или internal, в качестве базового для открытого (publ ic) интерфейса.

Как и в обычной иерархии классов, базовые интерфейсы определяют общее поведение, а их потомки конкретизируют и дополняют его. В интерфейсе-потомке можно также указать элементы, переопределяющие унаследованные элементы с такой же сигнатурой. В этом случае перед элементом указывается ключевое слово new, как и в аналогичной ситуации в классах. С помощью этого слова соответствующий элемент базового интерфейса скрывается. Вот пример из документации С#.

Метод F из интерфейса IBase скрыт интерфейсом ILeft, несмотря на то что в цепочке IDerived — IRight — IBase он не переопределялся. Класс, реализующий интерфейс, должен определять все его элементы, в том числе унаследованные. Если при этом явно указывается имя интерфейса, оно должно ссылаться на тот интерфейс, в котором был описан соответствующий элемент. Естественно, интерфейс не может быть наследником самого себя.

Интерфейс, на собственные или унаследованные элементы которого имеется явная ссылка, должен быть указан в списке предков класса. Класс наследует все методы своего предка, в том числе те, которые реализовывали интерфейсы. Он может переопределить эти методы с помощью спецификатора new, но обращаться к ним можно будет только через объект класса. Если использовать для обращения ссылку на интерфейс, вызывается не переопределенная версия.

Однако если интерфейс реализуется с помощью виртуального метода класса, после его переопределения в потомке любо й вариант обращения (через класс или через интерфейс) приведет к одному и тому же результату.

Метод интерфейса, реализованный явным указанием имени, объявлять виртуальным запрещается. При необходимости переопределить в потомках его поведение пользуются следующим приемом: из этого метода вызывается другой, защищенный метод, который объявляется виртуальным. В приведенном далее примере метод А интерфейса IBase реализуется посредством защищенного виртуального метода А_, который можно переопределять в потомках класса Base.

Существует возможность повторно реализовать интерфейс, указав его имя в списке предков класса наряду с классом-предком, уже реализовавшим этот интерфейс. При этом реализация переопределенных методов базового класса во внимание не принимается.

Вообще при реализации интерфейса учитывается наличие «подходящих» методов в классе независимо от их происхождения. Это могут быть методы, описанные в текущем или базовом классе, реализующие интерфейс явным или неявным образом.

Стандартные интерфейсы.NET

В библиотеке классов. NET определено множество стандартных интерфейсов, задающих желаемое поведение объектов. Например, интерфейс IComparable задает метод сравнения объектов по принципу больше или меньше, что позволяет выполнять их сортировку. Реализация интерфейсов lEnumerable и enumerate дает возможность просматривать содержимое объекта с помощью конструкции f o r e a c h, а реализация интерфейса I C l o n e a b l e — клонировать объекты.

Стандартные интерфейсы поддерживаются многими стандартными классами библиотеки. Например, работа с массивами с помощью цикла f o r e a c h возможна именно потому, что тип A r r a y реализует интерфейсы IEnumerable и IEnumerator. Можно создавать и собственные классы, поддерживающие стандартные интерфейсы, что позволит использовать объекты этих классов стандартными способами.

Перечисления

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

enum Menu { Read, W r i t e, Append, E x i t }

enum Радуга { Красный, Оранжевый, Желтый,Зеленый, Синий, Фиолетовый }

Для каждой константы задается ее символическое имя. По умолчанию константам присваиваются последовательные значения типа i n t, начиная с 0, но можно задать и собственные значения, например:

enum Nums { tw o = 2, t h r e e, f o u r, t e n = 10, e l e v e n, f i f t y = t e n + 4 0 }:

Константам t h r e e и f o u r присваиваются значения 3 и 4, константе e l e v e n — 11.

Имена перечисляемых констант внутри каждого перечисления должны быть уникальными, а значения могут совпадать. Преимущество перечисления перед описанием именованных констант состоит в том, что связанные константы нагляднее; кроме того, компилятор выполняет проверку типов, а интегрированная среда разработки подсказывает возможные значения констант, выводя их список.

Синтаксис перечисления:

[ атрибуты ] [ спецификаторы ] enum имя_перечисления [: базовыйтип ]

тело_перечисления [; ]

Спецификаторы перечисления имеют такой же смысл, как и для класса, причем допускаются только спецификаторы new, p u b l i c, p r o t e c t e d, i n t e r n a l и p r i v a t e.

Базовый тип — это тип элементов, из которых построено перечисление. По умолчанию используется тип i n t, но можно задать тип и явным образом, выбрав его среди целочисленных типов (кроме c h a r), а именно: b y t e, s b y t e, s h o r t, u s h o r t, i n t, u i n t, l o n g и u l o n g. Необходимость в этом возникает, когда значения констант невозможно или неудобно представлять с помощью типа i n t.

 

Тело перечисления состоит из имен констант, каждой из которых может быть присвоено значение. Если значение не указано, оно вычисляется прибавлением единицы к значению предыдущей константы. Константы по умолчанию имеют спецификатор доступа p u b l i c.



Поделиться:


Последнее изменение этой страницы: 2016-08-15; просмотров: 472; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.133.109.251 (0.007 с.)