Тверской государственный университет
Опубликован: 02.12.2009 | Доступ: свободный | Студентов: 3450 / 677 | Оценка: 4.41 / 4.23 | Длительность: 09:18:00
ISBN: 978-5-9963-0259-8
Лекция 2:

Типы и классы. Переменные и объекты

Типы, допускающие неопределенные значения

Инициализация переменных позволяет в момент объявления каждой переменной дать значение, принадлежащее множеству возможных значений данного типа. Тем самым исключается возможность работы с неопределенными значениями переменных. Однако для ссылочных типов возможным значением объектов является значение null, задающее неопределенную ссылку. Конечно, попытка вызвать метод или свойство объекта со значением null не приведет к успеху и возникнет ошибка периода выполнения. Тем не менее, полезно для ссылочных типов иметь null в качестве возможного значения.

Для значимых типов значение null не входит в множество возможных значений. Но в ряде ситуаций полезно, чтобы переменная значимого типа имела неопределенное значение. Язык C# позволяет из любого значимого типа данных построить новый тип, отличающийся лишь тем, что множество возможных значений дополнено специальным значением null. Так построенные типы данных называются типами, допускающими неопределенное значение (Nullable Types). Если построен тип T, то тип, допускающий неопределенные значения, определяется следующим образом:

System.Nullable<T>

Чаще используется эквивалентная, но более простая форма записи -

T?

Переменные таких типов могут получать значение null либо в результате присваивания, либо в процессе вычислений. Понятно, что если при вычислении выражения один из операндов будет иметь значение null, то и результат вычисления выражения будет иметь то же значение null. Над переменными этого типа определена специальная операция склеивания:

A ?? B

Результатом вычисления этого выражения будет операнд А, если значение А не равно null, и В, если первый операнд равен null.

Рассмотрим выполнение преобразований между типами Т? и Т. Очевидно, что преобразование из типа Т в тип Т? - безопасное преобразование и потому может выполняться неявно. В другую сторону преобразование является опасным и должно выполняться явно, например, путем кастинга - приведения к типу. Рассмотрим некоторые примеры работы с переменными подобных типов.

static void TestNullable()
{
    int x = 3, y = 7;
    int? x1 = null, y1, z1;
    y1 = x + y;
    z1 = x1 ?? y1;
    Console.WriteLine("x1 = {0}, y1 = {1}, z1 = {2}",
        x1, y1, z1);
}

В этом фрагменте вводятся переменные типа int? и int. Демонстрируется безопасное преобразование из типа int в тип int? и выполнение операции ?? - операции склеивания. Рассмотрим следующий фрагмент тестового метода:

//x = (int)x1;
y = (int)y1;
Console.WriteLine("x = {0}, y = {1}",
    x, y);
z1 = x1 * y ?? y1;
y1 = z1 - y1;
Console.WriteLine("x1 = {0}, y1 = {1}, z1 = {2}",
    x1, y1, z1);

Первая строка фрагмента закомментирована, поскольку попытка явного приведения типа переменной со значением null приведет к ошибке периода выполнения. В следующей строчке такое приведение успешно выполняется, поскольку переменная y1 имеет значение, допустимое для типа int. Заметьте, что операция ?? имеет более низкий приоритет, чем операция умножения, поэтому первый операнд этой операции будет иметь значение null и z1 получит значение y1. В следующем фрагменте демонстрируются оба эквивалентных способа задания типа double, допускающего неопределенные значения:

System.Nullable<double> u = x + x1;
double? v = y + y1, w;
w = u ?? v + y1;
Console.WriteLine("u = {0}, v = {1}, w = {2}", 
u, v, w);

В заключение взгляните на результаты работы этого тестового примера.

Типы, допускающие значение null

Рис. 2.1. Типы, допускающие значение null

Заметьте, при выводе на печать значение null не имеет специального обозначения и выводится как пробел.

