What’s New in C# 6

The C# language itself has changed little in version 6, the main importance of the release being the introduction of the Roslyn .NET Compiler Platform. However the New features and improvements that have been made to C# are welcome because they are aimed at aiding productivity. Paulo Morgado explains what they are, and how to use them.

Version 6 of the .NET C# programming language was introduced when Visual Studio 2015 was recently released. What were the most significant improvements in the language?

The main focus of the release was the new compiler platform (known as ‘Roslyn’): There weren’t many obvious improvements or additions to the language as such, but the changes that I’ll mention in this article will definitely be appreciated by anyone who uses the C# programming language to develop applications.

Improvements in Auto-Properties

C# has ‘auto-implemented properties’ (or, more concisely, ‘auto-properties’) that make property-declaration for simple properties more concise. Behind the scenes, a private, anonymous backing field is created that can be read or updated only via the property’s get and set accessors. They are declared with the get and set keyword followed by a semicolon.

Initializers for Auto-Properties

It is now possible to declare the auto-properties just as you would a field:

With this syntax, the initializer directly initializes the field that supports the property without actually using the set accessor of the property.

The property initializers are executed in the order they are declared, just as and along with field initializers. After all, they are, in fact, field initializers.

As with the field initializers, property initializers cannot refer to this because like field initializers, they run before the object is properly initialized.

The implementation of this new functionality is actually syntactic sugar, in that it generates code that is compatible with previous versions of the .NET platform. In fact, the code I’ve used to illustrate the new syntax is translated by the compiler to the following C# code 1:

Note that the <First>k__BackingField and <Last>k__BackingField have names that are not valid C# names. This is done to ensure that there is no chance of collision between a name that is given by the programmer and a name given by the compiler.

Read-Only Auto-Properties

If you only declare a get accessor, then the property is immutable everywhere except in its initializer and the constructor, so that it becomes read-only:

In this case, the field is declared generated as readonly (although this has importance only for the purpose of reflection).

As in the previous case, the generated code will be:

As with the read-only fields, you can also initialize a read-only auto-property in the constructor.

And, once again, the equivalent compiler generates code C# 1:

Expression Bodied Function Members

We can now use the same convenient fat arrow expression syntax (=>) to define lambda expressions to define function bodies.

Expression Bodied Method-Like Members

We use the lambda fat arrow (=>) to define the body of methods as well as user-defined operators, type conversions and indexers. The expression to the right of the lambda arrow represents the body of the method.

The effect is exactly the same as if the methods have only a return statement. The above examples are translated by the compiler to:

For methods whose return type is void (or Task for asynchronous methods), the lambda arrow (=>) syntax still applies but the subsequent expression must be a statement (this is similar to what already happens with lambdas):

…will be translated into…

Expression Bodied Property-Like Function Members

Expression bodies can also be used to define the body of properties and an indexers:

Note the absence of the get keyword, which instead is implied by the syntax of the expression.

The previous examples are translated by the compiler to:

The ‘using static’ Directive

We can now import the static or enumerated members of a class into our namespace. We can then use the static members of a class directly without qualifying them with their namespace or type name!

The previous code is translated by the compiler to:

This feature is great when you have a set of related functions with a particular domain that is often used, such as System.Math. We can reference the methods of the Math class as though they were members of the class invoking them. It also allows you to specify the names of the members of an enumerated class as members of System.DayOfWeek in the example above.

Extension Methods

