05.Feb.15

Vladimir Milev

Vladimir Milev

A practical analysis of the new features of C# 6: Part 2

This blog post is second in a series about my analysis on the new functionality in C# 6. If you haven't done so yet, check out part 1 as well.

Static using statements

The easiest way to explain this feature is to give you a short code example. Let's say we have the following function to calculate Pythagoras theorem:

C# 5.0

private double ComputePythagoras(Point start, Point end)
{
	return Math.Sqrt(Math.Pow(end.X - start.X, 2) + Math.Pow(end.Y - start.Y, 2));
}
This is pretty straightforward. Notice the repetition of the Math type? In the newest version we can get rid of it:

C# 6.0

using static System.Math;

/* some code omitted for brevity */

private double ComputePythagoras(Point start, Point end)
{
	return Sqrt(Pow(end.X - start.X, 2) + Pow(end.Y - start.Y, 2));
}
So C# 6 basically allows us to call static methods as if they were part of our class by declaring the following using statement: "using static FullyQualifiedTypeName;". For those of you following C# evolution closely, you will notice that there is a slight change in this syntax. In an earlier preview the syntax was "using System.Math;", however it was recently changed to "using static System.Math;"

Analysis

This one definitely goes down in my book as "syntactic sugar", which is not necessarily bad. It is a bit early for me to have a really definitive stand on this one as I haven't really seen it used a lot, but I think this should be used with caution. Yes, it makes your source code a bit more concise, however, there is always the danger of making it less readable. Taking away the qualified type name out of the code is taking out context, for which we should compensate in some other way. In the example I have given this feature works well, because my method and it's code are very domain specific and it is very difficult for any confusion to arise. The context there is given by the method name and the rest of the code itself. However, should we have a longer method with less precise purpose which makes use of multiple static classes I think this feature would bring in more damage (in the form of confusion) than help. My advice - use sparingly and carefully in places where your context is very clear from the rest of the code and don't do it with many static types in the same scope because you might end up with less readable code.

Null-conditional operator

Almost anyone who has had some experience with programming in C# is familiar with the dreaded NullReferenceException. Null checking is an integral part of coding in C# because of the existence of reference types. There is no escaping it. Luckily it is quite simple to do, though somewhat verbose:

C# 5.0:

/// <summary>
/// Returns the last submitted order for our customer if any.
/// </summary>
/// <param name="customer">The customer</param>
/// <returns>The last order or null if the customer has no orders</returns>
private Order GetLastOrderForCustomer(Customer customer)
{
	if (customer != null)
	{
		if (customer.Orders != null)
		{
			return customer.Orders.LastOrDefault();
		}
	}
	return null;
}
In the code above the type Customer is a reference type, as well as it's Orders collection. Therefore if we want to follow good defensive programming practices we are required to perform two null checks before returning a result. We don't need to handle null cases in any special way because the contract of this method doesn't require it. If we are unable to provide a last record we should simply return null. Such code is quite common and these null checks sometimes become quite tedious. This is where the new null-conditional operator comes into play:

C# 6.0

private Order GetLastOrderForCustomer(Customer customer)
{
	return customer?.Orders?.LastOrDefault();
}
The ?. operator turns the old method into a one-liner. The neat thing is that this operator can be chained and works really well with nested types. If it detects a null value somewhere along the chain it simply short-circuits the expression as we expect it to and returns null. This is not all, however. The null-conditional operator comes in a second form as well - for accessing indexed member objects:
//Retrieve a specific order by unique ID
return customer?.Orders?["009a7e26-e5ac-4c3c-b242-dd04545be24c"];
So far so good. If the invocation of a member which returns a reference type is null then the operator will return null as well. But what is the value returned if the rightmost member is a value type?
private int GetOrdersCountForCustomer(Customer customer)
{
	//CS0266	Cannot implicitly convert type 'int?' to 'int'.
	return customer?.Orders?.Count;
}
That's correct: the compiler will expand the primitive type to a nullable type. Makes sense since you expect the operator to return either the requested type or null.

Analysis

Null checks are a fact of life for C# developers. The introduction of the conditional-null operator streamlines a large chunk of that code. As long as you don't need to perform special actions such as throwing your own exceptions you can take advantage of it to write cleaner, more concise and even more readable code. The one caveat of this new feature is it's convenience. It may tempt you to write statements that return null values as opposed to raising exceptions and handling null references in a more specific way. In some cases we really don't need that and the conditional-null operator is just fine - for all others you should continue using if statements to handle more complex scenarios.

NameOf Expressions

Very often we have the need to refer to an identifier name as a string. So far the solution is not very elegant. You have to use a "magic string" that contains the identifier name, usually defined as a constant if you need to access it from multiple places. There is of course no compile-time check ensuring that someone who refactors your code also updates this string so you have to do it manually. This is a somewhat tedious process and many developers can forget it resulting in subtle bugs every now and then. Let's consider the following case - a simple definition of a dependency property:

C# 5.0

public string FirstName
{
	get { return (string)GetValue(FirstNameProperty); }
	set { SetValue(FirstNameProperty, value); }
}

public static readonly DependencyProperty FirstNameProperty =
	DependencyProperty.Register("FirstName", typeof(string), typeof(MainWindow), new PropertyMetadata(0));
Notice the call to DependencyProperty.Register("FirstName", ...) on the last line. If you are a WPF/Silverlight/WindowsRuntime developer this code will be very familiar to you. Something else that will probably also be familiar to you is that if you Refactor->Rename the FirstName property and you forget to change the string your code will stop working properly. This whole mess has been elegantly resolved in the new version of C# using the "nameof" keyword:

C# 6.0

public string FirstName
{
	get { return (string)GetValue(FirstNameProperty); }
	set { SetValue(FirstNameProperty, value); }
}

public static readonly DependencyProperty FirstNameProperty =
	DependencyProperty.Register(nameof(FirstName), typeof(string), typeof(MainWindow), new PropertyMetadata(0));
Our call to DependencyProperty.Register(nameof(FirstName), ...) has now changed to include the actual identifier. During compile time, the C# compiler will replace the nameof expression with the string representation of the property name. The important part is that we are no longer using a "magic string" so now when you do a Refactor->Rename our code will not be broken anymore. Another nifty usage of this keyword is with the argument related exceptions such as: ArgumentNullException and ArgumentOutOfRangeException. Here is one way I imagine using this:

C# 6.0

private int GetOrdersCountForCustomer(Customer customer)
{
	if (customer == null)
		throw new ArgumentNullException(nameof(customer));
	
        // Rest of method code.
}

Analysis

The function of this new operator is very straightforward - provide a string representation of your identifier. It works for class names, property names, method names etc. The guidance here is extremely simple - use it as much as possible whenever you need to. I have given you two common scenarios where this operator is useful, but I am sure you can think of many more. The impact of this feature is not to be underestimated. It will reduce the amount of code you write  and will make it more durable and resistant to refactoring errors. This time there is absolutely nothing not to like about this new part of the language. I am definitely looking forward to using this in the near future. This wraps up things for part 2 of this series. As always, feel free to share your opinion in the comments! Stay tuned for part 3!  

If you found this article useful, could you hit any of the share buttons below, so that others can benefit from it, too? Thanks!

Need consulting on this topic?

Yes No