Справочник по C#. Типы значений, допускающие значение NULLNullable value types (C# reference)

Тип значений, допускающий значение NULL, или T?, представляет все значения своего базового типа значения T, а также дополнительное значение NULL.A nullable value type T? represents all values of its underlying value type T and an additional null value. Например, можно присвоить переменной bool? любое из следующих трех значений: true, false или null.For example, you can assign any of the following three values to a bool? variable: true, false, or null. Базовый тип значения T не может соответствовать типу значения, допускающему значение NULL.An underlying value type T cannot be a nullable value type itself.

Примечание

В C# 8.0 появилась возможность использования ссылочных типов, допускающих значение NULL.C# 8.0 introduces the nullable reference types feature. Дополнительные сведения см. в статье Ссылочные типы, допускающие значение NULL.For more information, see Nullable reference types. Типы значений, допускающие значение NULL, доступны начиная с C# 2.The nullable value types are available beginning with C# 2.

Типы, допускающие значение NULL, представляют собой экземпляры универсальной структуры System.Nullable<T>.Any nullable value type is an instance of the generic System.Nullable<T> structure. Вы можете ссылаться на тип значения, допускающий значение NULL, с базовым типом T в любой из следующих взаимозаменяемых форм: Nullable<T> или T?.You can refer to a nullable value type with an underlying type T in any of the following interchangeable forms: Nullable<T> or T?.

Тип значения, допускающий значение NULL, следует использовать, когда нужно представить неопределенное значение его базового типа.You typically use a nullable value type when you need to represent the undefined value of an underlying value type. Например, логическая переменная (или bool) может иметь только значения true или false.For example, a Boolean, or bool, variable can only be either true or false. Однако в некоторых приложениях значение переменной может быть неопределенным или отсутствовать.However, in some applications a variable value can be undefined or missing. Например, поле базы данных может содержать значение true или false либо вообще никакого значения, то есть NULL.For example, a database field may contain true or false, or it may contain no value at all, that is, NULL. В этом сценарии можно использовать тип bool?.You can use the bool? type in that scenario.

Назначение и объявлениеDeclaration and assignment

Так как тип значения можно неявно преобразовать в соответствующий тип значения, допускающий значение NULL, вы назначаете значение переменной такого типа значения так же, как для базового типа значения.As a value type is implicitly convertible to the corresponding nullable value type, you can assign a value to a variable of a nullable value type as you would do that for its underlying value type. Вы можете также присвоить значение null.You can also assign the null value. Пример:For example:

double? pi = 3.14;
char? letter = 'a';

int m2 = 10;
int? m = m2;

bool? flag = null;

// An array of a nullable value type:
int?[] arr = new int?[10];

Значение по умолчанию для типа значения, допускающего значение NULL, представляет null, то есть это экземпляр, свойство Nullable<T>.HasValue которого возвращает false.The default value of a nullable value type represents null, that is, it's an instance whose Nullable<T>.HasValue property returns false.

Проверка экземпляра типа значения, допускающего значение NULLExamination of an instance of a nullable value type

Начиная с версии C# 7.0 можно использовать оператор is с шаблоном типа как для проверки экземпляра типа, допускающего значение NULL, для null, так и для извлечения значения базового типа:Beginning with C# 7.0, you can use the is operator with a type pattern to both examine an instance of a nullable value type for null and retrieve a value of an underlying type:

int? a = 42;
if (a is int valueOfA)
{
    Console.WriteLine($"a is {valueOfA}");
}
else
{
    Console.WriteLine("a does not have a value");
}
// Output:
// a is 42

Вы всегда можете использовать следующие свойства только для чтения, чтобы проверить и получить значение переменной типа, допускающего значение NULL:You always can use the following read-only properties to examine and get a value of a nullable value type variable:

В следующем примере используется свойство HasValue, чтобы проверить, содержит ли переменная значение, перед его отображением:The following example uses the HasValue property to test whether the variable contains a value before displaying it:

int? b = 10;
if (b.HasValue)
{
    Console.WriteLine($"b is {b.Value}");
}
else
{
    Console.WriteLine("b does not have a value");
}
// Output:
// b is 10

Можно также сравнить переменную типа значения, допускающего значение NULL, с null вместо использования свойства HasValue, как показано в следующем примере:You can also compare a variable of a nullable value type with null instead of using the HasValue property, as the following example shows:

int? c = 7;
if (c != null)
{
    Console.WriteLine($"c is {c.Value}");
}
else
{
    Console.WriteLine("c does not have a value");
}
// Output:
// c is 7

Преобразование из типа значения, допускающего значение NULL, в базовый типConversion from a nullable value type to an underlying type

