Can anyone explain why the code below, when run, will occasionally result in some “NULL!” strings being written to the Console window?
(That’s the TL;DR question, read on for further details)
This doesn’t happen every time and you may need to run this code a few times, but soon enough, you’ll see a string of “NULL!” being output to the Console window instead of a number.
Essentially, it’s initializing (using the shorthand collection initializer syntax) a generic collection of user-defined types (List<Thing>) that contains a single property which is a reference to another user-defined type (Numb). Numb is pre-defined in a collection of it’s own (nnn) and objects from that collection are created using the shorthand object initializer syntax from within the collection initializer using a lambda to select a random object based upon it’s sole num property.
The nnn.Where(n => n.num==rnd.Next(1,3)).FirstOrDefault() should never return a NULL object (i.e. the default part of FirstOrDefault()) as the random number should only ever select either a 1 or a 2, both of which exist within the nnn collection.
My suspicions tell me this is something to do with using a call (specifically multiple calls) to rnd.Next() within an object/collection initializer scenario.
The code (run as a console application):
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Random rnd = new Random();
var nnn = new List<Numb> {
new Numb() {
num = 1
},
new Numb() {
num = 2
}
};
new List<Thing> {
new Thing() {
numb = nnn.Where(n => n.num==rnd.Next(1,3)).FirstOrDefault()
},
new Thing() {
numb = nnn.Where(n => n.num==rnd.Next(1,3)).FirstOrDefault()
},
new Thing() {
numb = nnn.Where(n => n.num==rnd.Next(1,3)).FirstOrDefault()
},
new Thing() {
numb = nnn.Where(n => n.num==rnd.Next(1,3)).FirstOrDefault()
}
}.ForEach(t => Console.WriteLine(t.numb!=null ? t.numb.num.ToString() : "NULL!"));
Console.ReadLine();
}
}
public class Thing
{
public Numb numb { get; set; }
}
public class Numb
{
public int num {get; set; }
}
}
You’re generating a different random number on each test of the predicate. So just looking at one part:
That’s going to:
Numb– let’s call thatn1xn1.num == x, and yield it if soNumber– let’s call thatn2yn2.num == y, and yield it if soIn other words, you’re querying against a moving target. You want to generate one random number, then test against that for all of the values. (Then repeat for each member of the collection initializer.)