New Surprises
If the earlier features I mentioned were expected, other new features are not exactly obvious extensions from previous versions of the language. Let's start with expression-bodied members. In C# 6.0, you can create members of your type by using expressions, instead of the more heavyweight method syntax. These members can be property accessors, indexer accessors, or methods. They must be expressions, which limits their complexity. It's just a lightweight syntax that enables simpler syntax for smaller and simpler member definitions. For example, a Person type that returns the full name would need this definition in C# 5.0 (or earlier):
public class Person { public string FirstName { get; set; } public string LastName { get; set; } public string FullName { get { return FirstName + " " + LastName; } } public Address HomeAddress { get; set; } }
Now the FullName property can be written as follows:
public string FullName => FirstName + " " + LastName;
The familiar lambda arrow token introduces the member, and the right side of the arrow defines its behavior. There's a limit on complexity, however, because the right side of the arrow must be an expression, not a statement (or series of statements). For example, this is not valid:
public string FullName => { return FirstName + " " + LastName; }
Because statements are not valid, you don't need to worry that this feature will be abused for complicated methods that include extensive branching or looping logic.
You can also create methods that use the expression-bodied member syntax. I could write a FullName method that returns the full name formatted as either "last, first" or "first last" based on the value of an argument:
public string FullName(bool lastNameFirst) => (lastNameFirst) ? LastName + ", " + FirstName : FirstName + " " + LastName;
Notice that I must use the conditional expression instead of an if/else statement because expression-bodied members must be expressions, not statements (or compound statements). Yes, the introduction of the conditional expression introduces some complexity to this method, but it's still manageable. And yes, we could chain as many conditionals as we wanted in an expression-bodied member, but we don't need new features to write unmaintainable code. Instead, we should exercise discretion and use the new features where they increase readability.
The next feature I want to discuss is auto-implemented read-only properties. Suppose I want to modify the Person definition I showed earlier; now I want to make it immutable. I'd need to add a constructor to create the new object and to initialize all its fields. Using C# 6.0, I can write less code and still produce an immutable type using auto-implemented properties:
public Person(string firstName, string lastName) { this.FirstName = firstName; this.LastName = lastName; } public string FirstName { get;}; public string LastName { get; };
Finally, the C# 6.0 team has added syntax to make it as easy to initialize dictionary collections as it has been to initialize sequence collections (like List<T>). You declare a dictionary, and you can set any key value, in parentheses, to any value. The example below shows a dictionary of web error messages, but you can do this with any kind of key/value pair structure. It could be used to create a JSON-like syntax for different objects.
Dictionary<int, string> messages = new Dictionary<int, string> { [404] = "Endpoint not found", [302] = "Endpoint has moved. Forever. But it did leave a forwarding address", [500] = "The server has decided not to play along today" };