What's New in C# 6

The 6.0 release of C# contained many features that improve productivity for developers. Features in this release include:

The overall effect of these features is that you write more concise code that is also more readable. The syntax contains less ceremony for many common practices. It's easier to see the design intent with less ceremony. Learn these features well, and you'll be more productive, write more readable code, and concentrate more on your core features than on the constructs of the language.

The remainder of this topic provides details on each of these features.

Auto-Property enhancements

The syntax for automatically implemented properties (usually referred to as 'auto-properties') made it very easy to create properties that had simple get and set accessors:

public string FirstName { get; set; }
public string LastName { get; set; }

However, this simple syntax limited the kinds of designs you could support using auto-properties. C# 6 improves the auto-properties capabilities so that you can use them in more scenarios. You won't need to fall back on the more verbose syntax of declaring and manipulating the backing field by hand so often.

The new syntax addresses scenarios for read-only properties, and for initializing the variable storage behind an auto-property.

Read-only auto-properties

Read-only auto-properties provide a more concise syntax to create immutable types. The closest you could get to immutable types in earlier versions of C# was to declare private setters:

public string FirstName { get; private set; }
public string LastName { get; private set; }

Using this syntax, the compiler doesn't ensure that the type really is immutable. It only enforces that the FirstName and LastName properties are not modified from any code outside the class.

Read-only auto-properties enable true read-only behavior. You declare the auto-property with only a get accessor:

public string FirstName { get; }
public string LastName { get;  }

The FirstName and LastName properties can be set only in the body of a constructor:

public Student(string firstName, string lastName)
{
    if (IsNullOrWhiteSpace(lastName))
        throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));
    FirstName = firstName;
    LastName = lastName;
}

Trying to set LastName in another method generates a CS0200 compilation error:

public class Student
{
    public string LastName { get;  }

    public void ChangeName(string newLastName)
    {
        // Generates CS0200: Property or indexer cannot be assigned to -- it is read only
        LastName = newLastName;
    }
}

This feature enables true language support for creating immutable types and using the more concise and convenient auto-property syntax.

Auto-Property Initializers

Auto-Property Initializers let you declare the initial value for an auto-property as part of the property declaration. In earlier versions, these properties would need to have setters and you would need to use that setter to initialize the data storage used by the backing field. Consider this class for a student that contains the name and a list of the student's grades:

public Student(string firstName, string lastName)
{
    FirstName = firstName;
    LastName = lastName;
}

As this class grows, you may include other constructors. Each constructor needs to initialize this field, or you'll introduce errors.

C# 6 enables you to assign an initial value for the storage used by an auto-property in the auto-property declaration:

public ICollection<double> Grades { get; } = new List<double>();

The Grades member is initialized where it is declared. That makes it easier to perform the initialization exactly once. The initialization is part of the property declaration, making it easier to equate the storage allocation with public interface for Student objects.

Property Initializers can be used with read/write properties as well as read-only properties, as shown here.

public Standing YearInSchool { get; set; } = Standing.Freshman;

Expression-bodied function members

The body of a lot of members that we write consist of only one statement that can be represented as an expression. You can reduce that syntax by writing an expression-bodied member instead. It works for methods and read-only properties. For example, an override of ToString() is often a great candidate:

public override string ToString() => $"{LastName}, {FirstName}";

You can also use expression-bodied members in read-only properties as well:

public string FullName => $"{FirstName} {LastName}";

using static

The using static enhancement enables you to import the static methods of a single class. Previously, the using statement imported all types in a namespace.

Often we use a class' static methods throughout our code. Repeatedly typing the class name can obscure the meaning of your code. A common example is when you write classes that perform many numeric calculations. Your code will be littered with Sin, Sqrt and other calls to different methods in the Math class. The new using static syntax can make these classes much cleaner to read. You specify the class you're using:

using static System.Math;

And now, you can use any static method in the Math class without qualifying the Math class. The Math class is a great use case for this feature because it does not contain any instance methods. You can also use using static to import a class' static methods for a class that has both static and instance methods. One of the most useful examples is String:

