Главная страница

программирование. Руководство su P# a n Reference в herbert schildt полное руководство с 0 герберт шилдт


Скачать 3.32 Mb.
НазваниеРуководство su P# a n Reference в herbert schildt полное руководство с 0 герберт шилдт
Анкорпрограммирование
Дата25.01.2022
Размер3.32 Mb.
Формат файлаrtf
Имя файлаc-40-polnoe-rukovodstvo-2011.rtf
ТипРуководство
#341448
страница32 из 97
1   ...   28   29   30   31   32   33   34   35   ...   97

get; // возвратить следующее по порядку число set; // установить следующее число

}

}

// Реализовать интерфейс ISeries, class ByTwos : ISeries { int val;

public ByTwos()    {

val = 0;

}

// Получить или установить значение, public int Next { get {

val += 2; return val;

}

set {

val = value;

}

}

}

// Продемонстрировать применение интерфейсного свойства, class SeriesDemo3 { static void Main() {

ByTwos ob = new ByTwos();

// Получить доступ к последовательному ряду чисел с помощью свойства, for(int i=0; i < 5; i++)

Console.WriteLine("Следующее число равно " + ob.Next);

Console.WriteLine("ХпНачать с числа 21");

ob.Next = 21;

for (int i=0; i <5; i++)

Console.WriteLine("Следующее число равно " + ob.Next);

} .

}

При выполнении этого кода получается следующий результат.

Следующее число равно 2 Следующее число равно 4 Следующее число равно 6 Следующее число равно 8 Следующее число равно 10

Начать с числа 21 Следующее число равно 23 Следующее число равно 25

Следующее число равно 27 Следующее число равно 2 9 Следующее число равно 31

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

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

// Интерфейсный индексатор тип_элемента this[int индекс]{ get; set;

}

Как и прежде, в объявлении интерфейсных индексаторов, доступных только для чтения или только для записи, должен присутствовать единственный аксессор: get или set соответственно.

Ниже в качестве примера приведен еще один вариант реализации интерфейса ISeries, в котором добавлен индексатор только для чтения, возвращающий i‑u элемент числового ряда.

// Добавить индексатор в интерфейс, using System;

public interface ISeries {

// Интерфейсное свойство, int Next {

get; // возвратить следующее по порядку число set; // установить следующее число

}

// Интерфейсный индексатор, int this[int index] {

get; // возвратить указанное в ряду число

}

}

// Реализовать интерфейс ISeries, class ByTwos : ISeries { int val;

public ByTwos()    {

val = 0;

}

// Получить или установить значение с помощью свойства, public int Next { get {

val += 2; return val;

set {

val = value;

}

}

// Получить значение по индексу, public int this[int index] { get {

val = 0;

for(int i=0; i < index; i++) val += 2; return val;

}

}

}

// Продемонстрировать применение интерфейсного индексатора, class SeriesDemo4 { static void Main() {

ByTwos ob = new ByTwos();

// Получить доступ к последовательному ряду чисел с помощью свойства, for (int i=0; i < 5; i++)

Console.WriteLine("Следующее число равно " + ob.Next);

Console.WriteLine("ХпНачать с числа 21");

ob.Next = 21;

for (int i=0; i < 5; i++)

Console.WriteLine("Следующее число равно " + ob.Next);

Console.WriteLine("ХпСбросить в 0"); ob.Next = 0;

// Получить доступ к последовательному ряду чисел с помощью индексатора for (int i=0; i < 5; i++)

Console.WriteLine("Следующее число равно " + ob[i]);

}

}

Вот к какому результату приводит выполнение этого кода.

Следующее число равно 2 Следующее число равно 4 Следующее число равно 6 Следующее число равно 8 Следующее число равно 10

Начать с числа 21 Следующее число равно 23 Следующее число равно 25 Следующее число равно 27 Следующее число равно 2 9 Следующее число равно 31

Сбросить в О Следующее число равно О Следующее число равно 2 Следующее число равно 4 Следующее число равно 6 Следующёе число равно 8

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

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

