C# 3.0 Object Initializers
As I'm sure you are aware, C# 3.0 includes a new language feature called object initializers. Put simply, object initializers enable you to initialize the state of an object with a single expression that does not require use of parameterized constructors. A few million examples on the object initializer syntax ranging from the simple to complex are out there on the Internet, so there's no need for me to go into them here. I do, however, want to make a couple of quick points.
1. The object initializer syntax sits on top of the standard C# language - and it does not allow you to break the basic OO rules of the language. It feels to me like many of the language features coming down the pipe (at least for both C# 2 and 3) have their real value when taken in groups. For example, while a cool feature, anonymous methods didn't shine quite so brightly until used in conjunction with generic delegates and the type inference capabilities of C# 2.0 (obviously, lambda expressions are the latest incarnation of this capability). Similarly, object Initializers seem to have their most value when used in conjunction with anonymous types - and more specifically, when used with anonymous types in the context of a LINQ projection operation.
That said, object initializers should not be thought of as a way to side-step basic object heuristics (and from the implementation perspective, the C# compiler won't let you). For example, a basic tenant of OO design specifies that an object should be ready to use after constructed. As a result, many classes are designed such that there are no parameterless constructors (since the result would yield a class that still required initialization after the object was constructed). In this case, you must call a parameterized constructor even when using the object initializer syntax. It is worth pointing out, however, that the object initializer syntax is quite handy here in eliminating a lot of "shortcut constructors" from the code base. The constructor code can require only the parameters that are absolutely necessary to construct the object, while the object initializer syntax can be relied on for allowing the user to provide additional state to the constructed object.
Just in case you hadn't heard yet, this new language feature (and all of the other C# 3.0 features) were implemented without requiring any changes to the IL. Let's take a look, then, at an example of the syntax and the code generated by the compiler.
Customer jenn = new Customer("default",0) { Name = "jennifer", Age = 31 };
When the project is built and the assembly is reverse engineered, we can see that the generated code is as follows.
private static void Main(string[] args)
{
Customer <>g__initLocal0 = new Customer("default", 0);
<>g__initLocal0.Name = "jennifer";
<>g__initLocal0.Age = 0x1f;
Customer jenn = <>g__initLocal0;
}
We can see here that our parameterized constructor was called as it has always been, the constructed object is assigned to a temporary variable, our additional properties specified in the initializer syntax are set, and the reference held by our temporary variable is then assigned to our declared variable. Pretty straightforward.
2. Be cautious of using object initializers with value types. I have a real love-hate feeling for language features like this. On the one hand, they can make life easier and code cleaner. On the other hand, they can create a false sense of security if one assumes that the feature behaves the same in all contexts. Take another look at the generated code. For reference types, this is absolutely no big deal. A managed reference has a tiny footprint, and hey - what's one more reference for the GC to track <g>?
For value types, the story is a little different. Because you have 2 different variables, this single object initialization will end up consuming at least twice the memory required by the type itself. In the code shown, this is probably not a big deal. However, if your value type is a little bigger, and if you're initializing it a few hundred times in a loop, you can see how you could inadvertently increase the footprint of your code. For the DDD crowd, this is definitely something to be cognizant of - especially if you are using value types to implement value-objects.
I am currently the Editor-in-Chief for MSDN Magazine. I joined Microsoft in 2006 as a product planner with the certification team at Microsoft Learning. Prior to that, I spent my career as a developer and later as an architect. My main technology passions include pretty much anything on language theory, agile development, and service-oriented architecture.