C# null checking extension method on sub-object does not work

78 Views Asked by At

I have a null-checking extension method

public static IEnumerable<T> OrEmptyIfNull<T>(this IEnumerable<T> source)
{
    return source ?? Enumerable.Empty<T>();
}

which is nice to call to avoid extra null checks before running a loop, like this

int[] intArray = new int[] { 1, 2, 3 };
int sum = 0;
foreach (int i in intArray.OrEmptyIfNull())
    sum += i;

But in case the IEnumerable I'm using is inside another object, it will fail with NullReferenceException, if the surrounding object is null - like here

foreach (int i in object?.intArray.OrEmptyIfNull())
    sum += i;

After reading the discussion in In C#, what happens when you call an extension method on a null object? I understand the compiler translates the expression to

ExtensionClass.OrEmptyIfNull(object?.intArray)

But I experience a NullReferenceException here if object == null. Why?

Doesn't this behavior make any use of null-checking extension methods very dangerous?

2

There are 2 best solutions below

3
JP Alioto On

I think you're getting confused between Nullable Reference Types and Nullable Value Types.

Nullable reference types are simply that type. Since object is a reference type, you just have an object. If you dereference a null reference type, you get an exception. If you know for a fact that object is not null at runtime, you can override the compiler with the syntax ...

object!.intArray

for example. But, why would you do that? Obviously, in your case, object is null. Just check it.

2
mm8 On

Your extension method is never called if object is null because of short-circuiting, i.e. the expression object?.intArray.OrEmptyIfNull() is always null if object is null. intArray is not even evaluated in this case.

A cleaner approach would be to actually check the reference before trying to iterate:

if (object?.intArray != null)
    foreach (int i in object.intArray)
        ...

Or, at least in this particular case, you could always use LINQ:

int sum = object?.intArray?.Sum() ?? 0;