
Объектно-ориентированное программирование.-6
.pdf
хватит и одной буквы.
3.1.6.3. Константы
Можно запретить изменять значение переменной, задав при ее описании ключевое слово const, например:
const string s = "123"; const int a = 1;
s = "!!!"; // Ошибка a = 123; // Ошибка
Такие величины называют именованными константами, или просто константами. Они применяются для того, чтобы вместо значений констант можно был использовать в программе их имена. Это делает программу более понятной и облегчает внесение в нее изменений, поскольку изменить значение достаточно только в одном месте программы.
Улучшение читабельности происходит только при осмысленном выборе имен констант. Считается, что в хорошо написанной программе вообще не должно встречаться иных чисел, кроме 0 и 1, все остальные числа должны задаваться именованными константам с именами, отражающими их назначение. Именованные константы должны обязательно инициализироваться при описании. При инициализации можно использовать не только константу, но и выражение, главное, чтобы оно было вычисляемым на этапе компиляции, например
const string s = "123" + "!!!"; // "123!!!" const int a = 1;
const int b = a + 1; // 2 const double d; // Ошибка
131
§ 3.2. Форматирование. Консольный ввод и вывод
Хотя время консольных приложений уходит, многие программы, не требующие взаимодействия с пользователем, остаются консольными. Кроме того, очень часто требуется значения различных типов данных преобразовать в строку со специфическим форматом, или наоборот, извлечь из форматированной строки некоторые значения (осуществить разбор строки). При этом применяются те же инструменты форматирования и разбора, что и при консольном вводе-выводе. Также на таких программах удобно делать первые шаги при обучении новому языку.
Для работы с консолью в .NET используется класс Console. Удобство использование этого класса кроется в двух аспектах: все его методы являются статическими, так что не нужно создавать его экземпляр для использования. Во-вторых, он объединяет в себе ввод, вывод и анализ ошибок. По умолчанию ввод/вывод производится на стандартную консоль (если ее нет, например, в оконных приложениях, вывод просто игнорируется), но устройства ввода и вывода можно изменить.
Для работы с консолью обычно используется три метода – ReadLine, Write и WriteLine. Первый используются для ввода, последние два – для вывода. Но сначала нужно разобраться, как форматировать строки и как осуществлять разбор форматированных строк.
3.2.1. Методы Format и ToString
Для форматирования данных мы можем использовать несколько мето-
дов:
1)Перегруженный метод Object.ToString();
2)Собственный метод ToString(string format), определенный для каждого типа данных по значению (кроме типов bool, char, string);
3)Метод ToString (IFormatProvider provider), наследуемый типами дан-
ных по значению от интерфейса IConvertible;
4)Метод ToString (string format, IFormatProvider provider), наследуе-
мый типами данных по значению от интерфейса IFormattable (кроме типов bool, char, string);
5)Ряд статических методов String.Format с различным набором аргу-
ментов;
132
6) Статический метод Enum.Format (Type enumType, object value, string format) для элементов перечисляемого типа.
Первые четыре метода позволяют форматировать любые типы данных, кроме структур (т.к. они не имеют своего класса, а наследуются напрямую от System.ValueType, и их поля необходимо форматировать по отдельности), а также типов bool, char и string (к ним применимы только первый и третий вариант метода). Хотя для структур пользователя можно перегрузить метод Object.ToString (о том, как это сделать см. § 4.7) и написать другие варианты метода ToString с желаемым набором параметров. В частности, для структуры DateTime применимы все описанные варианты данного метода. Пятый метод позволяет форматировать любые типы данных, кроме структур (также за исключением DateTime). Шестой метод применим только для перечислений.
Пример: Samples\3.2\3_2_1_format.
3.2.1.1. Интерфейс IFormatProvider
Интерфейс IFormatProvider предоставляет механизм извлечения объекта для управления форматированием. Те методы форматирования, которые имеют аргумент типа IFormatProvider, используют его для указания дополнительных опций преобразования значения в строку (такие, как региональные стандарты и т.п.). В структуре классов .NET описаны три класса, реализующие данный интерфейс:
1)System.Globalization.NumberFormatInfo – определяет способ форма-
тирования и отображения числовых значений, зависящий от языка и региональных параметров;
2)System.Globalization.DateTimeFormatInfo – определяет способ фор-
матирования и отображения значений DateTime, зависящих от языка и региональных параметров;
3)System.Globalization.CultureInfo – служит для предоставления сведе-
ний об определенном языке и региональных параметрах. Наиболее универсальный класс, т.к. содержит поле DateTimeFormat, являющееся экземпляром класса DateTimeFormatInfo, и поле NumberFormat, являющееся экземпляром класса NumberFormatInfo.
Основные члены этих классов приведены в приложении Г.
Например, в русском языке целая часть числа отделяется от дробной
133

