C# 4.0

C# 4.0 - ,     C# Спе ...

 

 

C#

Спецификация языка

Версия 4.0


Уведомление

Корпорация Майкрософт (Microsoft Corp.), 1999-2010. Все права защищены.

Microsoft, Windows, Visual Basic, Visual C# и Visual C++ являются охраняемыми товарными знаками корпорации Майкрософт в США и других странах.

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


Оглавление

1. Введение.............................................................................................................................................. 1

1.1 Программа Hello world............................................................................................................... 2

1.2 Структура программы.................................................................................................................... 2

1.3 Типы и переменные......................................................................................................................... 4

1.4 Выражения....................................................................................................................................... 8

1.5 Операторы языка........................................................................................................................... 10

1.6 Классы и объекты......................................................................................................................... 14

1.6.1 Члены...................................................................................................................................... 14

1.6.2 Доступность............................................................................................................................ 15

1.6.3 Параметры типа...................................................................................................................... 15

1.6.4 Базовые классы....................................................................................................................... 16

1.6.5 Поля........................................................................................................................................ 16

1.6.6 Методы.................................................................................................................................... 17

1.6.6.1 Параметры........................................................................................................................ 17

1.6.6.2 Тело метода и локальные переменные........................................................................... 19

1.6.6.3 Статические методы и методы экземпляров.................................................................. 19

1.6.6.4 Виртуальные, переопределяющие и абстрактные методы............................................ 20

1.6.6.5 Перегрузка метода........................................................................................................... 22

1.6.7 Другие члены-функции.......................................................................................................... 23

1.6.7.1 Конструкторы................................................................................................................... 24

1.6.7.2 Свойства........................................................................................................................... 25

1.6.7.3 Индексаторы..................................................................................................................... 25

1.6.7.4 События............................................................................................................................ 26

1.6.7.5 Операторы........................................................................................................................ 26

1.6.7.6 Деструкторы..................................................................................................................... 27

1.7 Структуры..................................................................................................................................... 27

1.8 Массивы......................................................................................................................................... 28

1.9 Интерфейсы................................................................................................................................... 30

1.10 Перечисления.............................................................................................................................. 31

1.11 Делегаты...................................................................................................................................... 32

1.12 Атрибуты..................................................................................................................................... 33

2. Лексическая структура.................................................................................................................... 35

2.1 Программы.................................................................................................................................... 35

2.2 Грамматики................................................................................................................................... 35

2.2.1 Грамматическая нотация....................................................................................................... 35

2.2.2 Лексика.................................................................................................................................... 36

2.2.3 Синтаксическая грамматика.................................................................................................. 36

2.3 Лексический анализ...................................................................................................................... 36

2.3.1 Знаки завершения строки....................................................................................................... 37

2.3.2 Комментарии.......................................................................................................................... 37

2.3.3 Пробел.................................................................................................................................... 39

2.4 Маркеры........................................................................................................................................ 39

2.4.1 Управляющие последовательности символов Юникода...................................................... 39

2.4.2 Идентификаторы.................................................................................................................... 40

2.4.3 Ключевые слова...................................................................................................................... 42

2.4.4 Литералы................................................................................................................................. 42

2.4.4.1 Логические литералы....................................................................................................... 42

2.4.4.2 Целочисленные литералы................................................................................................ 43

2.4.4.3 Действительные литералы............................................................................................... 44

2.4.4.4 Символьные литералы..................................................................................................... 44

2.4.4.5 Строковые литералы........................................................................................................ 45

2.4.4.6 Литерал null...................................................................................................................... 47

2.4.5 Операторы и знаки пунктуации............................................................................................. 47

2.5 Препроцессорные директивы....................................................................................................... 48

2.5.1 Символы условной компиляции............................................................................................ 49

2.5.2 Препроцессорные выражения................................................................................................ 49

2.5.3 Директивы объявлений........................................................................................................... 50

2.5.4 Директивы условной компиляции......................................................................................... 51

2.5.5 Директивы диагностики......................................................................................................... 53

2.5.6 Директивы областей............................................................................................................... 54

2.5.7 Директивы строк..................................................................................................................... 54

2.5.8 Директивы pragma.................................................................................................................. 55

2.5.8.1 Директива pragma warning............................................................................................... 55

3. Основные принципы....................................................................................................................... 57

3.1 Запуск приложения....................................................................................................................... 57

3.2 Завершение приложения............................................................................................................... 58

3.3 Объявления.................................................................................................................................... 58

3.4 Члены............................................................................................................................................. 61

3.4.1 Члены пространства имен...................................................................................................... 61

3.4.2 Члены структуры.................................................................................................................... 61

3.4.3 Члены перечисления.............................................................................................................. 62

3.4.4 Члены класса.......................................................................................................................... 62

3.4.5 Члены интерфейса.................................................................................................................. 62

3.4.6 Члены массива........................................................................................................................ 62

3.4.7 Члены делегата....................................................................................................................... 62

3.5 Метод доступа к члену................................................................................................................. 62

3.5.1 Объявленная доступность...................................................................................................... 62

3.5.2 Домены доступности.............................................................................................................. 63

3.5.3 Защищенный доступ для членов экземпляров...................................................................... 66

3.5.4 Ограничения доступности..................................................................................................... 67

3.6 Сигнатуры и перегрузка............................................................................................................... 68

3.7 Области видимости....................................................................................................................... 69

3.7.1 Скрытие имени....................................................................................................................... 72

3.7.1.1 Скрытие через вложение.................................................................................................. 72

3.7.1.2 Скрытие через наследование........................................................................................... 73

3.8 Имена пространств имен и типов................................................................................................ 74

3.8.1 Полные имена......................................................................................................................... 77

3.9 Автоматическое управление памятью......................................................................................... 77

3.10 Порядок выполнения.................................................................................................................. 80

4. Типы................................................................................................................................................... 83

4.1 Типы значений............................................................................................................................... 83

4.1.1 Тип System.ValueType............................................................................................................ 84

4.1.2 Конструкторы по умолчанию................................................................................................ 84

4.1.3 Типы структуры...................................................................................................................... 85

4.1.4 Простые типы......................................................................................................................... 85

4.1.5 Целые типы............................................................................................................................. 86

4.1.6 Типы с плавающей запятой.................................................................................................... 87

4.1.7 Тип decimal............................................................................................................................. 89

4.1.8 Тип bool................................................................................................................................... 89

4.1.9 Перечисляемые типы............................................................................................................. 90

4.1.10 Обнуляемые типы................................................................................................................. 90

4.2 Ссылочные типы........................................................................................................................... 91

4.2.1 Типы классов.......................................................................................................................... 91

4.2.2 Тип объекта............................................................................................................................. 92

4.2.3 Динамический тип.................................................................................................................. 92

4.2.4 Строковый тип........................................................................................................................ 92

4.2.5 Типы интерфейсов.................................................................................................................. 92

4.2.6 Типы массивов........................................................................................................................ 92

4.2.7 Типы делегатов....................................................................................................................... 93

4.3 Упаковка и распаковка.................................................................................................................. 93

4.3.1 Преобразования упаковки...................................................................................................... 93

4.3.2 Преобразования распаковки.................................................................................................. 94

4.4 Сформированные типы................................................................................................................. 95

4.4.1 Аргументы типа...................................................................................................................... 96

4.4.2 Открытые и закрытые типы................................................................................................... 96

4.4.3 Связанные и несвязанные типы.............................................................................................. 97

4.4.4 Соблюдение ограничений...................................................................................................... 97

4.5 Параметры типа............................................................................................................................ 98

4.6 Типы дерева выражений............................................................................................................... 99

4.7 Динамический тип....................................................................................................................... 100

5. Переменные..................................................................................................................................... 101

5.1 Категории переменных............................................................................................................... 101

5.1.1 Статические переменные..................................................................................................... 101

5.1.2 Переменные экземпляра....................................................................................................... 101

5.1.2.1 Переменные экземпляра в классах................................................................................ 102

5.1.2.2 Переменные экземпляра в структурах.......................................................................... 102

5.1.3 Элементы массива................................................................................................................ 102

5.1.4 Параметры по значению...................................................................................................... 102

5.1.5 Параметры по ссылке........................................................................................................... 102

5.1.6 Выходные параметры........................................................................................................... 103

5.1.7 Локальные переменные........................................................................................................ 103

5.2 Значения по умолчанию............................................................................................................. 104

5.3 Определенное присваивание...................................................................................................... 104

5.3.1 Переменные с начальным значением.................................................................................. 105

5.3.2 Переменные без начального значения................................................................................ 106

5.3.3 Точные правила для выявления определенного присваивания.......................................... 106

5.3.3.1 Общие правила для операторов.................................................................................... 107

5.3.3.2 Операторы блока, операторы checked и unchecked..................................................... 107

5.3.3.3 Операторы-выражения................................................................................................... 107

5.3.3.4 Операторы объявления.................................................................................................. 107

5.3.3.5 Операторы If.................................................................................................................. 107

5.3.3.6 Операторы switch........................................................................................................... 108

5.3.3.7 Операторы while............................................................................................................. 108

5.3.3.8 Операторы do................................................................................................................. 108

5.3.3.9 Операторы for................................................................................................................ 109

5.3.3.10 Операторы break, continue и goto................................................................................ 109

5.3.3.11 Операторы throw.......................................................................................................... 109

5.3.3.12 Операторы return.......................................................................................................... 109

5.3.3.13 Операторы try-catch..................................................................................................... 110

5.3.3.14 Операторы try-finally................................................................................................... 110

5.3.3.15 Операторы try-catch-finally.......................................................................................... 110

5.3.3.16 Операторы foreach....................................................................................................... 111

5.3.3.17 Операторы using........................................................................................................... 111

5.3.3.18 Операторы lock............................................................................................................ 112

5.3.3.19 Операторы yield........................................................................................................... 112

5.3.3.20 Общие правила для простых выражений.................................................................... 112

5.3.3.21 Общие правила для выражений с внедренными выражениями................................. 112

5.3.3.22 Выражения вызова и выражения создания объекта.................................................... 113

5.3.3.23 Простые выражения присваивания.............................................................................. 113

5.3.3.24 Выражения &&............................................................................................................. 113

5.3.3.25 Выражения ||................................................................................................................. 114

5.3.3.26 ! Выражения !............................................................................................................... 115

5.3.3.27 ?? Выражения ??........................................................................................................... 116

5.3.3.28 Выражения ?:................................................................................................................ 116

5.3.3.29 Анонимные функции.................................................................................................... 117

5.4 Ссылочные переменные............................................................................................................. 117

5.5 Атомарность ссылок на переменные......................................................................................... 118

6. Преобразования.............................................................................................................................. 119

6.1 Неявные преобразования............................................................................................................ 119

6.1.1 Преобразование идентификатора........................................................................................ 120

6.1.2 Неявные преобразования числовых типов.......................................................................... 120

6.1.3 Неявные преобразования перечисляемых типов................................................................ 120

6.1.4 Неявные преобразования обнуляемых типов..................................................................... 120

6.1.5 Преобразования литерала null............................................................................................. 121

6.1.6 Неявные преобразования ссылочных типов....................................................................... 121

6.1.7 Преобразования упаковки.................................................................................................... 122

6.1.8 Неявные динамические преобразования............................................................................. 122

6.1.9 Неявные преобразования выражений констант.................................................................. 123

6.1.10 Неявные преобразования, включающие параметры типа................................................ 123

6.1.11 Пользовательские неявные преобразования..................................................................... 123

6.1.12 Преобразования анонимных функций и преобразования группы методов..................... 124

6.2 Явные преобразования................................................................................................................ 124

6.2.1 Явные преобразования числовых типов.............................................................................. 124

6.2.2 Явные преобразования перечисляемых типов.................................................................... 126

6.2.3 Явные преобразования обнуляемых типов......................................................................... 126

6.2.4 Явные преобразования ссылочных типов........................................................................... 127

6.2.5 Преобразования распаковки................................................................................................ 128

6.2.6 Неявные динамические преобразования............................................................................. 128

6.2.7 Явные преобразования, включающие параметры типа...................................................... 129

6.2.8 Пользовательские явные преобразования........................................................................... 130

6.3 Стандартные преобразования.................................................................................................... 130

6.3.1 Стандартные неявные преобразования............................................................................... 130

6.3.2 Стандартные явные преобразования................................................................................... 130

6.4 Пользовательские преобразования............................................................................................ 130

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

6.4.2 Операторы преобразования с нулификацией...................................................................... 131

6.4.3 Вычисление пользовательских преобразований................................................................. 131

6.4.4 Определенные пользователем неявные преобразования................................................... 132

6.4.5 Определенные пользователем явные преобразования....................................................... 133

6.5 Преобразования анонимных функций....................................................................................... 134

6.5.1 Вычисление преобразования анонимной функции к типу делегата.................................. 136

6.5.2 Вычисление преобразования анонимной функции к типу дерева выражений.................. 136

6.5.3 Пример реализации.............................................................................................................. 137

6.6 Преобразования группы методов............................................................................................... 139

7. Выражения...................................................................................................................................... 143

7.1 Классы выражений...................................................................................................................... 143

7.1.1 Значения выражений............................................................................................................ 144

7.2 Статическая и динамическая привязка...................................................................................... 144

7.2.1 Время привязки..................................................................................................................... 145

7.2.2 Динамическая привязка........................................................................................................ 145

7.2.3 Типы составных выражений................................................................................................. 146

7.3 Операторы................................................................................................................................... 146

7.3.1 Приоритет и ассоциативность операторов......................................................................... 147

7.3.2 Перегрузка операторов........................................................................................................ 148

7.3.3 Разрешение перегрузки унарных операторов..................................................................... 149

7.3.4 Разрешение перегрузки бинарных операторов................................................................... 149

7.3.5 Пользовательские операторы-кандидаты........................................................................... 150

7.3.6 Числовое расширение.......................................................................................................... 150

7.3.6.1 Числовое расширение унарных операторов................................................................. 151

7.3.6.2 Числовое расширение бинарных операторов............................................................... 151

7.3.7 Операторы с нулификацией................................................................................................. 152

7.4 Поиск членов............................................................................................................................... 152

7.4.1 Базовые типы........................................................................................................................ 154

7.5 Функции-члены........................................................................................................................... 154

7.5.1 Списки аргументов............................................................................................................... 157

7.5.1.1 Соответствующие параметры....................................................................................... 158

7.5.1.2 Вычисление списков аргументов во время выполнения.............................................. 159

7.5.2 Вывод типа............................................................................................................................ 161

7.5.2.1 Первый этап.................................................................................................................... 162

7.5.2.2 Второй этап.................................................................................................................... 162

7.5.2.3 Типы ввода...................................................................................................................... 162

7.5.2.4 Типы вывода................................................................................................................... 162

7.5.2.5 Зависимость.................................................................................................................... 162

7.5.2.6 Вывод типа вывода......................................................................................................... 163

7.5.2.7 Вывод явных типов параметров.................................................................................... 163

7.5.2.8 Точный вывод................................................................................................................. 163

7.5.2.9 Вывод нижних границ.................................................................................................... 163

7.5.2.10 Вывод по верхним границам....................................................................................... 164

7.5.2.11 Фиксирование............................................................................................................... 165

7.5.2.12 Выведенный тип возвращаемого значения................................................................. 165

7.5.2.13 Вывод типа при преобразовании групп методов........................................................ 166

7.5.2.14 Поиск наиболее подходящего общего типа для набора выражений......................... 166

7.5.3 Разрешение перегрузки........................................................................................................ 167

7.5.3.1 Применимая функция-член............................................................................................ 167

7.5.3.2 Более подходящая функция-член.................................................................................. 168

7.5.3.3 Лучшее преобразование из выражения......................................................................... 169

7.5.3.4 Лучшее преобразование из типа................................................................................... 170

7.5.3.5 Лучшая цель для преобразования................................................................................. 170

7.5.3.6 Перегрузка в универсальных классах........................................................................... 170

7.5.4 Проверка динамического разрешения перегрузки во время компиляции......................... 171

7.5.5 Вызов функции-члена........................................................................................................... 171

7.5.5.1 Вызов в упакованных экземплярах................................................................................ 172

7.6 Первичные выражения................................................................................................................ 173

7.6.1 Литералы............................................................................................................................... 173

7.6.2 Простые имена..................................................................................................................... 173

7.6.2.1 Инвариантность значения в блоках............................................................................... 175

7.6.3 Выражения со скобками....................................................................................................... 176

7.6.4 Доступ к члену..................................................................................................................... 176

7.6.4.1 Идентичные простые имена и имена типов.................................................................. 178

7.6.4.2 Грамматическая неоднозначность................................................................................ 179

7.6.5 Выражения вызова............................................................................................................... 179

7.6.5.1 Вызовы методов............................................................................................................. 180

7.6.5.2 Вызовы методов расширения........................................................................................ 181

7.6.5.3 Вызовы делегатов........................................................................................................... 184

7.6.6 Метод доступа к элементу................................................................................................... 184

7.6.6.1 Доступ к массиву........................................................................................................... 185

7.6.6.2 Доступ к индексатору.................................................................................................... 185

7.6.7 Доступ this............................................................................................................................ 186

7.6.8 Доступ base........................................................................................................................... 187

7.6.9 Постфиксные операторы инкремента и декремента.......................................................... 188

7.6.10 Оператор new..................................................................................................................... 189

7.6.10.1 Выражения создания объектов.................................................................................... 189

7.6.10.2 Инициализаторы объектов........................................................................................... 191

7.6.10.3 Инициализаторы коллекции........................................................................................ 193

7.6.10.4 Выражения создания массива...................................................................................... 194

7.6.10.5 Выражения создания делегата..................................................................................... 196

7.6.10.6 Выражения создания анонимных объектов................................................................ 197

7.6.11 Оператор typeof.................................................................................................................. 199

7.6.12 Операторы checked и unchecked....................................................................................... 201

7.6.13 Выражения значения по умолчанию................................................................................. 203

7.6.14 Выражения анонимного метода......................................................................................... 203

7.7 Унарные операторы.................................................................................................................... 204

7.7.1 Унарный оператор плюс.................................................................................................. 204

7.7.2 Унарный оператор минус................................................................................................ 204

7.7.3 Оператор логического отрицания....................................................................................... 205

7.7.4 Оператор побитового дополнения...................................................................................... 205

7.7.5 Префиксные операторы инкремента и декремента............................................................ 205

7.7.6 Выражения приведения типа................................................................................................ 206

7.8 Арифметические операторы...................................................................................................... 207

7.8.1 Оператор произведения....................................................................................................... 207

7.8.2 Оператор деления................................................................................................................. 208

7.8.3 Оператор остатка................................................................................................................. 209

7.8.4 Оператор сложения.............................................................................................................. 210

7.8.5 Оператор вычитания............................................................................................................ 212

7.9 Операторы сдвига....................................................................................................................... 214

7.10 Операторы отношения и проверки типа.................................................................................. 215

7.10.1 Операторы сравнения целых чисел................................................................................... 216

7.10.2 Операторы сравнения чисел с плавающей запятой.......................................................... 217

7.10.3 Операторы сравнения десятичных чисел.......................................................................... 218

7.10.4 Логические операторы равенства...................................................................................... 218

7.10.5 Операторы сравнения значений перечисления................................................................. 218

7.10.6 Операторы равенства значений ссылочного типа............................................................ 218

7.10.7 Операторы равенства строк............................................................................................... 220

7.10.8 Операторы равенства делегатов........................................................................................ 220

7.10.9 Операторы равенства и значение null............................................................................... 221

7.10.10 Оператор is....................................................................................................................... 221

7.10.11 Оператор as....................................................................................................................... 222

7.11 Логические операторы.............................................................................................................. 223

7.11.1 Логические операторы для целых чисел........................................................................... 223

7.11.2 Логические операторы для перечислений........................................................................ 224

7.11.3 Логические операторы....................................................................................................... 224

7.11.4 Обнуляемые логические операторы.................................................................................. 224

7.12 Условные логические операторы............................................................................................. 225

7.12.1 Логические условные операторы...................................................................................... 225

7.12.2 Пользовательские условные логические операторы........................................................ 226

