DEV Community

Mark Clearwater
Mark Clearwater

Posted on • Originally published at blog.csmac.nz on

Looking back on C# 6: Initializers

Looking back on C# 6: Initializers

With C# 8 on our doorstep, I figure it is a good time to reflect on recent additions to the language that have come before. There are some great improvements you may have missed, some that I really enjoy using, and some I consider have reached canonical usage status that I think are all worth some reflection.

Initializers have had a few upgrades since the early C# days. But before we look at more recent changes, we will review what came before.

Initializers or Object Initializers is a name given to code that performs the initial setup of a piece of memory (a struct or an object). Constructors are part of this but the idea goes more towards the holistic setup of the object data for all of the state you care to set up.

Taking a look at C for even more background, we have initializers like so:

struct MyType { char s; int n; };

// We can initialize a variable using a curly brace syntax:
MyType x = { 'a', 42 };
Enter fullscreen mode Exit fullscreen mode

What we see above is that we are initializing our variable x to have its data for .s and .n set to 'a' and 42 respectively. This is initialization. Now using C#, something more familiar:

public class Foo
{
    public string Name { get; set; }
    public int Number { get; set; }
}

// Without using initializers:
Foo myFoo = new Foo();
myFoo.Name = "a";
myFoo.Number = 42;

// In C# we can initialize in a similar way to C.
// This code compiles to the same instructions as above:
Foo myFoo = new Foo() { Name = "a", Number = 24 };

// Collections also support initialization
int[] myArray = new int[] { 1, 1, 2, 3, 5, 8 };
Enter fullscreen mode Exit fullscreen mode

I won't go into how C has advanced its own initializers, that it a different topic. Instead, we will look at how C# has taken this simple initializer above and enhanced in C# 6 on top of this initial syntax in C# 3.

Add Extension support for collections

When Initializers were implemented (or not long after if my memory deceives me) there was a little magic that made this work. Namely, the code written was simpler and more concise, but the implementation transformed into something more familiar.

The same is true for Collection initialization. If we review the example from above we can see this:

// Collection initialization
int[] myArray = new int[] { 1, 1, 2, 3, 5, 8 };

// The same code, more verbosely:
int[] myArray = new int[];
myArray.Add(1);
myArray.Add(1);
myArray.Add(2);
myArray.Add(3);
myArray.Add(5);
myArray.Add(8);
Enter fullscreen mode Exit fullscreen mode

Basically, the syntax is a shorthand for calling Add( on the collection. This allows for more complex situations where Add can take multiple arguments.

var myDict = new Dictionary<int, string>() { { 1, "One" }, { 2, "Two" } };

// This worked the same way as the last example. The same code is:
var myDict = new Dictionary<int, string>();
myDict.Add(1, "One");
myDict.Add(2, "Two");
Enter fullscreen mode Exit fullscreen mode

Again, this is all existing syntax. With C# 6, an ability was added so that instead of needing to implement Add on your type, you could create extension methods as well:

public static void Add(this Dictionary<int, string> dict, Foo value)
{
    dict.Add(value.Number, value.Name);
}

Foo foo1 = new Foo() { Name = "One", Number = 1 };
Foo foo2 = new Foo() { Name = "Two", Number = 2 };
var myDict = new Dictionary<int, string>() { foo1, foo2 };
Enter fullscreen mode Exit fullscreen mode

At this point, the possibilities are endless and fully customizable to create your own Domain-Specific-Language (DSL). You don't even have to use Collections because anything can have an Add overload added via an extension.

Index Initializer

Dictionary syntax is a bit odd. Add isn't really the right approach. And we already have indexer syntax:

var myDict = new Dictionary<int, string>();
myDict[7] = "seven";
myDict[9] = "nine";
myDict[13] = "thirteen;
Enter fullscreen mode Exit fullscreen mode

Why not extend Initializers to use the indexer as well? They did. C# 6 again.

var numbers = new Dictionary<int, string> {
    [7] = "seven",
    [9] = "nine",
    [13] = "thirteen"
};
Enter fullscreen mode Exit fullscreen mode

Works as expected, and is the same result as the code above. This works off of the same existing indexer pattern( in a similar manner to the Add usage earlier), so works with any custom indexer on any custom type.

// Using an existing type from Newtonsoft.Json:
var x = new JObject { ["x"] = 3 };
Enter fullscreen mode Exit fullscreen mode

Auto property initializers

That is a bunch of consumer side initialization. There was more added behind the scenes in C# 6 as well.

The humble Auto Property:

public int X { get; set; }
Enter fullscreen mode Exit fullscreen mode

First, we can now give a default initialization:

// This is the same as `X = 4` in the constructor
public int X { get; set; } = 4;
Enter fullscreen mode Exit fullscreen mode

And to make this trick even more complete, we have the getter-only property:

public int Y { get; } = 42;
Enter fullscreen mode Exit fullscreen mode

As well as the ability to initialize this from a constructor instead:

public class MyFoo
{
    public int Y { get; };    

    pubilc MyFoo()
    {
        Y = 42;
    }
}
Enter fullscreen mode Exit fullscreen mode

Sometimes you may have many constructor overloads, using the property initializer is less code duplication. Sometimes you may want to have different values for different overloads, then you can initialize in the constructor.

Either way, this gives you true power over you read-only without the verbose equivalent you used to have to write:

// Same code as above in "full":
public class MyFoo
{
    private readonly int _y;
    public int Y
    {
        get
        {
            return _y;
        }
    };    

    pubilc MyFoo()
    {
        _y = 42;
    }
}
Enter fullscreen mode Exit fullscreen mode

The newer way is much more concise, especially when there are many properties:

// Just imagine how many lines of code this would be the old way!
public class MyFoo
{
    public string X { get; } = "Foo";    
    public int Y { get; } = 42;    
    public decimal Z { get; } = 54m;
    public object Key { get; } = new object{};

    // Other read/write props
    public int Another { get; set; }
    public bool Widgets { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Wrapup

Object initialization had a major revamp in C# 6. How many of these do you use regularly? How many do you notice in your own code bases? These have been around for a while so expect only to see more of it. Do yourself a favour and start using it if you haven't already. Very readability, much fewer keystrokes. Canonical? You betcha!

Top comments (0)