Реализация интерфейсов.
Программирование.
Базовый курс с#
Когда один класс реализует несколько интерфейсов, возможны совпадения имен членов из разных интерфейсов. Для разрешения такой конфликтной ситуации в классе, реализующем интерфейс, используется квалифицированное имя члена интерфейса. Здесь существует ограничение — такая реализация члена называется явной и она не может быть открытой, т. е. для нее нельзя использовать модификатор public. Подробнее… Читать ещё >
Реализация интерфейсов. Программирование. Базовый курс с# (реферат, курсовая, диплом, контрольная)
Прежде чем рассмотреть реализацию интерфейса, уточним отличия интерфейсов от абстрактных классов. Наиболее важное отличие состоит в том, что при наследовании классов у каждого класса может быть только один базовый класс — множественное наследование классов в языке C# невозможно. При построении класса на основе интерфейсов их может быть любое количество. Другими словами, класс может реализовать сколько угодно интерфейсов, но при этом может иметь только один базовый класс. В интерфейсе не определяются поля и не может быть конструкторов. В интерфейсе нельзя объявлять статические члены.
Как и для абстрактных классов, невозможно определить объект с помощью интерфейса.
Чтобы интерфейсом можно было пользоваться, он должен быть реализован классом или структурой. В этой главе рассмотрим реализацию интерфейсов с помощью классов. Синтаксически отношение реализации интерфейса обозначается включением имени интерфейса в спецификацию базы класса. Напомним формат объявления класса со спецификацией базы (для простоты не указаны модификаторы класса):
class имя_класса спецификация_базы
{
объявления_членов_класса
}
Спецификация базы класса в этом случае имеет вид:
:имя_базового_классаор1, opt список_интерфейсоворt
Имя базового класса (и следующая за ним запятая) могут отсутствовать. В списке интерфейсов через запятые помещаются имена тех интерфейсов, которые должен реализовать класс. В спецификацию базы класса может входить только один базовый класс и произвольное число имен интерфейсов. При этом должно выполняться обязательное условие — класс должен реализовать все члены всех интерфейсов, включенных в спецификацию базы. Частный случай — класс, реализующий только один интерфейс:
class имя_класса: имя_интерфейса
{
объявления_членов_класса
У
Реализацией члена интерфейса является его полное определение в реализующем классе, снабженное модификатором доступа public.
Сигнатуры и типы возвращаемых значений методов, свойств и индексаторов в реализациях и в интерфейсе должны полностью совпадать.
Покажем на примерах, как при построении класса на основе интерфейса класс реализует его методы, свойства и индексаторы. Реализацию событий мы рассмотрим позднее в главе, посвященной событиям.
В следующей программе (1401.cs) интерфейс IPublication реализуется классом Item — «заметка в газете».
// 1401.cs — Интерфейс и его реализация using System; interface IPublication { // интерфейс публикаций
void write (); // готовить публикацию
void readQ; // читать публикацию
string Title { set; get; } // название публикации
У
class Item: IPublication { // заметка в газете
string newspaper = «Известия»; // название газеты string headline; // заголовок статьи
public string Title { // реализация свойства set { headline = value; } get { return headline; }.
>
public void write ().
{// реализация метода
/* операторы, имитирующие подготовку статьи */
У
public void read ().
{ // реализация метода /* операторы, имитирующие чтение статьи */
Console.WriteLine (@" npo4en в газете «» {0}" «статью „“ {1}» «.», newspaper. Title);
}
}
class Program.
{
static void Main ().
{
Console.WriteLine («Publication!»); Item article = new Item (); article. Title = «О кооперации»; article. read ();
} }
Результат выполнения программы:
Publication!
Прочел в газете «Известия» статью «О кооперации» .
Класс Item кроме реализаций членов интерфейса включает объявления закрытых полей: newspaper (название газеты) и headline (заголовок статьи). Для простоты в класс не включен конструктор и только условно обозначены операторы методов write () и read (). Реализация свойства Title приведена полностью — аксессоры get и set позволяют получить и задать название статьи, представляемой конкретным объектом класса Item. В методе Main () нет ничего незнакомого читателю — определен объект класса Item и ссылка article на него. С помощью обращения arcticle. Title задано название статьи.
В UML для изображения интерфейсов применяется та же символика, что и для классов (см. рис. 14.1). Конечно, имеется отличие — в верхней части, после имени интерфейса IPublication помещается служебное слово Interface. Тот факт, что класс реализует интерфейс, отображается с помощью специального символа и указанием имени интерфейса над прямоугольником, представляющим класс.
Рис. 14.1. Диаграмма классов с интерфейсом для программы 1401.cs
В качестве второго примера рассмотрим интерфейс, реализация членов которого позволит получать целочисленные значения членов числовых рядов [9]:
interface ISeries.
{
void setBeginQ; // восстановить начальное состояние int GetNext {get;} // вернуть очередной член ряда int this[int k] {get;} // вернуть к-й член ряда
У
Поясним роли прототипов, входящих в этот интерфейс. В приведенном объявлении: setBegin () — прототип метода; GetNext — имя свойства, позволяющего получить значение очередного члена ряда и настроить объект на следующий член. Индексатор в этом примере позволяет получить значение не очередного, а произвольного k-го члена ряда и перевести объект в состояние, при котором свойство GetNext вернет значение (к+1)-го члена.
Те функциональные возможности, которые приписаны членам интерфейса ISeries, при реализации в конкретных классах могут быть изменены. Но в этом случае нарушается общий принцип применения интерфейсов. Поэтому в примерах конкретных классов будем придерживаться описанных соглашений о ролях членов интерфейса ISeries.
Интерфейс ISeries можно реализовать для представления разных числовых рядов. Вот, например, регулярные числовые последовательности, которые можно представить классами, реализующими интерфейс ISeries:
- 1, 1, 2, 3, 5, 8, 13, … — ряд Фибоначчи: ai=ai_2+ai_1, где i > 2; аг= 1, а2= 1;
- 1.3.4.7.11.18.29. .—ряд Лукаса: at= а^+а^, где! >2; ах =1, а2= 3;
- 1.2.5.12.29.70. .—ряд Пелла: а* =а,_2 + 2 * а^_х, где i > 2; а1= 1, а2= 2. В следующей программе на основе интерфейса ISeries определен
класс, представляющий ряд Пелла (см. диаграмму на рис 14.2).
// 1402.cs — Интерфейс и его реализация using System;
interface ISeries // интерфейс числовых рядов
{
void setBegin (); // задать начальное состояние
int GetNext { get; } 11 вернуть очередной член ряда
int this[int k] { get; } // вернуть к-й член ряда
У
class Pell: ISeries 11 Ряд Пелла: 1, 2, 5, 12} …
{
int old, last; // два предыдущих члена ряда
public Pell () { setBeginQ; } 11 конструктор public void setBeginQ // задать начальное состояние { old = 1; last = 0; }.
public int GetNext // вернуть следующий после Last
{
get.
{
int now = old + 2 * last; old = last; last = now; return now;
} }
public int this[int к] // вернуть к-й член ряда.
{
get.
{
int now = 0; setBegin ();
if (k <= 0) return -1; for (int j = 0; j < k; j++) now = GetNext; return now;
} }
public void seriesPrint (int n).
{ // вывести n членов, начиная со следующего for (int i = 0; i < n; i++).
Console.Write (GetNext + ««);
Console.WriteLine ();
} }
class Program.
{
static void Main ().
{
Pell pell = new PellQ; pell. seriesPrint (9);
Console.WriteLine («pell[3] = «+ pell[3]); pell. seriesPrint (4); pell. seriesPrint (3);
} }
Результат выполнения программы:
1 2 5 12 29 70 169 408 985 pell[3] = 5 12 29 70 169 408 985 2378.
Кроме реализации членов интерфейса ISeries в классе Pell объявлен метод seriesPrint (). Он выводит значения нескольких членов ряда, следующих за текущим. Количество членов определяет аргумент метода seriesPrint (). После выполнения метода состояние ряда изменится — текущим членом станет последний выведенный член ряда. Обратите внимание, что при реализации индексатора нумерация членов ряда начинается с 1 (а не с нуля).
Рис. 14.2. Диаграмма классов с интерфейсом для программы 1402.cs.
Обратите внимание, что в реализации интерфейса заголовок метода должен полностью совпадать с заголовком прототипа этого метода в интерфейсе (за исключением появления модификатора доступа). Соответствие должно быть и при реализации других членов интерфейса.
В наших примерах интерфейсы и реализующие их классы размещены в одном файле. Если интерфейс объявлен не в том файле, где выполняется его реализация, то объявление интерфейса необходимо снабдить соответствующим модификатором доступа.
В приведенных выше программах классов Item и Pell была использована неявная реализация членов интерфейсов. Термин «неявная» употребляется для обозначения того, что в объявлении класса, реализующего интерфейс, не применяются квалифицированные имена членов интерфейса и их реализации снабжаются обязательным модификатором public.
Когда один класс реализует несколько интерфейсов, возможны совпадения имен членов из разных интерфейсов. Для разрешения такой конфликтной ситуации в классе, реализующем интерфейс, используется квалифицированное имя члена интерфейса. Здесь существует ограничение — такая реализация члена называется явной и она не может быть открытой, т. е. для нее нельзя использовать модификатор public. Подробнее об особенностях явной реализации интерфейсов можно узнать, например, из работ [1, 5].