Здесь значение, содержащееся в переменной типа int(myInt)
, благополучно умещается в диапазон допустимых значений для типа byte
; следовательно, можно было бы ожидать, что сужающая операция не должна привести к ошибке во время выполнения. Однако из-за того, что язык C# создавался с расчетом на безопасность в отношении типов, все-таки будет получена ошибка на этапе компиляции.
Если нужно проинформировать компилятор о том, что вы готовы мириться с возможной потерей данных из-за сужающей операции, тогда потребуется применить языка С#. Взгляните на показанную далее модификацию класса
Program
:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("***** Fun with type conversions *****");
short numb1 = 30000, numb2 = 30000;
// Явно привести int к short (и разрешить потерю данных).
short answer = (short)Add(numb1, numb2);
Console.WriteLine("{0} + {1} = {2}",
numb1, numb2, answer);
NarrowingAttempt;
Console.ReadLine;
}
static int Add(int x, int y)
{
return x + y;
}
static void NarrowingAttempt
{
byte myByte = 0;
int myInt = 200;
// Явно привести int к byte (без потери данных).
myByte = (byte)myInt;
Console.WriteLine("Value of myByte: {0}", myByte);
}
}
Теперь компиляция кода проходит успешно, но результат сложения оказывается совершенно неправильным:
***** Fun with type conversions *****
30000 + 30000 = -5536
Value of myByte: 200
Как вы только что удостоверились, явное приведение заставляет компилятор применить сужающее преобразование, даже когда оно может вызвать потерю данных. В случае метода NarrowingAttempt
это не было проблемой, т.к. значение 200 умещалось в диапазон допустимых значений для типа byte
. Тем не менее, в ситуации со сложением двух значений типа short
внутри Main
конечный результат получился полностью неприемлемым (30000 + 30000 = -5536?).
Для построения приложений, в которых потеря данных не допускается, язык C# предлагает ключевые слова checked
и unchecked
, которые позволяют гарантировать, что потеря данных не останется необнаруженной.
Использование ключевого слова checked
Давайте начнем с выяснения роли ключевого слова checked
. Предположим, что в класс Program
добавлен новый метод, который пытается просуммировать две переменные типа byte
, причем каждой из них было присвоено значение, не превышающее допустимый максимум (255). По идее после сложения значений этих двух переменных (с приведением результата int
к типу byte
) должна быть получена точная сумма.
static void ProcessBytes
{
byte b1 = 100;
byte b2 = 250;
byte sum = (byte)Add(b1, b2);
// В sum должно содержаться значение 350.
// Однако там оказывается значение 94!
Console.WriteLine("sum = {0}", sum);
}
Удивительно, но при просмотре вывода приложения обнаруживается, что в переменной sum содержится значение 94 (а не 350, как ожидалось). Причина проста. Учитывая, что System.Byte
может хранить только значение в диапазоне от 0 до 255 включительно, в sum
будет помещено значение переполнения (350-256 = 94). По умолчанию, если не предпринимаются никакие корректирующие действия, то условия переполнения и потери значимости происходят без выдачи сообщений об ошибках.
Для обработки условий переполнения и потери значимости в приложении доступны два способа. Это можно делать вручную, полагаясь на свои знания и навыки в области программирования. Недостаток такого подхода произрастает из того факта, что мы всего лишь люди, и даже приложив максимум усилий, все равно можем попросту упустить из виду какие-то ошибки.
К счастью, язык C# предоставляет ключевое слово checked
. Когда оператор (или блок операторов) помещен в контекст checked
, компилятор C# выпускает дополнительные инструкции CIL, обеспечивающие проверку условий переполнения, которые могут возникать при сложении, умножении, вычитании или делении двух значений числовых типов.
Бьерн Страуструп , Бьёрн Страуструп , Валерий Федорович Альмухаметов , Ирина Сергеевна Козлова
Программирование, программы, базы данных / Базы данных / Программирование / Учебная и научная литература / Образование и наука / Книги по IT