Если необходимо присвоить значение типа, допускающего значение NULL, переменной типа значения, не допускающего значения NULL, может потребоваться указать значение, назначаемое вместо null.If you want to assign a value of a nullable value type to a non-nullable value type variable, you might need to specify the value to be assigned in place of null. Для этого используйте оператор объединения со значением NULL ?? (можно также применить метод Nullable<T>.GetValueOrDefault(T) для той же цели):Use the null-coalescing operator ?? to do that (you can also use the Nullable<T>.GetValueOrDefault(T) method for the same purpose):

int? a = 28;
int b = a ?? -1;
Console.WriteLine($"b is {b}");  // output: b is 28

int? c = null;
int d = c ?? -1;
Console.WriteLine($"d is {d}");  // output: d is -1

Если вы хотите использовать значение по умолчанию базового типа значения вместо null, воспользуйтесь методом Nullable<T>.GetValueOrDefault().If you want to use the default value of the underlying value type in place of null, use the Nullable<T>.GetValueOrDefault() method.

Вы можете также явно привести тип значения, допускающий значение NULL, к типу, не допускающему значение NULL, как показано в примере ниже.You can also explicitly cast a nullable value type to a non-nullable type, as the following example shows:

int? n = null;

//int m1 = n;    // Doesn't compile
int n2 = (int)n; // Compiles, but throws an exception if n is null

Во время выполнения, если значение типа значения, допускающего значение NULL, равно null, явное приведение вызывает исключение InvalidOperationException.At run time, if the value of a nullable value type is null, the explicit cast throws an InvalidOperationException.

Тип T, не допускающий значение NULL, неявно преобразуется в соответствующий тип, допускающий значение NULL, T?.A non-nullable value type T is implicitly convertible to the corresponding nullable value type T?.

Операторы с нулификациейLifted operators

Предопределенные унарные и бинарные операторы или любые перегруженные операторы, поддерживаемые типом значения T, также поддерживаются соответствующим типом значения, допускающим значение NULL, т. е. T?.The predefined unary and binary operators or any overloaded operators that are supported by a value type T are also supported by the corresponding nullable value type T?. Эти операторы, также называемые операторами с нулификацией, возвращают значение null, если один или оба операнда имеют значение null. В противном случае оператор использует содержащиеся значения операндов для вычисления результата.These operators, also known as lifted operators, produce null if one or both operands are null; otherwise, the operator uses the contained values of its operands to calculate the result. Пример:For example:

int? a = 10;
int? b = null;
int? c = 10;

a++;        // a is 11
a = a * c;  // a is 110
a = a + b;  // a is null

Примечание

Для типа bool? предопределенные операторы & и | не следуют правилам, описанным в этом разделе: результат вычисления оператора может быть отличным от NULL, даже если один из операндов имеет значение null.For the bool? type, the predefined & and | operators don't follow the rules described in this section: the result of an operator evaluation can be non-null even if one of the operands is null. См. подробнее о логических операторах, поддерживающих значение NULL в описании логических операторов.For more information, see the Nullable Boolean logical operators section of the Boolean logical operators article.

Для операторов сравнения <, >, <= и >=, если один или оба операнда равны null, результат будет равен false. В противном случае сравниваются содержащиеся значения операндов.For the comparison operators <, >, <=, and >=, if one or both operands are null, the result is false; otherwise, the contained values of operands are compared. Тут важно не полагать, что если какая-то операция сравнения (например, <=) возвращает false, то противоположное сравнение (>) обязательно вернет true.Do not assume that because a particular comparison (for example, <=) returns false, the opposite comparison (>) returns true. В следующем примере показано, что 10The following example shows that 10 is

  • не больше и не равно значению null,neither greater than or equal to null
  • не меньше чем null.nor less than null
int? a = 10;
Console.WriteLine($"{a} >= null is {a >= null}");
Console.WriteLine($"{a} < null is {a < null}");
Console.WriteLine($"{a} == null is {a == null}");
// Output:
// 10 >= null is False
// 10 < null is False
// 10 == null is False

int? b = null;
int? c = null;
Console.WriteLine($"null >= null is {b >= c}");
Console.WriteLine($"null == null is {b == c}");
// Output:
// null >= null is False
// null == null is True

Для оператора равенства ==, если оба операнда равны null, результат будет равен true. Если один из операндов равен null, результат будет равен false. В противном случае сравниваются содержащиеся значения операндов.For the equality operator ==, if both operands are null, the result is true, if only one of the operands is null, the result is false; otherwise, the contained values of operands are compared.

Для оператора неравенства !=, если оба операнда равны null, результат будет равен false. Если один из операндов равен null, результат будет равен true. В противном случае сравниваются содержащиеся значения операндов.For the inequality operator !=, if both operands are null, the result is false, if only one of the operands is null, the result is true; otherwise, the contained values of operands are compared.

Если между двумя типами данных определено пользовательское преобразование типов, то это же преобразование можно также использовать между соответствующими типами, допускающими значение NULL.If there exists a user-defined conversion between two value types, the same conversion can also be used between the corresponding nullable value types.