запятой, а в английском – точкой. Какой символ будет использован по умолчанию? Это определяет свойство CultureInfo.InstalledUICulture – региональные настройки операционной системы:
double value = 12345.6789;
Console.WriteLine(value);
Console.WriteLine(value.ToString(CultureInfo.InstalledUICulture));
Действительно, в обоих случаях на экран выводится строка «12345,6789», как это принято в русском языке (если, конечно, на Вашем компьютере установлена русская версия ОС Windows).
Хотя операционная система позволяет менять формат отображения чисел, времени и дат. Для этого на панели управления необходимо выбрать элемент «Язык и региональные стандарты» (рис. 3.6).
Рис. 3.6 – Региональные параметры ОС Windows XP
В появившемся диалоге для каждого поддерживаемого языка можно посмотреть образцы формата чисел, валюты, времени, а также полной и ко-
134

роткой даты. Для изменения этих форматов нажмите кнопку «Настройка», появится диалог настройки региональных параметров (рис. 3.7). Эти настройки влияют на все приложения, установленные в системе, поэтому не следует их менять без особой необходимости. Если стоит задача изменить, например, запятую на точку при отображении чисел, можно использовать класс NumberFormatInfo.
Рис. 3.7 – Настройка региональных параметров ОС Windows XP
Пример:
NumberFormatInfo nfi = new NumberFormatInfo();
nfi.NumberDecimalSeparator = "."; Console.WriteLine(value.ToString(nfi)); nfi.NumberDecimalSeparator = ","; Console.WriteLine(value.ToString(nfi));
В первом случае, независимо от настроек ОС, для разделения целой и дробной части числа будет использована точка (12345.6789), а во втором – запятая (12345,6789).
135

