Saturday, February 18, 2012

C# Initializers

C# generic collections offer a lot of flexibility and power for organizing your program's data. However, if you need to set one up manually, you may have written code that looks something like this:

List<string> list1 = new List<string>();
list1.Add("list1 item1");
list2.Add("list1 item2");
list2.Add("list1 item3");

List<string> list2 = new List<string>();
list2.Add("list2 item1");
list2.Add("list2 item2");
list2.Add("list2 item3");

List<string> list3 = new List<string>();
list2.Add("list3 item1");
list2.Add("list3 item2");
list2.Add("list3 item3");

Dictionary<string, List<string>> dictionary = new Dictionary<int, List<string>>();
dictionary.Add("list1", list1);
dictionary.Add("list2", list2);
dictionary.Add("list3", list3);

Wouldn't it be nice if you could declare a dictionary in one statement? Or a list? Or an array? Or even an entire class, complete with all data members, which may themselves be other classes, lists, dictionaries, or arrays? Well, turns out that you can. Find out how after the break!

The method for declaring and populating an object in C# is called an initializer. Perhaps the simplest and most straightforward initializer is the array initializer:

int[] intArray = new int[5];
int[] intArray2 = new int[] { 1, 2, 3, 4, 5 };

The first line will create an array of 5 ints, all initialized to the default value, which is 0. The second will also create an array of ints, but we have omitted the length from the square brackets and instead provided a list of initial values, which will be used to size and populate the array.

You can initialize a generic list in a similar manner:

List<int> intList = new List<int> { 1, 2, 3, 4, 5};

This makes a List that has the same contents as our array.

Let's look at a dictionary now:

Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>()
{
    { "list1", new List<string> { "list1 item1", "list1 item2", "list1 item3" } },
    { "list2", new List<string> { "list2 item1", "list2 item2", "list2 item3" } },
    { "list3", new List<string> { "list3 item1", "list3 item2", "list3 item3" } }
};

Notice the () before the initializer list, and the two sets of curly braces - one enclosing the list of items, and a second enclosing each individual key-value pair. Also notice that we were able to nest initializers for Lists inside the dictionary. The result is the same as our initial example, but in a much more concise and readable form.

Now, let's say you have your own class you want to use as a dictionary key or value, or have some other need to initialize. You can do that, provided that 1) there is a public, default constructor, and 2) all the properties you wish to initialize have public setters. Here's an example of such a class:

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
}

My Person class uses auto-implemented properties, and does not declare any constructors - it gets a public, default constructor for free. If I had done this instead:

class Person
{
    private Person() { }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
}

I couldn't use an initializer for this class, because the default constructor is not public. Now, let's see how to initialize this class:

Person john_jackson = new Person() { FirstName="John", LastName="Jackson", 37 };
Person jack_johnson = new Person() { FirstName="Jack", LastName="Johnson" };

One thing to notice here is that you only have to initialize the properties you care about. Jack Johnson didn't have his age initialized, so it will retain its default value, 0.

Now, a lot of the time, it will probably be more useful to just call an object constructor instead. However, sometimes you'll have a dumb class whose only function is to group data together, or that only has a default constructor and therefore would take several statements to fully initialize. Those cases are prime candidates for initializers.

Now, if we want to get really crazy, we can actually use a class initializer without even declaring a class! Behold:

var data = new { Key="key", Value="value" };
Console.WriteLine(data.Key);
Console.WriteLine(data.Value);
key
value

Cool, huh? "Yeah, but why would I ever really want to do that?" Well, probably only if you're using LINQ. Consider our dictionary example - what if you wanted to pull out the first item in each list, and group it with its key? Here's the LINQ way to do it:

var firstItems = from kvp in dictionary 
                 select new { Key=kvp.Key, FirstItem=Value.First() };
for (var firstItem in firstItems)
{
    Console.WriteLine("Key: {0}, Value: {1}", firstItem.Key, firstItem.Value);
}
Key: list1 Value: list1 item1
Key: list2 Value: list2 item1
Key: list3 Value: list3 item1

In conclusion, initializers can be really great when you need to statically initialize some data in code in a concise, readable manner. They are available beginning in .NET 3.5. Enjoy!

No comments:

Post a Comment