18

Reading a lot about the Null propagation operator ?., I found no answer whether it is helpful in the following scenario.

Code that throws:

int[] values = null;

foreach ( var i in values ) // Throws since values is null.
{
    // ...
}

To make this working, I have to add a null check before access to the values variable.

Most likely the above code is out of scope of the design considerations for the Null propagation operator. Still, to be sure, I have to ask.

My question:

Is the Null propagation operator helpful when trying to access null collections in a foreach loop?

Paulo Morgado
  • 14,111
  • 3
  • 31
  • 59
Uwe Keim
  • 39,551
  • 56
  • 175
  • 291

4 Answers4

12

I've found another, working way:

When using Jon Skeet's (et al) fantastic MoreLinq extensions, there is a ForEach extension method which I can use in my initial example like:

int[] values = null;

values?.ForEach(i=> /*...*/); // Does not throw, even values is null.
maxp
  • 24,209
  • 39
  • 123
  • 201
Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
  • 1
    There is a `ForEach` extension in `System.Collections.Generic`. There is no need to use MoreLinq anymore. And it also works with null propagation without throwing an exception. – Azimuth Feb 01 '21 at 14:15
8

No it is not. It's designed to allow access members of an object in a secure way. In this case you have to check whether the array is null.

Selman Genç
  • 100,147
  • 13
  • 119
  • 184
3

How would you porpose to use it?

The code you provided:

int[] values = null;

foreach (var i in values)
{
    // ...
}

enxpands into something:

int[] values = null;

var enumerator = values.GetEnumerator();
try
{
    while (enumerator.MoveNext())
    {
        var i = enumerator.Current;
        // ...
    }
}
finally
{
    var disposable = enumerator as IDisposable;
    if (disposable != null)
    {
        disposable.Dispose();
    }
}

I guess you could write something like this:

int[] values = null;

foreach (var i in values?.)
{
    // ...
}

The compiler would have to expand it to something like this:

int[] values = null;

var enumerator = values?.GetEnumerator();
try
{
    while (enumerator?.MoveNext() ?? false)
    {
        var i = enumerator.Current;
        // ...
    }
}
finally
{
    var disposable = enumerator as IDisposable;
    if (disposable != null)
    {
        disposable.Dispose();
    }
}

And have in mind that:

a = b?.M();

expands into:

a = b == null ? null : b.M();

If you want to not explicitly write the if statement, you can always rely on the good old null-coalescing operator (??):

int[] values = null;

foreach (var i in values ?? Enumerable.Empty<int>())
{
    // ...
}
Paulo Morgado
  • 14,111
  • 3
  • 31
  • 59
1

For List<T> you can use list?.ForEach(i => ...); and for other enumerables you can make your own ForEach extension like this:

public static void ForEach<T>(this IEnumerable<T> source, Action<T> action) {

    if (source == null) { return; }

    foreach (T item in source) {
        action(item);
    }
}

You call it like this: myPossiblyNullEnumerable.ForEach(i => ...);

If you want your ForEach extension to throw when called for null enumerables, you can just leave the null check away, and call it with the same elvis syntax as the List's version: myPossiblyNullEnumerable?.ForEach(i => ...);