Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Объектно-ориентированное программирование.-6

.pdf
Скачиваний:
6
Добавлен:
05.02.2023
Размер:
4.5 Mб
Скачать

ConvertFromUtf32(int utf32)

из кодировки UTF-32 в UTF-16

 

 

 

 

 

static int

Обратное преобразование

 

 

ConvertToUtf32(...)

 

 

 

 

 

 

static double

Преобразует числовой символ Unicode в

GetNumericValue(...)

число типа double

 

 

 

 

 

 

 

 

static UnicodeCategory

Относит указанный символ

Unicode к

GetUnicodeCategory(...)

категории, определенной одним из зна-

 

 

чений UnicodeCategory(1)

 

 

static bool

Показывает,

относится ли

указанный

Is<категория>(...)

символ Unicode к некоторой категории

(2)

 

 

 

 

static char ToLower(char c,

Преобразует значение символа в его эк-

...)

вивалент в нижнем регистре

 

 

 

 

 

 

 

 

static char

То же самое,

но инвариантно к регио-

ToLowerInvariant(char c)

нальным параметрам

 

 

 

 

 

 

 

static char ToUpper(char c,

Преобразует значение символа в его эк-

...)

вивалент в верхнем регистре

 

 

 

 

 

 

 

 

static char

То же самое,

но инвариантно к регио-

ToUpperInvariant(char c)

нальным параметрам

 

 

 

 

 

 

 

 

 

 

Примечания:

 

 

 

 

(1)При просмотре Таблицы символов Windows тоже можно выбирать одну из категорий символов (см. поле «Группировка» на рис. 3.1). В перечислении UnicodeCategory описано весьма много констант, поэтому мы не будем приводить их список. Посмотреть их можно в справочной системе MSDN.

(2)Речь идет о некоторых категориях, описанных в перечисляемом типе

UnicodeCategory. Например, если имеется константа UnicodeCategory.Control,

то существует и соответствующий ей метод IsControl. Данных методов также весьма много, полный список можно увидеть в справочной системе MSDN.

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

Вотличие от рассмотренных выше типов данных, при работе с перечисляемым типом (или перечислением, от англ. enumeration) мы не просто используем уже реализованный тип, а создаем свой собственный. Причем, в отличие от языка C++, новый тип нельзя описывать внутри метода. Он должен быть объявлен в каком-либо классе или пространстве имен.

Таким образом, синтаксис определения перечисляемого типа таков:

91

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

"{"

<константа1> [= <значение1>] [, <константа2> [= <значение2>]] [, ...]

"}" [;]

Разберем аспекты описания перечисления:

1)Атрибуты, модификаторы. Т.к. перечисляемый тип является членом класса или пространства имен, при его объявлении можно использовать различные атрибуты и модификаторы, которые используются при объявлении членов классов и пространств имен в целом. Об атрибутах подробнее мы поговорим в § 5.4, а о модификаторах – в § 4.2.

2)Имя перечисления является идентификатором. Об идентификаторах мы будем говорить в п. 3.1.6.

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

enum DayOfWeek : byte { }

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

enum DayOfWeek { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday }

5) Значения. По умолчанию константам присваиваются последовательные значения базового типа, начиная с 0. Но можно задать и собственные значения (причем они могут и совпадать для различных констант), например:

enum DayOfWeek { Sunday = 1, Monday = 2, Tuesday = 3, Wednesday = 4, Thursday = 5, Friday = 6, Saturday = 7 }

Если значение не указано, оно вычисляется прибавлением единицы к значению предыдущей константы. Пример:

enum Nums { two = 2, three, four, ten = 10, eleven, fifty = ten + 40 }

Константам three и four присваиваются значения 3 и 4, константе eleven

– 11, fifty – 50.

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

92

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

Перечисления удобно использовать для представления битовых флагов, например:

enum BitFlags : byte

{

bit1 = 0x01, bit2 = 0x02, bit3 = 0x04, bit4 = 0x08, bit5 = 0x10, bit6 = 0x20, bit7 = 0x40, bit8 = 0x80

}

Все перечисления в языке C# являются потомками базового класса System.Enum (см. п. 3.1.5), от которого они наследуют некоторые полезные методы:

static string GetName(Type enumType, object value); static string[] GetNames(Type enumType);

static Array GetValues(Type enumType);

static bool IsDefined(Type enumType, object value); static Type GetUnderlyingType(Type enumType); static object ToObject(Type enumType, <тип> value);