Упаковка-преобразование и распаковка-преобразованиеBoxing and unboxing

Экземпляр типа значения, допускающего значение NULL, T?упакован следующим образом:An instance of a nullable value type T? is boxed as follows:

  • Если HasValue возвращает false, создается пустая ссылка.If HasValue returns false, the null reference is produced.
  • Если HasValue возвращает true, упаковывается соответствующее значение базового типа T, а не экземпляр Nullable<T>.If HasValue returns true, the corresponding value of the underlying value type T is boxed, not the instance of Nullable<T>.

Можно распаковать упакованный тип значения T в соответствующий тип, допускающий значение NULL, T?, как показано в следующем примере:You can unbox a boxed value of a value type T to the corresponding nullable value type T?, as the following example shows:

int a = 41;
object aBoxed = a;
int? aNullable = (int?)aBoxed;
Console.WriteLine($"Value of aNullable: {aNullable}");

object aNullableBoxed = aNullable;
if (aNullableBoxed is int valueOfA)
{
    Console.WriteLine($"aNullableBoxed is boxed int: {valueOfA}");
}
// Output:
// Value of aNullable: 41
// aNullableBoxed is boxed int: 41

Идентификация типа значений, допускающего значение NULLHow to identify a nullable value type

В следующем примере показано, как определить, представляет ли экземпляр System.Type сконструированный тип значений, допускающий значение NULL, т. е. тип System.Nullable<T> с указанным параметром типа T:The following example shows how to determine whether a System.Type instance represents a constructed nullable value type, that is, the System.Nullable<T> type with a specified type parameter T:

Console.WriteLine($"int? is {(IsNullable(typeof(int?)) ? "nullable" : "non nullable")} value type");
Console.WriteLine($"int is {(IsNullable(typeof(int)) ? "nullable" : "non-nullable")} value type");

bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;

// Output:
// int? is nullable value type
// int is non-nullable value type

Как показано в примере, при помощи оператора typeof можно создать экземпляр System.Type.As the example shows, you use the typeof operator to create a System.Type instance.

Если вы хотите определить, принадлежит ли экземпляр к типу значений, допускающему значение NULL, не используйте метод Object.GetType для получения экземпляра Type для тестирования с помощью приведенного выше кода.If you want to determine whether an instance is of a nullable value type, don't use the Object.GetType method to get a Type instance to be tested with the preceding code. При вызове метода Object.GetType в экземпляре типа значений, допускающего значение NULL, экземпляр упаковывается в Object.When you call the Object.GetType method on an instance of a nullable value type, the instance is boxed to Object. Так как упаковка экземпляра типа значений, допускающего значение NULL, значение которого отлично от NULL, эквивалентна упаковке значения базового типа, GetType возвращается экземпляр Type, представляющий базовый тип типа значений, допускающего значение NULL:As boxing of a non-null instance of a nullable value type is equivalent to boxing of a value of the underlying type, GetType returns a Type instance that represents the underlying type of a nullable value type:

int? a = 17;
Type typeOfA = a.GetType();
Console.WriteLine(typeOfA.FullName);
// Output:
// System.Int32

Кроме того, не используйте оператор is, чтобы определить, принадлежит ли экземпляр к типу значений, допускающему значение NULL.Also, don't use the is operator to determine whether an instance is of a nullable value type. Как показано в следующем примере, вы не можете отличить типы экземпляра типа значений, допускающего значение NULL, и его экземпляра базового типа с помощью оператора is:As the following example shows, you cannot distinguish types of a nullable value type instance and its underlying type instance with the is operator:

int? a = 14;
if (a is int)
{
    Console.WriteLine("int? instance is compatible with int");
}

int b = 17;
if (b is int?)
{
    Console.WriteLine("int instance is compatible with int?");
}
// Output:
// int? instance is compatible with int
// int instance is compatible with int?

Чтобы определить, принадлежит ли экземпляр типу значений, допускающему значение NULL, можно использовать код, представленный в следующем примере:You can use the code presented in the following example to determine whether an instance is of a nullable value type:

int? a = 14;
Console.WriteLine(IsOfNullableType(a));  // output: True

int b = 17;
Console.WriteLine(IsOfNullableType(b));  // output: False

bool IsOfNullableType<T>(T o)
{
    var type = typeof(T);
    return Nullable.GetUnderlyingType(type) != null;
}

Примечание

Методы, описанные в этом разделе, неприменимы в случае ссылочных типов, допускающих значения NULL.The methods described in this section are not applicable in the case of nullable reference types.

Спецификация языка C#C# language specification

Дополнительные сведения см. в следующих разделах статьи Спецификация языка C#:For more information, see the following sections of the C# language specification:

См. такжеSee also