Search This Blog

2009-06-02

C# 3.0 Tutorial -4:Object Initializers

It is fairly common in C# code to see an object be instantiated using the "new" keyword and then having its fields and/or properties set. Until C# 3.0, this could only be done by instantiating the object, storing it in a variable and then doing assignments to the various properties. In C# 3.0, object initializers make this possible within a single expression.Suppose we want to instantiate a 5 year old male monkey called Norbert and add it to the jungle. In previous versions of C# we would have written:

A few things are frustrating here. First is that if we have to mention the variable, NewCreation, each time. Second, we may not really need the variable NewCreation at all - we just want to add a monkey to the collection. Finally, it would be better from a linguistic point of view if we could have pulled the Add ahead of the monkey creation, so when you read the code you can see the purpose of creating the new monkey.

Object initializers allow us to set the initial values of fields or properties of an object as part of the new statement. For example, we can re-write the above like this:Monkey NewCreation = new Monkey() {Name = "Norbert",Sex = SexEnum.Male,Age = 5};Jungle.Add(NewCreation);

Here we have added a set of curly braces at the end of the "new" expression. Inside them, we can do assignments to the fields and properties without having to write the name of the object that is being referred to. Note the use of commas between the assignments rather than semicolons.

The fact that we don't have to name the object we are initializing - that is, setting the fields/properties of - means we can do a further refactoring:Jungle.Add(new Monkey() {Name = "Norbert",Sex = SexEnum.Male,Age = 5});Now the intermediate variable is gone. Finally, if there are no parameters to pass to the constructor, we are permitted to save ourselves two more characters and remove the brackets after the type name:Jungle.Add(new Monkey {Name = "Norbert",Sex = SexEnum.Male,Age = 5});

Initializing Nested ObjectsOur Monkey class may have, as one of its fields, an field that holds an instance of the Tail class. In this case, there are two possibilities. One is that the class does not instantiate the Tail for us. In this case, we can use the new keyword to instantiate it and set properties of it - basically, just nesting what we already know.Jungle.Add(new Monkey {Name = "Norbert",Sex = SexEnum.Male,Age = 5,Tail = new Tail { Length = 50 }});The other possibility is that the class does instantiate tail and we just need to set some properties of it. In this case we can omit not only the "new" keyword, but also the name of the class too, since that can be worked out by the compiler.

You can nest as deeply as you wish, but be careful not to harm readability. Good use of whitespace can help on that front.Collection InitializersCollections can contain many values. Sometimes you will create a collection and then immediately add some values to it. Just as object initializer syntax made a common use case neater for objects, collection initializer syntax makes one neater for collections.

Again, let's take an example. Notice that I am already using the new C# 3 "var" keyword.var Jungle = new List();Jungle.Add(new Monkey());Jungle.Add(new Tiger());Jungle.Add(new Panda());Using a collection initializer, we can write this as:var Jungle = new List{ new Monkey(), new Tiger(), new Panda() };

There are some rules concerning the use of collection initializers. First, if you are writing your own collections and want them to work with collection initializer syntax, they must implement the ICollection interface. Second, the elements of the collection must all be of the same type (or more precisely, they must all have an implicit coercion to a single type).

Initializer PerformanceShorter code doesn't always mean a performance improvement at runtime. In this case, the new object initializer syntax will almost certainly compile down to the same IL instructions as if you had not used it. You might save a tiny amount of memory due to not having to allocate space for the local variable. However, the compiler should have been able to optimize that away anyway. In short, expect equivalent performance: no better and no worse.