vs2008

.NET Musings

Wandering thoughts of a developer, architect, speaker, and trainer

NAVIGATION - SEARCH

Object and Collection Initialization in VS 2008 and C# 3.0

Technorati Tags: ,,,

In my last post, I talked about Automatic Properties, and how they are essentially compiler magic.  The new Object and Collection Initialization syntax are two more features in C# 3.0 in the same vein.  that deal with initialization of objects (and collections of objects) that not only save typing, but can significantly clean up your design.

Object Initialization

Constructor Initialization is by far the easiest method to create a new object and set it's state.  In prior versions of .Net, to help with this, we wrote lots of constructors for our classes to handle all of the various combinations of properties that needed to be set.  Now (more often than not) we don't need to add all of this superfluous code into our classes.

I'm a test-centric developer, so let's write some tests to see how we can use this code.  First, our baseline (failing) test:

  1: var s = "This Is A Test";
  2: MyClass c = new MyClass(s);
  3: Assert.AreEqual(s, c.TestString);

To pass this test, we create this basic class:

  1: public class MyClass
  2: {
  3:     public MyClass() : this("DefaultConstructor") {}
  4:     public MyClass(string testString)
  5:     {
  6:         TestString = testString;
  7:     }
  8:     public string TestString { get; set; }
  9: }

We can either create this class by calling the default (no-arg) constructor, or by calling the custom constructor that initializes our one property.  Imagine how many different constructors you would have to write if there were 10 properties, not just the one in our overly-simplistic example?


And, as expected, it passes. This might seem a little backwards, but we are going to refactor the test to use the object initialization syntax.  If you are using ReSharper (R#), this becomes quite the trivial task (R# prompts you for it, and with a single "alt-enter", we get:

  1: var s = "This Is A Test";
  2: MyClass c = new MyClass { TestString = s };
  3: Assert.AreEqual(s, c.TestString);

And it passes. Now we can refactor our class to get rid of the custom constructor...or should we? As with all apparent goodness, there is a catch.  You'll note in the default constructor, I am setting a value for TestString.  The downside is that the default constructor is always called when you use this syntax.  Therefore, it can be a very bad idea if you have some long running code in there, especially if it all gets thrown away due to the object initialization.


Here is the resultant IL for the test (extra stuff removed):

  1:   IL_0001:  ldstr      "This Is A Test"
  2:   IL_0007:  newobj     instance void ClassLibrary1.MyClass::.ctor()
  3:   IL_000f:  callvirt   instance void ClassLibrary1.MyClass::set_TestString(string)
  4:   IL_0019:  callvirt   instance string ClassLibrary1.MyClass::get_TestString()
  5:   IL_001e:  call       void [MbUnit.Framework]MbUnit.Framework.Assert::AreEqual(object,
  6:                                                                                 object)
  7: 

Note the call to the default constructor on line 2.  Line 3 then assigns the value to the property.  I'm not saying to avoid the new syntax, I'm suggesting that just because you can doesn't mean you should.  As my good friend Leon Gersing always says, use "Critical Thought".


Collection Initialization


Along the same lines as Object Initialization comes Collection Initialization. In this case, however, we don't have the same double execution issues.  We are simply reducing keystrokes.


The test I built to prove this out is as follows:

  1: [Test]
  2: public void Should_Build_List_Of_Strings()
  3: {
  4:    var s1 = "1";
  5:    var s2 = "2";
  6:    var s3 = "3";
  7:    var list = MyClass.CreateStringList(s1,s2,s3);
  8:    Assert.AreEqual(3, list.Count);
  9:    Assert.AreEqual(s1, list[0]);
 10:    Assert.AreEqual(s2, list[1]);
 11:    Assert.AreEqual(s3, list[2]);
 12: }
 13: 

Out initial Method:

  1: public static IList<string> CreateStringList(string s1, string s2, string s3)
  2: {
  3:     List<string> myStrings = new List<string>();
  4:     myStrings.Add(s1);
  5:     myStrings.Add(s2);
  6:     myStrings.Add(s3);
  7:     return myStrings;
  8: }
  9: 

The tests are green, lets refactor.  If you are using R#, this is a breeze, as it will prompt you to use collection initialization. And, with a single "alt-enter", we have:

  1: public static IList<string> CreateStringList(
  2:      string s1, string s2, string s3)

  3: {
  4:     List<string> myStrings = new List<string> {s1, s2, s3};
  5:     return myStrings;
  6: }

The tests still pass, and we have eliminated quite a bit of typing. 


Happy Coding!

Managed Windows Shared Hosting by OrcsWeb