Мы уже говорили о том, что класс CultureInfo содержит, в том числе,
экземпляры классов NumberFormatInfo и DateTimeFormatInfo. Поэтому изме-
нить десятичный разделитель при выводе можно и с его помощью:
CultureInfo ci = (CultureInfo)CultureInfo.InstalledUICulture.Clone();
ci.NumberFormat.NumberDecimalSeparator = "."; Console.WriteLine(value.ToString(ci));
Число будет выведено с точкой.
А можно создать свой экземпляр CultureInfo, указав требуемый региональный стандарт. Существует несколько способов сделать это:
1)Клонировать имеющийся вариант региональных стандартов (как это было сделано выше);
2)Создать новый вариант, используя метод CreateSpecificCulture:
Console.WriteLine(value.ToString(CultureInfo.CreateSpecificCulture("en-
GB")));
Console.WriteLine(value.ToString(CultureInfo.CreateSpecificCulture("ru-
RU")));
В первом случае число выведется в формате, специфическом для английского языка (English – Great Britain) (с точкой), во втором – для русского языка (с запятой).
3) Создать новый вариант, используя конструктор класса CultureInfo:
Console.WriteLine(value.ToString(new CultureInfo("en-GB")));
Console.WriteLine(value.ToString(new CultureInfo("ru-RU")));
Результат будет аналогичен предыдущему. Если ожидается многократное использование региональных параметров, то ссылку на созданный экземпляр CultureInfo имеет смысл сохранить:
CultureInfo us = CultureInfo.CreateSpecificCulture("en-US"); CultureInfo ru = new CultureInfo("ru-RU");
Console.WriteLine(value.ToString(us));
Console.WriteLine(value.ToString(ru));
В американском диалекте английского языка (English – United States) также в качестве десятичного разделителя используется точка.
3.2.1.2. Метод ToString
Далее мы будем рассматривать только один вариант метода ToString из четырех описанных выше – <тип>.ToString (string format, IFormatProvider provider). Почему? Рассмотрим остальные варианты этого метода:
136

1)Метод Object.ToString() соответствует вызову метода <тип>.ToString c форматом по умолчанию (обычно это формат «G», о чем мы будем говорить далее) и региональными параметрами CultureInfo.InstalledUICulture;
2)Метод <тип>.ToString(string format) соответствует вызову метода <тип>.ToString c указанным форматом format, а также региональными пара-
метрами CultureInfo.InstalledUICulture;
3)Метод <тип>.ToString (IFormatProvider provider) соответствует вызо-
ву метода <тип>.ToString c форматом по умолчанию, а также указанным интерфейсом управления форматированием provider.
Таким образом, метод <тип>.ToString (string format, IFormatProvider provider) наиболее гибкий, а остальные методы ToString – его упрощенные варианты. Исключение составляют классы Boolean, Char и String, для которых этот метод не определен (как и метод <тип>.ToString(string format)).
Объявим в нашем проекте следующие переменные, форматированием которых мы будем заниматься далее:
byte v1 = 178; sbyte v2 = -111; ushort v3 = 50000; short v4 = -15555;
uint v5 = 4000000000; int v6 = -2123123123;
ulong v7 = 123456789123456789; long v8 = -123456789123456789; float v9 = 123456789.123456789f; double v10 = 123456789.123456789;
decimal v11 = 123456789.123456789m; bool v12 = true;
char v13 = 'A'; string v14 = "ABC";
DayOfWeek v15 = DayOfWeek.Friday;
DateTime v16 = new DateTime(2010, 8, 20, 18, 30, 0, 55);
3.2.1.3. Предопределенные форматы
Денежный формат
Обозначается буквой «C» или «c» (currency), за которой может следовать целое число – точность (количество знаков после запятой, по умолчанию – 2). На формат вывода оказывают влияние региональные параметры и следующие поля класса NumberFormatInfo (см. табл. Г.1 в приложении Г):
•int CurrencyNegativePattern – шаблон для отрицательных чисел (0-15);
•int CurrencyPositivePattern – шаблон для положительных чисел (0-3);
137

•string CurrencySymbol – обозначение денежной единицы;
•int[] CurrencyGroupSizes – число цифр в группах;
•string CurrencyGroupSeparator – разделитель групп;
•int CurrencyDecimalDigits – число десятичных разрядов (0-99);
•string CurrencyDecimalSeparator – десятичный разделитель;
•string NegativeSign – обозначение отрицательного знака.
|
В табл. 3.12 |
расшифрованы |
шаблоны |
для CurrencyNegativePattern |
||||||||
и CurrencyPositivePattern, а |
также |
для |
рассмотренных |
далее |
шаблонов |
|||||||
NumberNegativePattern, PercentNegativePattern и PercentPositivePattern. |
||||||||||||
|
|
|
|
Табл. 3.12 – Шаблоны форматирования чисел |
||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
№ шаблона |
|
Для положительной валюты |
Для отрицатель- |
ной валюты |
|
Для отрицатель- |
ных чисел |
|
Для положительных процен- |
тов |
|
Для отрицательных процентов |
|
|
|
|
|
|
|
|
|
|
|
||
0 |
|
$n |
($n) |
|
(n) |
|
n % |
|
|
–n % |
||
|
|
|
|
|
|
|
|
|
|
|
||
1 |
|
n$ |
–$n |
|
–n |
|
n% |
|
|
–n% |
||
|
|
|
|
|
|
|
|
|
|
|
||
2 |
|
$ n |
$–n |
|
– n |
|
%n |
|
|
–%n |
||
|
|
|
|
|
|
|
|
|
|
|
||
3 |
|
n $ |
$n– |
|
n– |
|
% n |
|
|
%–n |
||
|
|
|
|
|
|
|
|
|
|
|
||
4 |
|
|
(n$) |
|
n – |
|
|
|
|
%n– |
||
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
|
|
–n$ |
|
|
|
|
|
|
|
n–% |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
|
|
n–$ |
|
|
|
|
|
|
|
n%– |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
|
n$– |
|
|
|
|
|
|
|
–% n |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
|
|
–n $ |
|
|
|
|
|
|
|
n %– |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
|
|
–$ n |
|
|
|
|
|
|
|
% n– |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
|
|
n $– |
|
|
|
|
|
|
|
% –n |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
|
|
$ n– |
|
|
|
|
|
|
|
n– % |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
|
|
$ –n |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
|
|
n– $ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
|
|
($ n) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
|
|
(n $) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Здесь «n» – число, «$» – обозначение валюты, «–» – обозначение знака «минус». Пример:
Console.WriteLine(v9.ToString("C", CultureInfo.InvariantCulture));
138

Console.WriteLine(us.NumberFormat.CurrencySymbol); Console.WriteLine(v9.ToString("C3", us)); us.NumberFormat.CurrencyGroupSizes = new int[] { 6 }; Console.WriteLine(v9.ToString("C3", us)); Console.WriteLine(ru.NumberFormat.CurrencySymbol); Console.WriteLine(v4.ToString("C", ru)); ru.NumberFormat.CurrencySymbol = " рублей";
Console.WriteLine(v4.ToString("C", ru));
Вывод на консоль:
¤123,456,800.00
$
$123,456,800.000 $123,456800.000
р.
-15 555,00р.
-15 555,00 рублей
Видим, что в инвариантных региональных настройках применяется нейтральный знак валюты, в настройках США – доллар (причем знак валюты ставится перед суммой), в настройках России – рубль (после суммы).
Данный формат применим только для целых типов и типов с плавающей точкой (т.е. не применим к типам Enum и DateTime). При попытке использовать его для форматирования неподходящего типа компилятор генерирует исключительную ситуацию System.FormatException (это относится и ко всем остальным форматам).
Десятичный формат
Обозначается буквой «D» или «d» (decimal), за которой может следовать целое число – количество позиций, отводимых для строкового представления числа. Неиспользованные позиции заполняются нулями. На формат вывода оказывает влияние поле NumberFormatInfo.NegativeSign.
Пример:
Console.WriteLine(v1.ToString("D", us)); Console.WriteLine(v1.ToString("D6", us)); Console.WriteLine(v2.ToString("D", ru)); ru.NumberFormat.NegativeSign = "минус ";
Console.WriteLine(v2.ToString("D6", ru));
Вывод на консоль:
178
000178 -111
минус 000111
Только для целых чисел. Также есть свой формат «D/d» для перечисле-
139

ний и DateTime, об этом – ниже.
Научный (экспоненциальный) формат
Обозначается буквой «E» или «e» (exponential), за которой может следовать целое число – точность (количество знаков после запятой, по умолчанию – 6). Регистр формата влияет на регистр обозначения экспоненты. На формат вывода (а также на другие форматы для чисел с плавающей точкой) оказывают влияние следующие поля класса NumberFormatInfo:
•string PositiveInfinitySymbol – обозначение «+∞»;
•string NegativeInfinitySymbol – обозначение «–∞»;
•string NaNSymbol – обозначение Not a Number (NaN);
•string NumberDecimalSeparator – десятичный разделитель;
•string PositiveSign – обозначение положительной экспоненты;
•string NegativeSign – обозначение отрицательного знака мантиссы и экспоненты.
Пример:
Console.WriteLine(v3.ToString("E", us)); Console.WriteLine(v10.ToString("E", new CultureInfo("fr-FR"))); Console.WriteLine(v10.ToString("E10", ru)); Console.WriteLine(v3.ToString("e4", ru)); Console.WriteLine(nfi.PositiveInfinitySymbol); Console.WriteLine(nfi.NegativeInfinitySymbol); Console.WriteLine(nfi.NaNSymbol);
nfi.NaNSymbol = "Неопределенность!"; float x = 0.0f / 0.0f; Console.WriteLine(x.ToString("E", nfi));
Вывод на консоль:
5.000000E+004
1,234568E+008
1,2345678912E+008
5,0000e+004 Infinity -Infinity NaN
Неопределенность!
Видим, что во Франции, как и в России, в качестве десятичного разделителя используется запятая.
Данный формат применим только для целых типов и типов с плавающей точкой.
140