Sunny Ahuwanya's Blog

Mostly notes on .NET and C#

Exploring C# 6

C# 6, the latest version of the C# programming language, is here and is (almost) feature complete.
In this post, we'll explore the new language features interactively. Let's start with the most fun feature.

Using Static

using static System.Console;
using static System.Tuple;

WriteLine(Create("C#", 6));

//Hit Go to run this code


Using static lets you import static members of types directly into scope.
In the above example, WriteLine is imported from the System.Console type and Create is imported from the System.Tuple type.
This feature is useful if you frequently use a group of static methods.

Using static also lets you import static members of non static types, including structs and enums.

using static System.String;
using static System.DateTime;
using static System.DayOfWeek;

Concat("Is today a ", Monday, " or a ", Tuesday, " ? : ", Now.DayOfWeek == Monday || Now.DayOfWeek == Tuesday)


In the example above, Concat is imported from the non-static System.String class, Monday and Tuesday are imported from the System.DayOfWeek enum and Now is imported from System.DateTime.

When you import a namespace, you bring into scope all visible types and all extension methods in the namespace.
With using static, you can now import extension methods from a single class.

using static System.Xml.Linq.XDocument;
using static System.Xml.Linq.Extensions;

var cities = Parse(
@"<Root>
    <State name='California'>
        <City name='Los Angeles' />
        <City name='San Francisco' />
    </State>
    <State name='New York'>
        <City name='New York City' />    
    </State>
</Root>").Descendants("City");

cities.AncestorsAndSelf()


In the example above, Parse is imported from the XDocument class and AncestorsAndSelf is imported from the System.Linq.Extensions class.
This code is able to use the extension methods in the System.Linq.Extensions class without bringing all types in the System.Xml.Linq namespace into scope.

 

nameof expressions

Occasionally, you need to supply the name of a variable, type, type member or other symbol in your code. A common example is when throwing an ArgumentNullException and the invalid parameter needs to be identified.

In the past, you would have to use reflection to find the name, which is a bit tedious, or hardcode the name, which is error prone.
nameof expressions validate and extract the name of the symbol as a string literal at compile time.

void MyMethod(string input)
{
    Console.WriteLine(nameof(input));    
    if (input == null) throw new ArgumentNullException(nameof(input));
}

MyMethod(null);


In the above example we're able to throw an ArgumentNullException specifying the name of the erring parameter without hardcoding its name.
If the parameter name is changed in the future via a refactoring operation, the new name will be reflected in the thrown exception.

nameof expressions work on different kinds of code symbols.

string s  = "a string";

//On an instance variable of a type
Console.WriteLine( nameof(s) );
//On a dotted member of the instance
Console.WriteLine( nameof(s.Length.ToString) );
//On a dotted member of a type
Console.WriteLine( nameof(Console.Write) );

In each case in the example above, the name of the last identifier in the expression is extracted.

 

Null-conditional operators

Null-conditional operators let you evaluate members only if a null resolution was not reached in the evaluation chain.

string s = null;

int? length = s?.Length;
Console.WriteLine(length == null);

//In an indexer/element access
char? firstChar = s?[0];
Console.WriteLine(firstChar == null);


As seen in the example above, this eliminates the need to write code that checks for null on each member access.
Null-conditional operators can be chained.

string GetFirstItemInLowerCase(IEnumerable<string> collection)
{
    return collection?.FirstOrDefault()?.ToLower();
    
    /*
    //Pre C# 6 code:
    if(collection == null || collection.FirstOrDefault() == null) return null;
    return collection.First().ToLower();
    */    
}

GetFirstItemInLowerCase(new string[0])


The method above returns the first item in a collection of strings in lower case.
It returns null if the collection is null or the collection is empty or the first item in the collection is null.

Null-conditional operators make the code more succinct than the pre-C# 6 code.

The null-conditional operators work with the null coalescing operator ( ?? ) to provide a default result when the expression evaluates to null.

string[] arr = null; //new string[]{"paper","pen"};
int length = arr?[0]?.Length ?? -1;

length


In the example above, length is -1 because the null-coalescing operator provides a default value.
Replace arr = null; with arr = new string[]{"paper","pen"} to get the length of the first element.

 

String Interpolation

String interpolation lets you format strings in an easier and safer manner.

var now = DateTime.Now;

var msg = $"Have a happy {now.DayOfWeek}! Today is day {now.DayOfYear} of {now.Year}";
/* //Same as:
var msg = String.Format("Have a happy {0}! Today is day {1} of {2}", now.DayOfWeek, now.DayOfYear, now.Year);
*/

msg


In the example above, the now.DayOfWeek, now.DayOfYear and now.Year expressions are placed exactly where they will appear.
You can now safely modify the string without worrying if the arguments at the end line up properly as is the case when String.Format is used.

To insert braces in an interpolated string, specify two opening braces ( {{ ) for an opening brace or two closing braces ( }} ) for a closing brace.
Interpolated strings can span multiple lines if the $ symbol is followed by an @ symbol, keeping in style with regular C# multiline string literals.

var val1 = "interpolated";
var val2 = "multiple";

var str = $@"

This is an {val1} string
spanning {val2} lines.

Braces can be escaped like {{this}}.

";

str


Expressions in interpolated holes can be complex. Alignment and format specifiers can also be specified, just as with String.Format, as shown in the following example.

//Standard currency numeric format
var amount = $"{-123.456:C2}";

//Guid, left aligned
var guid1 = $"|{Guid.NewGuid(),-50}|";

//Guid, right aligned with format specifier
var guid2 = $"|{Guid.NewGuid(),50:N}|";

//Custom DateTime format
var str = $"Yesterday was {DateTime.Now - TimeSpan.FromDays(1):dddd, d-MMM-yyyy.}";

Console.WriteLine(amount);
Console.WriteLine(guid1);
Console.WriteLine(guid2);
Console.WriteLine(str);


Interpolated strings are formatted using the current culture.
In a later release, interpolated strings will implement the IFormattable interface (but will be implicitly convertible to a string). This will allow interpolated strings to be formatted in other cultures.

var invariant = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0:C}", 20);

