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

 

В интерфейсе можно определить и индексатор. Объявление индексатора в интерфейсе имеет следующий формат записи:

// Интерфейсный индексатор

тип_элемента this[int индекс]{

get;

set;

}

Индексаторы, предназначенные только для чтения или только для записи, содержат только get- или set-метод, соответственно.

6.4 Наследование интерфейсов

Один интерфейс может унаследовать "богатство" другого. Синтаксис этого механизма аналогичен синтаксису, используемому для наследования классов. Если класс реализует интерфейс, который наследует другой интерфейс, этот класс должен обеспечить способы реализации для всех членов, определенных внутри цепочки наследования интерфейсов. Рассмотрим такой пример:

/ / Один интерфейс может наследовать другой.

usingSystem;

public interface A {

void meth1 () ;

void meth2();

}

/ / Интерфейс В теперь включает методы methl() и meth2(),

/ / а также добавляет метод meth3().

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() { y

MyClass ob = new MyClass();

ob.meth1();

ob.meth2();

ob.meth3();

}}

 

6.5 Сокрытие имен с помощью наследования интерфейсов

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

 

 

6.6 Явная реализация членов интерфейса

 

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

interface IMyiF {

int myMeth(int x);

}

Вполне допустимо реализовать интерфейс IMyiF следующим образом:

class MyClass : IMyIF{

int IMyIF.myMeth(int x) {

return x / 3;

}}

При реализации метода myMeth() члена интерфейса IMyiF указывается его полное имя, включающее имя интерфейса.

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

 

6.7Выбор между интерфейсом и абстрактным классом

 

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

 


 

6.8 Структуры

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

Структуры объявляются с использованием ключевого слова struct и синтаксически подобны классам. Формат записи структуры таков:

struct имя : интерфейсы {

// объявления членов

}

Элемент имя означает имя структуры.

Структуры не могут наследовать другие структуры или классы. Структуры не могут использоваться в качестве базовых для других структур или классов. (Однако, подобно другим С#-типам, структуры наследуют класс object). Структура может реализовать один или несколько интерфейсов. Они указываются после имени структуры и отделяются запятыми. Как и у классов, членами структур могут быть методы, поля, индексаторы, свойства, операторные методы и события. Структуры могут также определять конструкторы, но не деструкторы. Однако для структуры нельзя определить конструктор по умолчанию (без параметров). Дело в том, что конструктор по умолчанию автоматически определяется для всех структур, и его изменить нельзя. Поскольку структуры не поддерживают наследования, члены структуры нельзя определять с использованием модификаторов abstract, virtual или protected.

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

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

using System;

// Определениеструктуры,

struct Book {

public string author;

public string title;

public int copyright;

public Book(string a, string t, int c) {

author = a;

title = t;

copyright = c;

}}

// Демонстрируем использование структуры Book,

classStructDemo {

public static void Main() {

Book book1 = new Book("Herb Schildt","C# A Beginner's Guide",2001); // Вызов явно заданного конструктора.

Bookbook2 = newBook(); // Вызов конструктора

// по умолчанию.

BookbоокЗ; // Создание объекта без вызова

// конструктора.

Console.WriteLine(book1.title + ", автор " +book1.author +

“, (с) " + book1. copyright );

Console.WriteLine();

if(book2.title == null)

Console.WriteLine("Член book2.title содержит null.");

// Теперь поместим в структуру book2 данные.

book2.title = "Brave New World";

book2.author = "Aldous Huxley";

book2.copyright = 1932;

Console.Write("Теперь структура book2 содержит:\n" ) ;

Console.WriteLine(book2.title + ", автор " +book2.author +

", (c) " + book2.copyright);

Console.WriteLine();

// Console.WriteLine(bоокЗ.title); // Ошибка: сначала

// необходима

// инициализация.

bоокЗ.title = "Red Storm Rising";

Console.WriteLine(bоокЗ.title); // Теперь все Ok!

}}

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

С# A Beginner's Guide, автор Herb Schildt, (с) 2001

Структура может быть создана либо с помощью оператора new, который вызывает соответствующий конструктор, либо простым объявлением объекта. При использовании оператора new поля структуры будут инициализированы, причем это сделает либо конструктор по умолчанию (он инициализирует все поля значениями по умолчанию), либо конструктор, определенный пользователем. Если оператор new не используется, как в случае объекта bоокЗ, созданный таким образом объект остается неинициализированным, и его поля должны быть установлены до начала использования.

При присваивании одной структуры другой создается копия этого объекта. Это — очень важное отличие struct-объекта от сlass-объекта. Как упоминалось выше, присваивая одной ссылке на класс другую, вы просто меняете объект, на который ссылается переменная, стоящая с левой стороны от оператора присваивания. А присваивая одной struct-переменной другую, вы создаете копию объекта, указанного с правой стороны от оператора присваивания.

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

 

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

Перечисление(enumeration) — это множество именованных целочисленных констант. Ключевое слово enum объявляет перечислимый тип. Формат записи перечисления таков:

enum имя {список_перечисления);

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

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

enum apple { Jonathan, GoldenDel, RedDel, Winsap,

Cortland, Mclntosh } ;

Каждый символ списка перечисления означает целоечисло, причем каждое следующее число (представленное идентификатором) на единицу больше предыдущего. Поскольку значение первого символа перечисления равнонулю, следовательно, идентификатор Jonathan имеет значение 0, GoldenDel — значение 1 и т.д.

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

К членам перечисления доступ осуществляется посредством имени типа и оператора "точка". Например, при выполнении инструкции

Console.WriteLine(apple.RedDel + " имеетзначение " +(int)apple.RedDel);

будет отображено следующее.

RedDel имеет значение 2

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

Рассмотрим программу, которая демонстрирует использование перечисления apple.

// Демонстрация использования перечисления.

usingSystem;

classEnumDemo {

enum apple { Jonathan, GoldenDel, RedDel, Winsap,

Cortland, Mclntosh };

public static void Main() {

string[] color = {"красный", "желтый", "красный", "красный", "красный", "красно-зеленый"};

apple i; // Объявляем переменную перечислимого типа.

// Используем переменную i для обхода всех

// членовперечисления.

for(i = apple.Jonathan; i <= apple.Mclntosh; i++)

Console.WriteLine(i + " имеетзначение " + (int)i);

Console.WriteLine() ;

// Используем перечисление для индексации массива.

for(i = apple.Jonathan; i <= apple.Mclntosh; i++)

Console.WriteLine("Цветсорта " + i + " - " +

color[(int)i]);

}}

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