Tuesday, 18 May 2010

Object initialisers and Collections

Starting with .NET 3, we've received some pretty cool new syntax in C#, things that made coding a bit more pleasant, and the code look somewhat more tidy. One of these additions was the object initialiser syntax!

Basically, what it allows you to do is initialise an object, and assign a bunch of properties to this object in a single statement, instead of 1 statement per each assignment. Apart from reducing the number of statements, and grouping all these assignments (which improves readability as well), this was also used in initialising anonymous class variables, and using simple single-statement lambdas, but that's not the point of this page :)

While this works great in most cases, there's one situation where I thought I didn't have a choice than to assign some values outside the initialiser, and this was readonly collections! Because the object initialiser is used to assign values to properties, I couldn't use it to create a new object with some values in a readonly collection property, because every time I tried - I got a exception.

new DataTable
{
    Columns = new[] { new DataColumn("FirstCol") }
}

What I didn't know at the time was that this was easily fixed by using another well-known addition in .NET 3 - the array / collection initialiser!

Basically, they can be easily to create an array / list, and immediately instantiate it with a number of values. And it works just great, and we still get the nice one-line syntax. But what I didn't know was they can also be used to not only instantiate new lists and collections, but also populate existing collections, when used in object initialisers.

So in the example above, we can instantiate the DataTable with a number of columns, and even rows, like this:

new DataTable
{
    Columns = { new DataColumn("FirstCol"), "Second", "Last" },
    Rows = 
    {
        { 1, 2, 3 },
        { "2", "3", "5" },
        { true, 5.6m, "bye" }
    },
}

One thing to note here - this syntax can add items to any collection, and it does so by using the Add property. So as you can see, I was able to use different overrides of the Add property to initialise the Columns, even though they were all in a single "list" there.

Sure, initialising a DataTable with a couple static columns and rows doesn't seem like a useful thing to do, but this was just an example.. There are a lof of other places where this would be a lot more useful! For example, creating an RSS / Atom feed for your site could fit in a simple statement like this:

var model = new SyndicationFeed
{
    Title = SyndicationContent.CreatePlaintextContent(Settings.Default.Site_Title),
    Description = SyndicationContent.CreateHtmlContent(Settings.Default.Site_Description),
    Generator = "FlexLabs",
    Language = "en-GB",
    Id = new Uri(Request.Url, "/").ToString(),
    Items = pages.Select(p => new SyndicationItem
    {
        Id = p.Id.ToString(),
        Title = SyndicationContent.CreatePlaintextContent(p.Subject),
        Summary = SyndicationContent.CreateHtmlContent(p.Summary),
        Content = SyndicationContent.CreateHtmlContent(p.Summary + p.Body),
        PublishDate = p.DateCreated,
        LastUpdatedTime = p.DateEdited ?? p.DateCreated,
        Links = { new SyndicationLink(new Uri(p.Url), "alternate", p.Subject, "text/html", 0) },
    }),
    Authors =
    {
        new SyndicationPerson(
            Settings.Default.Author_Email,
            Settings.Default.Author_Name,
            Settings.Default.Author_Website)
    },
    Links = {new SyndicationLink(new Uri(Request.Url, "/"), "alternate", Settings.Default.Site_Title, "text/html", 0)},
};

In this case, the authors and the links are readonly collection properties, and I was easily able to initialise them without making my code look messier than it should be! It's a pretty simple syntax, but it's also pretty powerful, and it's a bit of a shame that it wasn't so well known before. After all, .NET 4 is already out, and I've never seen this used anywhere until now :)