Nullable<T>: Value vs GetValueOrDefault() in term of performance
Nullable types represent value-type variables that can be assigned the null value. You can use the Nullable<T>.HasValue
and Nullable<T>.Value
properties to test for null
and retrieve the value, as shown in the following example:
if (x.HasValue) // or x != null
{
_ = x.Value;
}
In CoreFX issue about adding new analyzers in Microsoft.NetCore.Analyzers, Stephen Toub suggests to replace Nullable<T>.Value
by Nullable<T>.GetValueOfDefault()
for performance reasons:
After checking a
Nullable<T>.HasValue
, it's common to see calls toNullable<T>.Value
; instead of callingValue
, it's less work to callGetValueOrDefault()
, asValue
repeats theHasValue
check. It's possible a future JIT could optimize away the duplicate check, but if nothing else usingGetValueOrDefault()
makes the job of the JIT easier.
Note that Stephen Toub made this change in CoreCLR and CoreFx a few months ago: https://github.com/dotnet/coreclr/pull/22297. The comments are very interesting. Stephen Toub shows the differences in terms of generated assembly code and Andy Ayers explains a few things about the JIT.
Here's the code of Nullable<T>.Value
and GetValueOfDefault()
so you can see why GetValueOfDefault()
should be faster:
private readonly bool hasValue;
internal T value;
public T Value
{
get
{
if (!hasValue)
{
ThrowHelper.ThrowInvalidOperationException_InvalidOperation_NoValue();
}
return value;
}
}
public T GetValueOrDefault() => value;
So, if you have already check that the nullable has a value or if you are ok with the default value, you should use GetValueOrDefault
instead of Value
for performance reason.
private int? _nullableInt = 10;
[Benchmark]
public int HasValue_Value()
{
var nullableInt = _nullableInt;
return nullableInt.HasValue ? nullableInt.Value : 0;
}
[Benchmark]
public int Coallesce()
{
var nullableInt = _nullableInt;
return nullableInt ?? 0;
}
[Benchmark]
public int GetValueOrDefault()
{
var nullableInt = _nullableInt;
return nullableInt.GetValueOrDefault();
}
Note that the performance of Nullable<T>.Value
may vary depending on the branch predictor of the CPU whereas GetValueOrDefault()
is stable.
The difference between the 3 implementations is really small. But if it's in the hot path such as in CoreFX or ASP.NET Core, every millisecond/nanosecond is important. If it's not the hot path, you can use the one you find the most explicit. I personally think that Value
is more readable when it case just after HasValue
.
Do you have a question or a suggestion about this post? Contact me!