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 };
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 };
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);
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");
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 };
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;
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"
};
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 };
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; }
First, we can now give a default initialization:
// This is the same as `X = 4` in the constructor
public int X { get; set; } = 4;
And to make this trick even more complete, we have the getter-only property:
public int Y { get; } = 42;
As well as the ability to initialize this from a constructor instead:
public class MyFoo
{
public int Y { get; };
pubilc MyFoo()
{
Y = 42;
}
}
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;
}
}
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; }
}
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)