﻿# Nullable Value Types

Nullable value type is a type that allows you to represent not only all values of its underlying type, but also the null value\. 

## General Info

Why do we need nullable value types? For example, an _int_ type variable can have values ranging from \-2,147,483,648 to 2,147,483,647\. Some cases require specification that a variable value is not defined or missing\. For example, a column value in a database row\. Nullable value types have been created for such cases\. These types are instances of the _System\.Nullable<T\> _structure\. 

You can define an _int_ variable that allows _null_ as follows:

```cpp
Nullable<int> nullableInt;
```

However, a shortened entry is more common:

```cpp
int? nullableInt;
```

For the above C\# declarations, the same IL code will be generated\.

Both values of the underlying type and _null_ are written to a variable via simple assignment:

```cpp
int? nullableIntLhs = 62;
int? nullableIntRhs = null;
```

### Properties

The _HasValue_ property allows you to find out whether a variable of nullable value type contains a value of the underlying type:

```cpp
int? iNullable = 62;
int result;
if (iNullable.HasValue)
  result = iNullable.Value;
else
  result = -1;
// result == 62
```

In addition to calling the _HasValue_ property, you can check for a value by comparing to _null_\. The following checks are equal, the same IL code generates for them:

```cpp
int? nullableInt = null;
bool hasValue1 = nullableInt.HasValue;
bool hasValue2 = nullableInt != null;
```

The _Value_ property returns the value of the underlying type if there is one \(_Nullable<T\>\.HasValue_ \- _true_\)\. Otherwise, we'll get the _InvalidoperationException_:

```cpp
int? iNullable = ....;
int result;
if (iNullable.HasValue)
  result = iNullable.Value; // OK
else
  result = iNullable.Value; // InvalidOperationException
```

### Methods

The _T GetValueOrDefault\(\)_ method is basically similar to _Value_\. The difference is that _T GetValueOrDefault\(\) _ doesn't throw an exception, but returns a _default _value of _T_ type if there is no value of the underlying type:

```cpp
int? iNullable = ....;
int result;
if (!iNullable.HasValue)
  result = iNullable.GetValueOrDefault(); // result == 0
```

The _T GetValueOrDefault \(T defaultValue\)_ method is similar to the _Value property\._ The only difference is that it does not generate an exception\. It returns the value of the _DefaultValue_ argument if there is no value of the underlying type:

```cpp
int? iNullable = ....;
int result;
if (!iNullable.HasValue)
  result = iNullable.GetValueOrDefault(62); // result == 62
```

### Conversion Operators

For _Nullable<T\>_, there are defined operators: implicit conversion from _T_ to _Nullable<T\>_ and explicit conversion from _Nullable<T\>_ to _T_\.

You can assign _T_ values to _Nullable<T\>_ variables directly:

```cpp
Nullable<int> nullableInt;
nullableInt = 62;
```

To write a value from _Nullable<T\>_ to a _T_ variable, you will need to perform explicit casting\. If the underlying value is missing \(_Nullable<T\>\.HasValue_ \- _false_\) in _Nullable<T\>_, we'll get _InvalidOperationException_ when performing explicit casting\. 

Example:

```cpp
Nullable<int> nullableIntLhs = 62;
int resultLhs = (int)nullableIntLhs; // OK, 62
Nullable<int> nullableIntRhs = null;
int resultRhs = (int)nullableIntRhs; // InvalidOperationException
```

## Specifications of Using Nullable Value Types 

### Nullable<T\> can't have the null value

This may be confusing given what you've read above\. Besides, the following code is successfully compiling:

```cpp
Nullable<int> nullableInt = null;
```

However, you should remember that _Nullable<int\>_ is a value type\. Hence _null_ here is just syntax sugar\. In this case, the _nullableInt_ variable will be initialized with the _default\(Nullable<int\>\)_ value\.

All the variables below will have the same value:

```cpp
Nullable<int> nInt1 = null;
Nullable<int> nInt2 = new Nullable<int>();
Nullable<int> nInt3 = default(Nullable<int>);

int? nInt4 = null;
int? nInt5 = new int?();
int? nInt6 = default(int?);
```