using static System.String;

Note

You must use the fully qualified class name, System.String in a static using statement. You cannot use the string keyword instead.

You can now call static methods defined in the String class without qualifying those methods as members of that class:

if (IsNullOrWhiteSpace(lastName))
    throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));

The static using feature and extension methods interact in interesting ways, and the language design included some rules that specifically address those interactions. The goal is to minimize any chances of breaking changes in existing codebases, including yours.

Extension methods are only in scope when called using the extension method invocation syntax, not when called as a static method. You'll often see this in LINQ queries. You can import the LINQ pattern by importing Enumerable.

using static System.Linq.Enumerable;

This imports all the methods in the Enumerable class. However, the extension methods are only in scope when called as extension methods. They are not in scope if they are called using the static method syntax:

public bool MakesDeansList()
{
    return Grades.All(g => g > 3.5) && Grades.Any();
    // Code below generates CS0103: 
    // The name 'All' does not exist in the current context.
    //return All(Grades, g => g > 3.5) && Grades.Any();
}

This decision is because extension methods are typically called using extension method invocation expressions. In the rare case where they are called using the static method call syntax it is to resolve ambiguity. Requiring the class name as part of the invocation seems wise.

There's one last feature of static using. The static using directive also imports any nested types. That enables you to reference any nested types without qualification.

Null-conditional operators

Null values complicate code. You need to check every access of variables to ensure you are not dereferencing null. The null conditional operator makes those checks much easier and fluid.

Simply replace the member access . with ?.:

var first = person?.FirstName; 

In the preceding example, the variable first is assigned null if the person object is null. Otherwise, it gets assigned the value of the FirstName property. Most importantly, the ?. means that this line of code does not generate a NullReferenceException when the person variable is null. Instead, it short-circuits and produces null.

Also, note that this expression returns a string, regardless of the value of person. In the case of short circuiting, the null value returned is typed to match the full expression.

You can often use this construct with the null coalescing operator to assign default values when one of the properties are null:

first = person?.FirstName ?? "Unspecified";

The right hand side operand of the ?. operator is not limited to properties or fields. You can also use it to conditionally invoke methods. The most common use of member functions with the null conditional operator is to safely invoke delegates (or event handlers) that may be null. You'll do this by calling the delegate's Invoke method using the ?. operator to access the member. You can see an example in the
delegate patterns topic.

The rules of the ?. operator ensure that the left-hand side of the operator is evaluated only once. This is important and enables many idioms, including the example using event handlers. Let's start with the event handler usage. In previous versions of C#, you were encouraged to write code like this:

var handler = this.SomethingHappened;
if (handler != null)
    handler(this, eventArgs);

This was preferred over a simpler syntax:

// Not recommended
if (this.SomethingHappened != null)
    this.SomethingHappened(this, eventArgs);

Important

The preceding example introduces a race condition. The SomethingHappened event may have subscribers when checked against null, and those subscribers may have been removed before the event is raised. That would cause a NullReferenceException to be thrown.

In this second version, the SomethingHappened event handler might be non-null when tested, but if other code removes a handler, it could still be null when the event handler was called.

The compiler generates code for the ?. operator that ensures the left side (this.SomethingHappened) of the ?. expression is evaluated once, and the result is cached:

// preferred in C# 6:
this.SomethingHappened?.Invoke(this, eventArgs);

Ensuring that the left side is evaluated only once also enables you to use any expression, including method calls, on the left side of the ?. Even if these have side-effects, they are evaluated once, so the side effects occur only once. You can see an example in our content on events.

String Interpolation

C# 6 contains new syntax for composing strings from a format string and expressions that can be evaluated to produce other string values.

Traditionally, you needed to use positional parameters in a method like string.Format:

public string FullName
{
    get
    {
        return string.Format("{0} {1}", FirstName, LastName);
    }
}

With C# 6, the new string interpolation feature enables you to embed the expressions in the format string. Simple preface the string with $:

