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