И, конечно, сюда входят все члены, описанные в п. 3.1.2. Пример использования некоторых методов класса Enum:

enum DayOfWeek { Monday = 1, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } // 1

[FlagsAttribute] enum BitFlags : byte { bit1 = 0x01, bit2 = 0x02, bit3 = 0x04, bit4 = 0x08, bit5 = 0x10, bit6 = 0x20, bit7 = 0x40, bit8 = 0x80 } // 2

static int Main(string[] args)

{

DayOfWeek dow = DayOfWeek.Friday; // 3 Console.WriteLine(Enum.GetName(dow.GetType(), dow)); // 4 Console.WriteLine(Enum.GetName(typeof(BitFlags), 8)); // 5

BitFlags bits = BitFlags.bit2 | BitFlags.bit4; // 6

Console.WriteLine(bits); // 7

string[] names = Enum.GetNames(typeof(DayOfWeek)); // 8 Console.Write("В перечислении DayOfWeek " + names.Length + "

элементов: "); // 9

foreach (string name in names) Console.Write(name + " "); // 10

Array values = Enum.GetValues(typeof(BitFlags)); // 11 Console.Write("\nЗначения констант перечисления BitFlags: "); foreach (byte value in values) Console.Write(value + " "); // 12

Console.WriteLine("\nВ перечислении DayOfWeek константа Tuesday " + (Enum.IsDefined(typeof(DayOfWeek), "Tuesday") ? "\" присутствует" : "\"

отсутствует")); // 13

Console.WriteLine("В перечислении BitFlags константа со значением

\"" + 7 + (Enum.IsDefined(typeof(BitFlags), (byte)7) ? "\" присутствует" : "\" отсутствует")); // 14

93

Console.WriteLine("Тип элементов в перечислении BitFlags: " + Enum.GetUnderlyingType(typeof(BitFlags))); // 15

TypeCode code = dow.GetTypeCode(); // 16

Console.WriteLine("Тип элементов в перечислении DayOfWeek: " + code); // 17

BitFlags bits2 = (BitFlags)Enum.ToObject(typeof(BitFlags), 0x55);

// 18

Console.WriteLine(bits2); // 19 return 0;

}

При запуске программа выведет на консоль следующие данные:

Friday bit4

bit2, bit4

Вперечислении DayOfWeek 7 элементов: Monday Tuesday Wednesday Thursday

Friday Saturday Sunday

Значения констант перечисления BitFlags: 1 2 4 8 16 32 64 128

Вперечислении DayOfWeek константа "Tuesday" присутствует

Вперечислении BitFlags константа со значением "7" отсутствует

Тип элементов в перечислении BitFlags: System.Byte Тип элементов в перечислении DayOfWeek: Int32 bit1, bit3, bit5, bit7

Пример: Samples\3.1\3_1_2_enum.

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

1)Описываем перечисление, содержащее дни недели. Значения констант начинаются с 1.

2)Описываем перечисление, содержащее маски для различных битов в байте: bit1 для младшего бита, bit8 – для старшего. Тип элементов перечисления – byte. Атрибут FlagsAttribute указывает, что перечисление может обрабатываться как битовое поле, которое является набором флагов. Про атрибуты мы будем говорить в § 5.4.

3)Пример описания переменной типа DayOfWeek и инициализация ее значением DayOfWeek.Friday. В общем случае, синтаксис доступа к именованной константе перечисляемого типа таков:

[<пространство имен>][<класс>]<тип перечисления>.<константа>

Но если тип описан в тех же пространстве имен и классе, в которых используется, то указывать их не обязательно. В принципе, вместо записи DayOfWeek можно использовать запись Program.DayOfWeek. Но, чтобы не усложнять программу ненужными конструкциями, так делается только в том случае, если перечисление объявлено в другом классе. А если бы наш класс

94

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

4)Статический метод GetName позволяет получить символическое имя константы по ее номеру. Первый параметр метода – метакласс, или объект, представляющий собой тип перечисления. Подробнее о метаклассах мы будем говорить в § 5.3. Получить метакласс для любого типа можно при помощи оператора языка C# typeof (о нем также поговорим позже, в § 3.3), либо, для перечислений, при помощи метода System.Enum.GetType. В данном случае используется второй вариант. Метод System.Enum.GetType не является статическим, поэтому для его вызова необходимо использовать экземпляр перечисления (в данном случае – dow). Второй параметр метода GetName – числовое значение константы. Здесь можно использовать dow, DayOfWeek.Friday или 5 – результат будет одинаковым, на консоли отобразится строка «Friday».