7.13 Оператор слияния с null........................................................................................................... 226

7.14 Условный оператор.................................................................................................................. 227

7.15 Выражения анонимных функций.............................................................................................. 228

7.15.1 Сигнатуры анонимных функций........................................................................................ 230

7.15.2 Тела анонимных функций.................................................................................................. 230

7.15.3 Разрешение перегрузки...................................................................................................... 231

7.15.4 Анонимные функции и динамическая привязка................................................................ 231

7.15.5 Внешние переменные......................................................................................................... 232

7.15.5.1 Захваченные внешние переменные............................................................................. 232

7.15.5.2 Создание экземпляров локальных переменных.......................................................... 232

7.15.6 Вычисление выражений анонимных функций.................................................................. 235

7.16 Выражения запросов................................................................................................................. 235

7.16.1 Неоднозначность в выражениях запросов........................................................................ 236

7.16.2 Перевод выражений запросов............................................................................................ 236

7.16.2.1 Предложения select и groupby с продолжениями....................................................... 237

7.16.2.2 Явные типы переменных диапазона............................................................................ 237

7.16.2.3 Выражения вырожденных запросов............................................................................ 238

7.16.2.4 Предложения from, let, where, join и orderby.............................................................. 238

7.16.2.5 Предложения select...................................................................................................... 242

7.16.2.6 Предложения groupby.................................................................................................. 242

7.16.2.7 Прозрачные идентификаторы...................................................................................... 242

7.16.3 Шаблон выражения запроса............................................................................................... 244

7.17 Операторы присваивания......................................................................................................... 245

7.17.1 Простое присваивание....................................................................................................... 246

7.17.2 Сложное присваивание....................................................................................................... 248

7.17.3 Присваивание событий....................................................................................................... 249

7.18 Выражение................................................................................................................................. 249

7.19 Константные выражения........................................................................................................... 249

7.20 Логические выражения............................................................................................................. 251

8. Операторы языка........................................................................................................................... 253

8.1 Конечные точки и достижимость............................................................................................... 253

8.2 Блоки........................................................................................................................................... 255

8.2.1 Списки операторов............................................................................................................... 255

8.3 Пустой оператор......................................................................................................................... 256

8.4 Помеченные операторы.............................................................................................................. 256

8.5 Операторы объявления............................................................................................................... 257

8.5.1 Объявления локальных переменных................................................................................... 257

8.5.2 Объявления локальных констант......................................................................................... 259

8.6 Операторы-выражения................................................................................................................ 259

8.7 Операторы выбора...................................................................................................................... 260

8.7.1 Оператор if........................................................................................................................... 260

8.7.2 Оператор switch.................................................................................................................... 261

8.8 Операторы итераций................................................................................................................... 264

8.8.1 Оператор while..................................................................................................................... 265

8.8.2 Оператор do.......................................................................................................................... 265

8.8.3 Оператор for......................................................................................................................... 266

8.8.4 Оператор foreach.................................................................................................................. 267

8.9 Операторы перехода................................................................................................................... 270

8.9.1 Оператор break..................................................................................................................... 271

8.9.2 Оператор continue................................................................................................................ 272

8.9.3 Оператор goto....................................................................................................................... 272

8.9.4 Оператор return..................................................................................................................... 273

8.9.5 Оператор throw..................................................................................................................... 274

8.10 Оператор try.............................................................................................................................. 275

8.11 Операторы checked и unchecked.............................................................................................. 278

8.12 Оператор lock............................................................................................................................ 278

8.13 Оператор using.......................................................................................................................... 279

8.14 Оператор yield........................................................................................................................... 281

9. Пространства имен......................................................................................................................... 285

9.1 Единицы компиляции.................................................................................................................. 285

9.2 Объявления пространства имен................................................................................................. 285

9.3 Внешние псевдонимы................................................................................................................. 287

9.4 Директивы using.......................................................................................................................... 287

9.4.1 Директивы using alias............................................................................................................ 288

9.4.2 Директивы using namespace................................................................................................. 291

9.5 Члены пространства имен.......................................................................................................... 292

9.6 Объявления типов....................................................................................................................... 293

9.7 Квалификаторы псевдонима пространства имен...................................................................... 293

9.7.1 Уникальность псевдонимов................................................................................................. 294

10. Классы........................................................................................................................................... 297

10.1 Объявления классов.................................................................................................................. 297

10.1.1 Модификаторы классов...................................................................................................... 297

10.1.1.1 Абстрактные классы.................................................................................................... 298

10.1.1.2 Запечатанные классы................................................................................................... 298

10.1.1.3 Статические классы..................................................................................................... 299

10.1.2 Модификатор partial........................................................................................................... 300

10.1.3 Параметры типа.................................................................................................................. 300

10.1.4 Спецификация базы класса................................................................................................ 300

10.1.4.1 Базовые классы............................................................................................................. 300

10.1.4.2 Реализация интерфейсов.............................................................................................. 302

10.1.5 Ограничения параметров типа........................................................................................... 302

10.1.6 Тело класса......................................................................................................................... 307

10.2 Разделяемые типы..................................................................................................................... 307

10.2.1 Атрибуты............................................................................................................................ 307

10.2.2 Модификаторы................................................................................................................... 308

10.2.3 Параметры и ограничения типа......................................................................................... 308

10.2.4 Базовый класс..................................................................................................................... 309

10.2.5 Базовые интерфейсы.......................................................................................................... 309

10.2.6 Члены.................................................................................................................................. 309

10.2.7 Разделяемые методы.......................................................................................................... 310

10.2.8 Привязка имен.................................................................................................................... 312

10.3 Члены класса............................................................................................................................. 313

10.3.1 Тип экземпляра................................................................................................................... 314

10.3.2 Члены сформированных типов.......................................................................................... 315

10.3.3 Наследование...................................................................................................................... 316

10.3.4 Модификатор new.............................................................................................................. 317

10.3.5 Модификаторы доступа..................................................................................................... 317

10.3.6 Составные типы.................................................................................................................. 317

10.3.7 Статические члены и члены экземпляра........................................................................... 317

10.3.8 Вложенные типы................................................................................................................. 318

10.3.8.1 Полные имена............................................................................................................... 318

10.3.8.2 Объявленная доступность........................................................................................... 319

10.3.8.3 Скрытие........................................................................................................................ 319

10.3.8.4 Доступ this.................................................................................................................... 320

10.3.8.5 Доступ к частным и защищенным членам типа-контейнера..................................... 321

10.3.8.6 Вложенные типы в универсальных классах................................................................ 322

10.3.9 Зарезервированные имена членов..................................................................................... 322

10.3.9.1 Имена членов, зарезервированные для свойств......................................................... 323

10.3.9.2 Имена членов, зарезервированные для событий........................................................ 323

10.3.9.3 Имена членов, зарезервированные для индексаторов............................................... 323

10.3.9.4 Имена членов, зарезервированные для деструкторов............................................... 324

10.4 Константы................................................................................................................................. 324

10.5 Поля........................................................................................................................................... 325

10.5.1 Статические поля и поля экземпляров.............................................................................. 327

10.5.2 Поля только для чтения..................................................................................................... 327

10.5.2.1 Использование статических полей только для чтения вместо констант.................. 328

10.5.2.2 Отслеживание версий констант и статических полей только для чтения................. 328

10.5.3 Поля с модификатором volatile......................................................................................... 329

10.5.4 Инициализация поля........................................................................................................... 330

10.5.5 Инициализаторы переменных............................................................................................ 330

10.5.5.1 Инициализация статического поля.............................................................................. 331

10.5.5.2 Инициализация поля экземпляра................................................................................. 333

10.6 Методы...................................................................................................................................... 333

10.6.1 Параметры метода............................................................................................................. 335

10.6.1.1 Параметры по значению.............................................................................................. 337

10.6.1.2 Параметры по ссылке.................................................................................................. 337

10.6.1.3 Выходные параметры.................................................................................................. 338

10.6.1.4 Массивы параметров.................................................................................................... 339

10.6.2 Статические методы и методы экземпляра....................................................................... 342

10.6.3 Виртуальные методы......................................................................................................... 342

10.6.4 Переопределяющие методы.............................................................................................. 344

10.6.5 Запечатанные методы........................................................................................................ 346

10.6.6 Абстрактные методы.......................................................................................................... 347

10.6.7 Внешние методы................................................................................................................ 348

10.6.8 Разделяемые методы.......................................................................................................... 349

10.6.9 Методы расширения........................................................................................................... 349

10.6.10 Тело метода...................................................................................................................... 350

10.6.11 Перегрузка метода........................................................................................................... 351

10.7 Свойства.................................................................................................................................... 351

10.7.1 Статические свойства и свойства экземпляра................................................................... 352

10.7.2 Методы доступа................................................................................................................. 352

10.7.3 Автоматически реализуемые свойства............................................................................. 358

10.7.4 Доступность........................................................................................................................ 358

10.7.5 Виртуальные, запечатанные, переопределяющие и абстрактные методы доступа........ 360

10.8 События..................................................................................................................................... 362

10.8.1 События, подобные полям................................................................................................. 363

10.8.2 Методы доступа к событиям............................................................................................. 365

10.8.3 Статические события и события экземпляров.................................................................. 366

10.8.4 Виртуальные, запечатанные, переопределяющие и абстрактные методы доступа........ 366

10.9 Индексаторы............................................................................................................................. 367

10.9.1 Перегрузка индексатора..................................................................................................... 371

10.10 Операторы............................................................................................................................... 371

10.10.1 Унарные операторы......................................................................................................... 373

10.10.2 Бинарные операторы........................................................................................................ 374

10.10.3 Операторы преобразования............................................................................................. 374

10.11 Конструкторы экземпляров.................................................................................................... 377

10.11.1 Инициализаторы конструкторов..................................................................................... 378

10.11.2 Инициализаторы переменных экземпляров.................................................................... 379

10.11.3 Выполнение конструктора............................................................................................... 379

10.11.4 Конструкторы по умолчанию.......................................................................................... 381

10.11.5 Закрытые конструкторы................................................................................................... 381

10.11.6 Необязательные параметры конструктора экземпляров................................................ 381

10.12 Статические конструкторы.................................................................................................... 382

10.13 Деструкторы............................................................................................................................ 384

10.14 Итераторы............................................................................................................................... 386

10.14.1 Интерфейсы перечислителя............................................................................................. 386

10.14.2 Перечислимые интерфейсы............................................................................................. 386

10.14.3 Тип yield............................................................................................................................ 386

10.14.4 Объекты перечислителя................................................................................................... 386

10.14.4.1 Метод MoveNext........................................................................................................ 387

10.14.4.2 Свойство Current........................................................................................................ 388

10.14.4.3 Метод Dispose............................................................................................................ 388

10.14.5 Перечислимые объекты................................................................................................... 389

10.14.5.1 Метод GetEnumerator................................................................................................. 389

10.14.6 Пример реализации.......................................................................................................... 389

11. Структуры..................................................................................................................................... 397

11.1 Объявления структур................................................................................................................ 397

11.1.1 Модификаторы структуры................................................................................................. 397

11.1.2 Модификатор partial........................................................................................................... 398

11.1.3 Интерфейсы структуры...................................................................................................... 398

11.1.4 Тело структуры.................................................................................................................. 398

11.2 Члены структуры...................................................................................................................... 398

11.3 Различия между классом и структурой................................................................................... 398

11.3.1 Семантика значений........................................................................................................... 399

11.3.2 Наследование...................................................................................................................... 400

11.3.3 Присваивание...................................................................................................................... 400

11.3.4 Значения по умолчанию..................................................................................................... 400

11.3.5 Упаковка и распаковка....................................................................................................... 401

11.3.6 Действие ключевого слова this.......................................................................................... 403

11.3.7 Инициализаторы полей...................................................................................................... 403

11.3.8 Конструкторы..................................................................................................................... 403

11.3.9 Деструкторы....................................................................................................................... 404

11.3.10 Статические конструкторы.............................................................................................. 404

11.4 Примеры структур.................................................................................................................... 404

11.4.1 Тип целочисленного значения в базе данных................................................................... 405

11.4.2 Логический тип базы данных............................................................................................. 406

12. Массивы........................................................................................................................................ 409

12.1 Типы массива............................................................................................................................ 409

12.1.1 Тип System.Array................................................................................................................ 410

12.1.2 Массивы и универсальный интерфейс IList...................................................................... 410

12.2 Создание массива...................................................................................................................... 411

12.3 Доступ к элементам массива.................................................................................................... 411

12.4 Члены массива.......................................................................................................................... 411

12.5 Ковариация массивов................................................................................................................ 411

12.6 Инициализаторы массива......................................................................................................... 412

13. Интерфейсы................................................................................................................................... 415

13.1 Объявления интерфейсов......................................................................................................... 415

13.1.1 Модификаторы интерфейса............................................................................................... 415

13.1.2 Модификатор partial........................................................................................................... 416

13.1.3 Списки параметров типа варианта.................................................................................... 416

13.1.3.1 Безопасность вариативности....................................................................................... 416

13.1.3.2 Вариантные преобразования....................................................................................... 417

13.1.4 Базовые интерфейсы.......................................................................................................... 417

13.1.5 Тело интерфейса................................................................................................................. 418

13.2 Члены интерфейса.................................................................................................................... 418

13.2.1 Методы интерфейса........................................................................................................... 419

13.2.2 Свойства интерфейса......................................................................................................... 420

13.2.3 События интерфейса.......................................................................................................... 420

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

13.2.5 Доступ к членам интерфейса............................................................................................. 421

13.3 Полные имена членов интерфейса........................................................................................... 423

13.4 Реализация интерфейсов........................................................................................................... 423

13.4.1 Явные реализации членов интерфейса.............................................................................. 424

13.4.2 Уникальность реализованных интерфейсов..................................................................... 426

13.4.3 Реализация универсальных методов................................................................................. 427

13.4.4 Сопоставление интерфейсов.............................................................................................. 428

13.4.5 Наследование реализаций интерфейсов............................................................................ 431

13.4.6 Повторная реализация интерфейса................................................................................... 433

13.4.7 Абстрактные классы и интерфейсы................................................................................... 434

14. Перечисляемые типы................................................................................................................... 435

14.1 Объявления перечислений........................................................................................................ 435

14.2 Модификаторы перечисления.................................................................................................. 436

14.3 Члены перечисления................................................................................................................. 436

14.4 Тип System.Enum....................................................................................................................... 438

14.5 Значения перечисления и операции......................................................................................... 438

15. Делегаты........................................................................................................................................ 441

15.1 Объявления делегатов.............................................................................................................. 441

15.2 Совместимость делегатов........................................................................................................ 443

15.3 Создание экземпляра делегата................................................................................................. 444

15.4 Вызов делегата.......................................................................................................................... 444

16. Исключения.................................................................................................................................. 447

16.1 Причины исключений............................................................................................................... 447

16.2 Класс System.Exception............................................................................................................. 447

16.3 Обработка исключений............................................................................................................. 447

16.4 Общие классы исключений...................................................................................................... 448

17. Атрибуты....................................................................................................................................... 451

17.1 Классы атрибутов..................................................................................................................... 451

17.1.1 Использование атрибутов.................................................................................................. 451

17.1.2 Позиционные и именованные параметры......................................................................... 452

17.1.3 Типы параметров атрибута................................................................................................ 453

17.2 Спецификация атрибута........................................................................................................... 453

17.3 Экземпляры атрибутов............................................................................................................. 459

17.3.1 Компиляция атрибута........................................................................................................ 459

17.3.2 Извлечение экземпляра атрибута во время выполнения.................................................. 459

17.4 Зарезервированные атрибуты.................................................................................................. 460

17.4.1 Атрибут AttributeUsage...................................................................................................... 460

17.4.2 Атрибут Conditional........................................................................................................... 461

17.4.2.1 Условные методы......................................................................................................... 461

17.4.2.2 Классы условных атрибутов....................................................................................... 463

17.4.3 Атрибут Obsolete................................................................................................................ 464

17.5 Атрибуты для взаимодействия................................................................................................. 465

17.5.1 Взаимодействие с компонентами COM и Win32.............................................................. 465

17.5.2 Взаимодействие с другими языками .NET........................................................................ 465

17.5.2.1 Атрибут IndexerName.................................................................................................. 465

18. Небезопасный код........................................................................................................................ 467

18.1 Небезопасные контексты.......................................................................................................... 467

18.2 Типы указателя.......................................................................................................................... 470

18.3 Фиксированные и перемещаемые переменные....................................................................... 472

18.4 Преобразования указателей..................................................................................................... 473

18.4.1 Массивы указателей........................................................................................................... 474

18.5 Указатели в выражениях........................................................................................................... 475

18.5.1 Косвенное обращение по указателю................................................................................. 475

18.5.2 Доступ к члену по указателю............................................................................................ 476

18.5.3 Доступ к элементу по указателю....................................................................................... 477

18.5.4 Оператор адреса................................................................................................................. 477

18.5.5 Увеличение и уменьшение указателя................................................................................ 478

18.5.6 Арифметические операции с указателем.......................................................................... 479

18.5.7 Сравнение указателей........................................................................................................ 479

18.5.8 Оператор sizeof.................................................................................................................. 480

18.6 Оператор fixed.......................................................................................................................... 480

18.7 Буферы фиксированного размера............................................................................................ 484

18.7.1 Объявления буферов фиксированного размера................................................................ 484

18.7.2 Буферы фиксированного размера в выражениях.............................................................. 486

18.7.3 Проверка определенного присваивания............................................................................ 487

18.8 Выделение стека....................................................................................................................... 487

18.9 Динамическое выделение памяти............................................................................................ 488

A. Комментарии к документации.................................................................................................... 491

A.1 Введение..................................................................................................................................... 491

A.2 Рекомендованные теги............................................................................................................... 492

A.2.1 <c>........................................................................................................................................ 493

A.2.2 <code>................................................................................................................................... 494

A.2.3 <example>............................................................................................................................. 494

A.2.4 <exception>........................................................................................................................... 494

A.2.5 <include>............................................................................................................................... 495

A.2.6 <list>...................................................................................................................................... 496

A.2.7 <para>................................................................................................................................... 496

A.2.8 <param>................................................................................................................................ 497

A.2.9 <paramref>............................................................................................................................ 497

A.2.10 <permission>....................................................................................................................... 498

A.2.11 <remark>............................................................................................................................. 498

A.2.12 <returns>............................................................................................................................. 498

A.2.13 <see>................................................................................................................................... 499

A.2.14 <seealso>............................................................................................................................. 499

A.2.15 <summary>.......................................................................................................................... 500

A.2.16 <value>................................................................................................................................ 500

A.2.17 <typeparam>........................................................................................................................ 500

A.2.18 <typeparamref>................................................................................................................... 501

A.3 Обработка файла документации............................................................................................... 501

A.3.1 Формат строки идентификатора......................................................................................... 501

A.3.2 Примеры строк идентификаторов...................................................................................... 503

A.4 Пример........................................................................................................................................ 506

A.4.1 Исходный код C#................................................................................................................. 506

A.4.2 Результирующий XML........................................................................................................ 508

B. Грамматика.................................................................................................................................... 513

B.1 Лексика........................................................................................................................................ 513

B.1.1 Знаки завершения строки.................................................................................................... 513

B.1.2 Комментарии........................................................................................................................ 513

B.1.3 Пробел.................................................................................................................................. 514

B.1.4 Маркеры............................................................................................................................... 514

B.1.5 Управляющие последовательности символов Юникода................................................... 514

B.1.6 Идентификаторы.................................................................................................................. 514

B.1.7 Ключевые слова................................................................................................................... 516

B.1.8 Литералы.............................................................................................................................. 516

B.1.9 Операторы и знаки пунктуации........................................................................................... 518

B.1.10 Директивы предварительной обработки........................................................................... 518

B.2 Синтаксис.................................................................................................................................... 521

B.2.1 Основные принципы............................................................................................................ 521

B.2.2 Типы...................................................................................................................................... 521

B.2.3 Переменные.......................................................................................................................... 523

B.2.4 Выражения............................................................................................................................ 523

B.2.5 Операторы............................................................................................................................ 529