// Пример наследования интерфейсов, using System;

public interface IA { void Methl(); void Meth2() ;

}

// В базовый интерфейс включены методы Methl() и Meth2(),

// а в производный интерфейс добавлен еще один метод – Meth3(). public interface IB : IA { void Meth3();

}

// В этом классе должны быть реализованы все методы интерфейсов IA и IB. class MyClass : IB { public void Methl()    {

Console.WriteLine("Реализовать метод Methl().");

}

public void Meth2() {

Console.WriteLine("Реализовать метод Meth2().");

}

public void Meth3() {

Console.WriteLine("Реализовать метод Meth3().");

}

}

class IFExtend {

static void Main() {

MyClass ob = new MyClass();

ob.Methl() ; ob.Meth2 (); ob.M^th3();

Ради интереса попробуйте удалить реализацию метода Methl () из класса MyClass. Это приведет к ошибке во время компиляции. Как пояснялось ранее, в любом классе, реализующем интерфейс, должны быть реализованы все методы, определенные в этом интерфейсе, в том числе и те, что наследуются из другцх интерфейсов.

Сокрытие имен при наследовании интерфейсов

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

Явные реализации

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

interface IMylF { int MyMeth(int x) ;

}

то следующая его реализация считается вполне допустимой:

class MyClass : IMylF { int IMylF.MyMeth(int x) { return x / 3;

}

}

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

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

В приведенном ниже примере программы демонстрируется интерфейс IEven, в котором объявляются два метода: IsEven ( ) и IsOdd () . В первом из них определяется четность числа, а во втором его нечетность. Интерфейс IEven затем реализуется в классе MyClass. При этом метод 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 о = this; // Интерфейсная ссылка на вызывающий объект, return !о.IsOdd(х);

}

}

class Demo {

static void Main() {

MyClass ob = new MyClass(); bool result;

result = ob.IsEven (4);

if(result) Console.WriteLine("4 – четное число.");

// result = ob.IsOdd(4); // Ошибка, член IsOdd интерфейса IEven недоступен

// Но следующий код написан верно, поскольку в нем сначала создается // интерфейсная ссылка типа IEven на объект класса MyClass, а затем по // этой ссылке вызывается метод IsOdd().

IEven iRef = (IEven) ob; result = iRef.IsOdd(3);

if(result) Console.WriteLine("3 – нечетное число.");

}

}

В приведенном выше примере метод IsOdd () реализуется явно, а значит, он недоступен как открытый член класса MyClass. Напротив, он доступен только по интерфейсной ссылке. Именно поэтому он вызывается посредством переменной о ссылочного типа IEven в реализации метода IsEven ().

Ниже приведен пример программы, в которой реализуются два интерфейса, причем в обоих интерфейсах объявляется метод Meth (). Благодаря явной реализации исключается неоднозначность, характерная для подобной ситуации.

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

}

}

