C# 6.0

What's new, what's good

Syntactic Sugar

  • Auto-property initializers
  • Static imports
  • Index initializers
  • Expression-bodied members
  • String interpolation

Auto-property initializers

  • Directly initializes the backing field; doesn’t go through the setter
  • Initializers are executed in order as written, just as – and along with – field initializers
public string First { get; set; } = "Jane";
public string Last { get; set; } = "Doe";

Static imports

  • Import all accessible static members of a type, making them available without qualification in subsequent code

     
  • Also lets you directly specify named values of an enum type
using static System.Console;
using static System.Math;

class Program
{
    static void Main()
    {
        WriteLine(Sqrt(3*3 + 4*4)); 
    }
}
using static System.DayOfWeek;

//...

WriteLine(Friday - Monday); 

Index initializers

  • Set values to keys through any indexer that the new object exposes
var numbers = new Dictionary<int, string> 
{
    [7] = "seven",
    [9] = "nine",
    [13] = "thirteen"
};

Expression-bodied members

  • Definition bodies of function-like members can now be written as lambda expressions
  • The effect is exactly the same as if the member had a block body with a single return statement

Expression-bodied members

  • Methods as well as user-defined operators and conversions can be given an expression body by using the => arrow
public int Plus(int x, int y) => x + y;

public Point Move(int dx, int dy)     
    => new Point(x + dx, y + dy); 

public static Foo operator +(Foo a, Foo b)
    => a.Add(b);

public static implicit operator string(Person p) 
    => p.First + " " + p.Last;

Method-like members

Expression-bodied members

  • Expression bodies can be used to write get-only properties and indexers
  • No get keyword necessary; it is implied by the expression-body syntax
public string Name => First + " " + Last;

public Person this[long id] => _people.Find(id); 

public static decimal BaseRate => 0.08m;

Property-like members

String interpolation

  • Code expressions in string literals with $ prefix
var then = String.Format("{0} is {1} years old",
            p.Name, 
            p.Age);
var now = $"{p.Name} is {p.Age} years old";

var sum = var s = $"{x} + {y} is {x + y}";
var s = $"{p.Name,20} is {p.Age:D3} years old"
//oops, wrong order:
var oops = String.Format("Name: {0}, {1} {2}",
            p.First, 
            p.Last,
            p.Middle);

//more readable:
var ok = $"Name: {p.Last}, {p.First} {p.Middle}";
  • Supports alignment and format specifiers just like string.Format
  • Unlike string.Format, no need to match indexed placeholders to parameter order/count

String interpolation

  • The blocks trigger Intellisense and can be any valid C# expression...
var s = $"{p.Name} is {p.Age}" + 
        $" year{(p.Age == 1 ? "" : "s")}" +
         " old";
  • ...but, of course, you don't want to let it get out of hand
var s = $"{p.Name} is " + 
  $"{(Enum.Parse(typeof(DateIncrement, incr)))}" +
  $"{((p?.GetAge() ?? 0) == 1 ? "" : "s")}" +
  " old";

New Behavior

  • await in catch/finally
  • Exception filters
  • The nameof operator
  • Improved overload resolution

await in catch/finally

  • It's not just try anymore
try
{
    // You could do this.
    res = await Resource.OpenAsync(...);       
} 
catch(ResourceException e)
{
    // Now you can do this...
    await Resource.LogAsync(res, e);         
}
finally
{
    // ...and this.
    if (res != null) await res.CloseAsync(); 
}

Exception filters

  • Exception filters are preferable to catching and rethrowing because they leave the stack unharmed
try 
{
    //...
}
catch (MyException e) when (myfilter(e))
{
    //...
}

Exception filters

try 
{
    //... 
} 
catch (Exception e) when (Log(e)) 
{
    //this part never runs
}

//...

private static bool Log(Exception e) 
{ 
    //log it, and then:

    return false; 
}
  • A common and accepted form of "abuse" is to use exception filters for side effects; e.g. logging
  • Can inspect an exception in passing without intercepting it
  • In those cases, the filter will often be a call to a false-returning helper function which executes the side effects

The nameof operator

if (x == null) 
{
    throw new ArgumentNullException(nameof(x));
}

// prints "ZipCode":
WriteLine(nameof(person.Address.ZipCode)); 
  • Using string literals for this purpose is simple, but error prone
  • The compiler checks that you have something of the given name, and Visual Studio knows what it refers to, so navigation and refactoring will work

A Little of Both

  • Read-only auto-properties
  • Extension Add in collection initializers
  • Null-conditional operators

(Feels like syntactic sugar, but there's more under the surface)

Read-only auto-properties

  • Auto-props can now be declared without a setter
public string First { get; } = "Jane";
public class Customer
{
    public string Name { get; }
    public Customer(string first, string last)
    {
        Name = first + " " + last;
    }
}
  • Can be assigned to in the declaring type’s constructor body
  • This means types can now have auto-props and still be immutable
  • Backing field is implicitly declared as readonly

Extension Add methods in collection initializers

  • When C# first got collection initializers, the Add methods that get called couldn’t be extension methods. This has been fixed.
  • Already allowed in VB

Null-conditional operators

  • Allow "lifted" member access
  • Nulls propagate up the member-access chain
  • Works for the . operator (e.g. foo?.bar) and indexers (e.g. foo?[bar])
// null if customers is null
int? length = customers?.Length; 

// null if customers is null
Customer first = customers?[0];

// 0 if customers is null
int length = customers?.Length ?? 0;

// null if customers is null
int? first = customers?[0].Orders.Count();

// null if Orders is null
// NullReferenceException if customers is null
int? first = customers[0].Orders?.Count();

// a is...
// null    - if b is null
// null    - if b[0] is null
// null    - if b[0].c is null
// (int?)d - if d is an int
int? a = b?[0]?.c?.d;

Null-conditional operators

  • Not valid for method calls (e.g. foo?())...
  • ...but you don't need that in C# because the type signature is known at compile time
// does not compile
int? count = customers.Count?();

//but that's fine, since this will never be true
customers.Count == null
  • Delegates aren't known at compile time—but don't worry, there's a workaround
// does not compile
var result = myDelegate?(x);

// works just fine
var result = myDelegate?.Invoke(x);

Null-conditional operators

This is more than syntactic sugar. There's no perfect equivalent that I'm aware of in previous versions of C#.

// C# 6.0 version
var a1 = b()?.c()?.d()?.e;

// C# < 6 version
var a2 = b() != null && 
         b().c() != null && 
         b().c().d() != null 
       ? b().c().d().e
       : null;

In the example, both a1 and a2 receive the same value, but the C# 5 version evaluated b() four times, c() three times, and d() twice. The C# 6 version called each method exactly once.

C# 6.0: Overview

By Justin Morgan

C# 6.0: Overview

  • 353