To get a trial key
fill out the form below
Team License (standard version)
Enterprise License (extended version)
* By clicking this button you agree to our Privacy Policy statement

** This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
Request our prices
New License
License Renewal
--Select currency--
USD
EUR
GBP
RUB
* By clicking this button you agree to our Privacy Policy statement

** This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
To get the licence for your open-source project, please fill out this form
* By clicking this button you agree to our Privacy Policy statement

** This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
To get the licence for your open-source project, please fill out this form
* By clicking this button you agree to our Privacy Policy statement

** This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
I am interested to try it on the platforms:
* By clicking this button you agree to our Privacy Policy statement

** This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
Message submitted.

Your message has been sent. We will email you at


If you haven't received our response, please do the following:
check your Spam/Junk folder and click the "Not Spam" button for our message.
This way, you won't miss messages from our team in the future.

>
>
>
Nullable Value Types

Nullable Value Types

Jul 14 2021

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:

Nullable<int> nullableInt;

However, a shortened entry is more common:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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

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

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

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

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

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

Additional links

Popular related articles
Characteristics of PVS-Studio Analyzer by the Example of EFL Core Libraries, 10-15% of False Positives

Date: Jul 31 2017

Author: Andrey Karpov

After I wrote quite a big article about the analysis of the Tizen OS code, I received a large number of questions concerning the percentage of false positives and the density of errors (how many erro…
The Evil within the Comparison Functions

Date: May 19 2017

Author: Andrey Karpov

Perhaps, readers remember my article titled "Last line effect". It describes a pattern I've once noticed: in most cases programmers make an error in the last line of similar text blocks. Now I want t…
PVS-Studio ROI

Date: Jan 30 2019

Author: Andrey Karpov

Occasionally, we're asked a question, what monetary value the company will receive from using PVS-Studio. We decided to draw up a response in the form of an article and provide tables, which will sho…
Static analysis as part of the development process in Unreal Engine

Date: Jun 27 2017

Author: Andrey Karpov

Unreal Engine continues to develop as new code is added and previously written code is changed. What is the inevitable consequence of ongoing development in a project? The emergence of new bugs in th…
Appreciate Static Code Analysis!

Date: Oct 16 2017

Author: Andrey Karpov

I am really astonished by the capabilities of static code analysis even though I am one of the developers of PVS-Studio analyzer myself. The tool surprised me the other day as it turned out to be sma…
Free PVS-Studio for those who develops open source projects

Date: Dec 22 2018

Author: Andrey Karpov

On the New 2019 year's eve, a PVS-Studio team decided to make a nice gift for all contributors of open-source projects hosted on GitHub, GitLab or Bitbucket. They are given free usage of PVS-Studio s…
The way static analyzers fight against false positives, and why they do it

Date: Mar 20 2017

Author: Andrey Karpov

In my previous article I wrote that I don't like the approach of evaluating the efficiency of static analyzers with the help of synthetic tests. In that article, I give the example of a code fragment…
The Ultimate Question of Programming, Refactoring, and Everything

Date: Apr 14 2016

Author: Andrey Karpov

Yes, you've guessed correctly - the answer is "42". In this article you will find 42 recommendations about coding in C++ that can help a programmer avoid a lot of errors, save time and effort. The au…
PVS-Studio for Java

Date: Jan 17 2019

Author: Andrey Karpov

In the seventh version of the PVS-Studio static analyzer, we added support of the Java language. It's time for a brief story of how we've started making support of the Java language, how far we've co…
The Last Line Effect

Date: May 31 2014

Author: Andrey Karpov

I have studied many errors caused by the use of the Copy-Paste method, and can assure you that programmers most often tend to make mistakes in the last fragment of a homogeneous code block. I have ne…

Comments (0)

Next comments

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
This website uses cookies and other technology to provide you a more personalized experience. By continuing the view of our web-pages you accept the terms of using these files. If you don't want your personal data to be processed, please, leave this site.
Learn More →
Accept