5)В данном случае для получения метакласса перечисления BitFlags был использован оператор typeof. Для этого создание экземпляра перечисления не требуется. Во втором параметре метода GetName можно указать 8 или BitFlags.bit4, в результате на консоль будет выведена строка «bit4».

6)Т.к. перечисление BitFlags содержит битовые поля, при конструировании экземпляра данного перечисления можно комбинировать несколько констант, используя операцию побитовой дизъюнкции (подробнее об операциях в § 3.3).

7)Как мы уже говорили, многие объекты перегружают виртуальный метод ToString, который наследуют от базового класса System.Object. Перегружает его и класс System.Enum. Для экземпляра переменной перечисляемого типа при выводе на консоль выводится значение этой переменной. Например, в строке 4 получить аналогичный результат мы могли еще проще:

Console.WriteLine(dow);

В данном случае на консоли мы увидим строку «bit2, bit4». Если бы атрибут FlagsAttribute не был указан, компилятор не знал бы, что значение bits может являться комбинацией констант перечисления, и вывел бы на экран

95

просто число «10» (bit2 | bit4 = 0x0216 | 0x0816 = 0x0a16 = 1010), т.к. константы со значением 10 в перечислении нет.

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

9)Выводим на экран размер массива names. На консоль будет выведена строка «В перечислении DayOfWeek 7 элементов».

10)В цикле foreach (см. § 3.4) перебираем все элементы массива names

ивыводим их на консоль:

Monday Tuesday Wednesday Thursday Friday Saturday Sunday

11)Статический метод GetValues формирует массив значений констант, составляющих перечисление.

12)Выводим на экран значения констант:

1 2 4 8 16 32 64 128

13)Статический метод IsDefined возвращает значение true, если константа с заданным символическим именем описана в указанном перечислении, и false в противном случае. У данного метода два аргумента. Первый – это метакласс перечисления. Второй – искомая символическая константа. Причем ее можно задавать в любом формате. В данном случае используется строковое представление. Оператор «?:» по логике своей работы соответствует тренарному оператору языка C++. Т.к. константа «Tuesday» в перечислении DayOfWeek определена, видим на консоли строку «В перечислении DayOfWeek константа "Tuesday" присутствует».

14)Здесь используется числовое представление константы во втором аргументе метода IsDefined. Т.к. константы со значением 7 в перечислении BitFlags нет, видим на консоли строку «В перечислении BitFlags константа со значением "7" отсутствует».

15)Статический метод GetUnderlyingType возвращает имя базового типа, на котором построено перечисление. Возвращает он метакласс базового типа, а в его строковое представление при выводе на консоль формируется вызовом перегруженного метода ToString. Для метакласса он возвращает имя класса-оригинала. Для перечисления BitFlags этот метод вернет typeof(byte), поэтому видим на экране базовый тип «System.Byte».

96

16)Метод GetTypeCode тоже возвращает информацию о базовом типе перечисления, но уже в виде экземпляра перечисления TypeCode.

17)Здесь мы снова пользуемся тем же фактом, что и в строке 7: любой объект может быть представлен в виде строки. На консоль выводится текст «Тип элементов в перечислении DayOfWeek: Int32».

18)Метод ToObject имеет несколько реализаций, отличающихся типом второго аргумента (есть реализации для всех целых типов и типа object). В данном случае использовалась целая константа, поэтому вызывался метод

public static object ToObject(Type enumType, int value);

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

кBitFlags.

19)Константа 0x5516 = 010101012, поэтому в перечисление были включены символические константы bit1, bit3, bit5 и bit7, в чем мы и убеждаемся, выводя bits2 на консоль.

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

3.1.2.6. Структуры

Структуры – еще один вид определяемых пользователем типов данных. Структуры наследуются напрямую от класса System.ValueType, поэтому не реализуют тех интерфейсов и их методов, которые перечислены в п. 3.1.2. Структуры обычно используются для инкапсуляции небольших объектов, в противном случае лучше написать класс.

Синтаксис описания структур следующий:

[<атрибуты>] [<модификаторы>] struct <имя структуры> [: <интерфейс1> [, <интерфейс2>] [, ...]]

"{"

[<тело структуры>]

"}" [;]

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

97

это будет рассматриваться в главе 4 «Классы и интерфейсы». Новый экземпляр структуры, как и любого типа по значению, можно создать, используя оператор «=» (выражение в правой части должно являться структурой такого же типа) или оператор new:

