Which Works Faster- Null Coalescing Operator or GetValueOrDefault or Conditional Operator

Which Works Faster- Null Coalescing Operator or GetValueOrDefault or Conditional Operator

After my article Top 15 Underutilized Features of .NET provoked an interesting discussion. I was curious to learn which method is faster- ?? (null coalescing operator)GetValueOrDefault method or ?: (conditional operator). Recently I read in Stack Overflow that most people believe that the GetValueOrDefault method is the fastest among these three. However, I decided to do my research. I am not trying to micro-optimize. I think it won’t matter in 99% of the cases which one of the three approaches you are going to use. Usually, you should choose the one that it is easier to maintain. I am not going to argue which one is more readable because that is another topic. Rather I am going to present to you my research’s benchmark results.

Null Coalescing Operator ??

The ?? operator returns the left-hand operand if it is not null, or else it returns the right operand. A nullable type can contain a value, or it can be undefined. The ?? operator defines the default value to be returned when a nullable type is assigned to a non-nullable type.

int? x = null;
int y = x ?? –1;
Console.WriteLine("y now equals -1 because x was null => {0}", y);
int i = DefaultValueOperatorTest.GetNullableInt() ?? default(int);
Console.WriteLine("i equals now 0 because GetNullableInt() returned null => {0}", i);
string s = DefaultValueOperatorTest.GetStringValue();
Console.WriteLine("Returns 'Unspecified' because s is null => {0}", s ?? "Unspecified");

GetValueOrDefault Method

Retrieves the value of the current Nullable object, or the object’s default value. It is faster than ?? operator.

float? yourSingle = –1.0f;
Console.WriteLine(yourSingle.GetValueOrDefault());
yourSingle = null;
Console.WriteLine(yourSingle.GetValueOrDefault());
// assign different default value
Console.WriteLine(yourSingle.GetValueOrDefault(–2.4f));
// returns the same result as the above statement
Console.WriteLine(yourSingle ?? –2.4f);

If you don’t specify a default value as a parameter to the method, the default value of the used type is going to be used.

Conditional Operator ?:

int input = Convert.ToInt32(Console.ReadLine());
// ?: conditional operator.
string classify = (input > 0) ? "positive" : "negative";

GetValueOrDefault and Null Coalescing Operator Internals

You can find the source code for the GetValueOrDefault method on the following URL. There are two overloads for the method, one without parameters and one that requires the default value to be returned if the variable is null.

[System.Runtime.Versioning.NonVersionable]
public T GetValueOrDefault()
{
    return value;
}

[System.Runtime.Versioning.NonVersionable]
public T GetValueOrDefault(T defaultValue)
{
    return hasValue ? value : defaultValue;
}
public class GetValueOrDefaultAndNullCoalescingOperatorInternals
{
    public void GetValueOrDefaultInternals()
    {
        int? a = null;
        var x = a.GetValueOrDefault(7);
    }

    public void NullCoalescingOperatorInternals()
    {
        int? a = null;
        var x = a ?? 7;
    }
}

GetValueOrDefault CIL