var current = string.Format(System.Globalization.CultureInfo.CurrentCulture, "{0:C}", 20);

var interpolated = $"{20:C}";

Console.WriteLine(invariant);
Console.WriteLine(current);
Console.WriteLine(interpolated);

/* 
//NOTE: Will work in a future update.
//Formats interpolated string in Japanese culture

public string InJapaneseCulture(IFormattable formattable)
{
    return formattable.ToString(null, new System.Globalization.CultureInfo("ja-JP"));
}

InJapaneseCulture($"{20:C}") //Displays "¥20"
*/

 

Parameterless constructors in structs

Structs can now have parameterless constructors.

struct Point
{
    public int X {get; set;}
    public int Y {get; set;}
    public int Z {get; set;}
    
    public Point()
    {
        X = -1;
        Y = -2;
        Z = -3;
    }
}

new Point()


This feature is useful if you need to create a new instance of a struct with a parameterless constructor that initializes fields to values that are not the default type value.

Consider a generic method that returns a list of a generic type.

public List<T> CreateList<T>(int count) where T:new()
{
    List<T> list = new List<T>();
    for(int i = 0; i < count; i++)
    {
        list.Add(new T());
    }
    
    return list;
}

struct Point
{
    public int X {get; set;}
    public int Y {get; set;}
    public int Z {get; set;}
    
    public Point()
    {
        X = -1; Y = -2; Z = -3;
    }
}

CreateList<Point>(3)


Calling CreateList<Point>(50) in older versions of C# will return a list of 50 Point objects, however, each point will be invalid since the X,Y and Z properties will default to zero.
With C# 6, all points in the list will have the desired initial values for X, Y and Z.

There is a bug in this feature and it doesn't currently work quite as expected.

 

Expression bodied methods and properties

This feature lets methods and properties have bodies that are expressions instead of statement blocks.

public string Hexify(int i) => i.ToString("X");

/* //Same as:
public string Hexify(int i)
{
    return i.ToString("X");
}

*/

Hexify(7012)


The body of the Hexify method above is expressed as an expression, like a lambda expression, and is equivalent to the commented out regular style body.
Bodies of getter-only properties and indexers can also be expressed as expressions.

class Order
{
    private Guid _id = Guid.NewGuid();
    
    public string Id => _id.ToString();
    
    /* //Same as:
    public string Id
    {
        get
        {
            return _id.ToString();
        }
    } */
}

new Order().Id

Auto-Property Initializers

Auto properties can now be initialized with an initial value.

class Order
{
    public string Instructions {get; set;} = "None";
    
    public string Id {get;} = Guid.NewGuid().ToString();
}

new Order()


A new instance of the Order class above will have the Instructions property set to "None" and the Id property set to a new Guid string.
It's important to note that the default values are initialized once on the underlying backing field. This means each call to the Id property will return the value on the backing field and not a brand new Guid.

Getter-only auto-properties can also be assigned an initial value in the type's constructor.

class Order
{
    public string Instructions {get; set;} // Okay in C# 5
    public string Id {get;} // Only okay in C# 5 if class is abstract or extern
    
    public Order()
    {
        Instructions = "None"; // Okay in C# 5
        Id =  Guid.NewGuid().ToString(); // New in C# 6
    }
}

new Order()

 

Index Initializers

This feature lets you initialize the indices of a newly created object as part of the object initializer.

var birthdays = new Dictionary<string, DateTime> {
    ["Turing"] = new DateTime(1912, 6, 23),
    ["Lovelace"] = new DateTime(1815, 12, 10),
    ["Neumann"] = new DateTime(1903, 12, 28)
};

birthdays


This feature makes it easy to compose hierarchical objects, suitable for JSON serialization.

var locations = new Dictionary<string, object>
{
    ["Canada"] = null,
    ["Mexico"] = "Distrito Federal",
    ["United States"] = new Dictionary<string, object>
    {
        ["Illinois"] = null,
        ["New York"] = "New York City",
        ["California"] = new string[]{"Los Angeles", "San Jose"}
    }
};

locations


The object above is easier to read and compose than having to assign values to indices in separate statements after each new dictionary is created.

 

Exception filters

This feature allows a catch block to execute only when some criteria is met.

int state = 3;
try
{
    throw new InvalidOperationException();
}
catch (InvalidOperationException) when (state == 2)
{
    Console.WriteLine("Exception thrown! State = 2");
}
catch (InvalidOperationException) when (state == 3)
{
    Console.WriteLine("Exception thrown! State = 3");
}
catch
{
    Console.WriteLine("Exception thrown!");
}


In the example above, the first catch block is not executed because state isn't 2, even though that catch block handles the InvalidOperationException.
The exception processing logic then moves to the next catch block which is executed because it handles the exception type and the filter evaluates to true.

This is cleaner than having a big catch block that checks the criteria within the block. An added benefit of using exception filters instead of catching and rethrowing exceptions is that it doesn't change the stack -- useful when debugging the source of an exception.

 

Conclusion

C# 6 is a nice improvement that addresses many of the annoyances C# developers face daily.
I applaud the direction in which the C# team is taking the language. I really like using static, nameof expressions, null conditional operators and string interpolation.
These features will undoubtedly make life easier for many developers.

Loading