It becomes more obvious if you look at IL code, where the same value is explicitly used to initialize all variables:

```cpp
IL_0001:  ldloca.s   nInt1
IL_0003:  initobj    valuetype [mscorlib]System.Nullable`1<int32>
IL_0009:  ldloca.s   nInt2
IL_000b:  initobj    valuetype [mscorlib]System.Nullable`1<int32>
IL_0011:  ldloca.s   nInt3
IL_0013:  initobj    valuetype [mscorlib]System.Nullable`1<int32>
IL_0019:  ldloca.s   nInt4
IL_001b:  initobj    valuetype [mscorlib]System.Nullable`1<int32>
IL_0021:  ldloca.s   nInt5
IL_0023:  initobj    valuetype [mscorlib]System.Nullable`1<int32>
IL_0029:  ldloca.s   nInt6
IL_002b:  initobj    valuetype [mscorlib]System.Nullable`1<int32>
```

### Boxing and Unboxing

Boxing of _Nullable<T\>_ values has a number of specifications:

* suppose _Nullable<T\>\.HasValue — true_\. Then not the _Nullable<T\>_ instance itself is boxed, but the value of the underlying type \- _Nullable<T\>\.Value_;
* if _Nullable<T\>\.HasValue_ is _false_, the result of the boxing will be _null_;
* if _null_ is unboxed, the result is _default\(Nullable<T\>\)_\.

### Operators Defined for T

If unary and binary operators \(for example, '\+', '\-'\) are supported by _T_, then the following rule applies to _Nullable<T\>_:

* if the value of at least one operand is _null_, the result is _null_;
* if both operands are not _null_, the resulting value is the result of the operator for the underlying values of operands \(_Nullable<T\>\.Value_\)\.

Results table:

![NullableValueTypes/image1.png](https://import.viva64.com/docx/blog/NullableValueTypes/image1.png)

For greater/less comparison operators \('<', '<\=', '\>', '\>\='\):

* if at least one of the operands is _null_, the result is _false_;
* if both operands are not _null_, the resulting value is the result of the operator for the underlying values of operands \(_Nullable<T\>\.Value_\)\.

Results table:

![NullableValueTypes/image2.png](https://import.viva64.com/docx/blog/NullableValueTypes/image2.png)

Equality operator \('\=\='\):

* if both operands are _null_, the result is _true_;
* if one operand is _null_, the other is not _null_, the result is _false_;
* if both operands are not _null_, the resulting value is the result of the '\=\=' operator for the values of the underlying type\.

Results table:

![NullableValueTypes/image3.png](https://import.viva64.com/docx/blog/NullableValueTypes/image3.png)

Inequality operator \('\!\='\):

* if both operands are _null_, the result is _false_;
* if one operand is _null_, the other is not _null_, the result is _true_;
* if both operands are not _null_, the resulting value is the result of the '\!\=' operator for the values of the underlying type\.

Results table:

![NullableValueTypes/image4.png](https://import.viva64.com/docx/blog/NullableValueTypes/image4.png)

Operator '&':

* if both operands are _null_, the result is _null_;
* if one operand is _null_, the other is _true_, the result is _null_;
* if one operand is _null_, the other is _false_, the result is _false_;
* if both operands are not _null_, the resulting value is _lhs & rhs_\.

Results table:

![NullableValueTypes/image5.png](https://import.viva64.com/docx/blog/NullableValueTypes/image5.png)

Operator '\|':

* if both operands are _null_, the result is _null_;
* if one operand is _null_, the other is _false_, the result is _null_;
* if one operand is _null_, the other is _true_, the result is _true_;
* if both operands are not _null_, the resulting value is _lhs \| rhs_\.

Results table:

![NullableValueTypes/image6.png](https://import.viva64.com/docx/blog/NullableValueTypes/image6.png)

## Additional links

* [Check how you remember nullable value types\. Let's peek under the hood](https://pvs-studio.com/en/blog/posts/csharp/0772/)\.