B.2.6 Пространства имен.............................................................................................................. 533

B.2.7 Классы.................................................................................................................................. 534

B.2.8 Структуры............................................................................................................................ 542

B.2.9 Массивы................................................................................................................................ 543

B.2.10 Интерфейсы........................................................................................................................ 543

B.2.11 Перечисления..................................................................................................................... 544

B.2.12 Делегаты............................................................................................................................. 545

B.2.13 Атрибуты............................................................................................................................ 545

B.3 Грамматические расширения для небезопасного кода............................................................ 546

C. Ссылки............................................................................................................................................ 551

 


1. Введение

C# (произносится как Си-шарп или Си-диез) это простой, современный, типобезопасный объектно-ориентированный язык программирования. C# базируется на семействе языков программирования C и будет хорошо знаком программистам, работавшим с языками C, C++ и Java. Язык C# стандартизирован ECMA на соответствие стандарту ECMA-334, а также ISO/IEC на соответствие стандарту ISO/IEC 23270. Компилятор Microsoft C# для платформы .NET Framework реализован в полном соответствии с требованиями обоих стандартов.

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

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

В C# применяется унифицированная система типов. Все типы C#, включая простые типы (например, int и double), наследуются от единственного корневого типа object. Таким образом, все типы используют набор общих операций, что обеспечивает согласованные хранение, передачу и обработку значений любого типа. Кроме того, в C# поддерживаются пользовательские ссылочные типы и типы значений, что обеспечивает динамическое размещение объектов в памяти и встроенное хранение упрощенных структур.

Чтобы обеспечить совместимость и возможность дальнейшего развития программ и библиотек C#, в языке C# большое внимание уделяется управлению версиями. В большинстве языков программирования этому вопросу уделяется недостаточное внимание, в результате чего в создаваемых на таких языках программах чаще обычного возникают проблемы при переходе на новые версии зависимых библиотек. В C# реализованы следующие возможности по управлению версиями: разделение модификаторов virtual и override, применение правил разрешения перегрузки метода и поддержка явного объявления членов интерфейса.

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

1.1 Программа Hello world

Программа Hello, World традиционно используется для знакомства с языком программирования. Ниже приведен пример этой программы на языке C#:

using System;

class Hello
{
static void Main() {
Console.WriteLine("Hello, World");
}
}

Исходные файлы C# обычно имеют расширение cs. Если программа Hello, World сохранена в файле hello.cs, то чтобы откомпилировать ее с помощью компилятора Microsoft C#, введите следующую команду в командной строке:

csc hello.cs

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

Hello, World

Программа Hello, World начинается с директивы using, которая ссылается на пространство имен System. Пространства имен используются для иерархического упорядочения программ и библиотек C#. Пространства имен могут содержать типы и другие пространства имен. Например, пространство имен System содержит набор типов (например, используемый в программе класс Console), а также ряд других пространств имен (например, IO и Collections). Директива using ссылается на заданное пространство имен и обеспечивает безусловное использование типов, являющихся его членами. Благодаря применению директивы using в программе можно использовать сокращенную форму записи Console.WriteLine вместо System.Console.WriteLine.

Класс Hello, объявленный в программе Hello, World, содержит единственный член метод Main. Метод Main объявляется с помощью модификатора static. В C# методы могут ссылаться на конкретный экземпляр вмещающего объекта с помощью ключевого слова this, однако статические методы работают без ссылки на конкретный объект. По соглашению статический метод Main является точкой входа в программу.

Выходные данные программы формируются с помощью метода WriteLine класса Console, принадлежащего пространству имен System. Этот класс содержится в библиотеках классов платформы .NET Framework, на которые по умолчанию автоматически ссылается компилятор Microsoft C#. Обратите внимание, что в C# не используется собственная библиотека времени выполнения. Вместо этого в C# в качестве библиотеки времени выполнения используется платформа .NET Framework.

1.2 Структура программы

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

Например:

using System;

namespace Acme.Collections
{
public class Stack
{
Entry top;

public void Push(object data) {
top = new Entry(top, data);
}

public object Pop() {
if (top == null) throw new InvalidOperationException();
object result = top.data;
top = top.next;
return result;
}

class Entry
{
public Entry next;
public object data;

public Entry(Entry next, object data) {
this.next = next;
this.data = data;
}
}
}
}

В примере объявляется класс Stack, принадлежащий пространству имен Acme.Collections. Полное имя этого класса Acme.Collections.Stack. Этот класс содержит несколько членов: поле top, два метода Push и Pop, а также вложенный класс Entry. Класс Entry в свою очередь содержит три члена: поля next и data, а также конструктор. Если исходный код примера хранится в файле acme.cs, команда

csc /t:library acme.cs

компилирует пример в виде библиотеки (код без точки входа Main) и создает сборку с именем acme.dll.

Сборки содержат исполняемый код в форме инструкций промежуточного языка (Intermediate Language, IL), а также символьные данные в форме метаданных. Перед выполнением код IL сборки автоматически преобразуется в код для конкретного процессора с помощью JIT-компилятора среды .NET CLR.

Поскольку сборка представляет собой самодокументируемый функциональный модуль, содержащий как код, так и метаданные, в файлах заголовка C# не требуется использовать директивы #include. В программе C# возможность обращения к открытым типам и членам, содержащимся в конкретной сборке, реализуется посредством ссылки на эту сборку во время компиляции программы. Например, если в программе используется класс Acme.Collections.Stack, содержащийся в сборке acme.dll:

using System;
using Acme.Collections;

class Test
{
static void Main() {
Stack s = new Stack();
s.Push(1);
s.Push(10);
s.Push(100);
Console.WriteLine(s.Pop());
Console.WriteLine(s.Pop());
Console.WriteLine(s.Pop());
}
}

Если программа хранится в файле test.cs, во время его компиляции ссылка на сборку acme.dll реализуется с помощью параметра компилятора /r:

csc /r:acme.dll test.cs

В результате этого создается исполняемый файл сборки test.exe, при выполнении которого получаются следующие выходные данные:

100
10
1

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

1.3 Типы и переменные

В C# все типы подразделяются на две основные категории: типы значений и ссылочные типы. Переменные типа значений непосредственно содержат данные, тогда как переменные ссылочного типа хранят ссылки на соответствующие данные (объекты). Две переменные ссылочного типа могут ссылаться на один объект. Это позволяет изменять объект, на который ссылается одна переменная, выполняя соответствующие операции с другой. Каждая переменная типа значений содержит собственную копию данных. В связи с этим операции с одной переменной не влияют на другую (за исключением переменных параметров ref и out).

Типы значений в C# подразделяются на простые типы, перечисляемые типы, типы структур и обнуляемые типы. Ссылочные типы в C# подразделяются на типы классов, типы интерфейсов, типы массивов и типы делегатов.

В следующей таблице представлен обзор системы типов C#.

 

Категория Описание
Типы значений Простые типы Целые со знаком: sbyte, short, int, long
Целые без знака: byte, ushort, uint, ulong
Символы Юникода: char
IEEE с плавающей запятой: float, double
Десятичный с повышенной точностью: decimal
Логический: bool
Перечисляемые типы Пользовательские типы вида enum E {...}
Типы структур Пользовательские типы вида struct S {...}
Обнуляемые типы Расширения любых других типов значений, допускающие значение null
Ссылочные типы Типы классов Первичный базовый класс для всех типов: object
Строки Юникода: string
Пользовательские типы вида class C {...}
Типы интерфейсов Пользовательские типы вида interface I {...}
Типы массивов Одно- и многомерные, например int[] и int[,]
Типы делегатов Пользовательские типы, например вида delegate int D(...)

 

Восемь целых типов обеспечивают поддержку 8-, 16-, 32- и 64-разрядных значений со знаком и без знака.

Два типа с плавающей запятой (float и double) представляются в 32-разрядном (одинарная точность) или 64-разрядном (двойная точность) формате IEEE 754.

Тип decimal представляет собой 128-разрядный тип данных, ориентированный на применение в финансовых и денежных вычислениях.

Тип bool в языке C# используется для представления логических значений true или false.

Обработка знаков и строк в C# выполняется с применением кодировки Юникод. Тип char представляет элемент кода UTF-16, а тип string последовательность элементов кода UTF-16.

В следующей таблице представлено краткое описание числовых типов C#.

 

Категория Разрядность Тип Диапазон и точность
Целые со знаком sbyte 128...127
short 32 768...32 767
int 2 147 483 648...2 147 483 647
long 9 223 372 036 854 775 808... 9 223 372 036 854 775 807
Целые без знака byte 0...255
ushort 0...65 535
uint 0...4 294 967 295
ulong 0...18 446 744 073 709 551 615
С плавающей запятой float От 1,5 10−45 до 3,4 1038 с точностью до 7 знаков
double От 5,0 10−324 до 1,7 10308 с точностью до 15 знаков
Десятичный decimal От 1,0 10−28 до 7,9 1028 с точностью до 28 знаков

 

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

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

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

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

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

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

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

В C# поддерживаются одно- и многомерные массивы любого типа. В отличие от типов, перечисленных выше, типы массива необязательно объявлять перед использованием. Чтобы сформировать тип массива, необходимо ввести квадратные скобки после имени массива. Например, int[] это одномерный массив типа int; int[,] двумерный массив типа int, а int[][] одномерный массив, состоящий из одномерных массивов типа int.

Обнуляемые типы также не требуется объявлять перед использованием. Для каждого необнуляемого типа значений T существует соответствующий обнуляемый тип T?, который дополнительно содержит значение null. Например, тип int? может содержать любое 32-разрядное целое значение, а также значение null.

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

using System;

class Test
{
static void Main() {
int i = 123;
object o = i; // Boxing
int j = (int)o; // Unboxing
}
}

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

В унифицированной системе типов C# типы значений могут преобразовываться в объекты по требованию. Благодаря унификации, как ссылочные типы, так и типы значений могут использовать универсальные библиотеки, использующие тип object.

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

 

Тип переменной Возможное содержимое
Необнуляемый тип значений Значение такого же типа
Обнуляемый тип значений Значение null или значение такого же типа
object Пустая ссылка, ссылка на объект любого ссылочного типа или ссылка на упакованное значение любого типа значений
Тип класса Пустая ссылка, ссылка на экземпляр заданного типа класса или ссылка на экземпляр класса, производного от заданного типа
Тип интерфейса Пустая ссылка, ссылка на экземпляр типа класса, который реализует заданный тип интерфейса, или ссылка на упакованное значение типа значений, который реализует заданный тип интерфейса
Тип массива Пустая ссылка, ссылка на экземпляр заданного типа массива или ссылка на экземпляр совместимого типа массива
Тип делегата Пустая ссылка или ссылка на экземпляр заданного типа делегата

 

1.4 Выражения

Выражения состоят из операндов и операторов. Операторы в выражении указывают, какие операции производятся с операндами. К операторам относятся, например, +, -, *, / и new. К операндам относятся, например, литералы, поля, локальные переменные и выражения.

Когда выражение содержит несколько операторов, порядок вычисления отдельных операторов задается приоритетом операторов. Например, выражение x + y * z вычисляется как x + (y * z), поскольку оператор * имеет более высокий приоритет по сравнению с оператором +.

Большинство операторов могут быть перегружены. Перегрузка операторов позволяет задавать пользовательские реализации операторов, в которых один или несколько операндов имеют пользовательский тип класса или структуры.

В следующей таблице приводится описание операторов C# в порядке убывания приоритета соответствующих категорий операторов. Операторы одной категории обладают одинаковым приоритетом.

 

Категория Expression Описание
Первичный x.m Метод доступа к члену
x(...) Вызов методов и делегатов
x[...] Доступ к массиву и индексатору
x++ Постфиксный инкремент
x-- Постфиксный декремент
new T(...) Создание объекта и делегата
new T(...){...} Создание объекта с использованием инициализатора
new {...} Инициализатор анонимного объекта
new T[...] Создание массива
typeof(T) Получение объекта System.Type для T
checked(x) Вычисление выражения в контексте checked
unchecked(x) Вычисление выражения в контексте unchecked
default(T) Получение значения по умолчанию типа T
delegate {...} Анонимная функция (анонимный метод)
Унарный +x Идентификация
-x Отрицание
!x Логическое отрицание
~x Побитовое отрицание
++x Префиксный инкремент
--x Префиксный декремент
(T)x Явное преобразование x к типу T
Мультипликатив-ный x * y Умножение
x / y Деление
x % y Остаток
Аддитивный x + y Сложение, объединение строк, объединение делегатов
x y Вычитание, удаление делегата

 

Сдвиг x << y Поразрядный сдвиг влево
x >> y Поразрядный сдвиг вправо
Отношение и проверка типа x < y Меньше
x > y Больше
x <= y Меньше или равно
x >= y Больше или равно
x is T Возвращает true, если x имеет тип T; в противном случае возвращает false
x as T Возвращает значение x, типизированное как T, или null, если x имеет тип, отличный от T
Равенство x == y Равно
x != y Не равно
Логическое И x & y Целое побитовое И, логическое И
Исключающее ИЛИ x ^ y Целое побитовое исключающее ИЛИ, логическое исключающее ИЛИ
Логическое ИЛИ x | y Целое побитовое ИЛИ, логическое ИЛИ
Условное И x && y y вычисляется только в том случае, если x имеет значение true
Условное ИЛИ x || y y вычисляется только в том случае, если x имеет значение false
Объединение с null X ?? y Если x имеет значение null, вычисляется y; в противном случае вычисляется x
Условный x ? y : z Если x имеет значение true, вычисляется y; если x имеет значение false, вычисляется z
Присваивание или анонимная функция x = y Присваивание
x op= y Составное присваивание; поддерживаются следующие операторы: *= /= %= += -= <<= >>= &= ^= |=
(T x) => y Анонимная функция (лямбда-выражение)

 

1.5 Операторы языка

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

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

Операторы объявления используются для объявления локальных переменных и констант.

Операторы выражений используются для вычисления выражений. Как операторы могут использоваться следующие выражения: вызовы методов, выделение объектов с помощью оператора new, операторы присваивания = и составного присваивания, операции приращения и уменьшения с помощью операторов ++ и --.

Операторы выбора используются для выполнения одного из нескольких возможных операторов на основании значения какого-либо выражения. В этой группе представлены операторы if и switch.

Операторы итераций используются для повторяющегося выполнения внедренного оператора. В этой группе представлены операторы while, do, for и foreach.

Операторы перехода используются для передачи управления. В этой группе представлены операторы break, continue, goto, throw, return и yield.

Оператор try...catch используется для перехвата исключений, происходящих во время выполнения блока. Оператор try...finally используется для задания кода финализации, который выполняется всегда (независимо от того, возникло или нет исключение).

Операторы checked и unchecked используются для управления контекстом контроля переполнения в арифметических операциях и преобразованиях целых типов.

Оператор lock используется для получения блокировки взаимного исключения для заданного объекта, выполнения оператора и последующего снятия блокировки.

Оператор using используется для получения ресурса, выполнения оператора и последующего удаления ресурса.

В следующей таблице представлены операторы C# и примеры их использования.

 

Оператор Пример
Объявление локальной переменной static void Main() { int a; int b = 2, c = 3; a = 1; Console.WriteLine(a + b + c); }
Объявление локальной константы static void Main() { const float pi = 3.1415927f; const int r = 25; Console.WriteLine(pi * r * r); }
Оператор выражения static void Main() { int i; i = 123; // Expression statement Console.WriteLine(i); // Expression statement i++; // Expression statement Console.WriteLine(i); // Expression statement }
Оператор if static void Main(string[] args) { if (args.Length == 0) { Console.WriteLine("No arguments"); } else { Console.WriteLine("One or more arguments"); } }

 

Оператор switch static void Main(string[] args) { int n = args.Length; switch (n) { case 0: Console.WriteLine("No arguments"); break; case 1: Console.WriteLine("One argument"); break; default: Console.WriteLine("{0} arguments", n); break; } } }
Оператор while static void Main(string[] args) { int i = 0; while (i < args.Length) { Console.WriteLine(args[i]); i++; } }
Оператор do static void Main() { string s; do { s = Console.ReadLine(); if (s != null) Console.WriteLine(s); } while (s != null); }
Оператор for static void Main(string[] args) { for (int i = 0; i < args.Length; i++) { Console.WriteLine(args[i]); } }
Оператор foreach static void Main(string[] args) { foreach (string s in args) { Console.WriteLine(s); } }
Оператор break static void Main() { while (true) { string s = Console.ReadLine(); if (s == null) break; Console.WriteLine(s); } }
Оператор continue static void Main(string[] args) { for (int i = 0; i < args.Length; i++) { if (args[i].StartsWith("/")) continue; Console.WriteLine(args[i]); } }

 