public string FullName => $"{FirstName} {LastName}";

This initial example used variable expressions for the substituted expressions. You can expand on this syntax to use any expression. For example, you could compute a student's grade point average as part of the interpolation:

public string GetFormattedGradePoint() =>
    $"Name: {LastName}, {FirstName}. G.P.A: {Grades.Average()}";

Running the preceding example, you would find that the output for Grades.Average() might have more decimal places than you would like. The string interpolation syntax supports all the format strings available using earlier formatting methods. You add the format strings inside the braces. Add a : following the expression to format:

public string GetGradePointPercentage() =>
    $"Name: {LastName}, {FirstName}. G.P.A: {Grades.Average():F2}";

The preceding line of code will format the value for Grades.Average() as a floating-point number with two decimal places.

The : is always interpreted as the separator between the expression being formatted and the format string. This can introduce problems when your expression uses a : in another way, such as a conditional operator:

public string GetGradePointPercentages() =>
    $"Name: {LastName}, {FirstName}. G.P.A: {Grades.Any() ? Grades.Average() : double.NaN:F2}";

In the preceding example, the : is parsed as the beginning of the format string, not part of the conditional operator. In all cases where this happens, you can surround the expression with parentheses to force the compiler to interpret the expression as you intend:

public string GetGradePointPercentages() =>
    $"Name: {LastName}, {FirstName}. G.P.A: {(Grades.Any() ? Grades.Average() : double.NaN):F2}";

There aren't any limitations on the expressions you can place between the braces. You can execute a complex LINQ query inside an interpolated string to perform computations and display the result:

public string GetAllGrades() =>
    $@"All Grades: {Grades.OrderByDescending(g => g)
    .Select(s => s.ToString("F2")).Aggregate((partial, element) => $"{partial}, {element}")}";

You can see from this sample that you can even nest a string interpolation expression inside another string interpolation expression. This example is very likely more complex than you would want in production code. Rather, it is illustrative of the breadth of the feature. Any C# expression can be placed between the curly braces of an interpolated string.

String interpolation and specific cultures

All the examples shown in the preceding section will format the strings using the current culture and language on the machine where the code executes. Often you may need to format the string produced using a specific culture. The object produced from a string interpolation is a type that has an implicit conversion to either String or FormattableString.

The FormattableString type contains the format string, and the results of evaluating the arguments before converting them to strings. You can use public methods of FormattableString to specify the culture when formatting a string. For example, the following will produce a string using German as the language and culture. (It will use the ',' character for the decimal separator, and the '.' character as the thousands separator.)

FormattableString str = $"Average grade is {s.Grades.Average()}";
var gradeStr = string.Format(null, 
    System.Globalization.CultureInfo.CreateSpecificCulture("de-de"),
    str.GetFormat(), str.GetArguments());

Note

The preceding example is not supported in .NET Core version 1.0.1. It is only supported in the .NET Framework.

In general, string interpolation expressions produce strings as their output. However, when you want greater control over the culture used to format the string, you can specify a specific output. If this is a capability you often need, you can create convenience methods, as extension methods, to enable easy formatting with specific cultures.

Exception Filters

Another new feature in C# 6 is exception filters. Exception Filters are clauses that determine when a given catch clause should be applied. If the expression used for an exception filter evaluates to true, the catch clause performs its normal processing on an exception. If the expression evaluates to false, then the catch clause is skipped.

One use is to examine information about an exception to determine if a catch clause can process the exception:

public static async Task<string> MakeRequest()
{ 
    var client = new System.Net.Http.HttpClient();
    var streamTask = client.GetStringAsync("https://localHost:10000");
    try {
        var responseText = await streamTask;
        return responseText;
    } catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))
    {
        return "Site Moved";
    }
}

The code generated by exception filters provides better information about an exception that is thrown and not processed. Before exception filters were added to the language, you would need to create code like the following:

public static async Task<string> MakeRequest()
{ 
    var client = new System.Net.Http.HttpClient();
    var streamTask = client.GetStringAsync("https://localHost:10000");
    try {
        var responseText = await streamTask;
        return responseText;
    } catch (System.Net.Http.HttpRequestException e)
    {
        if (e.Message.Contains("301"))
            return "Site Moved";
        else
            throw;
    }
}

The point where the exception is thrown changes between these two examples. In the previous code, where a throw clause is used, any stack trace analysis or examination of crash dumps will show that the exception was thrown from the throw statement in your catch clause. The actual exception object will contain the original call stack, but all other information about any variables in the call stack between this throw point and the location of the original throw point has been lost.

Contrast that with how the code using an exception filter is processed: the exception filter expression evaluates to false. Therefore, execution never enters the catch clause. Because the catch clause does not execute, no stack unwinding takes place. That means the original throw location is preserved for any debugging activities that would take place later.

Whenever you need to evaluate fields or properties of an exception, instead of relying solely on the exception type, use an exception filter to preserve more debugging information.

Another recommended pattern with exception filters is to use them for logging routines. This usage also leverages the manner in which the exception throw point is preserved when an exception filter evaluates to false.

A logging method would be a method whose argument is the exception that unconditionally returns false:

public static bool LogException(this Exception e)
{
    Console.Error.WriteLine($"Exceptions happen: {e}");
    return false;
} 

Whenever you want to log an exception, you can add a catch clause, and use this method as the exception filter:

public void MethodThatFailsSometimes()
{
    try {
        PerformFailingOperation();
    } catch (Exception e) when (e.LogException())
    {
        // This is never reached!
    }
} 

The exceptions are never caught, because the LogException method always returns false. That always false exception filter means that you can place this logging handler before any other exception handlers:

public void MethodThatFailsButHasRecoveryPath()
{
    try {
        PerformFailingOperation();
    } catch (Exception e) when (e.LogException())
    {
        // This is never reached!
    }
    catch (RecoverableException ex)
    {
        Console.WriteLine(ex.ToString());
        // This can still catch the more specific
        // exception because the exception filter
        // above always returns false.
        // Perform recovery here 
    }
}

The preceding example highlights a very important facet of exception filters. The exception filters enable scenarios where a more general exception catch clause may appear before a more specific one. It's also possible to have the same exception type appear in multiple catch clauses:

public static async Task<string> MakeRequestWithNotModifiedSupport()
{ 
    var client = new System.Net.Http.HttpClient();
    var streamTask = client.GetStringAsync("https://localHost:10000");
    try {
        var responseText = await streamTask;
        return responseText;
    } catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))
    {
        return "Site Moved";
    } catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("304"))
    {
        return "Use the Cache";
    }
}

Another recommended pattern helps prevent catch clauses from processing exceptions when a debugger is attached. This technique enables you to run an application with the debugger, and stop execution when an exception is thrown.

In your code, add an exception filter so that any recovery code executes only when a debugger is not attached:

public void MethodThatFailsWhenDebuggerIsNotAttached()
{
    try {
        PerformFailingOperation();
    } catch (Exception e) when (e.LogException())
    {
        // This is never reached!
    }
    catch (RecoverableException ex) when (!System.Diagnostics.Debugger.IsAttached)
    {
        Console.WriteLine(ex.ToString());
        // Only catch exceptions when a debugger is not attached.
        // Otherwise, this should stop in the debugger. 
    }
}

After adding this in code, you set your debugger to break on all unhandled exceptions. Run the program under the debugger, and the debugger breaks whenever PerformFailingOperation() throws a RecoverableException. The debugger breaks your program, because the catch clause won't be executed due to the false-returning exception filter.

nameof Expressions

The nameof expression evaluates to the name of a symbol. It's a great way to get tools working whenever you need the name of a variable, a property, or a member field.

One of the most common uses for nameof is to provide the name of a symbol that caused an exception:

if (IsNullOrWhiteSpace(lastName))
    throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));

Another use is with XAML based applications that implement the INotifyPropertyChanged interface:

