При попытке выполнить компиляцию этого кода будет выведено следующее сообщение об ошибке:
Cannot implicitly convert type 'short' to 'byte'
(Невозможно неявно преобразовать тип 'short' в 'byte')
К счастью для нас, компилятор С# умеет обнаруживать отсутствие необходимых явных преобразований! Для того чтобы такой код можно было откомпилировать, нужно добавить код, который выполнял бы это преобразование явно. Наиболее простой способ добиться этого в данном контексте — это привести переменную типа short к переменной типа byte. Приведение типа, как правило, означает принудительный перевод данных из одного типа в другой и имеет следующий синтаксис:
{destinationType) sourceVar
Эта запись приведет к преобразованию значения переменной sourceVar к типу
Теперь можно модифицировать наш пример таким образом, чтобы добиться преобразования из типа short в тип byte:
Посмотрим на двоичное представление этих двух чисел, а также максимально допустимого значения для типа byte, равного 255:
Легко заметить, что потерян самый левый бит исходного значения. Один из способов предвидеть потерю битов — проверка исходного значения и его сравнение с известными предельными значениями новой переменной. Но имеется и другой способ, который заставляет систему во время работы уделять особое внимание данному преобразованию. Попытка присвоить переменной слишком большое значение приводит к переполнению,и это как раз та ситуация, которую требуется контролировать.
Контекст проверки на переполнениезадается с помощью двух ключевых слов: checked (проверяемое) и unchecked(непроверяемое), которые используются следующим образом:
checked(выражение)
unchecked(выражение)
Итак, давайте потребуем осуществить проверку на переполнение в предыдущем примере:
Процесс выполнения кода будет прерван появлением сообщения об ошибке. Однако в том случае, если заменить ключевое слово checked на unchecked, получится тот же результат и никакой ошибки не возникнет. Такой вариант идентичен поведению по умолчанию.
Выполнение явных преобразований
с помощью команд преобразования
Прежде мы осуществляли преобразования строковых значений в числа посредством таких команд, как Convert.ToDouble(), которые совершенно очевидно не смогут работать с любой строкой.
Если, например, попытаться преобразовать строку "Number" в число типа double с помощью Convert.ToDouble(), то в процессе выполнения кода будет открыто диалоговое окно с сообщением: "Необрабатываемая исключительная ситуация типа 'System.FormatException' в mscorlib.dll. Дополнительная информация: Входная строка имеет неверный Continue формат."
Для того чтобы подобный тип преобразования мог осуществиться, передаваемая строка должна быть допустимым представлением числа и это число не должно приводить к переполнению. Допустимым называется такое представление числа, которое содержит необязательный знак (плюс или минус), ноль или несколько цифр, необязательную десятичную точку, за которой следует одна или несколько цифр, а также необязательный символ "е" или "Е", за которым следуют необязательный знак и одна или несколько цифр, и ничего более, кроме пробелов (располагающихся перед или после этой последовательности). Используя все эти дополнительные опции, мы можем распознавать строки типа -1.245е-24 как число.
Существует много явных преобразований, которые могут быть определены таким же образом:
Команда
Результат
Convert.ToBoolean(val)
val преобразовано в bool
Convert.ToByte(val)
val преобразовано в byte
Convert.ToChar(val)
val преобразовано в char
Convert.ToDecimal(val)
val преобразовано в decimal
Convert.ToDouble(val)
val преобразовано в double
Convert.ToIntl6(val)
val преобразовано в short
Convert.ToInt32(val)
val преобразовано в int
Convert.ToInt64(val)
val преобразовано в long
Convert.ToSByte(val)
val преобразовано в sbyte
Convert.ToSingle(val)
val преобразовано в float
Convert.ToString(val)
val преобразовано в string
Convert.ToUInt16(val)
val преобразовано в ushort
Convert.ToUInt32(val)
val преобразовано в uint
Convert.ToUInt64(val)
val преобразовано в ulong
Здесь val может быть переменной почти любого типа (а если она не может быть обработана данными командами, то компилятор выдаст соответствующее сообщение). К сожалению, как можно заметить из приведенной таблицы, имена команд преобразования несколько отличаются от названий типов, принятых в С#; например, для того чтобы произвести преобразование в тип int, следует использовать команду Convert.Tolnt32 (). Это объясняется тем, что имена в командах взяты из пространства имен System .NET Framework и не являются родными для С#. Зато благодаря этому их можно использовать из любых других языков, совместимых с .NET. В процессе таких преобразований всегда осуществляется контроль за переполнением, при этом ключевые слова checked и unchecked, а также установки, заданные для всего проекта, влияния не имеют.
Все типы преобразования в примере выполняются как при обычном присваивании (это отражено в приведенных выше коротеньких примерах), так и в выражениях. Рассмотрим оба случая, поскольку выполнение любого не унарного оператора - а не только оператора присваивания — может привести к преобразованию типов.
Например:
shortVal * floatVal
Здесь перемножаются значения типа short и типа float. В подобных ситуациях, когда выполнение преобразования явно не указано, если это возможно, будет выполняться неявное преобразование. В данном примере единственным преобразованием, имеющим смысл, является преобразование short в значение типа float (поскольку преобразование из float в short должно задаваться явно), поэтому именно оно и будет использовано. Однако при желании мы можем переопределить такое поведение:
shortVal * (short)floatVal
Это вовсе не означает, что при выполнении данной операции будет получено значение типа short. Поскольку результатом перемножения двух значений типа short с большой вероятностью может оказаться число, превосходящее 32 767 (максимально допустимое значение, которое может содержаться в типе short), то в действительности эта операция возвращает значение типа int.
Явные преобразования, определяемые с помощью этого синтаксиса изменения типов, имеют такое же старшинство, как и другие унарные операторы (например, оператор + + , используемый в качестве префикса): их старшинство максимально.
Когда встречается выражение, в котором используются смешанные типы, то преобразования осуществляются по мере выполнения отдельных операторов в соответствии с их старшинством. Это означает, что могут происходить некоторые "промежуточные" преобразования. Например:
doubleResult = floatVal + (shortVal * floatVal);
Первый оператор, который должен выполняться в данном случае,— это оператор *, который, как только что было сказано, приведет к преобразованию значения shortval к типу float. Затем будет выполняться оператор 4-, которому не потребуется производить никаких преобразований, поскольку он будет работать с двумя значениями типа float (floatval и значением типа float, получившимся в результате выполнения операции shortval * floatVal). Наконец, значение типа float будет преобразовано в значение типа double при выполнении оператора =.