In this blog post I'm going to demystify how C# 6 features work under the hood. I'm going to introduce a feature first and then inspect how it is implemented using kind of Reflector tool called ILSpy.
Auto-Property Initializers
This feature allows us to set a value for a property at the time it is declared in a class:
class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName => $"{FirstName} {LastName}";
public DateTime FirstOrderDate { get; } = DateTime.Today;
public Address Address = new Address();
}
Back in the days we could do it in the constructor:
class Customer
{
public Customer()
{
FirstOrderDate = DateTime.Today;
}
...
}
One cool feature about auto-property initializers is that if the property has no setter it is truly immutable, meaning You, as a developer can't assign something to the property outside the constructor.
Let's see how it is implemented. Well, FirstOrderDate has only getter with an automatically generated backing field, in addition to that the property is marked as [CompilerGenerated]. It's worth noticing that the constructor is assigning directly to the backing field. Trying to assign to the FirstOrderDate from any member except the constructor will throw a compiler error: Error CS0200 Property or indexer 'Customer.FirstOrderDate' cannot be assigned to -- it is read only.
String Interpolation/Expression-bodied members
Let's move along and look at a feature called String Interpolation. Ruby had this feature for a long time and at last, it is implemented in the .NET world. Basically, interpolation is a nice syntactic sugar for String.Format:
public string FullName => $"{FirstName} {LastName}";
=> In the code above means expressioned-body syntax and again it is yet another syntactic sugare which allows to reduce get {...} notation:
Dictionary Initializers
Is controversial feature in my opinion, which allows You to assign a value directly to a key:
private static Dictionary<int, Customer> GetCustomers()
{
return new Dictionary<int, Customer>
{
[1] = new Customer
{
FirstName = "John",
LastName = "Smith"
},
[2] = new Customer
{
FirstName = "Bill",
LastName = "Stein"
},
[3] = null
};
}
The idea under the hood is very simple - initialize a dictionary and assign it to a variable, use that variable to initialize key/value pairs later on:
I personally see no huge difference between Dictionary/Collection initializers syntax:
Dictionary<int, StudentName> students = new Dictionary<int, StudentName>()
{
{ 111, new StudentName {FirstName="Sachin", LastName="Karnik", ID=211}},
{ 112, new StudentName {FirstName="Dina", LastName="Salimzianova", ID=317}},
{ 113, new StudentName {FirstName="Andy", LastName="Ruth", ID=198}}
};
Null-conditional operator
Imagine Your Customer class encapsulates Address, which encapsulates City. So potentially this line of code
customer.Address.City
may break if either customer is null or Address is null. More robust notation would be:
if (null != customer)
{
if ( null != customer.Address)
{
return customer.Address.City;
}
}
But there is a lot of ceremony with that code. Null conditional ?. has much cleaner and easier syntax:
customer?.Address?.City
Under the hood it is still implemented via ifs:
Before C#6 I used Monads.NET library, which provides With extention method and solve similar problem via lambdas:
customer.With(c=>c.Address).With(a=>a.City);
Static Imports
Allows to reduce fully qualified notation for static members. Having
using static System.Math;
allows to replace Math.PI (where PI is a static member of Math class) notation with just PI:
private static void TestStaticUsing()
{
var radius = 10;
//Instead of Math.PI/Math.Pow
var result = PI * Pow(radius, 2);
}
Again it is yet another syntactic sugar and under the hood Math.PI notation is being used:
Nameof
Another cool feature is a nameof operator, which obtains a string name of a variable. One of the uses case is member names logging. You can refactor Your code, change the names of the variables/members and Your logging logic will be intact (no more acrobatics with changing variable names in strings while refactoring, etc.). Here is an example:
Logger.Log(nameof(customer), customer);
where Logger is:
internal class Logger
{
internal static void Log(string variable, Customer customer, [CallerMemberName] string method = null)
{
Console.WriteLine($"Error in {method}; variable name {variable}; value : {customer}");
}
}
Under the hood it looks like nameof and CallerMemberName are being replaced with string literals at compile time:
Useful links
GitHub repo with blog's samples
C# 6.0 Features That Every ASP.NET Developer Should Know About
C# 6 features that help you write cleaner code
New Features in C# 6 and Visual Studio 2015 course by Kathleen Dollard
Useful tools
While working on this research I opened a live coding plugin for Visual Studio called Alive, which is definitely worth sharing. It also uses Roslyn compiler, which makes new language features in C# 6 possible. I do think that in the foreseeable future the number of tools like this, which heavily use rich code analysis APIs provided by Roslyn, will increase.
I hope this research is helpful and would like to hear your feedback as well.