.method public hidebysig instance void GetValueOrDefaultInternals() cil managed
{
    .locals init (
        [0] valuetypeSystem.Nullable`1 < int32 > a
    )

    IL_0000: ldloca.s a
    IL_0002: initobj valuetypeSystem.Nullable`1 < int32 >
    IL_0008: ldloca.s a
    IL_000a: ldc.i4.7
    IL_000b: call instance int32 valuetypeSystem.Nullable`1 < int32 >::GetValueOrDefault(!0)
    IL_0010: pop
    IL_0011: ret
}

Null Coalescing Operator CIL

.method public hidebysig instance void NullCoalescingOperatorInternals() cil managed
{
    .locals init (
        [0] valuetypeSystem.Nullable`1 < int32 > a,
        [1] valuetypeSystem.Nullable`1 < int32 > CS$0$0000
    )

    IL_0000: ldloca.s a
    IL_0002: initobj valuetypeSystem.Nullable`1 < int32 >
    IL_0008: ldloc.0
    IL_0009: stloc.1
    IL_000a: ldloca.s CS$0$0000
    IL_000c: call instance bool valuetypeSystem.Nullable`1 < int32 >::get_HasValue()
    IL_0011: brtrue.s IL_0014

    IL_0013: ret

    IL_0014: ldloca.s CS$0$0000
    IL_0016: call instance int32 valuetypeSystem.Nullable`1 < int32 >::GetValueOrDefault()
    IL_001b: pop
    IL_001c: ret
}

As far as I can cope the CIL code I think that the x ?? y is transformed into x.HasValue ? x.GetValueOrDefault() : y.  Which automatically should mean that most probably the former is going to be much faster than the later.

Which Works Faster- Null Coalescing Operator or GetValueOrDefault or Conditional Operator

To benchmark the different test cases I created a specialized profiler class.

public static class Profiler
{
    public static TimeSpan Profile(long iterations, Action actionToProfile)
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();

        var watch = new Stopwatch();
        watch.Start();
        for (int i = 0; i < iterations; i++)
        {
            actionToProfile();
        }
        watch.Stop();

        return watch.Elapsed;
    }

    public static string FormatProfileResults(long iterations, TimeSpan profileResults)
    {
        StringBuilder sb = new StringBuilder();
        sb.AppendLine(string.Format("Total: {0:0.00} ms ({1:N0} ticks) (over {2:N0} iterations)",
            profileResults.TotalMilliseconds, profileResults.Ticks, iterations));
        var avgElapsedMillisecondsPerRun = profileResults.TotalMilliseconds / (double)iterations;
        var avgElapsedTicksPerRun = profileResults.Ticks / (double)iterations;
        sb.AppendLine(string.Format("AVG: {0:0.00} ms ({1:N0} ticks) (over {2:N0} iterations)",
            avgElapsedMillisecondsPerRun, avgElapsedTicksPerRun, iterations));

        return sb.ToString();
    }
}
GC.Collect();
GC.WaitForPendingFinalizers();

These are the six test cases that I benchmarked.

public static class GetValueOrDefaultVsNullCoalescingOperatorTest
{
    public static void ExecuteWithGetValueOrDefault()
    {
        int? a = null;
        int? b = 3;
        int? d = null;
        int? f = null;
        int? g = null;
        int? h = null;
        int? j = null;
        int? k = 7;

        var profileResult = Profiler.Profile(100000,
            () =>
            {
                var x = a.GetValueOrDefault(7);
                var y = b.GetValueOrDefault(7);
                var z = d.GetValueOrDefault(6) + f.GetValueOrDefault(3) + g.GetValueOrDefault(1) + h.GetValueOrDefault(1) + j.GetValueOrDefault(5) + k.GetValueOrDefault(8);
            });
        string formattedProfileResult = Profiler.FormatProfileResults(100000, profileResult);
        FileWriter.WriteToDesktop("ExecuteWithGetValueOrDefaultT", formattedProfileResult);
    }

    public static void ExecuteWithNullCoalescingOperator()
    {
        int? a = null;
        int? b = 3;
        int? d = null;
        int? f = null;
        int? g = null;
        int? h = null;
        int? j = null;
        int? k = 7;

        var profileResult = Profiler.Profile(100000,
            () =>
            {
                var x = a ?? 7;
                var y = b ?? 7;
                var z = (d ?? 6) + (f ?? 3) + (g ?? 1) + (h ?? 1) + (j ?? 5) + (k ?? 8);
            });
        string formattedProfileResult = Profiler.FormatProfileResults(100000, profileResult);
        FileWriter.WriteToDesktop("ExecuteWithNullCoalescingOperatorT", formattedProfileResult);
    }