class FQIFNames {

static void Main() {

MyClass ob = new MyClassO;

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 (). А поскольку явно реализованный метод может вызываться только по интерфейсной

ссылке, то в классе MyClass создаются две такие ссылки: одна – для интерфейса IMyIF_A, а другая – для интерфейса IMyIF_B. Именно по этим ссылкам происходит обращение к объектам данного класса с целью вызвать методы соответствующих интерфейсов, благодаря чему и устраняется неоднозначность.

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

Одна из самых больших трудностей программирования на C# состоит в правильном выборе между интерфейсом и абстрактным классом в тех случаях, когда требуется описать функциональные возможности, но не реализацию. В подобных случаях рекомендуется придерживаться следующего общего правила: если какое‑то понятие можно описать с точки зрения функционального назначения, не уточняя конкретные детали реализации, то следует использовать интерфейс. А если требуются некоторые детали реализации, то данное понятие следует представить абстрактным классом.

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

Для среды .NET Framework определено немало стандартных интерфейсов, которыми можно пользоваться в программах на С#. Так, в интерфейсе System. IComparable определен метод CompareTo () , применяемый для сравнения объектов, когда требуется соблюдать отношение порядка. Стандартные интерфейсы являются также важной частью классов коллекций, предоставляющих различные средства, в том числе стеки и очереди, для хранения целых групп объектов. Так, в интерфейсе System. Collections . ICollection определяются функции для всей коллекции, а в интерфейсе System.Collections . IEnumerator – способ последовательного обращения к элементам коллекции. Эти и многие другие интерфейсы подробнее рассматриваются в части II данной книги.

Структуры

Как вам должно быть уже известно, классы относятся к ссылочным типам данных. Это означает, что объекты конкретного класса доступны по ссылке, в отличие от значений простых типов, доступных непосредственно. Но иногда прямой доступ к рбъектам как к значениям простых типов оказывается полезно иметь, например, ради повышения эффективности программы. Ведь каждый доступ к объектам (даже самым мелким) по ссылке связан с дополнительными издержками на расход вычислительных ресурсов и оперативной памяти. Для разрешения подобных затруднений в C# предусмотрена структура, которая подобна классу, но относится к типу значения, а не к ссылочному типу данных.

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

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

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

}

где имя обозначает конкретное имя структуры.

Одни структуры не могут наследовать другие структуры и классы или служить в качестве базовых для других структур и классов. (Разумеется, структуры, как и все остальные типы данных в С#, наследуют класс obj ect.) Тем не менее в структуре можно реализовать один или несколько интерфейсов, которые указываются после имени структуры списком через запятую. Как и у классов, у каждой структуры имеются свои члены: методы, поля, индексаторы, свойства, операторные методы и события. В структурах допускается также определять конструкторы, но не деструкторы. В то же время для структуры нельзя определить конструктор, используемый по умолчанию (т.е. конструктор без параметров). Дело в том, что конструктор, вызываемый по умолчанию, определяется для всех структур автоматически и не подлежит изменению. Такой конструктор инициализирует поля структуры значениями, задаваемыми по умолчанию. А поскольку структуры не поддерживают наследование, то их члены нельзя указывать как 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, class StructDemo { static void Main() {

Book bookl = new Book("Герберт Шилдт",

"Полный справочник ho C# 4.0",

2010); // вызов явно заданного конструктора Book book2 = new Book(); // вызов конструктора по умолчанию Book ЬоокЗ; // конструктор не вызывается

Console.WriteLine(bookl.Author + ", " +

bookl.Title + ",    (c)    "    +    bookl.Copyright);

Console.WriteLine();

if (book2.Title == null)

Console.WriteLine("Член book2.Title пуст.");

// А теперь ввести информацию в структуру book2. book2.Title = "О дивный новый мир"; book2.Author = "Олдос Хаксли"; book2.Copyright = 1932;

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

Console.WriteLine(book2.Author + ", " +

book2.Title + ",    (c)    "    + book2.Copyright);

Console.WriteLine() ;

// Console.WriteLine(ЬоокЗ.Title); // неверно, этот член структуры

// нужно сначала инициализировать

ЬоокЗ.Title = "Красный шторм";

Console.WriteLine(ЬоокЗ.Title); // теперь верно

}

}

При выполнении этой программы получается следующий результат.

Герберт Шилдт, Полный справочник по C# 4.0,    (с) 2010

Член book2.Title пуст.

Структура Ьоок2 теперь содержит:

Олдос Хаксли, О дивный новый мир, (с) 1932

Красный шторм

Как демонстрирует приведенный выше пример программы, структура может быть инициализирована с помощью оператора new для вызова конструктора или же путем простого объявления объекта. Так, если используется оператор new, то поля структуры инициализируются конструктором, вызываемым по умолчанию (в этом случае во всех полях устанавливается задаваемое по умолчанию значение), или же конструктором, определяемым пользователем. А если оператор new не используется, как это имеет место для структуры ЬоокЗ, то объект структуры не инициализируется, а его поля должны быть установлены вручную перед тем, как пользоваться данным объектом.

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

// Скопировать структуру.

using System;