public string LastName
{
    get { return lastName; }
    set
    {
        if (value != lastName)
        {
            lastName = value;
            PropertyChanged?.Invoke(this, 
                new PropertyChangedEventArgs(nameof(LastName)));
        }
    }
}
private string lastName;

The advantage of using the nameof operator over a constant string is that tools can understand the symbol. If you use refactoring tools to rename the symbol, it will rename it in the nameof expression. Constant strings don't have that advantage. Try it yourself in your favorite editor: rename a variable, and any nameof expressions will update as well.

The nameof expression produces the unqualified name of its argument (LastName in the previous examples) even if you use the fully qualified name for the argument:

public string FirstName
{
    get { return firstName; }
    set
    {
        if (value != firstName)
        {
            firstName = value;
            PropertyChanged?.Invoke(this, 
                new PropertyChangedEventArgs(nameof(UXComponents.ViewModel.FirstName)));
        }
    }
}
private string firstName;

This nameof expression produces FirstName, not UXComponents.ViewModel.FirstName.

Await in Catch and Finally blocks

C# 5 had several limitations around where you could place await expressions. One of those has been removed in C# 6. You can now use await in catch or finally expressions.

The addition of await expressions in catch and finally blocks may appear to complicate how those are processed. Let's add an example to discuss how this appears. In any async method, you can use an await expression in a finally clause.

With C# 6, you can also await in catch expressions. This is most often used with logging scenarios:

public static async Task<string> MakeRequestAndLogFailures()
{ 
    await logMethodEntrance();
    var client = new System.Net.Http.HttpClient();
    var streamTask = client.GetStringAsync("https://localHost:10000");
    try {
        var responseText = await streamTask;
        return responseText;
    } catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))
    {
        await logError("Recovered from redirect", e);
        return "Site Moved";
    }
    finally
    {
        await logMethodExit();
        client.Dispose();
    }
}

The implementation details for adding await support inside catch and finally clauses ensures that the behavior is consistent with the behavior for synchronous code. When code executed in a catch or finally clause throws, execution looks for a suitable catch clause in the next surrounding block. If there was a current exception, that exception is lost. The same happens with awaited expressions in catch and finally clauses: a suitable catch is searched for, and the current exception, if any, is lost.

Note

This behavior is the reason it's recommended to write catch and finally clauses carefully, to avoid introducing new exceptions.

Index Initializers

Index Initializers is one of two features that make collection initializers more consistent. In earlier releases of C#, you could use collection initializers only with sequence style collections:

private List<string> messages = new List<string> 
{
    "Page not Found",
    "Page moved, but left a forwarding address.",
    "The web server can't come out to play today."
};

Now, you can also use them with Dictionary<TKey,TValue> collections and similar types:

private Dictionary<int, string> webErrors = new Dictionary<int, string>
{
    [404] = "Page not Found",
    [302] = "Page moved, but left a forwarding address.",
    [500] = "The web server can't come out to play today."
};

This feature means that associative containers can be initialized using syntax similar to what's been in place for sequence containers for several versions.

Extension Add methods in collection initializers

Another feature that makes collection initialization easier is the ability to use an extension method for the Add method. This feature was added for parity with Visual Basic.

The feature is most useful when you have a custom collection class that has a method with a different name to semantically add new items.

For example, consider a collection of students like this:

public class Enrollment : IEnumerable<Student>
{
    private List<Student> allStudents = new List<Student>();

    public void Enroll(Student s)
    {
        allStudents.Add(s);
    }

    public IEnumerator<Student> GetEnumerator()
    {
        return ((IEnumerable<Student>)allStudents).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return ((IEnumerable<Student>)allStudents).GetEnumerator();
    }
}

The Enroll method adds a student. But it doesn't follow the Add pattern. In previous versions of C#, you could not use collection initializers with an Enrollment object:

var classList = new Enrollment()
{
    new Student("Lessie", "Crosby"),
    new Student("Vicki", "Petty"),
    new Student("Ofelia", "Hobbs"),
    new Student("Leah", "Kinney"),
    new Student("Alton", "Stoker"),
    new Student("Luella", "Ferrell"),
    new Student("Marcy", "Riggs"),
    new Student("Ida", "Bean"),
    new Student("Ollie", "Cottle"),
    new Student("Tommy", "Broadnax"),
    new Student("Jody", "Yates"),
    new Student("Marguerite", "Dawson"),
    new Student("Francisca", "Barnett"),
    new Student("Arlene", "Velasquez"),
    new Student("Jodi", "Green"),
    new Student("Fran", "Mosley"),
    new Student("Taylor", "Nesmith"),
    new Student("Ernesto", "Greathouse"),
    new Student("Margret", "Albert"),
    new Student("Pansy", "House"),
    new Student("Sharon", "Byrd"),
    new Student("Keith", "Roldan"),
    new Student("Martha", "Miranda"),
    new Student("Kari", "Campos"),
    new Student("Muriel", "Middleton"),
    new Student("Georgette", "Jarvis"),
    new Student("Pam", "Boyle"),
    new Student("Deena", "Travis"),
    new Student("Cary", "Totten"),
    new Student("Althea", "Goodwin")
};

Now you can, but only if you create an extension method that maps Add to Enroll:

public static class StudentExtensions
{
    public static void Add(this Enrollment e, Student s) => e.Enroll(s);
}

What you are doing with this feature is to map whatever method adds items to a collection to a method named Add by creating an extension method:

public class Enrollment : IEnumerable<Student>
{
    private List<Student> allStudents = new List<Student>();

    public void Enroll(Student s)
    {
        allStudents.Add(s);
    }

    public IEnumerator<Student> GetEnumerator()
    {
        return ((IEnumerable<Student>)allStudents).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return ((IEnumerable<Student>)allStudents).GetEnumerator();
    }
}
public class ClassList
{
    public Enrollment CreateEnrollment()
    {
        var classList = new Enrollment()
        {
            new Student("Lessie", "Crosby"),
            new Student("Vicki", "Petty"),
            new Student("Ofelia", "Hobbs"),
            new Student("Leah", "Kinney"),
            new Student("Alton", "Stoker"),
            new Student("Luella", "Ferrell"),
            new Student("Marcy", "Riggs"),
            new Student("Ida", "Bean"),
            new Student("Ollie", "Cottle"),
            new Student("Tommy", "Broadnax"),
            new Student("Jody", "Yates"),
            new Student("Marguerite", "Dawson"),
            new Student("Francisca", "Barnett"),
            new Student("Arlene", "Velasquez"),
            new Student("Jodi", "Green"),
            new Student("Fran", "Mosley"),
            new Student("Taylor", "Nesmith"),
            new Student("Ernesto", "Greathouse"),
            new Student("Margret", "Albert"),
            new Student("Pansy", "House"),
            new Student("Sharon", "Byrd"),
            new Student("Keith", "Roldan"),
            new Student("Martha", "Miranda"),
            new Student("Kari", "Campos"),
            new Student("Muriel", "Middleton"),
            new Student("Georgette", "Jarvis"),
            new Student("Pam", "Boyle"),
            new Student("Deena", "Travis"),
            new Student("Cary", "Totten"),
            new Student("Althea", "Goodwin")
        };
        return classList;
    }           
}

public static class StudentExtensions
{
    public static void Add(this Enrollment e, Student s) => e.Enroll(s);
}

Improved overload resolution

This last feature is one you probably won't notice. There were constructs where the previous version of the C# compiler may have found some method calls involving lambda expressions ambiguous. Consider this method:

static Task DoThings() 
{
     return Task.FromResult(0); 
}

In earlier versions of C#, calling that method using the method group syntax would fail:

Task.Run(DoThings); 

The earlier compiler could not distinguish correctly between Task.Run(Action) and Task.Run(Func<Task>()). In previous versions, you'd need to use a lambda expression as an argument:

Task.Run(() => DoThings());

The C# 6 compiler correctly determines that Task.Run(Func<Task>()) is a better choice.