views:

597

answers:

2

The following code compiles, but fails with a NullReferenceException:

class Test
{
    public Dictionary<string, string> Dictionary { get; set; }
}

static void Main(string[] args)
{
    var x = new Test
    {
        Dictionary =   // fails
        {
            { "key", "value" }, { "key2", "value2" }
        }
    };
}

If you replace the line marked 'fails' with the following, it works (as expected):

Dictionary = new Dictionary<string, string>

Is there any purpose to the failing syntax--can it be used successfully in some other case? Or is this an oversight in the compiler?

+12  A: 

No, it's not a mistake... it's a flaw in your understanding of initialization syntax :)

The idea of the

Dictionary = { ... }

is for cases where the caller has read access to a collection property, but not write access. In other words, situations like this:

class Test
{
    private readonly Dictionary<string, string> dictionary 
        = new Dictionary<string, string>();
    public Dictionary<string, string> Dictionary { get { return dictionary; } }
}

Basically it ends up being calls to Add, but without creating a new collection first. So this code:

Test test = new Test { Dictionary = { { "a", "b"}, {"c", "d" } };

is equivalent to:

Test tmp = new Test();
Dictionary<string, string> tmpDictionary = tmp.Dictionary;
tmpDictionary.Add("a", "b");
tmpDictionary.Add("c", "d");
Test test = tmp;

A good example of where this is useful is with the Controls collection for a UI. You can do this:

Form form = new Form
{
    Controls = 
    {
        new Button { Text = "Hi" }, 
        new TextBox { Text = "There" } 
    }
};

but you couldn't actually set the Controls property, because it's read-only.

Jon Skeet
So it's used to add items to a dictionary created by the constructor--I should have realized that. But it's an odd use of the equals operator, since the effect is to add to whatever's already in the dictionary (the constructor may have added items first).
Ben M
Sort of, yes... but at the same time it's used to set the initial values in the collection, so it fits in in that way.
Jon Skeet
Right. The missing `new` should have been a red flag .. but having never used this syntax, I took the equals operator too literally.
Ben M
A: 

It fails with a null reference exception because you have declared a variable (Dictionary) that is unintialised, hence it is null.

When you attempt to add the entries into it using the initialiser syntax, you are trying to write data into a null object.

When you replace the line with a "= new Dictionary...", you are creating a new object for Dictionary to reference, and hence you are then able to add entries into it successfully.

(In Jon Skeet's example, the Controls collection must already have been created by the Form, hence it works ok)

Jason Williams
Yes, of course. My question was: why allow this syntax?
Ben M
Fair enough. Jon answered your question so I thought I'd fill in the reason in case you didn't understand the failure.
Jason Williams