Object initializers

When you would like to create an object – you simply call the ctor of specific class. But sometimes building plenty of constructors just for adding another parameter is a waste of time (yours and developer who will use your code). This is why in C# 3.0 the object initializers were introduced.

But let’s start with simple example where we have a class such as below.

class Person
{
    public string Name { get; set; }
    public string Surname { get; set; }

    public Person(string name)
    {
        Name = name;
    }
}

With that to initialize it we were used to do it like that:

var p1 = new Person("Tom");
p1.Surname = "Kuj";

But starting from C# 3.0 you could also do it like that:

var p2 = new Person("Tom2") { Surname = "Kuj2" };

Ok – looking at that there seems to be only the language (compiler) feature to write the initialization differently. BUT there is also a different execution approach which can have significant influence on your multithreaded application.

IL code examination

Let’s check the IL code generated by the compiler.

.locals init (
  [0] class TestObjectInitialazerConsoleApplication.Person p1,
  [1] class TestObjectInitialazerConsoleApplication.Person p2,
  [2] class TestObjectInitialazerConsoleApplication.Person '<>g__initLocal0'
)
IL_0000: nop IL_0001: ldstr "Tom" IL_0006: newobj instance void TestObjInitApp.Person::.ctor(string) IL_000b: stloc.0 IL_000c: ldloc.0 IL_000d: ldstr "Kuj" IL_0012: callvirt instance void TestObjInitApp.Person::set_Surname(string) IL_0017: nop IL_0018: ldstr "Tom2" IL_001d: newobj instance void TestObjInitApp.Person::.ctor(string) IL_0022: stloc.2 IL_0023: ldloc.2 IL_0024: ldstr "Kuj2" IL_0029: callvirt instance void TestObjInitApp.Person::set_Surname(string) IL_002e: nop IL_002f: ldloc.2 IL_0030: stloc.1 IL_0031: ret

First look at this code does not provide much information but with a closer look – there will be interesting flow.

First of all let’s look at the locals initialization – we have 3, not 2 objects in there!!! We have two named p1 and p2 and there is something called ‘<>g__initLocal0’ – this is just temporary variable which will be used for the object creation (you probably understand it already :-)).

For our investigation the most interesting lines starts at IL_0022  where we allocate the object created in IL_001d object into [2] local and we work on that one. The lines IL_002f and IL_0030 – the temporary variable is assigned to the p2 object.

Summary

So object initialization gives us another temporary variable and when it’s finished – this is assigned to our object. But what do we get because of that? Why did designers decide not to work on the original object?

The answer is – the multithreading. You can imagine that it’s possible that some thread will read p1 when its ctor was called but not all properties were set. That is why using the object initialization is a better idea – will make our code more “atomic” I can write. So the object will never be in the state which we did not expect – it’s either not assigned or assigned as expected.

Additional summary (EDIT)

This seems to be the path taken by the designers with more than just object initializers. If you inspect the IL code of collection initializers – this works exactly the same way (with the temporary variable).

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s