    public static void ExecuteWithConditionalOperator()
    {
        int? a = null;
        int? b = 3;
        int? d = null;
        int? f = null;
        int? g = null;
        int? h = null;
        int? j = null;
        int? k = 7;

        var profileResult = Profiler.Profile(100000,
            () =>
            {
                var x = a.HasValue ? a : 7;
                var y = b.HasValue ? b : 7;
                var z = (d.HasValue ? d : 6) + (f.HasValue ? f : 3) + (g.HasValue ? g : 1) + (h.HasValue ? h : 1) + (j.HasValue ? j : 5) + (k.HasValue ? k : 8);
            });
        string formattedProfileResult = Profiler.FormatProfileResults(100000, profileResult);
        FileWriter.WriteToDesktop("ExecuteWithConditionalOperatorT", formattedProfileResult);
    }

    public static void ExecuteWithGetValueOrDefaultZero()
    {
        int? a = null;

        var profileResult = Profiler.Profile(100000,
            () =>
            {
                var x = a.GetValueOrDefault();
            });
        string formattedProfileResult = Profiler.FormatProfileResults(100000, profileResult);
        FileWriter.WriteToDesktop("ExecuteWithGetValueOrDefaultZeroT", formattedProfileResult);
    }

    public static void ExecuteWithNullCoalescingOperatorZero()
    {
        int? a = null;

        var profileResult = Profiler.Profile(100000,
            () =>
            {
                var x = a ?? 0;
            });
        string formattedProfileResult = Profiler.FormatProfileResults(100000, profileResult);
        FileWriter.WriteToDesktop("ExecuteWithNullCoalescingOperatorZeroT", formattedProfileResult);
    }

    public static void ExecuteWithConditionalOperatorZero()
    {
        int? a = null;

        var profileResult = Profiler.Profile(100000,
            () =>
            {
                var x = a.HasValue ? a : 0;
            });
        string formattedProfileResult = Profiler.FormatProfileResults(100000, profileResult);
        FileWriter.WriteToDesktop("ExecuteWithConditionalOperatorZeroT", formattedProfileResult);
    }
}

Performed Test Cases

Home-made Benchmark Results

After several test runs, you can view the results from my research.

Pro Benchmark through JustTrace

The results from my home-made benchmark were not enough for me so I installed JustTrace (2-in-1 memory and performance profiler for .NET and native apps). The results for the same test cases were slightly different.

Related Articles

Development, Resources

Most Complete MSTest Unit Testing Framework Cheat Sheet

An essential part of every UI test framework is the usage of a unit testing framework. One of the most popular ones in the .NET world is MSTest. However, you ca

Most Complete MSTest Unit Testing Framework Cheat Sheet

Development

Get Property Names Using Lambda Expressions in C#

In this article, I am going to present to you how to get property and method names from lambda expressions. You can pass properties and methods as methods' para

Get Property Names Using Lambda Expressions in C#

Development

Types Of Code Coverage- Examples In C#

Code coverage analysis is used to measure the quality of software testing, usually using dynamic execution flow analysis. There are many different types of code

Types Of Code Coverage- Examples In C#

Development, Resources

Most Complete NUnit Unit Testing Framework Cheat Sheet

An essential part of every UI test framework is the usage of a unit testing framework. One of the most popular ones in the .NET world is NUnit. However, you can

Most Complete NUnit Unit Testing Framework Cheat Sheet

Development

Assert DateTime the Right Way MSTest NUnit C# Code

NUnit has added built-in support for this using the keyword Within.

Assert DateTime the Right Way MSTest NUnit C# Code

Development

Neat Tricks for Effortlessly Formatting Currency in C#

In the article, I am going to present to you a few ways how to create a handy class that effortlessly formatting currency in C#. Initially, I came up with the i

Neat Tricks for Effortlessly Formatting Currency in C#
Anton Angelov

About the author

Anton Angelov is Managing Director, Co-Founder, and Chief Test Automation Architect at Automate The Planet — a boutique consulting firm specializing in AI-augmented test automation strategy, implementation, and enablement. He is the creator of BELLATRIX, a cross-platform framework for web, mobile, desktop, and API testing, and the author of 8 bestselling books on test automation. A speaker at 60+ international conferences and researcher in AI-driven testing and LLM-based automation, he has been recognized as QA of the Decade and Webit Changemaker 2025.