// Определить структуру, struct MyStruct { public int x;

11 Продемонстрировать присваивание структуры, class StructAssignment { static void Main() {

MyStruct a;

MyStruct b;

a.x    = 10;

b.x    = 20;

Console.WriteLine("a.x {0}, b.x {1}", a.x, b.x);

a = b;

b.x = 30;

Console.WriteLine("a.x {0}, b.x {1}", a.x, b.x);

}

}

Вот к какому результату приводит выполнение этой программы.

а.х 10, b.x 20 а.х 20, b.x 30

Как показывает приведенный выше результат, после присваивания

а = Ь;

переменные структуры а и b по‑прежнему остаются совершенно обособленными, т.е. переменная а не указывает на переменную b и никак не связана с ней, помимо того, что она содержит копию значения переменной Ь. Ситуация была бы совсем иной, если бы переменные а и b были ссылочного типа, указывая на объекты определенного класса. В качестве примера ниже приведен вариант предыдущей программы, где демонстрируется присваивание переменных ссылки на объекты определенного класса.

// Использовать ссылки на объекты определенного класса, using System;

// Создать класс, class MyClass { public int x;

}

// Показать присваивание разных объектов данного класса, class ClassAssignment { static void Main() {

MyClass a = new MyClass();

MyClass b = new MyClass();

a.x    = 10;

b.x    = 20;

Console.WriteLine("a.x {0}, b.x {1}", a.x, b.x);

a = b;

b.x = 30;

Console.WriteLine("а.х {0}, b.x {1}", а.х, Ь.х);

}

}

Выполнение этой программы приводит к следующему результату.

а.х 10, Ь.х 20 а.х 30, Ь.х 30

Как видите, после того как переменная b будет присвоена переменной а, обе переменные станут указывать на один и тот же объект, т.е. на тот объект, на который первоначально указывала переменная Ь.

О назначении структур

В связи с изложенным выше возникает резонный вопрос: зачем в C# включена структура, если она обладает более скромными возможностями, чем класс? Ответ на этот вопрос заключается в повышении эффективности и производительности программ. Структуры относятся к типам значений, и поэтому ими можно оперировать непосредственно, а не по ссылке. Следовательно, для работы со структурой вообще не требуется переменная ссылочного типа, а это означает в ряде случаев существенную экономию оперативной памяти. Более того, работа со структурой не приводит к ухудшению производительности, столь характерному для обращения к объекту класса. Ведь доступ к структуре осуществляется непосредственно, а к объектам – по ссылке, поскольку классы относятся к данным ссылочного типа. Косвенный характер доступа к объектам подразумевает дополнительные издержки вычислительных ресурсов на каждый такой доступ, тогда как обращение к структурам не влечет за собой подобные издержки. И вообще, если нужно просто сохранить группу связанных вместе данных, не требующих наследования и обращения по ссылке, то с точки зрения производительности для них лучше выбрать структуру.

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

// Структуры удобны для группирования небольших объемов данных, using System;

// Определить структуру пакета, struct PacketHeader {

public uint PackNum; // номер пакета public ushort PackLen; // длина пакета }

// Использовать структуру PacketHeader для создания записи транзакции    (

// в сфере электронной коммерции, class Transaction {

static uint transacNum = 0;

PacketHeader ph; // ввести структуру PacketHeader в класс Transaction string accountNum; double amount;

public Transaction(string acc, double val) {

// создать заголовок пакета

ph.PackNum = transacNum++;

ph.PackLen =512; // произвольная длина

accountNum = acc; amount = val;

}

// Сымитировать транзакцию, public void sendTransaction()    {

Console.WriteLine("Пакет #: " + ph.PackNum +

", Длина: " + ph.PackLen +

",\n Счет #: " + accountNum +

", Сумма: {0:C}\n", amount);

}

}

// Продемонстрировать применение структуры в виде пакета транзакции, class PacketDemo { static void Main() {

Transaction t = new Transaction("31243", ‑100.12);

Transaction t2 = new Transaction("AB4655", 345.25);

