Nullable<T>: Value vs GetValueOrDefault() in term of performance

 
 
  • Gérald Barré

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:

C#
if (x.HasValue) // or x != null
{
    _ = x.Value;
}

In a CoreFX issue about adding new analyzers in Microsoft.NetCore.Analyzers, Stephen Toub suggests replacing Nullable<T>.Value with Nullable<T>.GetValueOrDefault() for performance reasons:

After checking a Nullable<T>.HasValue, it's common to see calls to Nullable<T>.Value; instead of calling Value, it's less work to call GetValueOrDefault(), as Value repeats the HasValue check. It's possible a future JIT could optimize away the duplicate check, but if nothing else using GetValueOrDefault() 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 GetValueOrDefault() so you can see why GetValueOrDefault() should be faster:

C#
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 checked that the nullable has a value, or if you are fine with the default value, you should use GetValueOrDefault instead of Value for performance reasons.

C#
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 three implementations is very small. However, in hot paths such as CoreFX or ASP.NET Core, every nanosecond matters. Outside of hot paths, use whichever you find most readable. Personally, I find Value more readable when it appears immediately after a HasValue check.

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?