<экземпляр> = <выражение>;

<экземпляр> = new <имя структуры> ([<параметры конструктора>]); <экземпляр> = new <имя структуры> "{" [<поле> = <значение> [, ...]] "}"

Если рассматривать язык C++, то можно прийти к выводу, что в нем классы и структуры отличаются немногим: члены структур по умолчанию имеют модификатор доступа public, а члены классов – private. В языке C# различия между классами и структурами более значительны:

1) Структура не может наследоваться от другой структуры или класса и не может быть основой для других классов. Структуры могут реализовывать только интерфейсы (см. § 4.9). Пример:

interface ITest { }

 

 

class CTest { }

 

 

 

struct MyStruct1

: CTest, ITest

{

} // Ошибка

struct MyStruct2

: ITest { } //

ОК

 

class MyClass1 :

CTest, ITest {

}

// ОК

class MyClass2 :

MyStruct2 { } //

Ошибка

 

 

 

 

2) В объявлении структуры поля не могут быть инициализированы до тех пор, пока они не будут объявлены как постоянные или статические. Пример:

struct MyStruct

{

public int A1 = 1; // Ошибка public const int A2 = 1; // ОК public int A3; // ОК

}

class MyClass

{

public int A1 = 1; // ОК public const int A2 = 1; // ОК

}

Постоянные и статические члены классов рассмотрены в главе 4.

3) Структура не может объявлять используемый по умолчанию конструктор (конструктор без параметров) и деструктор. Пример:

struct MyStruct2

{

public int A3;

public MyStruct2(int a) { A3 = a; } // ОК

}

98

struct MyStruct3

{

public MyStruct3() { } // Ошибка ~MyStruct3() { } // Ошибка

}

class MyClass1

{

public int A1;

public MyClass1(int a) { A1 = a; } // ОК ~MyClass1() { } // ОК

}

class MyClass3

{

public MyClass3() { }

}

class MyClass4

{

public int a;

}

static int Main(string[] args)

{

MyStruct2 x1 = new MyStruct2(); // ОК MyStruct2 x2 = new MyStruct2(1); // ОК MyClass1 y1 = new MyClass1(); // Ошибка MyClass1 y2 = new MyClass1(1); // ОК MyClass4 y4 = new MyClass4(); // ОК return 0;

}

В структуре MyStruct3 была сделана попытка объявления конструктора по умолчанию и деструктора. Результат – ошибка компиляции. В классе MyClass3 есть конструктор по умолчанию, а в классе MyClass1 – деструктор, и это ошибкой не является. Однако, при создании экземпляра структуры (x1) мы можем использовать конструктор по умолчанию. Он добавляется компилятором неявно и инициализирует все поля структуры значениями по умолчанию.

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

И, естественно, можем также использовать явно описанные конструкторы с параметрами структур (x2) и любые явно описанные конструкторы классов (y2).

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

99

вания оператора new. Пример:

struct MyStruct2

{

public int A3;

}

class MyClass1

{

public int A1;

}

static int Main(string[] args)

{

MyStruct2 x5; Console.WriteLine(x5.A3); // Ошибка x5.A3 = 1; // ОК Console.WriteLine(x5.A3); // ОК

MyClass1 y5;

y5.A1 = 1; // Ошибка y5 = new MyClass1(); y5.A1 = 1; // ОК return 0;

}

При описании переменной x5 был создан экземпляр структуры MyStruct2. Но, т.к. никакой конструктор не вызывался, ее поля остались неинициализированными. Поэтому при попытке использования любого поля этой структуры на чтение мы видим ошибку компилятора. После того, как поле A3 инициализировано (вызовом конструктора класса int, присвоением числовой константы или другой переменной), мы можем его использовать в операторе вывода. На консоли появится число 1. При описании переменной y5 экземпляр класса MyClass1 не создается! Поэтому любая попытка использовать поля этого экземпляра влечет ошибку компиляции.

Также при создании структуры можно сразу инициализировать некоторые поля:

MyStruct2 x51;

x51 = new MyStruct2 { A3 = 1, B = 0.5, C = "!!!" };

5) Самым серьезным отличием, как уже было сказано, является то, что структуры – это типы по значению, а классы – ссылочные типы. Поэтому при присваивании структуры копируют свое содержимое, а классы – только ссылку. Вспомним, когда в языке C++ происходит копирование значения одной переменной в другую переменную:

при использовании оператора присваивания;

при передаче переменной в качестве аргумента по значению (напом-

100