Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Курс_ред1 С#.doc
Скачиваний:
2
Добавлен:
01.03.2025
Размер:
1.8 Mб
Скачать

Перечислимый тип

Все типы, которые рассматривались до сих пор (за исключением типа string), имеют четко определенное множество допустимых значений. Это множество может быть настолько большим (как, например, у типа double), что будет близко к бесконечности, однако все равно это фиксированное множество.

В качестве простейшего примера можно привести тип bool, который может принимать только одно из двух значений: true или false.

Существует большое количество ситуаций, когда требуется переменная, принимающая значение из фиксированного множества. Например, может возникнуть необходимость в использовании переменной типа orientation (ориентация), которая принимает одно из значений: north (север), south (юг), east (восток) или west (запад).

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

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

Определение перечислимых типов

Перечислимый тип описывается с помощью ключевого слова enum следующим образом:

enum имяТипа

{

значение1,

значение2,

значение3,

. . .

значениеN

}

Затем объявляются переменные этого типа:

имяТипа имяПеременной;

Им присваиваются конкретные значения:

имяПеременной = имяТипа.значение;

Перечислимый тип обладает базовым типом (underlying type), который используется для хранения. Любое из значений, которые этот тип может принимать, будет храниться в памяти как значение базового типа (по умолчанию это тип int). Однако существует возможность задать в качестве базового другой тип, добавив к описанию типа имя его базового типа:

enum имяТипа: базовыйТип

{

значение1,

. . .

значениеN

}

Перечислимые типы могут использовать в качестве базовых следующие типы: byte, sbyte, short, ushort, int, uint, long и ulong. По умолчанию каждому значению перечислимого типа автоматически присваивается соответствующее значение базового типа, начиная с нуля, в том порядке, в котором они описаны. Другими словами, значение1 получит базовое значение 0, значение2 — 1, значение3 — 2 и т. д. Для того чтобы переопределить такой порядок, следует использовать оператор = и фактические базовые значения для каждого перечислимого значения:

enum имяТипа: базовыйТип

{

значение1 = фактическоеЗначение1,

значение2 = фактическоеЗначение2,

. . .

значениеN = фактическоеЗначениеN

}

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

enum имяТипа: базовыйТип

{

значение1 = фактическоеЗначение1,

значение2 = значение1,

значение3,

. . .

значениеN = фактическоеЗначениеN

}

Всем значениям, оставшимся неуказанными, будут автоматически присвоены базовые значения; для этого используется последовательность, начинающаяся со значения, на единицу большего последнего явно заданного значения. В вышеприведенном объявлении значение3 получит базовое значение, равное значение1 + 1.

Обратите внимание, что это может привести к возникновению непредвиденных проблем, когда какие-либо из базовых значений, заданных после определения вида значение2 = значение1, окажутся идентичными другим базовым значениям. Так, например, в следующем примере базовое значение для значение2 и значение4 окажутся одним и тем же:

enum имяТипа: базовыйТип

{

значение1 = фактическоеЗначение1,

значение2,

значение3 = значение1,

значение4,

. . .

значениеN = фактическоеЗначениеN

}

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

Рассмотрим практический пример. Реализуем перечислимый тип orientation для указания направления на стороны света:

using System;

namespace Orientation

{

class Program

{

enum orientation : byte

{

north = 1,

south = 2,

east = 3,

west = 4

}

static void Main(string[] args)

{

orientation myDirection = orientation.north;

Console.WriteLine("myDirection = {0}", myDirection);

Console.ReadLine();

}

}

}

Результат работы этой программы:

Внесем в текст функции Main() изменения:

byte directionByte;

string directionString;

orientation myDirection = orientation.north;

Console.WriteLine("myDirection = {0}", myDirection);

directionByte = (byte) myDirection;

directionString = Convert.ToString(myDirection);

Console.WriteLine("Приведено к типу byte = {0}", directionByte);

Console.WriteLine("Приведено к типу string= {0}", directionString);

Получим новый результат:

В данной программе определяется и используется перечислимый тип с именем orientation. Первое, на что следует обратить внимание: код с описанием нового типа помещен в пространство имен Program, а не внутри функции Main(). Это сделано потому, что в процессе выполнения программы не происходит последовательного выполнения строк определения нового типа данных, как это имеет место со строками кода функций. Выполнение приложения начинается с функции Main(), причем эта функция имеет доступ к новому типу, поскольку принадлежит тому же пространству имен.

Первый вариант примера демонстрирует основной метод создания переменной нового типа, присваивание ей значения и вывод этого значения на экран. Затем код был модифицирован, чтобы показать преобразование перечислимых значений в другие типы. Заметьте, что в таких случаях необходимо использовать явное преобразование. Несмотря на то, что базовым типом типа orientation является тип byte, нам все равно приходится использовать явное приведение типа (byte) для преобразования значения переменной myDirection в тип byte:

directionByte = (byte)myDirection;

Такое же явное приведение типа требуется и для обратного преобразования, когда тип byte нужно преобразовать в тип orientation. Например, для преобразования переменной типа byte с именем myByte в тип orientation и присвоения получившегося значения переменной myDirection можно воспользоваться следующим кодом:

myDirection = (orientation)myByte;

Естественно, в данном случае необходимо действовать очень аккуратно, поскольку не каждое из допустимых значений переменной типа byte соответствует какому-либо из описанных значений переменной типа orientation. В переменных типа orientation вполне могут храниться и другие значения типа byte, и, хотя это не приведет к непосредственному возникновению ошибки, логика дальнейшего выполнения приложения может быть нарушена.

Для получения строкового значения из значения перечислимого типа можно использовать Convert.ToString():

directionString = Convert.ToString(myDirection);

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

В качестве альтернативы можно воспользоваться командой ToString() самой переменной. Следующий код даст точно такой же результат, что и использование Convert.ToString():

directionString = myDirection.ToString();

Обратное преобразование значения типа string в значение перечислимого типа также возможно, однако для этого потребуется немного более сложный синтаксис. Для преобразований такого рода существует специальный метод Enum.Parse(), который используется следующим образом:

(перечислимыйТип)Enum.Parse{typeof(перечислимыйТип,значениеСтрокиПеречислимогоТипа);

Здесь задействован еще один оператор — typeof, который позволяет получать тип своего операнда. Мы могли бы воспользоваться им для нашего перечислимого типа orientation следующим образом:

string myString = "north";

orientation myDirection = (orientation) Enum.Parse(typeof(orientation),myString);

Естественно, далеко не каждое строковое значение будет соответствовать какому-либо значению типа orientation. Использование значения, которому не соответствует ни одно из значений перечислимого типа, приведет к возникновению ошибки. Как и все в С#, эти значения чувствительны к регистру, поэтому мы получим ошибку даже в том случае, если наша строка будет соответствовать перечислимому значению во всем, кроме регистра (например, если переменной myString будет присвоено значение "North" вместо "north").