Null, NaN и Infinity

Значение null не является единственным особым значением, входящим в множество возможных значений значимого типа. У вещественного типа данных ( double и float ) есть и другие особые значения, не являющиеся обыкновенными числами. Таких значений три - Infinity, NegativeInfinity и NaN. Первые два хорошо известны из математики - это бесконечность и отрицательная бесконечность. Третье значение NaN (Not a Number) появляется тогда, когда результат не является вещественным числом или значением null и Infinity. Рассмотрим правила, приводящие к появлению особых значений.

Если при выполнении операций умножения или деления результат по модулю превосходит максимально допустимое число, то значением является бесконечность или отрицательная бесконечность в зависимости от знака результата. У типов double и float есть константы, задающие эти значения. При выполнении операций сложения, вычитания и умножения бесконечности с обычными вещественными числами результат имеет значение бесконечность, возможно, с противоположным знаком. При делении вещественного числа на бесконечность результат равен нулю.

Если один из операндов вычисляемого выражения есть null, а остальные - обычные вещественные числа или бесконечность, то результат выражения будет иметь значение null - не определен.

Если бесконечность делится на бесконечность или ноль умножается на бесконечность, то результат будет NaN. Этот же результат будет появляться, когда результат выполнения некоторой операции не будет вещественным числом, например, при извлечении квадратного корня из отрицательного числа. Если NaN участвует в операциях, то результатом будет NaN. Это верно и тогда, когда другие операнды имеют значение null или бесконечность.

Рассмотрим примеры:

static void NullAndNan()
{
    double? u = null, v = 0, w = 1.5;                
    Console.WriteLine("u = {0}, v = {1}, w = {2}",
        u, v, w);

Пока что введены три переменные типа double?, одна из которых получила значение null. Введем еще несколько переменных этого же типа, которые получат в результате вычислений особые значения:

double? x, y, z;
x = u + v; y = w / v; z = x + y;
Console.WriteLine("x = u + v = {0}, y = w / v = {1}, " +
" z = x + y = {2}", x, y, z);

При вычислении значения переменной x в выражении участвует null, поэтому и x получит значение null. При вычислении значения переменной y выполняется деление на ноль, поэтому y получит значение бесконечность. При вычислении значения переменной z в выражении участвует null и бесконечность, поэтому z получит значение null. Рассмотрим еще один фрагмент кода:

x = -y; y = v * y; z = x + y;
Console.WriteLine("x = -y = {0}, y = v * y = {1}, " +
" z = x + y = {2}", x, y, z);

При вычислении значения переменной x происходит смена знака и x получает значение отрицательной бесконечности. При вычислении значения переменной y бесконечность умножается на ноль, результат не определен и будет иметь значение NaN. При сложении бесконечности со значением NaN результат будет NaN. Ну и еще один заключительный фрагмент:

double p = -(double)w, q = double.NegativeInfinity;
Console.WriteLine("p = {0}, q = {1}, 1 / q = {2}",
    Math.Sqrt(p), q, 1 / q);
p = 1e160; 
     Console.WriteLine("p = {0}, p * p = {1}", p, p * p);
float p1 = 1e20f;
Console.WriteLine("p1 = {0}, p1 * p1 = {1}", p1, p1 * p1);

Здесь вводятся переменные типа double и float. Показано, как при умножении появляются значения бесконечности, а также появляется одна из соответствующих констант, задающих бесконечность. В заключение взгляните на результаты работы этого тестового примера.

Значения null, NaN, Infinity

Рис. 2.2. Значения null, NaN, Infinity
Федор Антонов
Федор Антонов

Здравствуйте!

Записался на ваш курс, но не понимаю как произвести оплату.

Надо ли писать заявление и, если да, то куда отправлять?

как я получу диплом о профессиональной переподготовке?

Илья Ардов
Илья Ардов

Добрый день!

Я записан на программу. Куда высылать договор и диплом?