Extension methods are invoked like regular static methods, (See: https://msdn.microsoft.com/en-gb/magazine/dn879355.aspx) but they are called as if they were instance methods on the extended type. Instead of bringing these methods to the current scope, the static import functionality makes these methods available as extension methods without the need to import all extension methods in a namespace like before:

This means that it will now be a breaking change to change a normal static method into an extension method, which was not the case before. Extension methods are usually only invoked as static methods when there’s an ambiguity. And, in those cases, it seems legit to require full qualification anyway.

Null-Conditional Operator

It is often necessary to scatter null checks around code. Operators with null checks allow access to members and elements only when the receiver is not null, otherwise returning a null result:

The above code is translated into:

The null-conditional operators can be very convenient when used with the coalesce operator (??):

The null-conditional operators have a short-circuit behaviour. In the chain of access to members, elements or invocations immediately adjacent will only run if the original recipient is not null:

This example is, in essence, equivalent to:

…except that people is evaluated only once. None of the access to members or elements and invocations that follow the ? operator will run if the value of people is null.

Nothing prevents null-conditional operators from being chained, if null verification is required more than once in the chain:

In an invocation, a list of arguments in parentheses cannot be immediately preceded by the ‘?‘ operator – It would lead to too many ambiguities. Therefore, the invoking of a delegate that one might expect if it is not null is not allowed. However, the delegate can always be invoked via its Invoke method:

A common use of this feature is the event trigger:

…which is translated to…

This is a thread-safe way to see if the event has subscribers, because it only evaluates the left side of the invocation once, and keeps its value in a temporary variable. Without this feature, you used to have to write to this pattern yourself.

String Interpolation

The String.Format method, and other similar methods, is very versatile and useful, but its use is a bit awkward and error-prone due to numerical markers ({0}) that must match the position of the arguments supplied separately:

The interpolation syntax allows strings to directly replace the literal string indexes by “holes” with the expressions that correspond to the values:

As with the String.format method, it is possible to specify shapes and alignments:

The content of the holes can be any expression, including strings:

Note that the conditional expression is in brackets, so that: “s” is not confused with the format specifier.

Formattable Strings

When not otherwise specified, a formatting provider uses the current culture of the current thread when invoking the String.Format method, but this is not always what is wanted. That is why, as happens with lambda expressions, the compiler translates the interpolated string differently depending on the type of receptor expression.

If the receiver of expression is the IFormattable type…

…the compiler generates the following code:

This can be used as follows:

FormattableString

The concrete type returned by FormattableStringFactory.Create is derived from:

This provides access not only the format but also to the format string arguments.

Backward Compatibility

The features introduced by the C# 6 are compatible with the previous .NET platforms. However, this particular feature requires System.Runtime.CompilerServices.FormattableStringFactory and System.FormattableString types that have been introduced only in version 4.6 of the platform. The good news is that the compiler is not tied to the location of these types in a particular assembly and, if we are to use this functionality in an earlier version of the platform, we just need to add the implementation of these types.

‘nameof’ Expressions

Occasionally it is necessary to provide a string with the name of some program elements.

  • This can happen when throwing a System.ArgumentNullException;
  • Firing a PropertyChanged event;
  • And many other occasions.

We can use string literals for this, but they are error prone and are not changed when a name is changed by a code refactoring.

The nameof expressions are a sort of literal of type string in which the compiler validates the existence of something with that name. As it becomes a reference to the artefact, Visual Studio knows what it refers to and code navigation and refactoring work.

In essence, code such as the following:

is converted to:

Source Code vs. Metadata

The names used by the compiler are the source names and not the metadata names of the artifacts, and so the following code…

…is converted to…

Primitive Types

It is not allowed to use primitive types (int, long, char, bool, string, etc.) in nameof expressions because they are not expressions and the argument of nameof is an expression.

Add Extension Methods in Collection Initializers

When collection initializers were introduced in C#, the Add method calls could not be an extension method invocation. Visual Basic allowed its use but it seemed to have been forgotten for C#.

In this version the fault has been corrected and it is now possible to use Add extension methods in collection initializers.

It is a small but useful feature and, in terms of the implementation of the compiler, this was just the removal of the check of the condition that prevented it.

Index Initializers

Object and collection initializers are useful to initialize declaratively the fields and properties of objects or, in the case of collections, an initial set of elements.

Initializing dictionaries, on the other hand, were not so elegant, requiring the existence of an Add method that received as an argument the key and the value corresponding to that key. If a dictionary implementation in particular did not have an Add method with the aforementioned characteristics, it would not be possible to use an initializer.

From now on, it becomes possible to use initializers that use indexes:

which will be translated into:

Exception Filters

Exception filters a CLR feature already provided by Visual Basic and F# and will now also be available in C#:

If the evaluation of the expression in parentheses after the when keyword evaluates to true, the exception is caught. Otherwise, the catch block is ignored.

This allows them to be laid over a catch block for the same type of exception:

In the example above, the first catch block is only executed if an exception of type SqlException in the value of the Number property is 2. Otherwise you run the next block.

It is considered acceptable and commonplace “abuse” of exceptions filters with side effects such as logging.

WARNING: Exception filters are executed in the context of the throw and not in the context of the catch because the stack hasn’t been unwound yet.

‘await’ in ‘catch’ and ‘finally’ blocks

C# 5 was not allowed to use the await keyword in catch and finally blocks because, at the time of implementation of the async-functionality await, the team thought that this would not be possible to implement. But later, they found out that, after all, it was not impossible, although it can lead to some interesting semantics.

It becomes possible to write code like this:

Improvements in Overload Resolution Methods

They were introduced some improvements in overload method resolution (See: http://bc-programming.com/blogs/2015/06/c-6-features-improved-overload-resolution/) in order to make it more intuitive to determine how the compiler decides which method overloading to use.

Where it is more noticeable is the choice of method overloading when receiving nullable value types, or when moving a group of methods (rather than a lambda) methods for overload receiving delegates.

Resources:

  1. New Language Features in C# 6
  2. C# 7 Work List of Features
  3. Interpolated Strings (C# and Visual Basic Reference)