Transaction t3 = new Transaction ("8475‑09", 9800.00);

t.sendTransaction (); t2.sendTransaction (); t3.sendTransaction ();

}

}

Вот к какому результату может привести выполнение этого кода.

Счет #: 8475‑09, Сумма: $9,800.00

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

Любопытно, что в C++ также имеются структуры и используется ключевое слово struct. Но эти структуры отличаются от тех, что имеются в С#. Так, в C++ структура относится к типу класса, а значит, структура и класс в этом языке практически равноценны и отличаются друг от друга лишь доступом по умолчанию к их членам, которые оказываются закрытыми для класса и открытыми для структуры. А в C# структура относится к типу значения, тогда как класс – к ссылочному типу.

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

Перечисление представляет собой множество именованных целочисленных констант. Перечислимый тип данных объявляется с помощью ключевого слова enum. Ниже приведена общая форма объявления перечисления:

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

где имя – это имя типа перечисления, а список_перечисления – список идентификаторов, разделяемый запятыми.

В приведенном ниже примере объявляется перечисление Apple различных сортов яблок.

enum Apple { Jonathan, GoldenDel, RedDel, Winesap,

Cortland, McIntosh };

Следует особо подчеркнуть, что каждая символически обозначаемая константа в перечислении имеет целое значение. Тем не менее неявные преобразования перечислимого типа во встроенные целочисленные типы и обратно в C# не определены, а значит, в подобных случаях требуется явное приведение типов. Кроме того, приведение типов требуется при преобразовании двух перечислимых типов. Но поскольку перечисления обозначают целые значения, то их можно, например, использовать для управления оператором выбора switch или же оператором цикла for.

Для каждой последующей символически обозначаемой константы в перечислении задается целое значение, которое на единицу больше, чем у предыдущей константы. По умолчанию значение первой символически обозначаемой константы в перечислении равно нулю. Следовательно, в приведенном выше примере перечисления Apple константа Jonathan равна нулю, константа GoldenDel – 1, константа RedDel – 2 и т.д.

Доступ к членам перечисления осуществляется по имени их типа, после которого следует оператор‑точка. Например, при выполнении фрагмента кода

Console.WriteLine(Apple.RedDel + " имеет значение " +

(int)Apple.RedDel) ;

выводится следующий результат.

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

Как показывает результат выполнения приведенного выше фрагмента кода, для вывода перечислимого значения используется его имя. Но для получения этого значения требуется предварительно привести его к типу int.

Ниже приведен пример программы, демонстрирующий применение перечисления Apple.

11 Продемонстрировать применение перечисления.

using System;

class EnumDemo {

enum Apple { Jonathan, GoldenDel, RedDel, Winesap,

Cortland, McIntosh };

static void Main() { string[] color = {

"красный",

"желтый",

"красный",

"красный",

"красный",

"красновато‑зеленый"

};

Apple i; 11 объявить переменную перечислимого типа

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

// обращения к членам перечисления.

for(i = Apple.Jonathan; i <= Apple.McIntosh; i++)

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

Console.WriteLine ();

// Использовать перечисление для индексирования массива. for(i = Apple.Jonathan; i <= Apple.McIntosh; i++)

Console.WriteLine("Цвет сорта " + i + " – " + color[ (int)i]);

}

}

Ниже приведен результат выполнения этой программы.

Jonathan имеет значение О GoldenDel имеет значение 1 RedDel имеет значение 2 Winsap имеет‑ значение 3 Cortland имеет значение 4 McIntosh имеет значение 5

Цвет сорта Jonathan ‑ красный

Цвет сорта GoldenDel ‑ желтый

Цвет сорта RedDel ‑ красный

Цвет сорта Winsap ‑ красный

Цвет сорта Cortland ‑ красный

Цвет сорта McIntosh ‑ красновато‑зеленый

