4

I have encountered in my code similiar situation to the one presented in code below. The problem is that for some reason iterating in foreach loop throws NullReferenceException.

My question is, why this happens?

If I create iterator that returns empty element myself, foreach handles it, and simply prints empty line.

Result of following code is: test, test, NullReferenceException.

using System;
using System.Collections.Generic;
using System.Linq;

public class NestedB
{
    public string Test {get;set;}
}

public class NestedA
{
    public List<NestedB> NestedCollection {get;set;}
}

public class Program
{
    public static void Main()
    {
        var listOfA = new List<NestedA>
        {
            new NestedA
            {
                NestedCollection = new List<NestedB> 
                {
                    new NestedB {Test = "test"},
                    new NestedB {Test = "test"}
                }
            },
            new NestedA ()
        };
        
        var listOfB = listOfA.SelectMany(x => x.NestedCollection);
        
        foreach (var item in listOfB)
        {
            if (item != null)
            {
                Console.WriteLine(item.Test);
            }
        }
    }
    
}

Stacktrace:

Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
   at System.Linq.Enumerable.SelectManySingleSelectorIterator`2.MoveNext()
   at Program.Main()
Command terminated by signal 6
3
  • 1
    Does this answer your question? What is a NullReferenceException, and how do I fix it? Commented Jul 2, 2020 at 6:37
  • @SirRufo no, I know what NullReferenceException is, however I do not know why this happens. Commented Jul 2, 2020 at 6:49
  • Read the answer from that link and you will know how to fix it Commented Jul 2, 2020 at 6:53

3 Answers 3

4

This is the problem:

listOfA.SelectMany(x => x.NestedCollection)

Your second NestedA instance doesn't have a NestedCollection, so it's trying to find "all the items in a null reference". You'd have exactly the same problem if you did this manually:

var nestedA = new NestedA();
// This will throw an exception, because nestedA.NestedCollectoin is null
foreach (var nestedB in nestedA.NestedCollection)
{
}

The simplest fix to this would be to make NestedCollection a read-only property, but initialize it to start with:

public List<NestedB> NestedCollection { get; } = new List<NestedB>();

Then you'll need to modify the initialization of your first NestedA to use a collection initializer:

new NestedA
{
    NestedCollection =
    {
        new NestedB { Test = "test" },
        new NestedB { Test = "test" }
    }
}

If you don't want to do that, you could change the SelectMany call instead:

var listOfB = listOfA.SelectMany(x => x.NestedCollection ?? Enumerable.Empty<NestedB>())
Sign up to request clarification or add additional context in comments.

2 Comments

Oh, I thought that SelectMany would take two items of first instance and simply add a null at last place (merge of both collections) so it would be caught by ``if (item != null)```, not that it would try to iterate null.
@MPal: No, SelectMany has never converted "a null reference" into "a collection with a single element of the default value" before flattening. I could understand it possibly automatically treating a null reference as if it were an empty collection, but it doesn't. It just behaves as if you've got two foreach loops, effectively (one nested in the other) - and that would throw a NullReferenceException too.
0

The implementation of SelectMany is something like this:

public static IEnumerable<TResult> SelectMany<TSource, TResult>(
  this IEnumerable<TSource> source,
  Func<TSource, IEnumerable<TResult>> selector)
{
  if (source == null)
    throw Error.ArgumentNull(nameof (source));
  if (selector == null)
    throw Error.ArgumentNull(nameof (selector));
  return Enumerable.SelectManyIterator<TSource, TResult>(source, selector);
}

private static IEnumerable<TResult> SelectManyIterator<TSource, TResult>(
  IEnumerable<TSource> source,
  Func<TSource, IEnumerable<TResult>> selector)
{
  foreach (TSource source1 in source)
  {
    foreach (TResult result in selector(source1)) // The error throws here
      yield return result;
  }
}

Note the comment line. The selector(source1) will return a null for the second NestedA item and this line will try to get the enumerator of the null item(foreach). That's why you got the error.

Comments

0

To complement the existing answers:

Compilation optimization and runtime optimization can cause inaccuracies in the reported line numbers. It is useful to examine not just the enumerable, but also the foreach body for possible dereferenced nulls, especially if iterating over a (statically declared) array.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.