- •Тема 6. Основы программирования на языке c#
- •Условная операция
- •Операции checked и unchecked
- •Операция поглощения null
- •Безопасность типов
- •Преобразования типов
- •Неявные преобразования
- •Явные преобразования
- •Проверка равенства объектов
- •Упаковка и распаковка
- •Виртуальный метод Equals()
- •Статический метод Equals()
- •Проверка типов значений на равенство
- •Перегрузка операций
- •Как работают операции
- •Пример перегрузки операции: структура Vector
- •Добавление дополнительных перегрузок
- •Перегрузка операций сравнения
- •Пользовательские приведения
- •Реализация пользовательских приведений
- •Приведение между классами
- •Приведение между базовым и производным классами
- •Упаковывающие и распаковывающие приведения
- •Множественные приведения
Явные преобразования
Многие преобразования типов не могут быть выполнены явно, и компилятор выдаст ошибку при попытке сделать это. Ниже перечислены некоторые из них:
int в short возможна потеря данных;
int в uint возможна потеря данных;
uint в int возможна потеря данных;
float в int теряется все, что находится после десятичной запятой;
любой числовой тип в char возможна потеря данных;
decimal в любой числовой тип потому что тип decimal внутренне устроен иначе, чем целые числа и числа с плавающей точкой;
int? в int тип, допускающий null, может иметь значение null.
Однако такие преобразования можно выполнить принудительно, используя приведения (cast). Когда вы приводите один тип к другому, то сознательно заставляете компилятор выполнить преобразование. Приведение типов выглядит следующим образом:
long val = 30000;
int i = (int)val; // Корректное приведение.
// Максимальное значение int равно 2147483647.
Тип, к которому выполняется приведение, помещается в круглые скобки непосредственно перед преобразуемым значением. Если вы ранее имели дело с языком С, то подобное приведение должно быть знакомо. Если вы работали со специальными ключевыми словами приведения типов C++, такими как static_cast, то имейте в виду, что в C# ничего подобного нет, и надо использовать старый синтаксис приведения С.
Приведение может оказаться опасной операцией. Даже простейшее приведение long к int может вызвать проблемы, когда исходное значение long больше, чем максимально допустимое значение int:
long val = 3000000000;
int i = (int)val; // Неверное приведение.
// Максимальное значение int равно 2147483647.
В этом случае вы не получите ошибки, но также не получите и ожидаемого результата. Если запустить этот код и вывести на экран значение i, то получится вот что:
-1294967296
Всегда имеет смысл предполагать, что явное приведение не даст ожидаемого результата. Как вы уже видели, C# предлагает операцию checked, которую можно использовать для проверки того, вызывает ли действие арифметическое переполнение. С помощью этой операции можно проверить, безопасно ли приведение, и если нет — заставить исполняющую среду сгенерировать исключение:
long val = 3000000000;
int i = checked ((int)val);
Памятуя о том, что явные приведения типов потенциально опасны, вы должны позаботиться о включении в свое приложение кода, обрабатывающего возможные сбои таких приведений. Раздел 6.15 посвящена описанию обработки структурированных исключений с применением операторов try и catch. Используя приведения, можно преобразовывать большинство примитивных типов друг в друга; например, в следующем коде 0.5 добавляется к price, а результат приводится к int:
double price = 25.30;
int approximatePrice = (int)(price + 0.5);
Это даст цену, округленную до ближайшего доллара. Однако при таком преобразовании теряются данные именно те, что следуют после десятичной запятой. Поэтому подобное преобразование никогда не должно применяться, если нужно выполнять дальнейшие вычисления с модифицированным значением цены. Тем не менее, это удобно, когда необходимо вывести примерное значение завершенных либо частично завершенных вычислений, не утомляя пользователя множеством цифр после запятой.
В следующем примере показано, что произойдет при преобразовании беззнакового целого в char:
ushort с = 43;
char symbol = (char) с;
Console.WriteLine(symbol);
На экран будет выведен символ с ASCII-кодом 43 т.е. знак +. Таким образом, вы можете попробовать выполнить любое, какое захотите, преобразование, между числовыми типами (включая char), и оно будет работать (например, decimal в char или наоборот). Преобразование между типами значений не ограничено отдельными переменными, как мы видели до сих пор. Можно также преобразовывать элемент массива типа double в переменную-член структуры типа int:
struct ItemDetails
{
public string Description;
public int ApproxPrice;
}
//...
double [] Prices = { 25.30, 26.20, 27.40, 30.00 };
ItemDetails id;
id.Description = "Что-то";
id.ApproxPrice = (int) (Prices [0] +0.5);
Чтобы преобразовать тип, допускающий null, к типу, null не допускающему, или же в другой тип, допускающий null, когда при этом может происходить потеря данных, следует применять явное приведение. Важно отметить, что это истинно даже при преобразовании данных между элементами с одинаковым типом, лежащим в основе, например, из int? в int или из float? в float. Это потому, что допускающий null тип может Содержать значение null, которое невозможно представить типом, такого значения не допускающим. Там, где возможно явное приведение между двумя эквивалентными, не допускающими null типами, возможно также явное приведение между двумя аналогичными типами, допускающими null. Однако если выполняется приведение допускающего null типа в тип, не допускающий null, и при этом первая переменная имеет значение null, то генерируется исключение InvalidOperationException, например:
int? а = null;
int b = (int)а; // Сгенерирует исключение
Используя явное приведение и проявив немного осторожности и внимания, можно преобразовать любой экземпляр простого типа значений в почти любой другой тип. Однако существуют и ограничения поскольку предполагается применение типов значений, между собой можно преобразовывать числовые типы, тип char и типы перечислений. Приводить булевский тип в любой другой или наоборот нельзя.
Для выполнения преобразований между числами и строками в библиотеке классов .NET предусмотрены специальные методы. Класс Object реализует возвращающий строку метод ToString(), который переопределен во всех базовых типах .NET:
int i = 10;
string s = i.ToString();
Аналогично, если необходимо разобрать строку и извлечь из нее числовое или булевское значение, можно воспользоваться методом Parse(), который поддерживают все предопределенные типы значений:
string s = "100";
int i = int.Parse (s) ;
Console.WriteLine (i + 50) ; // Добавляем 50, чтобы доказать,
// что это действительно int.
Следует отметить, что Parse() регистрирует ошибку, генерируя исключение, если оказывается не в состоянии преобразовать строку (например, если вы попытаетесь преобразовать строку Hello в целое число). Исключения описаны в разделе 6.15.
