Most of the attention surrounding the release of C# 6 this year has been related to the bootstrapping of the compiler, courtesy of project Roslyn. While it’s true that there are no major new features in the latest release (like generics, LINQ, dynamic or async/await from previous releases) there are lots of new features designed to clean up code by removing boiler plate and noise in order to expose domain concepts in terser, simpler code.
We’ve been using C# 6 for a while, and so as well as explaining the new features, this post looks when and when not to use them as well as a few patterns they’ve enabled which you’re likely to run into.
First a quick note of caution: C# 6 was developed with lots of community input via 6 preview releases, which means there’s quite a bit of content on the web detailing features that didn’t make the final release, specifically Primary Constructors, Declaration Expressions, and Default (parameterless) Constructors for structs. You may also find syntax that was changed before the final release and I’ll call this out when discussing specific features.
Auto-property initializers
As well as making your code terser, this feature was also introduced to encourage a more functional style of programming. Prior to C# 6 immutable types were off-putting due to the amount of boiler plate code required. With this feature, getter only properties can be initialized like readonly fields i.e. at the point of declaration:
public int OrderId { get; } = GenerateId();
and in constructors:
public int OrderId { get; } public Order(int id) { OrderId = id; }
You no longer need a private setter and a backing field which makes immutable types much cleaner. Note this changes how you access the member from within the type as you must now always use the property getter, which can take a small bit of getting used to.
Expression bodied functions
Also somewhat related to the trend towards a more functional style, this feature allows you to write single line functions as an expression without any curly braces. If the function returns something then the return
keyword can be omitted, matching lambda expression syntax. So:
public int SquareAndAddTwo(int x) { return x * x + 2; }
becomes:
public int SquareAndAddTwo(int x) => x * x + 2;
While simple cases like this look similar to the equivalent function in F#:
let public SquareAndAddTwo(x) = x * x + 2
C# expression bodied functions can depend on state and have side effects, and so I would say the link to functional style programming is cosmetic only. However, they do encourage the decomposition of code into small logical steps leading to cleaner, more readable code.
String interpolation
This simply allows expressions to be placed within a string. The code I’m currently working on has lots of logging so this feature is really useful. For example, the following:
var message = string.Format("CPU Temperature: {0:00.0}. Fan is {1}", cpuTemp, GetFanState());
in C# 6 becomes:
var message = $"CPU Temperature: {cpuTemp:00.0}. Fan is {GetFanState()}";
Note:
- The dollar sign at the beginning of the string which denotes an interpolated string.
- You can place any expression within the string, including fields, properties or method return values.
- It’s possible to use numeric formatting strings just as before.
An interpolated string is not only easier to read, but it’s easier to maintain as removing one value doesn’t require updating the place-holder numbers for other values.
There are still some occasions when the old way of formatting strings is better. For example, when the format string is a runtime variable, or when the format string is short and the expressions to be interpolated are long e.g. involving calculations or the ternary operator.
Note, the syntax for string interpolation has changed since preview releases, so the use of backslashes, as in:
var message = "CPU Temperature: /{cpuTemp:00.0}. Fan is /{GetFanState()}";
is no longer valid.
nameof operator
The nameof operator gives you access to a string representation of an identifier in your program. So, for example:
if (input == null) throw new ArgumentNullException("input");
can be replaced by:
if (input == null) throw new ArgumentNullException(nameof(input));
The advantage is that, unlike the magic string "input"
, the expression nameof(input)
is available to static analysis and refactoring tools. Unlike workarounds that involve Expression<Func<T>>
and runtime reflection, the substitution happens at compile time and so you get the performance benefit of a magic string.
Null-conditional operator
This null-conditional operator ?.
is a convenient way to perform null checking before accessing an object’s members. The expression:
employee?.Name
returns null if the employee object is null, otherwise it returns the employee’s Name
property. This feature can also be used when indexing into collections, e.g.:
var fourthElement = myArray?[3]; var elementFour = myDictionary?["four"];
Similarly, if the collection is null the statement returns null. Note, you’ll still get an IndexOutOfRangeException
or KeyNotFoundException
if the index or key are not present in their respective collection.
When the member being accessed is a value type then the null-conditional operator must return a nullable, so the length
variable in
int? length = name?.Length;
is of type Nullable<int>
.
While this feature can be useful, I’m careful when using this it, and when performing null checks in general. A null object often indicates a bug in your application that you need to find. If the error is fatal then blowing up with a NullReferenceException
makes sense. If you can recover you should still log the error, and so, again, you’ll not want to use this feature.
That said, when a null object is expected the null-conditional operator allows you to handle it cleanly. For example it’s now common practice to use it when raising events, so you’ll now see:
var handler = PropertyChanged; if (handler != null) { handler(this, args); }
written much more simply as:
PropertyChanged?.Invoke(this, args);
In order to be thread safe the null-conditional operator causes the object to be copied before accessing its member. I can only think of contrived examples where this would be an issue, but it’s worth knowing.
Another candidate for this feature is when indexing into a deserialized object when you can’t guarantee the input was good. For example:
Order[] orders = JsonConvert.DeserializeObject<Order[]>(ordersJson); var itemCity = orders?[10]?.Item?.SourceWarehouses?["Spain"]?.Address?.City;
is much cleaner than all the required null checks that would be required prior to C# 6.
Finally, the null conditional operator is now commonly used with the null-coalescing operator ??
to provide a default when a value is null, e.g.:
int length = name?.Length ?? 0;
Exception filters
The ability to filter exceptions has been in the CLR and Visual Basic since the early days of .NET, and now it’s possible in C#. Using the when
keyword, you can provide a predicate that determines whether or not a catch block is executed. For example:
try { SomethingUnpredictable(); } catch (Exception e) when (e.InnerException != null) { UnpackAndLogException(e); }
This is cleaner than catching the exception, executing a predict, and then conditionally re-throwing.
Aside from the obvious usage this feature creates a few interesting, if not advisable, possibilities. One example is to realize pattern matching as described here. Another pattern I’ve seen is to perform some action within the predicate and then return false, so the action is always executed without ever catching the exception. For example:
try { SomethingUnpredictable(); } catch (Exception e) when (LogException(e)) { } private bool LogException(Exception e) { // Logging code... return false; }
It’s worth being able to recognize this pattern, but unless it becomes common practice it’s not one I would encourage using. It’s more explicit to catch, log, and re-throw.
await in catch and finally blocks
There was no design reason for this being impossible prior to C# 6, it was simply a limitation of the compiler. With C# 6 that limitation is gone so you can now write:
try { someResource = GrabResource(); } catch (IOException e) { await LogExceptionAsync(e); } finally { await ReleaseResourceAsync(someResource); }
Index initializers
We always had property initializers, and now we can initialize dictionary elements by index, so:
var numberStringsCs5 = new Dictionary<int, string> { { 0, "zero" }, { 1, "one" }, {50, "fifty" } };
can be replaced by:
var numberStringsCs6 = new Dictionary<int, string> { [0] = "zero", [1] = "one", [50] = "fifty" };
which reads a bit more naturally and matches how elements are accessed after initialization. You should note, however, that these two snippets are not equivalent. The old way uses the Add
method and could be written like this:
var numberStringsCs5 = new Dictionary<int, string>(); numberStringsCs5.Add(0, "zero"); numberStringsCs5.Add(1, "one"); numberStringsCs5.Add(50, "fifty");
while the new way is equivalent to:
var numberStringsCs6 = new Dictionary<int, string>(); numberStringsCs6[0] = "zero"; numberStringsCs6[1] = "one"; numberStringsCs6[50] = "fifty";
Unfortunately, this means if you add an element to the same index twice using the C# 6 syntax the first element will be overwritten silently, while the old syntax would throw an ArgumentException
. For this reason, and considering the old syntax is already pretty clear, you may choose to ignore this feature.
As an additional note, the Add
method used for collection initialization can now be an extension method. This was a limitation of the compiler that has been removed in C# 6.
using static
Already familiar to Java programmers, C# 6 now allows you to add a using directive for enums and all static members of a type. So, for example, if you include:
using static System.Console; using static System.ConsoleColor; using static System.Math;
you can then write:
ForegroundColor = Magenta; WriteLine($"PI ^ 1/2 = {Sqrt(PI)}");
without referencing System.Console
, System.ConsoleColor
and System.Math
each time the members/enums are used.
This feature will really clean up some code, for example with Sqrt
and PI
above. However, it can also make code harder to read, for example you can't tell from looking at the function calls what ForegroundColor
refers to, what namespace Magenta
comes from, and whether WriteLine
is from System.Console
or System.Debug
. That said, it's dryer to specify which namespace all WriteLine
s come from in a single place. The bottom line is that this feature should be used judiciously.
using static
is designed to work differently for extension methods as it doesn’t bring them into global scope. However, if you use them as instance methods (as they’re typically intended to be used) then this won’t make any difference. The only effect using static
can have regarding extension methods is to narrow the scope of methods that are included from namespace to class.
Note that the syntax of this feature changed during development. The static
keyword wasn’t originally part of the syntax, so code like this:
using System.Console; using System.Math;
is no longer valid C#.
Conclusion
The new C# 6 features provide lots of opportunities to write terser, more intention revealing code. If you open a legacy code base with ReSharper 10 installed you’ll get annotations indicating where you can use the new features, and this is a good way to see just how useful they are. As they don’t depend on any CLR updates your application can still target the same version of .NET, so these features should quickly become characteristic C#.
We’re interested in how these features have been received elsewhere, so if you’ve been using them for a while or seen them enable any more patterns let us know in the comments.
If you’re interested in learning more see our training courses section or get in touch.