Обратите внимание на то, как переменная типа Apple управляёт циклами for. Значения символически обозначаемых констант в перечислении Apple начинаются с нуля, поэтому их можно использовать для индексирования массива, чтобы получить цвет каждого сорта яблок. Обратите также внимание на необходимость производить приведение типов, когда перечислимое значение используется для индексирования массива. Как упоминалось выше, в C# не предусмотрены неявные преобразования перечислимых типов в целочисленные и обратно, поэтому для этой цели требуется явное приведение типов.

И еще одно замечание: все перечисления неявно наследуют от класса System. Enum, который наследует от класса System. ValueType, а тот, в свою очередь, – от класса object.
Инициализация перечисления

Значение одной или нескольких символически обозначаемых констант в перечислении можно задать с помощью инициализатора. Для этого достаточно указать после символического обозначения отдельной константы знак равенства и целое значение. Каждой последующей константе присваивается значение, которое на единицу больше значения предыдущей инициализированной константы. Например, в приведенном ниже фрагменте кода константе RedDel присваивается значение 10.

enum Apple { Jonathan, GoldenDel, RedDel = 10, Winesap,

Cortland, McIntosh };

В итоге все константы в перечислении принимают приведенные ниже значения.
Jonathan
0
GoldenDel
1
RedDel
10
Winesap
11
Cortland
12
McIntosh
13
Указание базового типа перечисления

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

enum Apple : byte { Jonathan, GoldenDel, RedDel,

Winesap, Cortland, McIntosh };

Теперь константа Apple . Winesap, например, имеет количественное значение типа byte.

Применение перечислений

На первый взгляд перечисления могут показаться любопытным, но не очень нужным элементом С#, но на самом деле это не так. Перечисления очень полезны, когда в программе требуется одна или несколько специальных символически обозначаемых констант. Допустим, что требуется написать программу для управления лентой конвейера на фабрике. Для этой цели можно создать метод Conveyor (), принимающий в качестве параметров следующие команды: "старт", "стоп", "вперед" и "назад". Вместо того чтобы передавать методу Conveyor () целые значения, например, 1 – в качестве команды "старт", 2 – в качестве команды "стоп" и так далее, что чревато ошибками, можно создать перечисление, чтобы присвоить этим значениям содержательные символические обозначения. Ниже приведен пример применения такого подхода.

// Сымитировать управление лентой конвейера, using System;

class ConveyorControl {

// Перечислить команды конвейера.

public enum Action { Start, Stop, Forward, Reverse };

public void Conveyor(Action com) { switch(com) {

case Action.Start:

Console.WriteLine("Запустить конвейер."); break; case Action.Stop:

Console.WriteLine("Остановить конвейер."); break; case Action.Forward:

*    Console.WriteLine("Переместить конвейер вперед.");

break; case Action.Reverse:

Console.WriteLine ("Переместить конвейер назад."); break;

}

}

}

class ConveyorDemo { static void Main() {

ConveyorControl с = new ConveyorControl();

с.Conveyor(ConveyorControl.Action.Start);

с.Conveyor(ConveyorControl.Action.Forward);

с.Conveyor(ConveyorControl.Action.Reverse);

с.Conveyor(ConveyorControl.Action.Stop);

}

}

Вот к какому результату приводит выполнение этого кода.

Запустить конвейер.

Переместить конвейер вперед.

Переместить конвейер назад.

Остановить конвейер.

Метод Conveyor () принимает аргумент типа Action, и поэтому ему могут быть переданы только значения, определяемые в перечислении Action. Например, ниже приведена попытка передать методу Conveyor () значение 22.

с.Conveyor(22); // Ошибка!

Эта строка кода не будет скомпилирована, поскольку отсутствует предварительно заданное преобразование типа int в перечислимый тип Action. Именно это и препятствует передаче неправильных команд методу Conveyor () . Конечно, такое

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

В приведенном выше примере обращает на себя внимание еще одно интересное обстоятельство: перечислимый тип используется для управления оператором switch. Как упоминалось выше, перечисления относятся к целочисленным типам данных, и поэтому их вполне допустимо использовать в операторе switch.

1   ...   28   29   30   31   32   33   34   35   ...   97


написать администратору сайта