Оператор goto static void Main(string[] args) { int i = 0; goto check; loop: Console.WriteLine(args[i++]); check: if (i < args.Length) goto loop; }
Оператор return static int Add(int a, int b) { return a + b; } static void Main() { Console.WriteLine(Add(1, 2)); return; }
Оператор yield static IEnumerable<int> Range(int from, int to) { for (int i = from; i < to; i++) { yield return i; } yield break; } static void Main() { foreach (int x in Range(-10,10)) { Console.WriteLine(x); } }
Операторы throw и try static double Divide(double x, double y) { if (y == 0) throw new DivideByZeroException(); return x / y; } static void Main(string[] args) { try { if (args.Length != 2) { throw new Exception("Two numbers required"); } double x = double.Parse(args[0]); double y = double.Parse(args[1]); Console.WriteLine(Divide(x, y)); } catch (Exception e) { Console.WriteLine(e.Message); } finally { Console.WriteLine(Good bye!); } }
Операторы checked и unchecked static void Main() { int i = int.MaxValue; checked { Console.WriteLine(i + 1); // Exception } unchecked { Console.WriteLine(i + 1); // Overflow } }

 

Оператор lock class Account { decimal balance; public void Withdraw(decimal amount) { lock (this) { if (amount > balance) { throw new Exception("Insufficient funds"); } balance -= amount; } } }
Оператор using static void Main() { using (TextWriter w = File.CreateText("test.txt")) { w.WriteLine("Line one"); w.WriteLine("Line two"); w.WriteLine("Line three"); } }

 

1.6 Классы и объекты

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

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

Ниже приведено объявление простого класса Point:

public class Point
{
public int x, y;

public Point(int x, int y) {
this.x = x;
this.y = y;
}
}

Экземпляры класса создаются с помощью оператора new, который выделяет память для нового экземпляра, вызывает конструктор для инициализации экземпляра и возвращает ссылку на экземпляр. С помощью следующих операторов создаются два объекта Point, ссылки на которые сохраняются в двух переменных:

Point p1 = new Point(0, 0);
Point p2 = new Point(10, 20);

Память, занимаемая объектом, автоматически освобождается, если объект более не используется. В C# не обязательно (и не допускается) явно освобождать память от объектов.

1.6.1 Члены

Класс может содержать статические члены или члены экземпляра. Статические члены принадлежат классам. Члены экземпляра принадлежат объектам (экземплярам класса).

В следующей таблице представлен обзор видов членов, которые может содержать класс.

 

Член Описание
Константы Постоянные значения, связанные с классом
Поля Переменные класса
Методы Вычисления и действия, которые могут выполняться классом
Свойства Действия, связанные с чтением и записью именованных свойств класса
Индексаторы Действия, связанные с индексацией экземпляров класса, например массива
События Уведомления, которые могут формироваться классом
Операторы Операторы преобразования и выражений, поддерживаемые классом
Конструкторы Действия, необходимые для инициализации экземпляров класса или самого класса
Деструкторы Действия, выполняемые перед окончательным удалением экземпляров класса
Типы Вложенные типы, объявленные в классе

 

1.6.2 Доступность

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

 

Доступность Значение
public Доступ не ограничен
protected Доступ ограничен этим классом и унаследованными от него классами
internal Доступ ограничен этой программой
protected internal Доступ ограничен этой программой и классами, унаследованными от этого класса
private Доступ ограничен этим классом

 

1.6.3 Параметры типа

В определении класса может задаваться набор параметров типа. Список имен параметров типа указывается за именем класса в угловых скобках. Параметры типа могут использоваться в теле объявлений класса для определения членов класса. В следующем примере для класса Pair задаются параметры типа TFirst и TSecond:

public class Pair<TFirst,TSecond>
{
public TFirst First;

public TSecond Second;
}

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

При использовании универсального класса для каждого параметра типа необходимо указать аргумент типа:

Pair<int,string> pair = new Pair<int,string> { First = 1, Second = two };
int i = pair.First; // TFirst is int
string s = pair.Second; // TSecond is string

Универсальный тип с предоставленными аргументами типа, например Pair<int,string>, называется сформированным типом.

1.6.4 Базовые классы

В объявлении класса может задаваться базовый класс, имя которого записывается после имени класса и параметров типа через двоеточие. Класс, для которого не задан базовый класс, считается производным от типа object. В следующем примере для класса Point3D базовым является Point, а базовым классом для Point является object:

public class Point
{
public int x, y;

public Point(int x, int y) {
this.x = x;
this.y = y;
}
}

public class Point3D: Point
{
public int z;

public Point3D(int x, int y, int z): base(x, y) {
this.z = z;
}
}

Класс наследует члены своего базового класса. Наследование означает, что класс неявно содержит все члены его базового класса, исключая конструкторы экземпляров и статические конструкторы, а также деструкторы базового класса. Производный класс может добавлять новые члены к своим унаследованным членам, но он не может удалить определение унаследованного члена. В предыдущем примере класс Point3D наследует поля x и y класса Point. Это означает, что каждый экземпляр класса Point3D содержит три поля: x, y и z.

Существует неявное преобразование из типа класса к любому из его базовых типов класса. Таким образом, переменная типа класса может ссылаться на экземпляр этого класса или любого производного от него класса. Например, если сделаны предыдущие объявления классов, переменная типа Point может ссылаться как на экземпляр класса Point, так и на экземпляр класса Point3D:

Point a = new Point(10, 20);
Point b = new Point3D(10, 20, 30);

1.6.5 Поля

Поле представляет собой переменную, связанную с классом или экземпляром класса.

Поле, объявленное с использованием модификатора static, определяет статическое поле. Статическое поле определяет только одно место хранения. Вне зависимости от количества создаваемых экземпляров класса всегда существует только одна копия статического поля.

Поле, объявленное без использования модификатора static, определяет поле экземпляра. Каждый экземпляр класса содержит отдельную копию всех полей экземпляра класса.

В следующем примере каждый экземпляр класса Color содержит отдельную копию полей экземпляра r, g и b. Однако существует только одна копия статических полей Black, White, Red, Green и Blue:

public class Color
{
public static readonly Color Black = new Color(0, 0, 0);
public static readonly Color White = new Color(255, 255, 255);
public static readonly Color Red = new Color(255, 0, 0);
public static readonly Color Green = new Color(0, 255, 0);
public static readonly Color Blue = new Color(0, 0, 255);

private byte r, g, b;

public Color(byte r, byte g, byte b) {
this.r = r;
this.g = g;
this.b = b;
}
}

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

1.6.6 Методы

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

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

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

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

1.6.6.1 Параметры

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

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

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

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

using System;

class Test
{
static void Swap(ref int x, ref int y) {
int temp = x;
x = y;
y = temp;
}

static void Main() {
int i = 1, j = 2;
Swap(ref i, ref j);
Console.WriteLine("{0} {1}", i, j); // Outputs "2 1"
}
}

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

using System;

class Test
{
static void Divide(int x, int y, out int result, out int remainder) {
result = x / y;
remainder = x % y;
}

static void Main() {
int res, rem;
Divide(10, 3, out res, out rem);
Console.WriteLine("{0} {1}", res, rem); // Outputs "3 1"
}
}

Массив-параметр используется для передачи методу переменного числа аргументов. Массив-параметр объявляется с помощью модификатора params. В качестве массива-параметра может использоваться только последний параметр метода. Массив-параметр должен являться одномерным массивом. Методы Write и WriteLine класса System.Console являются хорошими примерами использования массивов-параметров. Эти методы объявляются следующим образом.

public class Console
{
public static void Write(string fmt, params object[] args) {...}

public static void WriteLine(string fmt, params object[] args) {...}

...
}

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

Console.WriteLine("x={0} y={1} z={2}", x, y, z);

равнозначен следующей записи:

string s = "x={0} y={1} z={2}";
object[] args = new object[3];
args[0] = x;
args[1] = y;
args[2] = z;
Console.WriteLine(s, args);

1.6.6.2 Тело метода и локальные переменные

В теле метода определяются операторы, выполняемые при его вызове.

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

using System;

class Squares
{
static void Main() {
int i = 0;
int j;
while (i < 10) {
j = i * i;
Console.WriteLine("{0} x {0} = {1}", i, j);
i = i + 1;
}
}
}

В C# значение локальной переменной можно использовать только после явного присваивания ей значения. Например, если переменной i (см. выше) не присвоено начальное значение, при последующем ее использовании возникнет ошибка компиляции, поскольку в момент использования переменной i ей явно не присвоено значение.

В методе можно использовать операторы return для передачи управления вызвавшему его объекту. В методе, возвращающем void, операторы return не могут задавать выражение. В методе, возвращающем отличное от void значение, операторы return должны включать выражение, вычисляющее возвращаемое значение.

1.6.6.3 Статические методы и методы экземпляров

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

Метод, объявленный без использования модификатора static, называется методом экземпляра. Метод экземпляра выполняет операции только с конкретным экземпляром и может обращаться как к статическим членам, так и к членам экземпляра. Явное обращение к экземпляру, для которого вызывается метод экземпляра, выполняется с помощью ключевого слова this. При ссылке на this в статическом методе возникает ошибка.

В следующем примере класс Entity содержит как статические члены, так и члены экземпляра.

class Entity
{
static int nextSerialNo;

int serialNo;

public Entity() {
serialNo = nextSerialNo++;
}

public int GetSerialNo() {
return serialNo;
}

public static int GetNextSerialNo() {
return nextSerialNo;
}

public static void SetNextSerialNo(int value) {
nextSerialNo = value;
}
}

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

Статические методы GetNextSerialNo и SetNextSerialNo могут обращаться к статическому полю nextSerialNo, однако при непосредственном обращении этих методов к полю экземпляра serialNo возникнет ошибка.

В следующем примере показано использование класса Entity.

using System;

class Test
{
static void Main() {
Entity.SetNextSerialNo(1000);

Entity e1 = new Entity();
Entity e2 = new Entity();

Console.WriteLine(e1.GetSerialNo()); // Outputs "1000"
Console.WriteLine(e2.GetSerialNo()); // Outputs "1001"
Console.WriteLine(Entity.GetNextSerialNo()); // Outputs "1002"
}
}

Обратите внимание, что статические методы SetNextSerialNo и GetNextSerialNo вызываются для класса, а метод экземпляра GetSerialNo для экземпляра класса.

1.6.6.4 Виртуальные, переопределяющие и абстрактные методы

Если объявление метода экземпляра содержит модификатор virtual, метод является виртуальным методом. Если модификатор virtual отсутствует, метод является невиртуальным методом.

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

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

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

В следующем примере объявляется абстрактный класс Expression, представляющий узел дерева выражений, а также три производных класса: Constant, VariableReference и Operation, которые реализуют узлы дерева выражений для констант, ссылок на переменные и арифметических операций. (Эти классы похожи на типы дерева выражений, представленные в разделе 4.6. Однако их не следует путать.)

using System;
using System.Collections;

public abstract class Expression
{
public abstract double Evaluate(Hashtable vars);
}

public class Constant: Expression
{
double value;

public Constant(double value) {
this.value = value;
}

public override double Evaluate(Hashtable vars) {
return value;
}
}

public class VariableReference: Expression
{
string name;

public VariableReference(string name) {
this.name = name;
}

public override double Evaluate(Hashtable vars) {
object value = vars[name];
if (value == null) {
throw new Exception("Unknown variable: " + name);
}
return Convert.ToDouble(value);
}
}

public class Operation: Expression
{
Expression left;
char op;
Expression right;

public Operation(Expression left, char op, Expression right) {
this.left = left;
this.op = op;
this.right = right;
}

public override double Evaluate(Hashtable vars) {
double x = left.Evaluate(vars);
double y = right.Evaluate(vars);
switch (op) {
case '+': return x + y;
case '-': return x - y;
case '*': return x * y;
case '/': return x / y;
}
throw new Exception("Unknown operator");
}
}

Четыре приведенных выше класса могут использоваться для моделирования арифметических выражений. Например, с помощью экземпляров этих классов выражение x + 3 можно представить следующим образом.

Expression e = new Operation(
new VariableReference("x"),
'+',
new Constant(3));

Метод Evaluate экземпляра Expression вызывается для вычисления заданного выражения и возвращает значение типа double. Метод принимает в качестве аргумента параметр Hashtable, содержащий имена переменных (в качестве ключей записей) и значения (в качестве значений записей). Метод Evaluate представляет собой виртуальный абстрактный метод. Это означает, что в производных от него неабстрактных классах необходимо переопределить этот метод и предоставить его фактическую реализацию.

Реализация Constant метода Evaluate возвращает хранящееся значение константы. Реализация VariableReference осуществляет поиск имени переменной в хэш-таблице и возвращает значение результата. Реализация Operation сначала выполняет вычисление левого и правого операндов (посредством рекурсивного вызова соответствующих методов Evaluate), а затем выполняет заданную арифметическую операцию.

В следующей программе классы Expression используются для вычисления выражения x * (y + 2) с различными значениями x и y.

using System;
using System.Collections;

class Test
{
static void Main() {

Expression e = new Operation(
new VariableReference("x"),
'*',
new Operation(
new VariableReference("y"),
'+',
new Constant(2)
)
);

Hashtable vars = new Hashtable();

vars["x"] = 3;
vars["y"] = 5;
Console.WriteLine(e.Evaluate(vars)); // Outputs "21"

vars["x"] = 1.5;
vars["y"] = 9;
Console.WriteLine(e.Evaluate(vars)); // Outputs "16.5"
}
}

1.6.6.5 Перегрузка метода

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

class Test
{
static void F() {
Console.WriteLine("F()");
}

static void F(object x) {
Console.WriteLine("F(object)");
}

static void F(int x) {
Console.WriteLine("F(int)");
}

static void F(double x) {
Console.WriteLine("F(double)");
}

static void F<T>(T x) {
Console.WriteLine("F<T>(T)");
}

static void F(double x, double y) {
Console.WriteLine("F(double, double)");
}

static void Main() {
F(); // Invokes F()
F(1); // Invokes F(int)
F(1.0); // Invokes F(double)
F("abc"); // Invokes F(object)
F((double)1); // Invokes F(double)
F((object)1); // Invokes F(object)
F<int>(1); // Invokes F<T>(T)
F(1, 1); // Invokes F(double, double) }
}

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

1.6.7 Другие члены-функции

Члены класса, содержащие исполняемый код, в совокупности называются члены-функции. В предыдущем разделе описаны методы, представляющие собой простые члены-функции. В этом разделе описываются другие типы членов-функций, поддерживаемые в C#: конструкторы, свойства, индексаторы, события, операторы и деструкторы.

В следующей таблице описывается универсальный класс List<T>, который реализует расширяемый список объектов. Класс содержит несколько примеров наиболее распространенных типов членов-функций.

public class List<T> {
const int defaultCapacity = 4; Константа
T[] items; int count; Поля
public List(int capacity = defaultCapacity) { items = new T[capacity]; } Конструкторы
public int Count { get { return count; } } public int Capacity { get { return items.Length; } set { if (value < count) value = count; if (value != items.Length) { T[] newItems = new T[value]; Array.Copy(items, 0, newItems, 0, count); items = newItems; } } } Свойства

 

public T this[int index] { get { return items[index]; } set { items[index] = value; OnChanged(); } } Индексатор
public void Add(T item) { if (count == Capacity) Capacity = count * 2; items[count] = item; count++; OnChanged(); } protected virtual void OnChanged() { if (Changed != null) Changed(this, EventArgs.Empty); } public override bool Equals(object other) { return Equals(this, other as List<T>); } static bool Equals(List<T> a, List<T> b) { if (a == null) return b == null; if (b == null || a.count != b.count) return false; for (int i = 0; i < a.count; i++) { if (!object.Equals(a.items[i], b.items[i])) { return false; } } return true; } Методы
public event EventHandler Changed; Событие
public static bool operator ==(List<T> a, List<T> b) { return Equals(a, b); } public static bool operator !=(List<T> a, List<T> b) { return !Equals(a, b); } Операторы
}

 

1.6.7.1 Конструкторы

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

Конструктор объявляется аналогично методу без типа возвращаемого значения и имеет то же имя, что и содержащий его класс. Объявление конструктора, содержащее модификатор static, объявляет статический конструктор. В противном случае объявляется конструктор экземпляра.

Конструкторы экземпляров можно перегружать. Например, в классе List<T> объявляются два конструктора: один без параметров и один, принимающий параметр типа int. Вызов конструктора экземпляра осуществляется с помощью оператора new. В следующем примере выделяются два экземпляра List<string> с использованием каждого из конструкторов класса List.

List<string> list1 = new List<string>();
List<string> list2 = new List<string>(10);

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

1.6.7.2 Свойства

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

Свойство объявляется аналогично полю, однако объявление свойства должно заканчиваться не точкой с запятой, а методами доступа get или set, записанными между разделителями { и }. Свойство, для которого определены оба метода доступа get и set, называется свойством для чтения и записи. Свойство, для которого определен только метод доступа get, называется свойством только для чтения. Свойство, для которого определен только метод доступа set, называется свойством только для записи.

Метод доступа get соответствует не содержащему параметров методу, возвращаемое значение которого имеет тип свойства. За исключением случаев, когда свойство является конечным объектом операции присваивания, при ссылке на свойство в выражении вызывается метод доступа get для вычисления значения свойства.

Метод доступа set соответствует методу с одним параметром value, не имеющему возвращаемого типа. При ссылке на свойство как на целевой объект операции присваивания или как на операнд операторов ++ и -- метод доступа set вызывается с аргументом, который предоставляет новое значение.

В классе List<T> объявляются два свойства: Count и Capacity (только для чтения и только для записи соответственно). В следующем примере показано использование этих свойств.

List<string> names = new List<string>();
names.Capacity = 100; // Invokes set accessor
int i = names.Count; // Invokes get accessor
int j = names.Capacity; // Invokes get accessor

Как и в случае с полями и методами, в C# поддерживаются свойства экземпляров и статические свойства. Свойства, объявленные с использованием модификатора static, называются статическими. Все остальные свойства называются свойствами экземпляров.

Методы доступа свойства могут быть виртуальными. Если объявление свойства содержит модификатор virtual, abstract или override, соответствующий тип применяется и к его методам доступа.

1.6.7.3 Индексаторы

Индексатор это член, предназначенный для индексации объектов (аналогично массивам). Индексатор объявляется аналогично свойству, однако в качестве имени члена используется this, за которым следует список параметров, записанный между разделителями [ и ]. Параметры доступны в методах доступа индексатора. Как и свойства, индексаторы могут быть доступны только на чтение, только на запись или на чтение и запись. Методы доступа индексатора могут быть виртуальными.

В классе List объявляется один индексатор для чтения и записи, принимающий параметр int. С его помощью обеспечивается индексация экземпляров класса List с использованием значений типа int. Пример:

List<string> names = new List<string>();
names.Add("Liz");
names.Add("Martha");
names.Add("Beth");
for (int i = 0; i < names.Count; i++) {
string s = names[i];
names[i] = s.ToUpper();
}

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

1.6.7.4 События

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

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

В классе List<T> объявляется член-событие Changed, указывающий на добавление нового элемента в список. Событие Changed вызывается виртуальным методом OnChanged, в котором сначала проверяется, имеет ли событие значение null (т. е. для события отсутствуют обработчики). Понятие вызова события совершенно эквивалентно вызову делегата, представленного событием. Поэтому не существует специальных языковых конструкций для вызова событий.

Реакция клиента на событие реализуется с помощью обработчиков событий. Для добавления обработчиков событий используется оператор +=, для удаления оператор -=. В следующем примере к событию Changed класса List<string> присоединяется обработчик событий.

using System;

class Test
{
static int changeCount;

static void ListChanged(object sender, EventArgs e) {
changeCount++;
}

static void Main() {
List<string> names = new List<string>();
names.Changed += new EventHandler(ListChanged);
names.Add("Liz");
names.Add("Martha");
names.Add("Beth");
Console.WriteLine(changeCount); // Outputs "3"
}
}

В расширенных сценариях, в которых требуется управление базовым хранилищем события, в объявлении события можно явно определить методы доступа add и remove, которые во многом аналогичны методу доступа set свойства.

1.6.7.5 Операторы

Оператор это член, который определяет значение применения конкретного оператора выражения к экземплярам класса. Поддерживается определение операторов трех видов: унарные операторы, двоичные операторы и операторы преобразования. Все операторы должны объявляться с использованием модификаторов public и static.

В классе List<T> объявляются два оператора: == и !=, что позволяет определить новое значение для выражений, которые применяют эти операторы к экземплярам класса List. В частности, операторы определяют равенство двух экземпляров класса List<T> посредством сравнения всех содержащихся в них объектов с помощью методов Equals. В следующем примере оператор == используется для сравнения двух экземпляров класса List<int>.

using System;

class Test
{
static void Main() {
List<int> a = new List<int>();
a.Add(1);
a.Add(2);
List<int> b = new List<int>();
b.Add(1);
b.Add(2);
Console.WriteLine(a == b); // Outputs "True"
b.Add(3);
Console.WriteLine(a == b); // Outputs "False"
}
}

Первое выражение Console.WriteLine возвращает True, поскольку два списка содержат одинаковое число объектов с одинаковыми значениями и в одинаковом порядке. Если бы в классе List<T> не был определен оператор ==, первое выражение Console.WriteLine возвращало бы False, поскольку a и b ссылаются на различные экземпляры класса List<int>.

1.6.7.6 Деструкторы

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

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

Наиболее эффективный подход к уничтожению объектов обеспечивается при использовании оператора using.

1.7 Структуры

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

Структуры особенно удобны для работы с небольшим объемом данных, имеющих семантику значения. Примерами структур являются комплексные числа, точки в системе координат или словарные пары ключ-значение. Применение структур вместо классов при работе с небольшими структурами данных позволяет добиться значительной экономии выделяемой памяти и повышения производительности приложения. Например, в следующей программе создается и инициализируется массив из 100 точек. Если в качестве класса реализуется Point, создается 101 экземпляр объектов один для массива и по одному для каждого из 100 его элементов.

class Point
{
public int x, y;

public Point(int x, int y) {
this.x = x;
this.y = y;
}
}

class Test
{
static void Main() {
Point[] points = new Point[100];
for (int i = 0; i < 100; i++) points[i] = new Point(i, i);
}
}

Также можно реализовать Point как структуру.

struct Point
{
public int x, y;

public Point(int x, int y) {
this.x = x;
this.y = y;
}
}

В этом случае создается только один экземпляр объекта для массива. Экземпляры Point хранятся встроенными в массив.

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

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

Point a = new Point(10, 10);
Point b = a;
a.x = 20;
Console.WriteLine(b.x);

Если Point является классом, возвращается значение 20, поскольку a и b ссылаются на один объект. Если Point является структурой, возвращается значение 10, поскольку при присваивании a экземпляру b создается копия значения, на которую не влияет последующее присваивание a.x.

В предыдущем примере показаны два ограничения, накладываемые на структуры. Во-первых, копирование всей структуры обычно менее эффективно, чем копирование ссылки на объект, из-за чего операции присваивания и передачи параметров значения для структур потребляют больше ресурсов, чем аналогичные операции для ссылочных типов. Во-вторых, не поддерживается создание ссылок на структуры (за исключением параметров ref и out), что в некоторых случаях не позволяет использовать структуры.

1.8 Массивы

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

Типы массивов являются ссылочными типами, поэтому при объявлении переменной типа массива выделяется память для ссылки на экземпляр массива. Фактические экземпляры массива создаются динамически во время выполнения с помощью оператора new. Оператор new определяет длину создаваемого экземпляра массива, которая остается неизменной в течение всего времени существования экземпляра. Элементам массива присваиваются индексы в диапазоне от 0 до Length - 1. Оператор new автоматически инициализирует элементы массива с использованием значений по умолчанию (нули для всех числовых типов или null для всех ссылочных типов).

В следующем примере создается и инициализируется массив элементов типа int, после чего выводится содержимое созданного массива.

using System;

class Test
{
static void Main() {
int[] a = new int[10];
for (int i = 0; i < a.Length; i++) {
a[i] = i * i;
}
for (int i = 0; i < a.Length; i++) {
Console.WriteLine("a[{0}] = {1}", i, a[i]);
}
}
}

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

int[] a1 = new int[10];
int[,] a2 = new int[10, 5];
int[,,] a3 = new int[10, 5, 2];

Массив a1 содержит 10 элементов, массив a2 50 (10 5) элементов, а массив a3 100 (10 5 2) элементов.

Элемент массива может иметь любой тип, в том числе тип массива. Массив, содержащий элементы типа массива (jagged array, или массив массивов), иногда называется неравномерным массивом, поскольку его элементы могут иметь различную длину. В следующем примере выделяется память для неравномерного массива типа int:

int[][] a = new int[3][];
a[0] = new int[10];
a[1] = new int[5];
a[2] = new int[20];

В первой строке создается массив, состоящий из трех элементов типа int[], каждый из которых имеет начальное значение null. В последующих строках инициализируются три элемента со ссылками на отдельные экземпляры массивов различной длины.

Оператор new позволяет задать начальные значения элементов массива с помощью инициализатора массива, который представляет собой список выражений, записанных между разделителями { и }. В следующем примере выделяется и инициализируется массив int[], содержащий три элемента.

int[] a = new int[] {1, 2, 3};

Обратите внимание, что длина массива определяется на основании числа выражений между фигурными скобками { и }. Поддерживается сокращенная форма объявления локальных переменных и полей, что позволяет не указывать повторно тип массива.

int[] a = {1, 2, 3};

Оба предыдущих примера эквивалентны следующему:

int[] t = new int[3];
t[0] = 1;
t[1] = 2;
t[2] = 3;
int[] a = t;

1.9 Интерфейсы

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

Для интерфейсов допускается множественное наследование. В следующем примере интерфейс IComboBox одновременно наследуется от интерфейсов ITextBox и IListBox.

interface IControl
{
void Paint();
}

interface ITextBox: IControl
{
void SetText(string text);
}

interface IListBox: IControl
{
void SetItems(string[] items);
}

interface IComboBox: ITextBox, IListBox {}

Классы и структуры способны реализовывать несколько интерфейсов. В следующем примере класс EditBox реализует одновременно интерфейсы IControl и IDataBound.

interface IDataBound
{
void Bind(Binder b);
}

public class EditBox: IControl, IDataBound
{
public void Paint() {...}

public void Bind(Binder b) {...}
}

Если класс или структура реализует конкретный интерфейс, экземпляры такого класса или такой структуры могут быть неявно преобразованы к типу заданного интерфейса. Пример:

EditBox editBox = new EditBox();
IControl control = editBox;
IDataBound dataBound = editBox;

Если статически неизвестно, реализует ли экземпляр конкретный интерфейс, могут использоваться динамические приведения типов. Например, следующие операторы используют динамические приведения типов для получения реализаций интерфейсов IControl и IDataBound объекта. Поскольку фактическим типом объекта является EditBox, приведения выполняются успешно.

object obj = new EditBox();
IControl control = (IControl)obj;
IDataBound dataBound = (IDataBound)obj;

Для описанного выше класса EditBox метод Paint интерфейса IControl и метод Bind интерфейса IDataBound реализуются с использованием членов public. В C# также поддерживается явная реализация членов интерфейса, что позволяет не использовать в классах и структурах члены public. Явная реализация члена интерфейса записывается с использованием полного имени члена интерфейса. Например, класс EditBox может использовать явные реализации членов интерфейса для реализации методов IControl.Paint и IDataBound.Bind следующим образом.

public class EditBox: IControl, IDataBound
{
void IControl.Paint() {...}

void IDataBound.Bind(Binder b) {...}
}

Обращение к явным членам интерфейса осуществляется исключительно посредством типа интерфейса. Например, реализация метода IControl.Paint, предоставляемая описанным выше классом EditBox, может быть вызвана только с предварительным преобразованием ссылки на EditBox к типу интерфейса IControl.

EditBox editBox = new EditBox();
editBox.Paint(); // Error, no such method
IControl control = editBox;
control.Paint(); // Ok

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

Перечисляемый тип представляет собой тип значений, содержащий конечное число именованных констант. В следующем примере объявляется и используется перечисляемый тип Color, содержащий три постоянных значения: Red, Green и Blue.

using System;

enum Color
{
Red,
Green,
Blue
}

class Test
{
static void PrintColor(Color color) {
switch (color) {
case Color.Red:
Console.WriteLine("Red");
break;
case Color.Green:
Console.WriteLine("Green");
break;
case Color.Blue:
Console.WriteLine("Blue");
break;
default:
Console.WriteLine("Unknown color");
break;
}
}

static void Main() {
Color c = Color.Red;
PrintColor(c);
PrintColor(Color.Blue);
}
}

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

В следующем примере объявляется перечисляемый тип Alignment, базовым для которого является тип sbyte.

enum Alignment: sbyte
{
Left = -1,
Center = 0,
Right = 1
}

Как показано в приведенном выше примере, объявление члена перечисляемого типа может включать константное выражение, определяющее значение такого члена. Значение константы для каждого члена должно принадлежать диапазону базового типа. Если в объявлении перечисляемого типа значение не задается явно, первому члену типа присваивается нулевое значение, а каждому последующему члену значение текстуально предшествующего ему члена, увеличенное на единицу.

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

int i = (int)Color.Blue; // int i = 2;
Color c = (Color)2; // Color c = Color.Blue;

В качестве значения по умолчанию для любого перечисляемого типа используется целое нулевое значение, преобразованное к перечисляемому типу. Если переменные автоматически инициализируются с использованием значений по умолчанию, такие значения присваиваются переменным перечисляемого типа. Чтобы обеспечить доступность значений по умолчанию перечисляемого типа, литерал 0 неявно преобразуется к любому перечисляемому типу. Таким образом, допускаются выражения следующего вида.

Color c = 0;

1.11 Делегаты

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

В следующем примере объявляется и используется тип делегата Function.

using System;

delegate double Function(double x);

class Multiplier
{
double factor;

public Multiplier(double factor) {
this.factor = factor;
}

public double Multiply(double x) {
return x * factor;
}
}

class Test
{
static double Square(double x) {
return x * x;
}

static double[] Apply(double[] a, Function f) {
double[] result = new double[a.Length];
for (int i = 0; i < a.Length; i++) result[i] = f(a[i]);
return result;
}

static void Main() {
double[] a = {0.0, 0.5, 1.0};

double[] squares = Apply(a, Square);

double[] sines = Apply(a, Math.Sin);

Multiplier m = new Multiplier(2.0);
double[] doubles = Apply(a, m.Multiply);
}
}

Экземпляр типа делегата Function может ссылаться на любой метод, который принимает аргумент и возвращает значение типа double. Метод Apply применяет заданную функцию Function к элементам массива double[] и возвращает массив double[], содержащий результаты. В методе Main метод Apply используется для применения трех различных функций к массиву double[].

Делегат может ссылаться как на статический метод (например, Square или Math.Sin в предыдущем примере), так и на метод экземпляра (например, m.Multiply в предыдущем примере). Делегат, ссылающийся на метод экземпляра, также ссылается на конкретный объект. При вызове такого метода экземпляра с помощью делегата этот объект становится объектом this.

Делегаты также могут создаваться с помощью анонимных функций, которые представляют собой встроенные методы, создаваемые в процессе выполнения. Анонимные функции могут видеть локальные переменные окружающих их методов. Таким образом, приведенный выше пример множителя может быть записан проще с использованием класса Multiplier:

double[] doubles = Apply(a, (double x) => x * 2.0);

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

1.12 Атрибуты

Типы, члены и другие сущности C# поддерживают модификаторы, которые управляют определенными аспектами их поведения. Например, доступность метода управляется с помощью модификаторов public, protected, internal и private. Благодаря этой возможности в C# пользовательские типы декларативных сведений могут быть вложены в сущности программы и извлекаться во время выполнения. Такие дополнительные декларативные сведения задаются в программе посредством определения и использования атрибутов.

В следующем примере атрибут HelpAttribute присоединяется к сущностям программы и предоставляет ссылки на связанную с ними документацию.

using System;

public class HelpAttribute: Attribute
{
string url;
string topic;

public HelpAttribute(string url) {
this.url = url;
}

public string Url {
get { return url; }
}

public string Topic {
get { return topic; }
set { topic = value; }
}
}

Все классы атрибутов наследуются от базового класса System.Attribute, предоставляемого платформой .NET Framework. Чтобы применить атрибут, необходимо указать его имя и любые другие аргументы в квадратных скобках непосредственно перед связанным объявлением. Если имя атрибута заканчивается словом Attribute, при ссылке на него эту часть имени можно опустить. Например, атрибут HelpAttribute можно использовать следующим образом.

[Help("http://msdn.microsoft.com/.../MyClass.htm")]
public class Widget
{
[Help("http://msdn.microsoft.com/.../MyClass.htm", Topic = "Display")]
public void Display(string text) {}
}

В этом примере атрибут HelpAttribute присоединяется к классу Widget, а другой атрибут HelpAttribute к методу Display класса. Общие конструкторы класса атрибута управляют сведениями, которые предоставляются при вложении атрибута в сущность программы. Дополнительные сведения предоставляются посредством ссылки на открытые свойства для чтения и записи класса атрибута (например, ссылка на свойства Topic в предыдущем примере).

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

using System;
using System.Reflection;

class Test
{
static void ShowHelp(MemberInfo member) {
HelpAttribute a = Attribute.GetCustomAttribute(member,
typeof(HelpAttribute)) as HelpAttribute;
if (a == null) {
Console.WriteLine("No help for {0}", member);
}
else {
Console.WriteLine("Help for {0}:", member);
Console.WriteLine(" Url={0}, Topic={1}", a.Url, a.Topic);
}
}

static void Main() {
ShowHelp(typeof(Widget));
ShowHelp(typeof(Widget).GetMethod("Display"));
}
}

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


2. Лексическая структура

2.1 Программы

Программа на C# состоит из одного или более исходных файлов, формально называемых единицами компиляции (9.1). Исходный файл это упорядоченная последовательность символов Юникода. Исходные файлы обычно взаимнооднозначно соответствуют файлам файловой системы, но это соответствие не является обязательным. Для максимальной переносимости рекомендуется использовать для файлов в файловой системе кодировку UTF-8.

С концептуальной точки зрения программа компилируется в три этапа:

1. Преобразование: файл преобразуется из конкретного набора символов и схемы кодировки в последовательность символов Юникода.

2. Лексический анализ: поток входных символов Юникода преобразуется в поток лексем.

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

2.2 Грамматики

В настоящей спецификации представлен синтаксис языка программирования C#, использующий две грамматики. Лексическая грамматика (2.2.2) определяет, как объединяются символы Юникода для образования признаков конца строки, пробелов, комментариев, лексем и препроцессорных директив. Синтаксическая грамматика (2.2.3) определяет, как объединяются лексемы, полученные от лексической грамматики, для образования программ на C#.

2.2.1 Грамматическая нотация

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

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

оператор_while:
while ( логическое_выражение ) внедренный_оператор

определяет оператор_while как состоящий из лексемы while, последующей лексемы (, последующего логического_выражения, последующей лексемы ) и последующего внедренного_оператора.

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

список_операторов:
оператор
список_операторов оператор

определяет список_операторов как состоящий или из оператора, или из списка_операторов с последующим оператором. Иначе говоря, это определение является рекурсивным и указывает, что список операторов состоит из одного или более операторов.

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

блок:
{ список_операторовнеобязательно }

является краткой записью для:

блок:
{ }
{ список_операторов }

и определяет блок как состоящий из необязательного списка_операторов, заключенного в лексемы { и }.

Варианты обычно перечисляются в отдельных строках, но если вариантов много, фраза one of (одно из) может предшествовать списку подстановок, заданных в одной строке. Это просто краткая запись вместо перечисления каждого из вариантов отдельной строкой. Например, порождение:

суффикс_действительного_числа: один из следующих:
F f D d M m

является краткой записью для:

суффикс_действительного_типа:
F
f
D
d
M
m

2.2.2 Лексика

Лексическая грамматика C# представлена в 2.3, 2.4 и 2.5. Терминальными символами лексической грамматики являются символы набора символов Юникода. Лексическая грамматика определяет, как символы объединяются для образования лексем (2.4), пробелов (2.3.3), комментариев (2.3.2) и препроцессорных директив (2.5).

Каждый исходный файл программы на C# должен соответствовать порождению ввода лексической грамматики (2.3).

2.2.3 Синтаксическая грамматика

Синтаксическая грамматика C# представлена в главах и приложениях, следующих за этой главой. Терминальными символами синтаксической грамматики являются лексемы, определенные лексической грамматикой. Синтаксическая грамматика определяет, как лексемы объединяются для образования программ на C#.

Каждый исходный файл в программе на C# должен соответствовать порождению единицы_компиляции синтаксической грамматики (9.1).

2.3 Лексический анализ

Порождение ввода определяет лексическую структуру исходного файла на C#. Каждый исходный файл в программе на C# должен соответствовать этому порождению лексической грамматики.

ввод:
раздел_вводанеобязательно

раздел_ввода:
часть_раздела_ввода
раздел_ввода часть_раздела_ввода

часть_раздела_ввода:
элементы_вводанеобязательно новая_строка
директива_предварительной_обработки

элементы_ввода:
элемент_ввода
элементы_ввода элемент_ввода

элемент_ввода:
пробел
комментарий
лексема

Лексическую структуру исходного файла на C# составляют пять основных элементов: признаки конца строки (2.3.1), пробелы (2.3.3), комментарии (2.3.2), лексемы (2.4) и препроцессорные директивы (2.5). Из этих основных элементов только лексемы являются значимыми в синтаксической грамматике программы на C# (2.2.3).

Лексическая обработка исходного файла на C# состоит из сведения файла к последовательности лексем, становящейся вводом для синтаксического анализа. Признаки конца строки, пробелы и комментарии могут служить для разделения лексем; препроцессорные директивы могут вызывать пропуск разделов исходного файла, однако в других отношениях эти лексические элементы не влияют на синтаксическую структуру программы на C#.

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

2.3.1 Знаки завершения строки

Признаки конца строки разделяют на строки символы исходного файла на C#.

новая_строка:
Символ возврата каретки (U+000D)
Символ перевода строки (U+000A)
Символ возврата каретки (U+000D), за которым следует знак перевода строки (U+000A)
Символ следующей строки (U+0085)
Символ разделителя строк (U+2028)
Символ разделителя абзацев (U+2029)

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

если последним символом исходного файла является символ CTRL-Z (U+001A), этот символ удаляется;

символ возврата каретки (U+000D) добавляется к концу исходного файла, если файл не пустой и если последним символом исходного файла не является символ возврата каретки (U+000D), символ перевода строки (U+000A), символ разделителя строк (U+2028) или символ разделителя абзацев (U+2029).

2.3.2 Комментарии

Поддерживается две формы комментариев: однострочные комментарии и комментарии с разделителями. Однострочные комментарии начинаются с символов // и продолжаются до конца исходной строки. Комментарии с разделителями начинаются с символов /* и заканчиваются символами */. Комментарии с разделителями могут занимать несколько строк.

комментарий:
однострочный_комментарий
комментарий_с_разделами

однострочный_комментарий:
// символы_вводанеоб

входные_символы:
входной_символ
входные_символы входной_символ

входной_символ:
Любой знак Юникода за исключением символа_следующей_строки

символ_следующей_строки:
Символ возврата каретки (U+000D)
Символ перевода строки (U+000A)
Символ следующей строки (U+0085)
Символ разделителя строк (U+2028)
Символ разделителя абзацев (U+2029)

комментарий_с_разделами:
/* текст_комментария_с_разделаминеобязательно звездочка /

текст_комментария_с_разделами:
раздел_комментария_с_разделами
текст_комментария_с_разделами раздел_комментария_с_разделами

раздел_комментария_с_разделами:
/
звездочканеобязательно не_косая_черта_и_не_звездочка

звездочка:
*
звездочка *

не_косая_черта_и_не_звездочка:
Любой знак Юникода за исключением косой черты (/) или звездочки (*)

Комментарии не могут быть вложенными. Последовательности символов /* и */ не имеют специального значения внутри комментария //, а последовательности символов // и /* не имеют специального значения внутри комментария с разделителями.

Комментарии не обрабатываются внутри символьных и строковых литералов.

Например:

/* Hello, world program
This program writes hello, world to the console
*/
class Hello
{
static void Main() {
System.Console.WriteLine("hello, world");
}
}

содержит комментарий с разделителями.

Например:

// Hello, world program
// This program writes hello, world to the console
//
class Hello // any name will do for this class
{
static void Main() { // this method must be named "Main"
System.Console.WriteLine("hello, world");
}
}

показано несколько однострочных комментариев.

2.3.3 Пробел

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

пробел:
любой символ Юникода класса Zs
символ горизонтальной табуляции (U+0009)
символ вертикальной табуляции (U+000B)
символ перевода страницы (U+000C)

2.4 Маркеры

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

лексема:
идентификатор
ключевое_слово
целочисленный_литерал
действительный_литерал
символьный_литерал
строковый_литерал
оператор_или_знак_пунктуации

2.4.1 Управляющие последовательности символов Юникода

Управляющая последовательность символов Юникода представляет собой символ Юникода. Управляющие последовательности символов Юникода обрабатываются в идентификаторах (2.4.2), символьных литералах (2.4.4.4) и правильных строковых литералах (2.4.4.5). Управляющая последовательность символов Юникода не обрабатывается в других местах (например, для образования оператора, знака пунктуации или ключевого слова).

управляющая_последовательность_Юникода:
\u 16_р_циф 16_р_циф 16_р_циф 16_р_циф
\U 16_р_циф 16_р_циф 16_р_циф 16_р_циф 16_р_циф 16_р_циф 16_р_циф 16_р_циф

Управляющая последовательность Юникода (escape-последовательность Юникода) представляет собой один символ Юникода, образованный шестнадцатеричным числом, следующим за символами \u или \U. Поскольку в C# используется 16-разрядная кодировка элементов кода Юникода в символьных и строковых значениях, символ Юникода в диапазоне от U+10000 до U+10FFFF запрещен в строковом литерале и представляется с помощью суррогатной пары Юникода в строковом литерале. Символы Юникода с элементами кода выше 0x10FFFF не поддерживаются.

Многократные трансляции не выполняются. Например, строковый литерал \u005Cu005C эквивалентен \u005C, а не \. Значение Юникода \u005C является символом \.

Например:

class Class1
{
static void Test(bool \u0066) {
char c = '\u0066';
if (\u0066)
System.Console.WriteLine(c.ToString());
}
}

показано несколько использований \u0066, escape-последовательности для буквы f. Эта программа эквивалентна следующей:

class Class1
{
static void Test(bool f) {
char c = 'f';
if (f)
System.Console.WriteLine(c.ToString());
}
}

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

Правила для идентификаторов в этом разделе точно соответствуют правилам, рекомендованным в дополнении 31 к стандарту Юникода, за исключением следующего: знак подчеркивания разрешен в качестве начального символа (что традиционно для языка программирования C), escape-последовательности Юникода разрешены в идентификаторах, а символ @ разрешен в качестве префикса, чтобы можно было использовать ключевые слова в качестве идентификаторов.

идентификатор:
доступный_идентификатор
@ идентификатор_или_ключевое_слово

доступный_идентификатор:
идентификатор_или_ключевое_слово, не являющиеся ключевым_словом

идентификатор_или_ключевое_слово:
начальный_символ_идентификатора символы_части_идентификаторанеобязательно

начальный_символ_идентификатора:
буква
_ (знак подчеркивания, U+005F)

символы_части_идентификатора:
символ_части_идентификатора
символы_части_идентификатора символ_части_идентификатора

символ_части_идентификатора:
буква
десятичная_цифра
соединитель
несамостоятельный_символ
символ_управления_форматом

буква:
Символ Юникода классов Lu, Ll, Lt, Lm, Lo или Nl
управляющая_последовательность_Юникода, представляющая символы классов Lu, Ll, Lt, Lm, Lo или Nl

несамостоятельный_символ:
Символ Юникода классов Mn или Mc
управляющая_последовательность_Юникода, представляющая символ классов Mn или Mc

десятичная_цифра:
Символ Юникода класса Nd
управляющая_последовательность_Юникода, представляющая символ класса Nd

символ-соединитель:
Символ Юникода класса Pc
управляющая_последовательность_Юникода, представляющая символ класса Pc

символ_управления_форматом:
символ Юникода класса Cf
управляющая_последовательность_Юникода, представляющая символ класса Cf

Дополнительные сведения об упомянутых выше классах символов Юникода см. в документе Стандарт Юникода, версия 3.0, раздел 4.5.

Примеры допустимых идентификаторов: identifier1, _identifier2, @if.

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

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

В примере

class @class
{
public static void @static(bool @bool) {
if (@bool)
System.Console.WriteLine("true");
else
System.Console.WriteLine("false");
}
}

class Class1
{
static void M() {
cl\u0061ss.st\u0061tic(true);
}
}

определен класс с именем class со статическим методом с именем static, принимающим параметр с именем bool. Обратите внимание, что, поскольку управляющие последовательности Юникода не разрешены в ключевых словах, лексема cl\u0061ss является идентификатором, причем тем же идентификатором, что и @class.

Два идентификатора считаются одинаковыми, если они идентичны после применения следующих преобразований в таком порядке:

если используется префикс @, он удаляется;

каждая последовательность_управляющих_символов_Юникода преобразуется в соответствующий символ Юникода;

все символы_управления_форматом удаляются.

Идентификаторы, содержащие два последовательных символа подчеркивания (U+005F), зарезервированы для использования в реализации. Например, реализация может предоставлять расширенные ключевые слова, начинающиеся с двух символов подчеркивания.

2.4.3 Ключевые слова

Ключевое слово это подобная идентификатору зарезервированная последовательность символов. Ключевое слово нельзя использовать в качестве идентификатора за исключением его использования с префиксом @.

ключевое_слово: одно из следующих:
abstract as base bool break
byte case catch char checked
class const continue decimal default
delegate do double else enum
event explicit extern false finally
fixed float for foreach goto
if implicit in int interface
internal is lock long namespace
new null object operator out
override params private protected public
readonly ref return sbyte sealed
short sizeof stackalloc static string
struct switch this throw true
try typeof uint ulong unchecked
unsafe ushort using virtual void
volatile while

В некоторых положениях грамматики особые идентификаторы имеют особое значение, но не являются ключевыми словами. Такие идентификаторы иногда называются контекстными ключевыми словами. Например, в объявлении свойства идентификаторы get и set имеют особое значение (10.7.2). Идентификатор, отличный от get или set, никогда не разрешается в этих положениях, поэтому такое использование не приводит к конфликту с использованием этих слов в качестве идентификаторов. В других случаях, как, например, с идентификатором var в объявлениях с неявно введенной локальной переменной (8.5.1), контекстное ключевое слово может конфликтовать с объявленными именами. Тогда объявленное имя получает приоритет над использованием идентификатора в качестве контекстного ключевого слова.

2.4.4 Литералы

Литерал это представление значения в исходном коде.

литерал:
логический_литерал
целочисленный_литерал
действительный_литерал
символьный_литерал
строковый_литерал
литерал_null

2.4.4.1 Логические литералы

Существуют два значения логического литерала: true и false.

логический_литерал:
true
false

Логический_литерал имеет тип bool.

2.4.4.2 Целочисленные литералы

Целочисленные литералы используются для записи значений, имеющих тип int, uint, long и ulong. Целочисленные литералы имеют две возможных формы: десятичную и шестнадцатеричную.

целочисленный_литерал:
десятичный_целочисленный_литерал
шестнадцатеричный_целочисленный_литерал

десятичный_целочисленный_литерал:
десятичные_цифры суффикс_целочисленного_типанеобязательно

десятичные_цифры:
десятичная_цифра
десятичные_цифры десятичная_цифра

десятичная_цифра: одна из следующих:
0 1 2 3 4 5 6 7 8 9

суффикс_целочисленного_типа: один из следующих:
U u L l UL Ul uL ul LU Lu lU lu

шестнадцатеричный_целочисленный_литерал:
0x шестнадцатеричные_цифры суффикс_целочисленного_типанеобязательно
0X шестнадцатеричные_цифры суффикс_целочисленного_типанеобязательно

шестнадцатеричные_цифры:
шестнадцатеричная_цифра
шестнадцатеричные_цифры шестандцатеричная_цифра

16_р_цифра: одно из
0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f

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

если литерал без суффикса, его тип первый из этих типов, в которых его значение может быть представлено: int, uint, long и ulong;

если литерал с суффиксом U или u, его тип первый из этих типов, в которых его значение может быть представлено: uint и ulong;

если литерал с суффиксом L или l, его тип первый из этих типов, в которых его значение может быть представлено: long и ulong;

если литерал с суффиксом UL, Ul, uL, ul, LU, Lu, lU или lu, его тип ulong.

Если представленное целочисленным литералом значение находится за пределами диапазона типа ulong, выдается ошибка времени компиляции.

Рекомендуется использовать L вместо l при записи литералов типа long, так как букву l легко спутать с цифрой 1.

Для записи минимально возможных значений int и long в виде десятичных целочисленных литералов существуют следующие два правила:

если десятичный_целочисленный_литерал со значением 2147483648 (231) и без суффикса_целочисленного_типа появляется в качестве лексемы, непосредственно следующей за лексемой оператора унарного минуса (7.7.2), результатом является константа типа int со значением −2147483648 (−231). In all other situations, such a decimal-integer-literal is of type uint.

если десятичный_целочисленный_литерал со значением 9223372036854775808 (263) и без суффикса_целочисленного_типа или с суффиксом_целочисленного_типа L или l появляется в качестве лексемы, непосредственно следующей за лексемой оператора унарного минуса (7.7.2), результатом является константа типа long со значением −9223372036854775808 (−263). In all other situations, such a decimal-integer-literal is of type ulong.

2.4.4.3 Действительные литералы

Действительные литералы используются для записи значений типов float, double и decimal.

действительный_литерал:
десятичные_цифры . десятичные_цифры порядок_числанеобязательно суффикс_действительного_числанеобязательно
. десятичные_цифры порядок_числанеобязательно суффикс_действительного_числанеобязательно
десятичные_цифры порядок_числа суффикс_действительного_числанеобязательно
десятичные_цифры суффикс_действительного_числа

порядок_числа:
e знакнеобязательно десятичное_число
E знакнеобязательно десятичное_число

знак: один из следующих:
+ -

суффикс_действительного_числа: один из следующих:
F f D d M m

Если суффикс_действительного_типа не указан, типом действительного литерала является double. Иначе суффикс действительного типа определяет тип действительного литерала следующим образом:

действительный литерал с суффиксом F или f имеет тип float. Например, литералы 1f, 1.5f, 1e10f и 123.456F все имеют тип float;

действительный литерал с суффиксом D или d имеет тип double. Например, литералы 1d, 1.5d, 1e10d и 123.456D все имеют тип double;

действительный литерал с суффиксом M или m имеет тип decimal. Например, литералы 1m, 1.5m, 1e10m и 123.456M все имеют тип decimal. Этот литерал преобразуется в значение типа decimal, принимая его точное значение и при необходимости округляя до ближайшего могущего быть представленным значения с помощью банковского округления (4.1.7). Любой масштаб, видимый в литерале, сохраняется, если только значение не округляется и не равно нулю (в этом последнем случае знак и масштаб будут равны 0). Следовательно, синтаксический разбор литерала 2.900m создаст десятичное значение со знаком 0, коэффициентом 2900 и масштабом 3.

Если указанный литерал не удается представить в предписанном типе, выдается ошибка времени компиляции.

Значение действительного литерала с типом float или double определяется с помощью режима округления до ближайшего по стандарту IEEE.

Обратите внимание, что в действительном литерале всегда требуются десятичные цифры после десятичной точки. Например, 1.3F это действительный литерал, а 1.F нет.

2.4.4.4 Символьные литералы

Символьный литерал представляет один символ и обычно состоит из символа в кавычках, например 'a'.

символьный_литерал:
' символ '

символ:
отдельный_символ
простая_управляющая_последовательность
шестнадцатеричная_управляющая_последовательность
управляющая_последовательность_Юникода

отдельный_знак:
Любой знак за исключением знака апострофа (') (U+0027), косой черты (\) (U+005C) и символа_следующей_строки

простая_управляющая_последовательность: одно из следующих:
\' \" \\ \0 \a \b \f \n \r \t \v

шестнадцатеричная_управляющая_последовательность:
\x шестнадцатеричная_цифра шестнадцатеричная_цифранеобязательно шестнадцатеричная_цифранеобязательно шестнадцатеричная_цифранеобязательно

Символ, следующий за обратной косой чертой (\) в символе, должен быть одним из следующих: ', ", \, 0, a, b, f, n, r, t, u, U, x, v. В противном случае произойдет ошибка времени компиляции.

Шестнадцатеричная escape-последовательность представляет собой один символ Юникода со значением, образованным шестнадцатеричным числом, следующим за \x.

Если значение, представленное символьным литералом, больше U+FFFF, вызывается ошибка времени компиляции.

Управляющая последовательность символов Юникода (2.4.1) в символьном литерале должна быть в диапазоне от U+0000 до U+FFFF.

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

 

Escape-последовательность Имя символа Кодировка Юникода
\' Одинарная кавычка 0x0027
\" Двойные кавычки 0x0022
\\ Обратная косая черта 0x005C
\0 Null 0x0000
\a Предупреждение 0x0007
\b Возврат 0x0008
\f Перевод страницы 0x000C
\n Новая строка 0x000A
\r Возврат каретки 0x000D
\t Горизонтальная табуляция 0x0009
\v Вертикальная табуляция 0x000B

 

The type of a character-literal is char.

2.4.4.5 Строковые литералы

В C# поддерживается две формы строковых литералов: правильные строковые литералы и буквальные строковые литералы.

Правильный строковый литерал состоит из нуля или более символов, заключенных в двойные кавычки, например "hello", и может включать как простые управляющие последовательности (например \t для символа табуляции), так и шестнадцатеричные escape-последовательности и escape-последовательности Юникода.

Буквальный строковый литерал состоит из символа @ с последующими символом двойных кавычек, нулем или более символов и закрывающим символом двойных кавычек. Простым примером является @"hello". В буквальном строковом литерале символы между разделителями интерпретируются буквально, единственным исключением является управляющая_последовательность_кавычки. В частности, простые управляющие последовательности, шестнадцатеричные escape-последовательности и escape-последовательности Юникода не обрабатываются в буквальных строковых литералах. Буквальный строковый литерал может занимать несколько строк.

строковый_литерал:
правильный_строковый_литерал
буквальный_строковый_литерал

правильный_строковый_литерал:
" символы_правильного_строкового_литераланеобязательно "

символы_правильного_строкового_литерала:
символ_правильного_строкового_литерала
символы_правильного_строкового_литерала символ_правильного_строкового_литерала

символ_правильного_строкового_литерала:
один_символ_правильного_строкового_литерала
простая_управляющая_последовательность
шестнадцатеричная_управляющая_последовательность
управляющая_последовательность_Юникода

один_символ_правильного_строкового_литерала:
Любой символ за исключением символа кавычек (") (U+0022), косой черты (\) (U+005C) и символа_следующей_строки

буквальный_строковый_литерал:
@" символы_буквального_строкового_литераланеобязательно "

символы_буквального_строкового_литерала:
символ_буквального_строкового_литерала
символы_буквального_строкового_литерала символ_буквального_строкового_литерала

символ_буквального_строкового_литерала:
один_символ_буквального_строкового_литерала
управляющая_последовательность_кавычки

один_символ_буквального_строкового_литерала:
любой символ, кроме "

управляющая_последовательность_кавычки:
""

Символ, следующий за символом обратной косой черты (\) в символе_правильного_строкового_литерала, должен быть одним из следующих символов: ', ", \, 0, a, b, f, n, r, t, u, U, x, v. В противном случае произойдет ошибка времени компиляции.

Например:

string a = "hello, world"; // hello, world
string b = @"hello, world"; // hello, world

string c = "hello \t world"; // hello world
string d = @"hello \t world"; // hello \t world

string e = "Joe said \"Hello\" to me"; // Joe said "Hello" to me
string f = @"Joe said ""Hello"" to me"; // Joe said "Hello" to me

string g = "\\\\server\\share\\file.txt"; // \\server\share\file.txt
string h = @"\\server\share\file.txt"; // \\server\share\file.txt

string i = "one\r\ntwo\r\nthree";
string j = @"one
two
three";

Выше показаны разнообразные строковые литералы. Последний строковый литерал j является буквальным строковым литералом, занимающим несколько строк. Символы между кавычками, включая пробелы, например символы новой строки, сохраняются буквально.

Поскольку в шестнадцатеричной escape-последовательности может быть переменное число шестнадцатеричных цифр, строковый литерал "\x123" содержит один символ с шестнадцатеричным значением 123. Чтобы создать строку, содержащую символ с шестнадцатеричным значением 12 и с последующим символом 3, можно написать "\x00123" или "\x12" + "3".

Строковый_литерал имеет тип string.

Результатом каждого строкового литерала не обязательно является новый экземпляр строки. Если два или более строковых литерала, эквивалентных согласно оператору равенства строк (7.10.7), появляются в одной программе, эти строковые литералы ссылаются на один и тот же экземпляр строки. Например, программа

class Test
{
static void Main() {
object a = "hello";
object b = "hello";
System.Console.WriteLine(a == b);
}
}

выведет True, так как эти два литерала ссылаются на один и тот же экземпляр строки.

Null

литерал_null:
null

Литерал_null может быть неявно преобразован в ссылочный тип или обнуляемый тип.

2.4.5 Операторы и знаки пунктуации

Существует несколько видов операторов и знаков пунктуации. Операторы используются в выражениях для описания операций, включающих один или более операндов. Например, в выражении a + b используется оператор + для сложения двух операндов a и b. Знаки пунктуации служат для группирования и разделения.

оператор_или_знак_пунктуации: один из следующих:
{ } [ ] ( ) . , : ;
+ - * / % & | ^ ! ~
= < > ? ?? :: ++ -- && ||
-> == != <= >= += -= *= /= %=
&= |= ^= << <<= =>

сдвиг_вправо:
>|>

присваивание_сдвига_вправо:
>|>=

Вертикальная черта в порождениях сдвиг_вправо и присваивание_сдвига_вправо указывает, что, в отличие от других порождений в синтаксической грамматике, никакие символы (даже пробелы) не допустимы между этими лексемами. Эти порождения обрабатываются особо, чтобы сделать возможной правильную обработку списков_параметров_типа (10.1.3).

2.5 Препроцессорные директивы

Препроцессорные директивы дают возможность условно пропускать разделы исходных файлов, сообщать об условиях ошибок и предупреждений и устанавливать отдельные области исходного кода. Термин препроцессорные директивы используется только для сохранения единообразия с языками программирования C и C++. В C# нет отдельного шага предварительной обработки; препроцессорные директивы обрабатываются как часть этапа лексического анализа.

ПП_директива:
ПП_описание
ПП_условное_выражение
ПП_строка
ПП_диагностика
ПП_область
ПП_pragma

Доступны следующие препроцессорные директивы:

#define и #undef, используемые, соответственно, для определения и отмены определения символов условной компиляции (2.5.3);

#if, #elif, #else и #endif, используемые для условного пропуска разделов исходного кода (2.5.4);

#line, используемая для управления номерами строк, выдаваемыми для ошибок и предупреждений (2.5.7);

#error и #warning, используемые для выдачи, соответственно, ошибок и предупреждений (2.5.5);

#region и #endregion, используемые для явного выделения разделов исходного кода (2.5.6);

#pragma, используемая для задания компилятору необязательных сведений о контексте (2.5.8).

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

Исходная строка, содержащая директиву #define, #undef, #if, #elif, #else, #endif или #line, может оканчиваться однострочным комментарием. Комментарии с разделителями (вид комментариев с /* */) не разрешены в исходных строках, содержащих препроцессорные директивы.

Препроцессорные директивы не являются лексемами и не являются частью синтаксической грамматики C#. Тем не менее, препроцессорные директивы можно использовать для включения или исключения последовательностей лексем и таким образом влиять на значение программы C#. Например, скомпилированная программа:

#define A
#undef B

class C
{
#if A
void F() {}
#else
void G() {}
#endif

#if B
void H() {}
#else
void I() {}
#endif
}

приведет к той же последовательности лексем, что и программа:

class C
{
void F() {}
void I() {}
}

Таким образом, несмотря на то, что лексически эти программы различаются, синтаксически они идентичны.

2.5.1 Символы условной компиляции

Функциональность условной компиляции, предоставляемая директивами #if, #elif, #else и #endif, управляется посредством препроцессорных выражений (2.5.2) и символов условной компиляции.

условный_символ:
любой идентификатор_или_ключевое_слово, за исключением true и false

У символа условной компиляции есть два возможных состояния: определенное и неопределенное. В начале лексической обработки исходного файла символ условной компиляции не определен, если только он явным образом не определен внешним механизмом (таким как параметр командной строки компилятора). При обработке директивы #define символ условной компиляции, упомянутый в этой директиве, становится определенным в этом исходном файле. Этот символ остается определенным, пока не будет обработана директива #undef для того же самого символа, или пока не будет достигнут конец исходного файла. Это означает, что директивы #define и #undef в одном исходном файле не влияют на другие исходные файлы той же программы.

При ссылке в препроцессорном выражении определенный символ условной компиляции имеет логическое значение true, а неопределенный логическое значение false. Символы условной компиляции не обязательно должны быть явно объявлены, чтобы на них можно было ссылаться в препроцессорных выражениях. Необъявленные символы просто являются неопределенными и имеют, таким образом, значение false.

Пространство имен для символов условной компиляции отличается и является отдельным от всех других именованных сущностей в программе на C#. На символы условной компиляции можно ссылаться только в директивах #define и #undef и в препроцессорных выражениях.

2.5.2 Препроцессорные выражения

Препроцессорные выражения могут быть в директивах #if и #elif. В препроцессорных выражениях разрешены операторы !, ==, !=, && и ||, для группирования можно использовать скобки.

ПП_выражение:
пробелнеобязательно ПП_выражение_ИЛИ пробелнеобязательно

ПП_выражение_ИЛИ:
ПП_выражение_И
ПП_выражение_ИЛИ пробелнеобязательно || пробелнеобязательно ПП_выражение_И

ПП_выражение_И:
ПП_выражение_равенства
ПП_выражение_И пробелнеобязательно && пробелнеобязательно ПП_выражение_равенства

ПП_выражение_равенства:
ПП_унарное_выражение
ПП_выражение_равенства пробелнеобязательно == пробелнеобязательно ПП_унарное_выражение
ПП_выражение_равенства пробелнеобязательно != пробелнеобязательно ПП_унарное_выражение

ПП_унарное_выражение:
ПП_первичное_выражение
! пробелнеобязательно ПП_унарное_выражение

ПП_первичное_выражение:
true
false
символ_условной_компиляции
( пробелнеобязательно ПП_выражение пробелнеобязательно )

При ссылке в препроцессорном выражении определенный символ условной компиляции имеет логическое значение true, а неопределенный логическое значение false.

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

2.5.3 Директивы объявлений

Директивы объявлений используются для определения или отмены определения символов условной компиляции.

ПРО_описание:
пробелнеоб # пробелнеоб define пробел условный_символ ПРО_новая_строка
пробелнеоб # пробелнеоб undef пробел условный_символ ПРО_новая_строка

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

Обработка директивы #define делает данный символ условной компиляции определенным, начиная с исходной строки, следующей за директивой. Подобным же образом обработка директивы #undef делает данный символ условной компиляции неопределенным, начиная с исходной строки, следующей за директивой.

Все директивы #define и #undef в исходном файле должны стоять до первой лексемы (2.4) в исходном файле, иначе вызывается ошибка времени компиляции. Интуитивно понятно, что директивы #define и #undef должны предшествовать любому фактическому коду в исходном файле.

В примере

#define Enterprise

#if Professional || Enterprise
#define Advanced
#endif

namespace Megacorp.Data
{
#if Advanced
class PivotTable {...}
#endif
}

правильно, так как директивы #define предшествуют первой лексеме (ключевое слово namespace) в исходном файле.

Следующий пример приводит к ошибке времени компиляции, так как директива #define следует за фактическим кодом:

#define A
namespace N
{
#define B
#if B
class Class1 {}
#endif
}

Директива #define может определять уже определенный символ условной компиляции, без какого-либо вмешательства директивы #undef для этого символа. В следующем примере символ условной компиляции A определен, а затем определен снова.

#define A
#define A

Директива #undef может отменить определение не определенного символа условной компиляции. В следующем примере определен символ условной компиляции A, а затем дважды отменено его определение; хотя вторая директива #undef не действует, она все же допустима.

#define A
#undef A
#undef A

2.5.4 Директивы условной компиляции

Директивы условной компиляции используются для условного включения или исключения частей исходного файла.

ПП_условное_выражение:
ПП_раздел_if ПП_разделы_elifнеобязательно ПП_раздел_elseнеобязательно ПП_endif

ПП_раздел_if:
пробелнеобязательно # пробелнеобязательно if пробел ПП_выражение ПП_новая_строка раздел_условного_выражениянеобязательно

ПП_разделы_elif:
ПП_раздел_elif
ПП_разделы_elif ПП_раздел_elif

ПП_раздел_elif:
пробелнеобязательно # пробелнеобязательно elif пробел ПП_выражение ПП_новая_строка раздел_условного_выражениянеобязательно

ПП_раздел_else:
пробелнеобязательно # пробелнеобязательно else ПП_новая_строка раздел_условного_выражениянеобязательно

ПП_endif:
пробелнеобязательно # пробелнеобязательно endif ПП_новая_строка

раздел_условного_выражения:
раздел_ввода
пропущенный_раздел

пропущенные_символы:
часть_пропущенных_символов
пропущенный символ часть_пропущенных_символов

часть_пропущенных_символов:
пропущенные_символынеобязательно новая_строка
ПП_директива

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

не_знак_числа:
любой входной_символ, кроме #

В соответствии с синтаксисом, директивы условной компиляции должны быть записаны как наборы, состоящие из (по порядку) директивы #if, нуля или более директив #elif, нуля или одной директивы #else и директивы #endif. Между этими директивами находятся условные разделы исходного кода. Каждый раздел управляется непосредственно предшествующей директивой. Условный раздел может содержать вложенные директивы условной компиляции при условии, что они образуют полные наборы.

ПП_условное_выражение выделяет не более одного из содержащихся условных_разделов для обычной лексической обработки:

ПП_выражения директив #if и #elif вычисляются по порядку, пока одна из них не выдаст значение true. Если выражение выдает true, выбирается условный_раздел соответствующей директивы;

если все ПРО_выражения дают false и если имеется директива #else, выбирается условный_раздел директивы #else;

иначе условный_раздел не выбирается.

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

Оставшиеся условные_разделы, если они есть, обрабатываются как пропущенные_разделы: за исключением препроцессорных директив, исходный код в разделе не обязан соблюдать лексическую грамматику; в этом разделе не создаются лексемы из исходного кода, а препроцессорные директивы должны быть лексически правильными, но в других отношениях они не обрабатываются. Внутри условного_раздела, который обрабатывается как пропущенный_раздел, любые вложенные условные_разделы (содержащиеся во вложенных конструкциях #if...#endif и #region...#endregion) также обрабатываются как пропущенные_разделы.

В следующем примере показано, как могут вкладываться директивы условной компиляции:

#define Debug // Debugging on
#undef Trace // Tracing off

class PurchaseTransaction
{
void Commit() {
#if Debug
CheckConsistency();
#if Trace
WriteToLog(this.ToString());
#endif
#endif
CommitHelper();
}
}

За исключением препроцессорных директив, пропущенный исходный код не является предметом лексического анализа. Например, следующее допустимо, несмотря на незавершенный комментарий в разделе #else:

#define Debug // Debugging on

class PurchaseTransaction
{
void Commit() {
#if Debug
CheckConsistency();
#else
/* Do something else
#endif
}
}

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

Препроцессорные директивы не обрабатываются, если они находятся внутри элементов ввода в несколько строк. Например, программа

class Hello
{
static void Main() {
System.Console.WriteLine(@"hello,
#if Debug
world
#else
Nebraska
#endif
");
}
}

имеет результатом:

hello,
#if Debug
world
#else
Nebraska
#endif

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

#if X
/*
#else
/* */ class Q { }
#endif

в любом случае создается тот же самый поток лексем (class Q { }), независимо от того, определен X или нет. Если X определен, из-за многострочного комментария обрабатываются только директивы #if и #endif. Если X не определен, то три директивы (#if, #else и #endif) являются частью набора директив.

2.5.5 Директивы диагностики

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

ПП_диагностика:
пробелнеоб # пробелнеоб error ПП_сообщение
пробелнеоб # пробелнеоб warning ПП_сообщение

ПП_сообщение:
новая_строка
пробел входные_символынеобязательно новая_строка

В примере

#warning Code review needed before check-in

#if Debug && Retail
#error A build can't be both debug and retail
#endif

class Test {...}

в любом случае выдается предупреждение (Code review needed before check-in) и создается ошибка времени компиляции (A build cant be both debug and retail), если определены оба условных символа Debug и Retail. Обратите внимание, что ПП_сообщение может содержать произвольный текст; в частности, оно не должно содержать правильно сформированные лексемы; об этом свидетельствует использование одиночной кавычки в слове cant.

2.5.6 Директивы областей

Директивы областей используются для явного выделения областей исходного кода.

ПП_область:
ПП_начальная_область раздел_условного_выражениянеобязательно ПП_конечная_область

ПП_начальная_область:
пробелнеобязательно # пробелнеобязательно region ПП_сообщение

ПП_конечная_область:
пробелнеобязательно # пробелнеобязательно endregion ПП_сообщение

В области не вложены семантические значения; области используют программисты и автоматические средства, чтобы пометить раздел исходного кода. Сообщение, указанное в директиве #region или #endregion, также не имеет семантического значения, оно служит только для идентификации области. В соответствующих директивах #region и #endregion могут быть разные ПРО_сообщения.

Лексическая обработка области

#region
...
#endregion

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

#if true
...
#endif

2.5.7 Директивы строк

Директивы строк можно использовать для изменения номеров строк и имен исходных файлов, которые сообщаются в выводе компилятора, например в предупреждениях и ошибках.

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

ПП_строка:
пробелнеоб # пробелнеоб line пробел индикатор_строки ПП_новая_строка

индикатор_строки:
десятичные_цифры пробел имя_файла
десятичные_цифры
default
hidden

имя_файла:
" символы_имени_файла "

символы_имени_файла:
символ_имени_файла
символы_имени_файла символ_имени_файла

символ_имени_файла:
Любой входной_знак за исключением "

При отсутствии директив #line компилятор сообщает в своем выводе настоящие номера строк и имена исходных файлов. При обработке директивы #line, содержащей индикатор_строки, не являющийся default, компилятор обрабатывает строку после этой директивы как строку с указанным номером (и именем файла, если оно указано).

Директива #line default отменяет действие всех предшествующих директив #line. Компилятор сообщает настоящие сведения о строках для последующих строк, как если бы директивы #line не обрабатывались.

Директива #line hidden не влияет на файл и номера строк, передаваемые в сообщениях об ошибках, но влияет на отладку на исходном уровне. При отладке все строки между директивой #line hidden и последующей директивой #line (не являющейся директивой #line hidden) не имеют сведений о номерах строк. При пошаговом выполнении кода в отладчике эти строки полностью пропускаются.

Обратите внимание, что имя_файла отличается от правильных строковых литералов тем, что escape-символы не обрабатываются; символ \ просто означает обычный символ обратной косой черты в имени_файла.

Pragma

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

ПП_pragma:
пробелнеобязательно # пробелнеобязательно pragma пробел тело_директивы_pragma ПП_новая_строка

тело_директивы_pragma:
тело_предупреждения_директивы_pragma

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

Pragma warning

3. Основные принципы

3.1 Запуск приложения

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

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

Запуск приложений происходит, когда среда выполнения вызывает назначенный метод, известный как точка входа приложения. Этот метод точки входа всегда носит имя Main и может иметь одну из следующих сигнатур:

static void Main() {...}

static void Main(string[] args) {...}

static int Main() {...}

static int Main(string[] args) {...}

Как указано выше, точка входа может дополнительно возвратить значение int. Данное возвращаемое значение используется в завершении приложения (3.2).

Точка входа может дополнительно иметь один формальный параметр. Параметр может иметь любое имя, но тип параметра должен быть string[]. При наличии формального параметра среда выполнения создает и передает аргумент string[], содержащий аргументы командной строки, заданные при запуске приложения. Аргумент string[] никогда не принимает значение null, но может иметь нулевую длину, если не были заданы аргументы командной строки.

Так как C# поддерживает перегрузку методов, класс или структура могут содержать несколько определений какого-либо метода при условии того, что их сигнатуры отличаются. Однако в рамках одной программы класс или структуры не могут содержать более одного метода Main, определение которого позволяет использовать этот метод в качестве точки входа приложения. Другие перегруженные версии Main разрешены с условием наличия более одного параметра или с условием наличия только параметра, тип которого не является типом string[].

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

В C# каждый метод должен быть задан в качестве члена класса или структуры. Как правило, объявленная доступность (3.5.1) метода определяется модификаторами доступа (10.3.5), указанными в объявлении, и, аналогичным образом, объявленная доступность типа определяется модификаторами доступа, указанными в объявлении. Чтобы заданный метод заданного типа можно было вызвать, и тип, и член должны быть доступны. Однако точка входа приложения является особым случаем. В частности, среда выполнения может вызвать точку доступа приложения независимо от объявленной доступности в объявлениях вмещающих типов.

Метод точки входа приложения может не находиться в объявлении универсального класса.

Во всех других отношениях методы точки входа функционируют аналогично другим методам.

3.2 Завершение приложения

При завершении приложения управление возвращается среде выполнения.

Если возвращаемое значение метода точки входа приложения имеет тип int, то оно служит кодом состояния завершения приложения. Целью данного кода является передача среде выполнения сведений о том, успешно ли прошло выполнение, или имел место сбой.

Если возвращаемое значение метода точки входа имеет тип void, то достижение правой фигурной скобки (}), завершающей данный метод, или выполнение инструкции return без выражения приведет к коду статуса завершения со значением 0.

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

3.3 Объявления

Объявления в программе на C# определяют составные элементы программы. Программы на C# организованы посредством использования пространств имен (9), которые могут содержать объявления типов и объявления вложенных пространств имен. Объявления типов (9.6) используются для задания классов (10), структур (10.14), интерфейсов (13), перечисляемых типов (14) и делегатов (15). Типы членов, разрешенных в объявлении типов, зависят от формы объявления типа. Например, объявления классов могут содержать объявления для констант (10.4), полей (10.5), методов (10.6), свойств (10.7), событий (10.8), индексаторов (10.9), операторов (10.10), конструкторов экземпляров (10.11), статических конструкторов (10.12), деструкторов (10.13) и вложенных типов (10.3.8).

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

Существует несколько типов областей объявлений, которые представлены далее.

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

В рамках всех исходных файлов программы объявления_членов_пространства_имен в объявлении_пространства_имен с аналогичным полным именем пространства имен являются членами одной комбинированной области объявления.

Каждый класс, структура или интерфейс объявления создает новую область объявления. Имена вводятся в такую область объявления с помощью объявлений_членов_класса, объявлений_членов_структуры, объявлений_членов_интерфейса или параметров_типа. Исключая перегруженные объявления конструктора экземпляров и объявления статического конструктора, класс или структура не могут содержать объявление члена с одинаковым именем в классе или структуре. Класс, структура или интерфейс допускает объявление перегруженных методов и индексаторов. Более того, класс или структура допускают объявление перегруженных конструкторов экземпляров и операторов. Например, класс, структура или интерфейс могут содержать несколько объявлений методов с одним именем с условием, что данные объявления методов отличаются по сигнатуре (3.6). Обратите внимание, что базовые классы не размещаются в области объявления класса, а также базовые интерфейсы не размещаются в области объявления интерфейса. Таким образом производный класс или интерфейс могут содержать объявление члена с одинаковым именем в качестве унаследованного члена. Такой член скрывает унаследованный член.

Каждое объявление делегата создает новую область объявления. Имена размещаются в данной области объявления посредством формальных параметров (фиксированных_параметров и массивов_параметров) и параметров_типа.

Каждое объявление перечисляемого типа создает новую область объявления. Имена размещаются в данной области объявления посредством объявлений_членов_перечисляемых_типов.

Каждое объявление метода, объявление индексатора, объявление оператора, объявление конструктора экземпляров и анонимная функция создает новую область объявления, которая называется областью объявлений локальных переменных. Имена размещаются в данной области объявления посредством формальных параметров (фиксированных_параметров и массивов_параметров) и параметров_типа. Предполагается, что тело члена функции или анонимная функция (при наличии) вложены в область объявлений локальных переменных. Наличие в области объявления локальных переменных и вложенной области объявления локальных переменных элементов с одинаковым именем является ошибкой. Таким образом, в рамках вложенной области объявления невозможно объявить локальную переменную или константу с одинаковым именем в родительской области объявления. Две области объявления могут содержать элементы с одинаковыми именами, если ни одна из областей не содержит другую.

Каждый блок или блок_switch, а также инструкции for, foreach и using создают область объявлений для локальных переменных и локальных констант . Имена размещаются в данной области объявления посредством объявлений_локальной_переменной и объявлений_локальной_константы. Обратите внимание, что блоки, встречающиеся в качестве тела члена функции или анонимной функции (или внутри них), вложены в область объявлений локальных переменных, объявленной этими функциями для соответствующих параметров. Следовательно, наличие, к примеру, метода с одинаковыми именами локальной переменной и параметра является ошибкой.

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

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

Порядок объявления объявлений полей и локальных переменных определяет порядок выполнения их инициализаторов (при их наличии).

Локальные переменные должны быть заданы до их использования (3.7).

Порядок объявлений членов перечислений (14.3) важен, если опущены значения константных_выражений.

Область объявления пространства имен является открыто завершенной, поэтому два объявления пространств имен с одинаковыми полными именами размещаются в одной области объявления. Пример:

namespace Megacorp.Data
{
class Customer
{
...
}
}

namespace Megacorp.Data
{
class Order
{
...
}
}

Два рассмотренных выше объявления пространств имен размещаются в одной области объявления, и в этом случае объявляются два класса с полными именами Megacorp.Data.Customer и Megacorp.Data.Order. Поскольку два объявления размещаются в одной области объявления, если в каждом объявлении содержится объявление класса с одинаковым именем, происходит ошибка времени компиляции.

Согласно указанному выше, область объявления блока содержит любые вложенные блоки. Поэтому в следующем примере методы F и G приводят к ошибке времени выполнения, так как имя i объявлено во внешнем блоке и не может быть повторно объявлено во внутреннем блоке. Однако методы H и I верны, так как два имени i объявлены в отдельных невложенных блоках.

 

class A
{
void F() {
int i = 0;
if (true) {
int i = 1;
}
}

void G() {
if (true) {
int i = 0;
}
int i = 1;
}

void H() {
if (true) {
int i = 0;
}
if (true) {
int i = 1;
}
}

void I() {
for (int i = 0; i < 10; i++)
H();
for (int i = 0; i < 10; i++)
H();
}
}

3.4 Члены

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

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

3.4.1 Члены пространства имен

Пространства имен и типы, имеющие родительское пространство имен, являются членами глобального пространства имен. Это непосредственно соответствует именам, объявленным в глобальной области объявления.

Пространства имен и типы, объявленные в рамках пространства имен, являются членами данного пространства имен. Это непосредственно соответствует именам, объявленным в области объявления пространства имен.

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

3.4.2 Члены структуры

Членами структуры являются члены, объявленные в структуре, и члены, унаследованные от непосредственного базового класса структуры System.ValueType и косвенного базового класса object.

Члены простого типа непосредственно соответствуют членам типа структуры с псевдонимами по простому типу.

Члены sbyte являются членами структуры System.SByte.

Члены byte являются членами структуры System.Byte.

Члены short являются членами структуры System.Int16.

Члены ushort являются членами структуры System.UInt16.

Члены int являются членами структуры System.Int32.

Члены uint являются членами структуры System.UInt32.

Члены long являются членами структуры System.Int64.

Члены ulong являются членами структуры System.UInt64.

Члены char являются членами структуры System.Char.

Члены float являются членами структуры System.Single.

Члены double являются членами структуры System.Double.

Члены decimal являются членами структуры System.Decimal.

Члены bool являются членами структуры System.Boolean.

3.4.3 Члены перечисления

Членами перечисления являются члены, объявленные в перечислении, и члены, унаследованные от непосредственного базового класса перечисления System.Enum и косвенных базовых классов System.ValueType и object.

3.4.4 Члены класса

Членами класса являются члены, объявленные в классе, и члены, унаследованные от базового класса (исключая класс object, не имеющего базовый класс). Члены, унаследованные из базового класса, включают константы, поля, методы, свойства, события, индексаторы, операторы и типы базового класса, но не включают конструкторы экземпляров, деструкторы и статические конструкторы базового класса. Члены базового класса наследуются вне зависимости от их доступности.

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

Члены object и string непосредственно соответствуют членам типов класса, псевдонимами которых они являются.

Члены object являются членами структуры System.Object.

Члены string являются членами структуры System.String.

3.4.5 Члены интерфейса

Членами интерфейса являются члены, объявленные в интерфейсе и во всех базовых интерфейсах данного интерфейса. Члены в классе object, строго говоря, не являются членами какого-либо интерфейса (13.2). Тем не менее, члены в классе object доступны при поиске членов интерфейса любого типа (7.4).

3.4.6 Члены массива

Членами массива являются члены, унаследованные из класса System.Array.

3.4.7 Члены делегата

Членами делегата являются члены, унаследованные из класса System.Delegate.

3.5 Метод доступа к члену

Объявления членов позволяют осуществлять контроль доступа членов. Доступность члена устанавливается посредством объявленной доступности (3.5.1) члена в сочетании с доступностью непосредственно содержащем типе, при его наличии.

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

3.5.1 Объявленная доступность

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

Общая, выбираемая путем включения модификатора public в объявление члена. Интуитивное понимание модификатора public доступ не ограничен.

Защищенная, выбираемая путем включения модификатора protected в объявление члена. Интуитивное понимание модификатора protected доступ ограничен до класс-контейнера или типов, производных от класс-контейнера.

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

Внутренняя защищенная (обозначает защищенную или внутреннюю доступность), выбираемая путем включения модификаторов protected и internal в объявление члена. Интуитивное понимание модификатор protected internal доступ ограничен до данной программы или типов, производных от класс-контейнера.

Частная, выбираемая путем включения модификатора private в объявление члена. Интуитивное понимание модификатора private доступ ограничен до тип-контейнера.

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

Пространства имен неявно имеют объявленную доступность public. Модификаторы доступа запрещены для объявлений пространств имен.

Типы, объявленные в блоках компиляции, или пространства имен могут иметь объявленную доступность public или internal, а по умолчанию используется объявленная доступность internal.

Члены класса могут иметь любую из пяти типов объявленной доступности, а по умолчанию используется объявленная доступность private. (Обратите внимание, что тип, объявленный в качестве члена класса, может иметь любую из пяти объявленных доступностей, тогда как тип, объявленный в качестве члена пространства имен, может иметь только объявленную доступность public или internal.)

Члены структуры могут иметь объявленную доступность public, internal или private, а по умолчанию использует объявленная доступность private, так как структуры неявно запечатаны. Члены структуры, размещенные в структуре (т. е. не унаследованные данной структурой), не могут иметь статусы объявленной доступности protected или protected internal. (Обратите внимание, что тип, объявленный в качестве члена структуры, может иметь объявленную доступностей public, internal или private, тогда как тип, объявленный в качестве члена пространства имен, может иметь только объявленную доступность public или internal.)

Члены интерфейса неявно имеют объявленную доступность public. Модификаторы доступа запрещены для объявлений члена интерфейса.

Члены перечисления неявно имеют объявленную доступность public. Модификаторы доступа запрещены для объявлений члена перечисления.

3.5.2 Домены доступности

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

Домен доступности предопределенного типа (например, object, int или double) неограничен.

Домен доступности свободного типа верхнего уровня T (4.4.3), объявленного в программе P, определяется следующим образом.

Если для T объявлена доступность public, доменом доступности T является текст программы P и любая программа, ссылающаяся на P.

Если для T объявлена доступность internal, доменом доступности T является текст программы P.

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

Доменом доступности для сформированного типа T<A1, ...,AN> является пересечение домена доступности обобщенного свободного типа T и доменов доступности аргументов типа A1, ...,AN.

Домен доступности вложенного члена M, объявленного в типе T в программе P, определяется следующим образом (учитывая, что M может являться типом).

Если для M объявлена доступность public, домен доступности M совпадает с доменом доступности T.

Если для M объявлена доступность protected internal, пусть D будет объединением текста программы P и текста программы любого типа, произведенного из T, объявленного вне P. Доменом доступности M является пересечение домена доступности T с доменом доступности D.

Если для M объявлена доступность protected, пусть D будет объединением текста программы T и текста программы любого типа, произведенного из T. Доменом доступности M является пересечение домена доступности T с доменом доступности D.

Если для M объявлена доступность internal, доменом доступности M является пересечение домена доступности T с текстом программы P.

Если для M объявлена доступность private, домен доступности M совпадает с текстом программы T.

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

Интуитивно это значит, что при доступе к типу или члену M для обеспечения разрешения доступа вычисляются следующие шаги.

Сначала, если M объявлен в типе (в противоположность блоку компиляции или пространству имен), при недоступности данного типа возникает ошибка времени компиляции.

Затем, если M объявлен как public, доступ разрешается.

В противном случае, если M объявлен как protected internal, доступ разрешается в том случае, когда он происходит в рамках программы, в которой объявлен M, или когда он происходит в рамках класса, произведенного из класса, в котором объявлен M, и доступ выполняется посредством производного типа класса (3.5.3).

В противном случае, если M объявлен как protected, доступ разрешается в том случае, когда он происходит в рамках класса, в котором объявлен M, или когда он возникает в рамках класса, произведенного из класса, в котором объявлен M, и доступ выполняется посредством производного типа класса (3.5.3).

В противном случае, если M объявлен как internal, доступ разрешается, если он происходит в рамках программы, в которой объявлен M.

В противном случае, если M объявлен как private, доступ разрешается, если он происходит в рамках типа, в котором объявлен M.

В противном случае тип или член недоступны, и возникает ошибка времени компиляции.

Пример:

public class A
{
public static int X;
internal static int Y;
private static int Z;
}

internal class B
{
public static int X;
internal static int Y;
private static int Z;

public class C
{
public static int X;
internal static int Y;
private static int Z;
}

private class D
{
public static int X;
internal static int Y;
private static int Z;
}
}

классы и члены имеют следующие области доступности.

Домен доступности A и A.X неограничен.

Доменом доступности A.Y, B, B.X, B.Y, B.C, B.C.X и B.C.Y является текст программы содержащей программы.

Доменом доступности A.Z является текст программы A.

Доменом доступности B.Z и B.D является текст программы B, включая текст программы B.C и B.D.

Доменом доступности B.C.Z является текст программы B.C.

Доменом доступности B.D.X и B.D.Y является текст программы B, включая текст программы B.C и B.D.

Доменом доступности B.D.Z является текст программы B.D.

Как видно из примера, область доступности члена никогда не превышает области доступности содержащего типа. Например, несмотря на то, что все члены X имеют открытую объявленную доступность, все члены кроме A.X имеют домены доступности, ограниченные тип-контейнером.

Согласно описанию в разделе 3.4 все члены базового класса, кроме конструкторов экземпляров, деструкторов и статических конструкторов, наследуются производными типами. Это касается даже закрытых членов базового класса. Однако домен доступности закрытого класса включает только текст программы типа, в котором член объявлен. Пример:

class A
{
int x;

static void F(B b) {
b.x = 1; // Ok
}
}

class B: A
{
static void F(B b) {
b.x = 1; // Error, x not accessible
}
}

класс B наследует закрытый член x из класса A. Так как член является закрытым, он доступен только в рамках тела_класса A. Поэтому доступ к b.x успешно осуществляется в методе A.F, но невозможен в методе B.F.

3.5.3 Защищенный доступ для членов экземпляров.

При доступе к члену экземпляра protected вне текста программы, в котором он объявлен, и при доступе к члену экземпляра protected internal вне текста программы, в котором он объявлен, доступ должен осуществляться в объявлении класса, образованным от класса, в котором он объявлен. Дополнительно доступ должен осуществляться через экземпляр производного типа класса или типа класса сконструированного из него. Данное ограничение предотвращает доступ одного производного класса к закрытым членам другого производного класса, даже если члены унаследованы от одного базового класса.

Пусть B будет классом, в котором объявлен защищенный член экземпляра M, и пусть D будет производным классом B. В рамках тела_класса D доступ к M может принять одну из следующих форм.

Имя_типа или первичное_выражение без уточнения формы M.

Первичное_выражение формы E.M с условием, что тип E является T или производным от T классом, где T является типом класса D или типом класса, сконструированным из D.

Первичное_выражение формы base.M.

Дополнительно к данным формам доступа производный класс может осуществлять доступ к конструктору защищенных экземпляров базового класса в инициализаторе_конструктора (10.11.1).

In the example

public class A
{
protected int x;

static void F(A a, B b) {
a.x = 1; // Ok
b.x = 1; // Ok
}
}

public class B: A
{
static void F(A a, B b) {
a.x = 1; // Error, must access through instance of B
b.x = 1; // Ok
}
}

в рамках A можно осуществить доступ к x через экземпляры A и B, так как в любом случае доступ осуществляется через экземпляр A или производный от A класс. Однако в рамках B невозможно осуществить доступ к x через экземпляр A, так как A не произведен из B.

В примере:

class C<T>
{
protected T x;
}

class D<T>: C<T>
{
static void F() {
D<T> dt = new D<T>();
D<int> di = new D<int>();
D<string> ds = new D<string>();
dt.x = default(T);
di.x = 123;
ds.x = "test";
}
}

три присваивания x разрешены, так как они все осуществляются через экземпляры типов класса, сконструированные из универсального типа.

3.5.4 Ограничения доступности

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

Существуют следующие ограничения доступности.

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

Явный базовый интерфейс типа интерфейса должен быть не менее доступен, чем сам тип интерфейса.

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

Тип константы должен быть не менее доступен, чем сама константа.

Тип поля должен быть не менее доступен, чем само поле.

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

Тип свойства должен быть не менее доступен, чем само свойство.

Тип события должен быть не менее доступен, чем само событие.

Тип и типы параметра индексатора должны быть не менее доступны, чем сам индексатор.

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

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

Пример:

class A {...}

public class B: A {...}

класс B приводит к ошибке времени компиляции, так как A менее доступен, чем B.

Аналогично, в примере

class A {...}

public class B
{
A F() {...}

internal A G() {...}

public A H() {...}
}

метод H в B приводит к ошибке времени компиляции, так как тип возвращаемого значения A менее доступен, чем метод.

3.6 Сигнатуры и перегрузка

Методы, конструкторы экземпляров, индексаторы и операторы характеризуются их сигнатурами.

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

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

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

Сигнатура оператора состоит из имени оператора и типа каждого его формального параметра, учтенного в порядке слева направо. Сигнатура оператора специально не содержит тип результата.

Сигнатуры обеспечивают использование механизма перегрузки членов в классах, структурах и интерфейсах.

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

Перегрузка конструкторов экземпляров позволяет классу или структуре объявить несколько конструкторов экземпляров при условии, что их сигнатуры уникальны в рамках класса или структуры.

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

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

Несмотря на то, что модификаторы параметров out и ref считаются частью сигнатуры, члены, объявленные в одном типе, не могут отличаться по сигнатуре только параметрами ref и out. Если два члена, объявленные в одном типе, будут иметь одинаковые сигнатуры, в которых все параметры в обоих методах с модификаторами out изменены на модификаторы ref, возникает ошибка времени компиляции. Для других целей сопоставления сигнатур (например, для скрытия или переопределения) ref и out учитываются в качестве части сигнатуры и не соответствуют друг другу. (Данное ограничение позволяет простым способом транслировать программы на C# для выполнения в Common Language Infrastructure (CLI), не обеспечивающей способ определения методов, отличающихся исключительно по ref и out.)

Для сигнатур типы object и dynamic являются идентичными. Члены, объявленные в едином типе, могут не отличаться в сигнатурах одними лишь object и dynamic.

В следующем примере представлен набор объявлений перегруженных методов с их сигнатурами.

interface ITest
{
void F(); // F()

void F(int x); // F(int)

void F(ref int x); // F(ref int)

void F(out int x); // F(out int) error

void F(int x, int y); // F(int, int)

int F(string s); // F(string)

int F(int x); // F(int) error

void F(string[] a); // F(string[])

void F(params string[] a); // F(string[]) error
}

Обратите внимание, что модификаторы параметров ref и out (10.6.1) являются частью сигнатуры. Поэтому F(int) и F(ref int) являются уникальными сигнатурами. Однако F(ref int) и F(out int) не могут быть объявлены в одном интерфейсе, так как их сигнатуры отличаются только модификаторами ref и out. Также обратите внимание, что тип возвращаемого значения и модификатор params не являются частью сигнатуры, поэтому невозможно выполнить перегрузку только на основе типа возвращаемого значения или на основе включения или исключения модификатора params. По существу, объявления методов F(int) и F(params string[]), идентифицированных выше, приводят к ошибке времени компиляции.

3.7 Области видимости

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

Областью члена пространства имен, объявленного объявлением_члена_пространства_имен (9.5), без вмещающего объявления_пространства_имен является полный текст программы.

Областью члена пространства имен, объявленного объявлением_члена_пространства_имен в рамках объявления_пространства_имен с полным именем N, является тело_пространства_имен каждого объявления_пространства_имен с полным именем N, или начинающимся с N и следующим периодом.

Область имени, определенного директивой_внешнего_псевдонима, распространяется на использование_директив, глобальные_атрибуты и объявления_членов_пространства_имен незамедлительно содержащегося блока компиляции или тела пространства имен. Директива_внешнего_псевдонима не вносит какие-либо новые члены в нижестоящую область объявления. Другими словами, директива_внешнего_псевдонима не является транзитивной, а скорее влияет только на блок компиляции или тело пространства имен, в котором она происходит.

Область имени, заданная или импортированная путем использования_директивы (9.4), распространяется на объявления_членов_пространства_имен_блока_компиляции или тела_пространства_имен, в котором содержится использование_директивы. Использование_директивы может привести к образованию нуля или более пространств имен или типов имен, доступных в рамках определенного блока_компиляции или тела_пространства_имен, но не может привести к созданию новых членов в нижестоящей области объявления. Другими словами, использование_директивы не является транзитивным, а скорее влияет только на блок_компиляции или тело_пространства_имен, в котором находится.

Областью параметра типа, объявленного списком_параметров_типа в объявлении_класса (10.1), является база_класса, предложения_ограничений_параметров_типа и тело_класса данного объявления_класса.

Областью параметра типа, объявленного списком_параметров_типа в объявлении_структуры (11.1), является интерфейс_структуры, предложения_ограничений_параметров_типа и тело_структуры данного объявления_структуры.

Областью параметра типа, объявленного списком_параметров_типа в объявлении_интерфейса (13.1), является база_интерфейса, предложения_ограничений_параметров_типа и тело_интерфейса данного объявления_интерфейса.

Областью параметра типа, объявленного списком_параметров_типа в объявлении_делегата (15.1), является тип_возвращаемого_значения, список_формальных_параметров и предложения_ограничений_параметров_типа данного объявления_делегата.

Областью члена, объявленного объявлением_члена_класса (10.1.6), является тело_класса, в котором сделано объявление. Кроме того, область члена класса распространяется на тела_класса производных классов, включенных в домен доступности (3.5.2) члена.

Областью члена, объявленного объявлением_члена_структуры (11.2), является тело_структуры, в котором сделано объявление.

Областью члена, объявленного объявлением_члена_перечисляемого_типа (14.3), является тело_перечисляемого_типа, в котором сделано объявление.

Областью параметра, объявленного в объявлении_метода (10.6), является тело_метода данного объявления_метода.

Областью параметра, объявленного в объявлении_индексатора (10.9), являются объявления_метода_доступа данного объявления_индексатора.

Областью параметра, объявленного в объявлении_оператора (10.10), является блок данного объявления_оператора.

Областью параметра, объявленного в объявлении_конструктора (10.11), является инициализатор_конструктора и блок данного объявления_конструктора.

Областью параметра, объявленного в лямбда_выражении (), является тело_лямбда_выражения данного лямбда_выражения.

Областью параметра, объявленного в выражении_анонимного_метода (), является блок данного выражения_анонимного_метода.

Областью метки, объявленной в помеченном_операторе (8.4), является блок, в котором содержится объявление.

Областью локальной переменной, объявленной в объявлении_локальной_переменной (8.5.1), является блок, в котором содержится объявление.

Областью локальной переменной, объявленной в блоке_ветвления оператора switch (8.7.2), является блок_ветвления.

Областью локальной переменной, объявленной в инициализаторе_for оператора for (8.8.3), является инициализатор_for, условие_for, итератор_for и содержащий оператор оператора for.

Областью локальной константы, объявленной в объявлении_локальной_константы (8.5.2), является блок, в котором содержится объявление. Ссылка на локальную константу в текстовой позиции, предшествующей декларатору_константы, является ошибкой времени компилирования.

Область переменной, объявленной в качестве части оператора_foreach, оператора_using, оператора_lock или выражения_запроса, определяется путем расширения заданной конструкции.

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

class A
{
void F() {
i = 1;
}

int i = 0;
}

В данном примере ссылка F на i до объявления является верной.

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

class A
{
int i = 0;

void F() {
i = 1; // Error, use precedes declaration
int i;
i = 2;
}

void G() {
int j = (j = 1); // Valid
}

void H() {
int a = 1, b = ++a; // Valid
}
}

В вышеуказанном методе F первое присваивание i явно не ссылается на поле, объявленное во внешней области. Однако существует ссылка на локальную переменную, что приводит к ошибке времени компилирования, так как текстуальность предшествует объявлению переменной. В методе G использование j в инициализаторе для объявления j является верным, так как использование не предшествует декларатору_локальной_переменной. В методе H последующий декларатор_локальной_переменной верно ссылается на локальную переменную, объявленную в более раннем деклараторе_локальной_переменной в рамках того же декларатора_локальной_переменной.

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

Значение имени в рамках блока может отличаться в зависимости от контекста, в котором используется имя. Пример:

using System;

class A {}

class Test
{
static void Main() {
string A = "hello, world";
string s = A; // expression context

Type t = typeof(A); // type context

Console.WriteLine(s); // writes "hello, world"
Console.WriteLine(t); // writes "A"
}
}

имя A используется в контексте выражения для ссылки на локальную переменную A и в контексте типа для ссылки на класс A.

3.7.1 Скрытие имени.

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

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

3.7.1.1 Скрытие через вложение.

Скрытие имени через вложение возникает в результате вложения пространств имен или типов в рамках пространств имен, в результате вложения типов в рамках классов или структур и в результате объявлений параметров и локальных переменных.

Пример:

class A
{
int i = 0;

void F() {
int i = 1;
}

void G() {
i = 1;
}
}

в рамках метода F переменный экземпляр i скрыт локальной переменной i, но в рамках метода G экземпляр i все еще ссылается на переменный экземпляр.

Когда имя во внутренней области скрывает имя внешней области, скрываются все перегруженные вхождения данного имени. Пример:

class Outer
{
static void F(int i) {}

static void F(string s) {}

class Inner
{
void G() {
F(1); // Invokes Outer.Inner.F
F("Hello"); // Error
}

static void F(long l) {}
}
}

вызов F(1) приводит к вызову F, объявленному в Inner, так как все внешние вхождения F скрыты внутренним объявлением. По той же причине вызов F("Hello") приводит к ошибке времени компилирования.

3.7.1.2 Скрытие через наследование

Скрытие имени через наследование происходит, когда классы или структуры повторно объявляют имена, унаследованные из базовых классов. Этот тип скрытия имени принимает одну из следующих форм.

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

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

Индексатор, представленный в классе или структуре, скрывает все индексаторы базового класса с такой же сигнатурой (попадания и типы параметра).

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

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

class Base
{
public void F() {}
}

class Derived: Base
{
public void F() {} // Warning, hiding an inherited name
}

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

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

class Base
{
public void F() {}
}

class Derived: Base
{
new public void F() {}
}

Модификатор new указывает, что F в Derived является новым, и что действительно требуется скрыть унаследованный член.

Объявление нового члена приводит к скрытию унаследованного члена только в рамках области нового члена.

class Base
{
public static void F() {}
}

class Derived: Base
{
new private static void F() {} // Hides Base.F in Derived only
}

class MoreDerived: Derived
{
static void G() { F(); } // Invokes Base.F
}

В вышеуказанном примере объявление F в Derived приводит к скрытию F, унаследованного от Base, но, так как новый F в Derived имеет частный доступ, его область не распространяется на MoreDerived. Поэтому вызов F() в MoreDerived.G действителен, и он приведет к вызову Base.F.

3.8 Имена пространств имен и типов

Некоторые контексты в программе на C# требуют указания имени_пространства_имен или имени типа.

имя_пространства_имен:
имя_пространства_имен_или_типа

имя_типа:
имя_пространства_имен_или_типа

имя_пространства_имен_или_типа:
идентификатор список аргументов типанеобязательно
имя_пространства_имен_или_типа . идентификатор список_аргументов_типанеобязательно
уточненный_член_псевдонима

Имя_пространства_имен это имя_пространства_имен_или_типа, ссылающееся на пространства имен. Согласно описанию ниже следующее разрешение имени_пространства_имен_или_типа от имени_пространства_имен должно ссылаться на пространства имен, в противном случае возникает ошибка времени компилирования. Аргументы типов (4.4.1) не могут быть представлены в имени_пространства_имен (только типы с аргументами типа).

Имя_типа это имя_пространства_имен_или_типа, ссылающееся на тип. Согласно описанию ниже следующее разрешение имени_пространства_имен_или_типа от имени_типа должно ссылаться на тип, в противном случае возникает ошибка времени компилирования.

Если имя_пространства_имен_или_типа является членом квалифицированного псевдонима, его значение соответствует описанию в разделе 9.7. В противном случае имя_пространства_имен_или_типа имеет одну из следующих четырех форм.

I

I<A1, ..., AK>

N.I

N.I<A1, ..., AK>

где I отдельный идентификатор, N имя_пространства_имени_или_типа и <A1, ..., AK> дополнительный список_аргументов_типа. Когда список_аргументов_типа не указан, считается, что K равно нулю.

Значение имени_пространства_имен_или_типа определяется следующим образом.

Имя_пространства_имен_или_типа имеет одну из форм I или форму I<A1, ..., AK>:

o Если K имеет нулевое значение, имя_пространства_имен_или_ти