В языках программирования нет общепринятой нормы для использования символа точки с запятой при записи последовательности операторов. Есть три различных подхода и их вариации. Категорические противники точек с запятой считают, что каждый оператор должен записываться на отдельной строке (для длинных операторов определяются правила переноса). В этом случае точки с запятой (или другие аналогичные разделители) не нужны. Горячие поклонники точек с запятой (к ним относятся языки С++ и C#) считают, что точкой с запятой должен оканчиваться каждый оператор. В результате в операторе if перед else появляется точка с запятой. Третьи полагают, что точка с запятой играет роль разделителя операторов, поэтому перед else ее не должно быть. В приведенной выше записи блока, следуя синтаксису C#, каждый из операторов заканчивается символом "точка с запятой". Но, заметьте, блок не заканчивается этим символом! |


| Insert | Вставляет подстроку в заданную позицию |
| Remove | Удаляет подстроку в заданной позиции |
| Replace | Заменяет подстроку в заданной позиции на новую подстроку |
| Substring | Выделяет подстроку в заданной позиции |
| IndexOf, IndexOfAny, LastIndexOf, LastIndexOfAny | Определяются индексы первого и последнего вхождения заданной подстроки или любого символа из заданного набора |
| StartsWith, EndsWith | Возвращается true или false, в зависимости от того, начинается или заканчивается строка заданной подстрокой |
| PadLeft, PadRight | Выполняет набивку нужным числом пробелов в начале и в конце строки |
| Trim, TrimStart, TrimEnd | Обратные операции к методам Pad. Удаляются пробелы в начале и в конце строки, или только с одного ее конца |
| ToCharArray | Преобразование строки в массив символов |









| Comparer | Comparer | ICollection | ICollection |
| Dictionary | HashTable | IComparable | IComparable |
| LinkedList | ---- | IDictionary | IDictionary |
| List | ArrayList | IEnumerable | IEnumerable |
| Queue | Queue | IEnumerator | IEnumerator |
| SortedDictionary | SortedList | IList | IList |
| Stack | Stack |



| Бинарный поток | Стандартная | 355 байтов |
| Бинарный поток | Управляемая | 355 байтов |
| XML-документ | Стандартная | 1, 14 Кб. |
| XML-документ | Управляемая | 974 байта |
Введение в язык частных случаев усложняет его и свидетельствует о некоторых изъянах, для преодоления которых и вводятся частные случаи. Например, введение структур в язык C# позволило определять классы как развернутые типы. Конечно, проще было бы ввести в объявление класса соответствующий модификатор, позволяющий любой класс объявлять развернутым. Но этого сделано не было, а, следуя традиции языка С++, были введены структуры как частный случай классов. Подробнее о развернутых и ссылочных типах см. лекцию 17. |
Заметьте, рекомендуемый стиль программирования в C# отличается от стиля, принятого в языках С/C++, где функция, в которой возникла ошибка, завершается нормальным образом, уведомляя об ошибке в возвращаемом значении результата. Вызывающая программа должна анализировать результат, чтобы понять, была ли ошибка в работе вызванной функции и какова ее природа. При программировании в стиле C# ответственность за обнаружение ошибок лежит на вызванной программе. Она должна не только обнаружить ошибку, но и явно сообщить о ней, выбрасывая исключение соответствующего типа. Вызываемая программа должна попытаться исправить последствия ошибки в обработчике исключения. Подробности смотри в лекции про исключения. |



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




| GetNumericValue | Возвращает численное значение символа, если он является цифрой, и (-1) в противном случае |
| GetUnicodeCategory | Все символы разделены на категории. Метод возвращает Unicode категорию символа. Ниже приведен пример |
| IsControl | Возвращает true, если символ является управляющим |
| IsDigit | Возвращает true, если символ является десятичной цифрой |
| IsLetter | Возвращает true, если символ является буквой |
| IsLetterOrDigit | Возвращает true, если символ является буквой или цифрой |
| IsLower | Возвращает true, если символ задан в нижнем регистре |
| IsNumber | Возвращает true, если символ является числом (десятичной или шестнадцатиричной цифрой) |
| IsPunctuation | Возвращает true, если символ является знаком препинания |
| IsSeparator | Возвращает true, если символ является разделителем |
| IsSurrogate | Некоторые символы Unicode с кодом в интервале [0x1000, 0x10FFF] представляются двумя 16-битными "суррогатными" символами. Метод возвращает true, если символ является суррогатным |
| IsUpper | Возвращает true, если символ задан в верхнем регистре |
| IsWhiteSpace | Возвращает true, если символ является "белым пробелом". К белым пробелам, помимо пробела, относятся и другие символы, например, символ конца строки и символ перевода каретки |
| Parse | Преобразует строку в символ. Естественно, строка должна состоять из одного символа, иначе возникнет ошибка |
| ToLower | Приводит символ к нижнему регистру |
| ToUpper | Приводит символ к верхнему регистру |
| MaxValue, MinValue | Свойства, возвращающие символы с максимальным и минимальным кодом. Возвращаемые символы не имеют видимого образа |


![Класс char[] - массив символов](image/13-03.gif)















В лекции 9 введено строгое понятие корректности метода по отношению к его спецификациям, заданным в виде предусловия и постусловия метода. |


Обратите внимание, что всем победителям назначена одна и та же премия. Хотя понятно, что дело в программной ошибке, но в ней можно видеть и знак свыше. Коль скоро для победителей выбраны такие имена, почитаемые всеми программистами, то негоже пытаться расставить их по ранжиру даже в примере. Что же касается ошибки, то она связана с тем, что в данном случае свойство rnd следует сделать статическим, чтобы оно было одно на все экземпляры класса. В тексте описания варианта класса приведены оба варианта объявления свойства, один из которых закомментирован. |






О перегрузке операций при определении класса будет подробно сказано в лекции, посвященной классам. |





До недавнего времени Framework .Net и соответственно язык C# не поддерживали универсальность. Так что те, кто работает с языком C#, входящим в состав Visual Studio 2003 и ранних версий, должны смириться с отсутствием универсальных классов. Но в новой версии Visual Studio 2005, носящей кодовое имя Whidbey, проблема решена, и программисты получили наконец долгожданный механизм универсальности. Я использую в примерах этой лекции бета-версию Whidbey. Замечу, что хотя меня прежде всего интересовала реализация универсальности, но и общее впечатление от Whidbey самое благоприятное. |


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

Многообразие подходов к обработке исключений говорит о том, что не найден единый, удовлетворяющий всех подход. Чуть позже я расскажу о наиболее разумной, с моей точки зрения, схеме. К сожалению, в C# применяется не она. |
| В языке C++ все массивы являются статическими; более того, все массивы являются 0-базируемыми. Это означает, что нижняя граница всех индексов массива фиксирована и равна нулю. Введение такого ограничения имеет свою логику, поскольку здесь широко используется адресная арифметика. Так, несколько странное выражение mas + i , где mas - это имя массива, а i - индексное выражение, имеет вполне определенный смысл для C++ программистов. Имя массива интерпретируется как адрес первого элемента массива, к этому адресу прибавляется число, равное произведению i на размер памяти, необходимой для одного элемента массива. В результате сложения в такой адресной арифметике эффективно вычисляется адрес элемента mas[i]. |
| В языке C++ "классических" многомерных массивов нет. Здесь введены одномерные массивы и массивы массивов. Последние являются более общей структурой данных и позволяют задать не только многомерный куб, но и изрезанную, ступенчатую структуру. Однако использование массива массивов менее удобно, и, например, классик и автор языка C++ Бьерн Страуструп в своей книге "Основы языка C++" пишет: "Встроенные массивы являются главным источником ошибок - особенно когда они используются для построения многомерных массивов. Для новичков они также являются главным источником смущения и непонимания. По возможности пользуйтесь шаблонами vector, valarray и т.п.". Шаблоны, определенные в стандартных библиотеках, конечно, стоит использовать, но все-таки странной является рекомендация не пользоваться структурами, встроенными непосредственно в язык. Замечу, что в других языках массивы являются одной из любимых структур данных, используемых программистами. |





Разный приоритет префиксных и постфиксных операций носит условный характер. Эти операции применимы только к переменным, свойствам и индексаторам класса, то есть к выражениям, которым отведена область памяти. В языках C++ и C# такие выражения называются l-value, поскольку они могут встречаться в левых частях оператора присваивания. Как следствие, запись в C# выражения < --x++ > приведет к ошибке. Едва лишь к x слева или справа приписана одна из операций, выражение перестает принадлежать к классу l-value выражений, и вторую операцию приписать уже нельзя. |
Я уже многие годы не применяю этот оператор и считаю, что хороший стиль программирования не предполагает использования этого оператора в C# ни в каком из вариантов - ни в операторе switch, ни для организации безусловных переходов. |
Полагаю, что оператор switch - это самый неудачный оператор языка C# как с точки зрения синтаксиса, так и семантики. Неудачный синтаксис порождает запутанную семантику, являющуюся источником плохого стиля программирования. Понять, почему авторов постигла неудача, можно, оправдать - нет. Дело в том, что оператор унаследован от С++, где его семантика и синтаксис еще хуже. В языке C# синтаксически каждая case-ветвь должна заканчиваться оператором перехода (забудем на минуту о пустой последовательности), иначе возникнет ошибка периода компиляции. В языке С++ это правило не является синтаксически обязательным, хотя на практике применяется та же конструкция с конечным оператором break. При его отсутствии управление "проваливается" в следующую case-ветвь. Конечно, профессионал может с успехом использовать этот трюк, но в целом ни к чему хорошему это не приводит. Борясь с этим, в C# потребовали обязательного включения оператора перехода, завершающего ветвь. Гораздо лучше было бы, если бы последним оператором мог быть только оператор break, писать его было бы не нужно и семантика стала бы прозрачной - при совпадении значений двух выражений выполняются операторы соответствующей case-ветви, при завершении которой завершается и оператор switch. |


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


Некоторые детали будут пояснены позже при рассмотрении примеров. |








Понятие перегрузки методов и операций подробно будет рассмотрено в последующих лекциях (см. лекцию 8). |










Правильно ли, что обработчик события, а не сам класс, создающий событие, принимает решение о допуске изменения элемента списка? Все зависит от контекста. В прошлые времена молодые могли объявить о своей помолвке, но требовалось разрешение родителей на брак. Времена изменились - теперь на брак родительского благословения не требуется. Но в программистском мире ситуации, требующие внешнего разрешения, встречаются довольно часто. |


| 0 | Первичные | (expr) x.y f(x) a[x] x++ x-- new sizeof(t) typeof(t) checked(expr) unchecked(expr) | Слева направо |
| 1 | Унарные | + - ! ~ ++x --x (T)x | Слева направо |
| 2 | Мультипликативные (Умножение) | * / % | Слева направо |
| 3 | Аддитивные (Сложение) | + - | Слева направо |
| 4 | Сдвиг | << >> | Слева направо |
| 5 | Отношения, проверка типов | < > <= >= is as | Слева направо |
| 6 | Эквивалентность | == != | Слева направо |
| 7 | Логическое И | & | Слева направо |
| 8 | Логическое исключающее ИЛИ (XOR) | ^ | Слева направо |
| 9 | Логическое ИЛИ (OR) | | | Слева направо |
| 10 | Условное И | && | Слева направо |
| 11 | Условное ИЛИ | Слева направо | |
| 12 | Условное выражение | ? : | Справа налево |
| 13 | Присваивание | = *= /= %= += -= <<= >>= &= ^= |= | Справа налево |
В данном контексте понятие класс распространяется и на все его частные случаи - структуры, интерфейсы, делегаты. |
Уже в лекции 1 мы говорили о роли библиотеки FCL - статическом компоненте Framework .Net. В лекции 4 рассматривались возможности класса Convert этой библиотеки, а в лекции 7 - классы Math и Random. Изучение классов FCL будет постоянно сопровождать наш курс. |








| \b | При использовании его в квадратных скобках соответствует символу "обратная косая черта" с кодом - \u0008 |
| \t | Соответствует символу табуляции \u0009 |
| \r | Соответствует символу возврата каретки \u000D |
| \n | Соответствует символу новой строки \u000A |
| \e | Соответствует символу escape \u001B |
| \040 | Соответствует символу ASCII, заданному кодом до трех цифр в восьмеричной системе |
| \x20 | Соответствует символу ASCII, заданному кодом из двух цифр в шестнадцатиричной системе |
| \u0020 | Соответствует символу Unicode, заданному кодом из четырех цифр в шестнадцатиричной системе |
| . | Соответствует любому символу, за исключением символа конца строки |
| [aeiou] | Соответствует любому символу из множества, заданного в квадратных скобках |
| [^aeiou] | Отрицание. Соответствует любому символу, за исключением символов, заданных в квадратных скобках |
| [0-9a-fA-F] | Задание диапазона символов, упорядоченных по коду. Так, 0-9 задает любую цифру |
| \p{name} | Соответствует любому символу, заданному множеству с именем name, например, имя Ll задает множество букв латиницы в нижнем регистре. Поскольку все символы разбиты на подмножества, задаваемые категорией Unicode, то в качестве имени можно задавать имя категории |
| \P{name} | Отрицание. Большая буква всегда задает отрицание множества, заданного малой буквой |
| \w | Множество символов, используемых при задании идентификаторов - большие и малые символы латиницы, цифры и знак подчеркивания |
| \s | Соответствует символам белого пробела |
| \d | Соответствует любому символу из множества цифр |
| * | Итерация. Задает ноль или более соответствий; например, \w* |
| (abc)*. | Аналогично, {0,} |
| + | Положительная итерация. Задает одно или более соответствий; например, \w+ или (abc)+. Аналогично, {1,} |
| ? | Задает ноль или одно соответствие; например, \w? или (abc)?. Аналогично, {0,1} |
| {n} | Задает в точности n соответствий; например, \w{2} |
| {n,} | Задает, по меньшей мере, n соответствий; например, (abc){2,} |
| {n,m} | Задает, по меньшей мере, n, но не более m соответствий; например, (abc){2,5} |
| (? | При обнаружении соответствия выражению, заданному в круглых скобках, создается именованная группа, которой дается имя Name. Например, (? |
| () | Круглые скобки разбивают регулярное выражение на группы. Для каждого подвыражения, заключенного в круглые скобки, создается группа, автоматически получающая номер. Номера следуют в обратном порядке, поэтому полному регулярному выражению соответствует группа с номером 0 |
| (?imnsx) | Включает или выключает в группе любую из пяти возможных опций. Для выключения опции перед ней ставится знак минус. Например, (?i-s: ) включает опцию i, задающую нечувствительность к регистру, и выключает опцию s - статус single-line |
Замечание: Следует понимать тесную связь и идентичность встроенных типов языка C# и типов каркаса. Какими именами типов следует пользоваться в программных текстах - это спорный вопрос. Джеффри Рихтер в своей известной книге "Программирование на платформе Framework .Net" рекомендует использовать системные имена. Другие авторы считают, что следует пользоваться именами типов, принятыми в языке. Возможно, в модулях, предназначенных для межъязыкового взаимодействия, разумны системные имена, а в остальных случаях - имена конкретного языка программирования. |
| Bool | System.Boolean | true, false | 8 бит |
| Sbyte | System.SByte | -128 — 127 | Знаковое, 8 Бит |
| Byte | System.Byte | 0 — 255 | Беззнаковое, 8 Бит |
| Short | System.Short | -32768 —32767 | Знаковое, 16 Бит |
| Ushort | System.UShort | 0 — 65535 | Беззнаковое, 16 Бит |
| Int | System.Int32 | ?(-2*10^9 — 2*10^9) | Знаковое, 32 Бит |
| Uint | System.UInt32 | ?(0 — 4*10^9) | Беззнаковое, 32 Бит |
| Long | System.Int64 | ?(-9*10^18 — 9*10^18) | Знаковое, 64 Бит |
| Ulong | System.UInt64 | ?(0— 18*10^18) | Беззнаковое, 64 Бит |
| Float | System.Single | +1.5*10^-45 - +3.4*10^38 | 7 цифр |
| Double | System.Double | +5.0*10^-324 - +1.7*10^308 | 15-16 цифр |
| Decimal | System.Decimal | +1.0*10^-28 - +7.9*10^28 | 28-29 значащих цифр |
| Char | System.Char | U+0000 - U+ffff | 16 бит Unicode символ |
| String | System.String | Строка из символов Unicode | |
| Object | System.Object | Прародитель всех встроенных и пользовательских типов | |

| IsFixedSize | Интерфейс IList | True, если массив статический |
| IsReadOnly | Интерфейс IList | Для всех массивов имеет значение false |
| IsSynchronized | Интерфейс ICollection | True или False, в зависимости от того, установлена ли синхронизация доступа для массива |
| SyncRoot | Интерфейс ICollection | Собственный метод синхронизации доступа к массиву. При работе с массивом его можно закрыть на время обработки, что запрещает его модификацию каким-либо потоком: Array myCol = new int[]; lock( myCol.SyncRoot ) { foreach ( Object item in myCol ) { // безопасная обработка массива } |
| Length | Число элементов массива | |
| Rank | Размерность массива |
| BinarySearch | Двоичный поиск. Описание и примеры даны в тексте |
| Clear | Выполняет начальную инициализацию элементов. В зависимости от типа элементов устанавливает значение 0 для арифметического типа, false - для логического типа, Null для ссылок, "" - для строк. |
| Copy | Копирование части или всего массива в другой массив. Описание и примеры даны в тексте |
| CreateInstance | Класс Array, в отличие от многих классов, может создавать свои экземпляры не только с помощью конструктора new, но и при вызове метода CreateInstance: Array my2Dar = Array.CreateInstance(typeof(double), 2,2) |
| IndexOf | Индекс первого вхождения образца в массив. Описание и примеры даны в тексте |
| LastIndexOf | Индекс последнего вхождения образца в массив. Описание и примеры даны в тексте |
| Reverse | Обращение одномерного массива. Описание и примеры даны в тексте |
| Sort | Сортировка массива. Описание и примеры даны в тексте |






| Empty | Возвращается пустая строка. Свойство со статусом read only |
| Compare | Сравнение двух строк. Метод перегружен. Реализации метода позволяют сравнивать как строки, так и подстроки. При этом можно учитывать или не учитывать регистр, особенности национального форматирования дат, чисел и т.д. |
| CompareOrdinal | Сравнение двух строк. Метод перегружен. Реализации метода позволяют сравнивать как строки, так и подстроки. Сравниваются коды символов |
| Concat | Конкатенация строк. Метод перегружен, допускает сцепление произвольного числа строк |
| Copy | Создается копия строки |
| Format | Выполняет форматирование в соответствии с заданными спецификациями формата. Ниже приведено более полное описание метода |
| Intern, IsIntern | Отыскивается и возвращается ссылка на строку, если таковая уже хранится во внутреннем пуле данных. Если же строки нет, то первый из методов добавляет строку во внутренний пул, второй - возвращает null. Методы применяются обычно тогда, когда строка создается с использованием построителя строк - класса StringBuilder |
| Join | Конкатенация массива строк в единую строку. При конкатенации между элементами массива вставляются разделители. Операция, заданная методом Join, является обратной к операции, заданной методом Split. Последний является динамическим методом и, используя разделители, осуществляет разделение строки на элементы |



| Equals | Класс Object | Описание и примеры даны в предыдущих главах. |
| GetHashCode | Класс Object | Описание и примеры даны в предыдущих главах. |
| GetType | Класс Object | Описание и примеры даны в предыдущих главах. |
| ToString | Класс Object | Описание и примеры даны в предыдущих главах. |
| Clone | Интерфейс ICloneable | Позволяет создать плоскую или глубокую копию массива. В первом случае создаются только элементы первого уровня, а ссылки указывают на те же самые объекты. Во втором случае копируются объекты на всех уровнях. Для массивов создается только плоская копия. |
| CopyTo | Интерфейс ICollection | Копируются все элементы одномерного массива в другой одномерный массив, начиная с заданного индекса: col1.CopyTo(col2,0); |
| GetEnumerator | Интерфейс IEnumerable | Стоит за спиной цикла ForEach |
| GetLength | Возвращает число элементов массива по указанному измерению. Описание и примеры даны в тексте главы. | |
| GetLowerBound, GetUpperBound | Возвращает нижнюю и верхнюю границу по указанному измерению. Для массивов нижняя граница всегда равна нулю. | |
| GetValue, SetValue | Возвращает или устанавливает значение элемента массива с указанными индексами. | |
| Initialize | Может быть применен только к массивам значимого типа. Инициализирует элементы, вызывая соответствующий конструктор. Как правило, не используется в обычных программах. |
Область видимости, время жизни переменных, конфликты имен рассмотрены в лекции 5, семантика операторов - в лекции 8. Дополнительные сведения о семантике выполнения метода будут даны в этой лекции. |





Семантика присваивания рассматривалась в лекциях 3, 6 и 7. |

О точке большого взрыва и цепочке вызовов мы говорили еще в лекции 2. |