C# 6.0 Features

Introduction

This sixth iteration of the C# language is provided by the Roslyn compiler. This compiler came out with version 4.6 of the .NET Framework, however it can generate code in a backward compatible manner to allow targeting earlier framework versions. C# version 6 code can be compiled in a fully backwards compatible manner to .NET 4.0. It can also be used for earlier frameworks, however some features that require additional framework support may not function correctly.

Remarks

The sixth version of C# was released July 2015 alongside Visual Studio 2015 and .NET 4.6.

As well as adding some new language features it includes a complete rewrite of the compiler. Previously csc.exe was a native Win32 application written in C++, with C# 6 it is now a .NET managed application written in C#. This rewrite was known as project "Roslyn" and the code is now open source and available on GitHub.

Auto-property initializers

Introduction

Properties can be initialized with the = operator after the closing }. The Coordinate class below shows the available options for initializing a property:

6.0
public class Coordinate
{ 
    public int X { get; set; } = 34; // get or set auto-property with initializer

    public int Y { get; } = 89;      // read-only auto-property with initializer              
}

Accessors With Different Visibility

You can initialize auto-properties that have different visibility on their accessors. Here’s an example with a protected setter:

    public string Name { get; protected set; } = "Cheeze";

The accessor can also be internal, internal protected, or private.


Read-Only Properties

In addition to flexibility with visibility, you can also initialize read-only auto-properties. Here’s an example:

    public List<string> Ingredients { get; } = 
        new List<string> { "dough", "sauce", "cheese" };

This example also shows how to initialize a property with a complex type. Also, auto-properties can’t be write-only, so that also precludes write-only initialization.


Old style (pre C# 6.0)

Before C# 6, this required much more verbose code. We were using one extra variable called backing property for the property to give default value or to initialize the public property like below,

6.0
public class Coordinate
{
    private int _x = 34;
    public int X { get { return _x; } set { _x = value; } }

    private readonly int _y = 89;
    public int Y { get { return _y; } }
    
    private readonly int _z;
    public int Z { get { return _z; } }

    public Coordinate()
    {
        _z = 42;
    }
}

Note: Before C# 6.0, you could still initialize read and write auto implemented properties (properties with a getter and a setter) from within the constructor, but you could not initialize the property inline with its declaration

View Demo


Usage

Initializers must evaluate to static expressions, just like field initializers. If you need to reference non-static members, you can either initialize properties in constructors like before, or use expression-bodied properties. Non-static expressions, like the one below (commented out), will generate a compiler error:

// public decimal X { get; set; } = InitMe();  // generates compiler error

decimal InitMe() { return 4m; }

But static methods can be used to initialize auto-properties:

public class Rectangle
{
    public double Length { get; set; } = 1;
    public double Width { get; set; } = 1;
    public double Area { get; set; } = CalculateArea(1, 1);

    public static double CalculateArea(double length, double width)
    {
        return length * width;
    }
}

This method can also be applied to properties with different level of accessors:

public short Type { get; private set; } = 15;

The auto-property initializer allows assignment of properties directly within their declaration. For read-only properties, it takes care of all the requirements required to ensure the property is immutable. Consider, for example, the FingerPrint class in the following example:

public class FingerPrint
{
  public DateTime TimeStamp { get; } = DateTime.UtcNow;

  public string User { get; } =
    System.Security.Principal.WindowsPrincipal.Current.Identity.Name;

  public string Process { get; } =
    System.Diagnostics.Process.GetCurrentProcess().ProcessName;
}

View Demo


Cautionary notes

Take care to not confuse auto-property or field initializers with similar-looking expression-body methods which make use of => as opposed to =, and fields which do not include { get; }.

For example, each of the following declarations are different.

public class UserGroupDto
{
    // Read-only auto-property with initializer:       
    public ICollection<UserDto> Users1 { get; } = new HashSet<UserDto>();
    
    // Read-write field with initializer:
    public ICollection<UserDto> Users2 = new HashSet<UserDto>();

    // Read-only auto-property with expression body:
    public ICollection<UserDto> Users3 => new HashSet<UserDto>();
}

Missing { get; } in the property declaration results in a public field. Both read-only auto-property Users1 and read-write field Users2 are initialized only once, but a public field allows changing collection instance from outside the class, which is usually undesirable. Changing a read-only auto-property with expression body to read-only property with initializer requires not only removing > from =>, but adding { get; }.

The different symbol (=> instead of =) in Users3 results in each access to the property returning a new instance of the HashSet<UserDto> which, while valid C# (from the compiler's point of view) is unlikely to be the desired behavior when used for a collection member.

The above code is equivalent to:

public class UserGroupDto
{
    // This is a property returning the same instance
    // which was created when the UserGroupDto was instantiated.
    private ICollection<UserDto> _users1 = new HashSet<UserDto>();
    public ICollection<UserDto> Users1 { get { return _users1; } }

    // This is a field returning the same instance
    // which was created when the UserGroupDto was instantiated.
    public virtual ICollection<UserDto> Users2 = new HashSet<UserDto>();

    // This is a property which returns a new HashSet<UserDto> as
    // an ICollection<UserDto> on each call to it.
    public ICollection<UserDto> Users3 { get { return new HashSet<UserDto>(); } }
}

Await in catch and finally

It is possible to use await expression to apply await operator to Tasks or Task(Of TResult) in the catch and finally blocks in C#6.

It was not possible to use the await expression in the catch and finally blocks in earlier versions due to compiler limitations. C#6 makes awaiting async tasks a lot easier by allowing the await expression.

try
{
    //since C#5
    await service.InitializeAsync();
} 
catch (Exception e)
{
    //since C#6
    await logger.LogAsync(e);
}
finally
{
    //since C#6
    await service.CloseAsync();
}

It was required in C# 5 to use a bool or declare an Exception outside the try catch to perform async operations. This method is shown in the following example:

bool error = false;
Exception ex = null;

try
{
    // Since C#5
    await service.InitializeAsync();
} 
catch (Exception e)
{
    // Declare bool or place exception inside variable
    error = true;
    ex = e;
}

// If you don't use the exception
if (error)
{
    // Handle async task
}

// If want to use information from the exception
if (ex != null)
{
    await logger.LogAsync(e);
}    

// Close the service, since this isn't possible in the finally
await service.CloseAsync();

Disable Warnings Enhancements

In C# 5.0 and earlier the developer could only suppress warnings by number. With the introduction of Roslyn Analyzers, C# needs a way to disable warnings issued from specific libraries. With C# 6.0 the pragma directive can suppress warnings by name.

Before:

#pragma warning disable 0501

C# 6.0:

#pragma warning disable CS0501

Exception filters

Exception filters give developers the ability to add a condition (in the form of a boolean expression) to a catch block, allowing the catch to execute only if the condition evaluates to true.

Exception filters allow the propagation of debug information in the original exception, where as using an if statement inside a catch block and re-throwing the exception stops the propagation of debug information in the original exception. With exception filters, the exception continues to propagate upwards in the call stack unless the condition is met. As a result, exception filters make the debugging experience much easier. Instead of stopping on the throw statement, the debugger will stop on the statement throwing the exception, with the current state and all local variables preserved. Crash dumps are affected in a similar way.

Exception filters have been supported by the CLR since the beginning and they've been accessible from VB.NET and F# for over a decade by exposing a part of the CLR's exception handling model. Only after the release of C# 6.0 has the functionality also been available for C# developers.


Using exception filters

Exception filters are utilized by appending a when clause to the catch expression. It is possible to use any expression returning a bool in a when clause (except await). The declared Exception variable ex is accessible from within the when clause:

var SqlErrorToIgnore = 123;
try
{
    DoSQLOperations();
}
catch (SqlException ex) when (ex.Number != SqlErrorToIgnore)
{
    throw new Exception("An error occurred accessing the database", ex);
}

Multiple catch blocks with when clauses may be combined. The first when clause returning true will cause the exception to be caught. Its catch block will be entered, while the other catch clauses will be ignored (their when clauses won't be evaluated). For example:

try
{ ... }
catch (Exception ex) when (someCondition) //If someCondition evaluates to true,
                                          //the rest of the catches are ignored.
{ ... }
catch (NotImplementedException ex) when (someMethod()) //someMethod() will only run if
                                                       //someCondition evaluates to false
{ ... }
catch(Exception ex) // If both when clauses evaluate to false
{ ... }

Risky when clause

Caution

It can be risky to use exception filters: when an Exception is thrown from within the when clause, the Exception from the when clause is ignored and is treated as false. This approach allows developers to write when clause without taking care of invalid cases.

The following example illustrates such a scenario:

public static void Main()
{
    int a = 7;
    int b = 0;
    try
    {
        DoSomethingThatMightFail();
    }
    catch (Exception ex) when (a / b == 0)
    {
        // This block is never reached because a / b throws an ignored
        // DivideByZeroException which is treated as false.
    }
    catch (Exception ex)
    {
        // This block is reached since the DivideByZeroException in the 
        // previous when clause is ignored.
    }
}

public static void DoSomethingThatMightFail()
{
    // This will always throw an ArgumentNullException.
    Type.GetType(null);
}

View Demo

Note that exception filters avoid the confusing line number problems associated with using throw when failing code is within the same function. For example in this case the line number is reported as 6 instead of 3:

1. int a = 0, b = 0;
2. try {
3.     int c = a / b;
4. }
5. catch (DivideByZeroException) {
6.     throw;
7. }

The exception line number is reported as 6 because the error was caught and re-thrown with the throw statement on line 6.

The same does not happen with exception filters:

1. int a = 0, b = 0;
2. try {
3.     int c = a / b;
4. }
5. catch (DivideByZeroException) when (a != 0) {
6.     throw;
7. }

In this example a is 0 then catch clause is ignored but 3 is reported as line number. This is because they do not unwind the stack. More specifically, the exception is not caught on line 5 because a in fact does equal 0 and thus there is no opportunity for the exception to be re-thrown on line 6 because line 6 does not execute.


Logging as a side effect

Method calls in the condition can cause side effects, so exception filters can be used to run code on exceptions without catching them. A common example that takes advantage of this is a Log method that always returns false. This allows tracing log information while debugging without the need to re-throw the exception.

Be aware that while this seems to be a comfortable way of logging, it can be risky, especially if 3rd party logging assemblies are used. These might throw exceptions while logging in non-obvious situations that may not be detected easily (see Risky when(...) clause above).

try
{
    DoSomethingThatMightFail(s);
}
catch (Exception ex) when (Log(ex, "An error occurred"))
{
    // This catch block will never be reached
}

// ...

static bool Log(Exception ex, string message, params object[] args)
{
    Debug.Print(message, args);
    return false;
}

View Demo

The common approach in previous versions of C# was to log and re-throw the exception.

6.0
try
{
    DoSomethingThatMightFail(s);
}
catch (Exception ex)
{
     Log(ex, "An error occurred");
     throw;
}

// ...

static void Log(Exception ex, string message, params object[] args)
{
    Debug.Print(message, args);
}

View Demo


The finally block

The finally block executes every time whether the exception is thrown or not. One subtlety with expressions in when is exception filters are executed further up the stack before entering the inner finally blocks. This can cause unexpected results and behaviors when code attempts to modify global state (like the current thread's user or culture) and set it back in a finally block.

Example: finally block

private static bool Flag = false;

static void Main(string[] args)
{
    Console.WriteLine("Start");
    try
    {
        SomeOperation();
    }
    catch (Exception) when (EvaluatesTo())
    {
        Console.WriteLine("Catch");
    }
    finally
    {
        Console.WriteLine("Outer Finally");
    }
}

private static bool EvaluatesTo()
{
    Console.WriteLine($"EvaluatesTo: {Flag}");
    return true;
}

private static void SomeOperation()
{
    try
    {
        Flag = true;
        throw new Exception("Boom");
    }
    finally
    {
        Flag = false;
        Console.WriteLine("Inner Finally");
    }
}

Produced Output:

Start
EvaluatesTo: True
Inner Finally
Catch
Outer Finally

View Demo

In the example above, if the method SomeOperation does not wish to "leak" the global state changes to caller's when clauses, it should also contain a catch block to modify the state. For example:

private static void SomeOperation()
{
    try
    {
        Flag = true;
        throw new Exception("Boom");
    }
    catch
    {
       Flag = false;
       throw;
    }
    finally
    {
        Flag = false;
        Console.WriteLine("Inner Finally");
    }
}

It is also common to see IDisposable helper classes leveraging the semantics of using blocks to achieve the same goal, as IDisposable.Dispose will always be called before an exception called within a using block starts bubbling up the stack.

Expression-bodied function members

Expression-bodied function members allow the use of lambda expressions as member bodies. For simple members, it can result in cleaner and more readable code.

Expression-bodied functions can be used for properties, indexers, methods, and operators.


Properties

public decimal TotalPrice => BasePrice + Taxes;

Is equivalent to:

public decimal TotalPrice
{
    get
    {
        return BasePrice + Taxes;
    }
}

When an expression-bodied function is used with a property, the property is implemented as a getter-only property.

View Demo


Indexers

public object this[string key] => dictionary[key];

Is equivalent to:

public object this[string key]
{
    get
    {
        return dictionary[key];
    }
}

Methods

static int Multiply(int a, int b) => a * b;

Is equivalent to:

static int Multiply(int a, int b)
{
    return a * b;
}

Which can also be used with void methods:

public void Dispose() => resource?.Dispose();

An override of ToString could be added to the Pair<T> class:

public override string ToString() => $"{First}, {Second}";

Additionally, this simplistic approach works with the override keyword:

public class Foo
{
    public int Bar { get; }

    public string override ToString() => $"Bar: {Bar}";
}

Operators

This also can be used by operators:

public class Land
{
    public double Area { get; set; }

    public static Land operator +(Land first, Land second) =>
        new Land { Area = first.Area + second.Area };
}

Limitations

Expression-bodied function members have some limitations. They can't contain block statements and any other statements that contain blocks: if, switch, for, foreach, while, do, try, etc.

Some if statements can be replaced with ternary operators. Some for and foreach statements can be converted to LINQ queries, for example:

IEnumerable<string> Digits
{
    get
    {
        for (int i = 0; i < 10; i++)
            yield return i.ToString();
    }
}
IEnumerable<string> Digits => Enumerable.Range(0, 10).Select(i => i.ToString());

In all other cases, the old syntax for function members can be used.

Expression-bodied function members can contain async/await, but it's often redundant:

async Task<int> Foo() => await Bar();  

Can be replaced with:

Task<int> Foo() => Bar();

Improved overload resolution

Following snippet shows an example of passing a method group (as opposed to a lambda) when a delegate is expected. Overload resolution will now resolve this instead of raising an ambiguous overload error due to the ability of C# 6 to check the return type of the method that was passed.

using System;
public class Program
{
    public static void Main()
    {
        Overloaded(DoSomething);
    }

    static void Overloaded(Action action)
    {
       Console.WriteLine("overload with action called");
    }

    static void Overloaded(Func<int> function)
    {
       Console.WriteLine("overload with Func<int> called");
    }

    static int DoSomething()
    {
        Console.WriteLine(0);
        return 0;
    }
}

Results:

6.0

Output

overload with Func<int> called

View Demo

5.0

Error

error CS0121: The call is ambiguous between the following methods or properties: 'Program.Overloaded(System.Action)' and 'Program.Overloaded(System.Func)'

C# 6 can also handle well the following case of exact matching for lambda expressions which would have resulted in an error in C# 5.

using System;

class Program
{
    static void Foo(Func<Func<long>> func) {}
    static void Foo(Func<Func<int>> func) {}

    static void Main()
    {
        Foo(() => () => 7);
    }
}

Index initializers

Index initializers make it possible to create and initialize objects with indexes at the same time.

This makes initializing Dictionaries very easy:

var dict = new Dictionary<string, int>()
{
    ["foo"] = 34,
    ["bar"] = 42
};

Any object that has an indexed getter or setter can be used with this syntax:

class Program
{
    public class MyClassWithIndexer
    {
        public int this[string index]
        {
            set
            {
                Console.WriteLine($"Index: {index}, value: {value}");
            }
        }
    }

    public static void Main()
    {
        var x = new MyClassWithIndexer()
        {
            ["foo"] = 34,
            ["bar"] = 42
        };

        Console.ReadKey();
    }
}

Output:

Index: foo, value: 34
Index: bar, value: 42

View Demo

If the class has multiple indexers it is possible to assign them all in a single group of statements:

class Program
{
    public class MyClassWithIndexer
    {
        public int this[string index]
        {
            set
            {
                Console.WriteLine($"Index: {index}, value: {value}");
            }
        }
        public string this[int index]
        {
            set
            {
                Console.WriteLine($"Index: {index}, value: {value}");
            }
        }
    }

    public static void Main()
    {
        var x = new MyClassWithIndexer()
        {
            ["foo"] = 34,
            ["bar"] = 42,
            [10] = "Ten",
            [42] = "Meaning of life"
        };
    }
}

Output:

Index: foo, value: 34
Index: bar, value: 42
Index: 10, value: Ten
Index: 42, value: Meaning of life

It should be noted that the indexer set accessor might behave differently compared to an Add method (used in collection initializers).

For example:

var d = new Dictionary<string, int>
{
    ["foo"] = 34,
    ["foo"] = 42,
}; // does not throw, second value overwrites the first one

versus:

var d = new Dictionary<string, int>
{
    { "foo", 34 },
    { "foo", 42 },
}; // run-time ArgumentException: An item with the same key has already been added.

Minor changes and bugfixes

Parentheses are now forbidden around named parameters. The following compiles in C#5, but not C#6

5.0
Console.WriteLine((value: 23));

Operands of is and as are no longer allowed to be method groups. The following compiles in C#5, but not C#6

5.0
var result = "".Any is byte;

The native compiler allowed this (although it did show a warning), and in fact didn’t even check extension method compatibility, allowing crazy things like 1.Any is string or IDisposable.Dispose is object.

See this reference for updates on changes.

Null propagation

The ?. operator and ?[...] operator are called the null-conditional operator. It is also sometimes referred to by other names such as the safe navigation operator.

This is useful, because if the . (member accessor) operator is applied to an expression that evaluates to null, the program will throw a NullReferenceException. If the developer instead uses the ?. (null-conditional) operator, the expression will evaluate to null instead of throwing an exception.

Note that if the ?. operator is used and the expression is non-null, ?. and . are equivalent.


Basics

var teacherName = classroom.GetTeacher().Name;
// throws NullReferenceException if GetTeacher() returns null

View Demo

If the classroom does not have a teacher, GetTeacher() may return null. When it is null and the Name property is accessed, a NullReferenceException will be thrown.

If we modify this statement to use the ?. syntax, the result of the entire expression will be null:

var teacherName = classroom.GetTeacher()?.Name;
// teacherName is null if GetTeacher() returns null

View Demo

Subsequently, if classroom could also be null, we could also write this statement as:

var teacherName = classroom?.GetTeacher()?.Name;
// teacherName is null if GetTeacher() returns null OR classroom is null

View Demo

This is an example of short-circuiting: When any conditional access operation using the null-conditional operator evaluates to null, the entire expression evaluates to null immediately, without processing the rest of the chain.

When the terminal member of an expression containing the null-conditional operator is of a value type, the expression evaluates to a Nullable<T> of that type and so cannot be used as a direct replacement for the expression without ?..

bool hasCertification = classroom.GetTeacher().HasCertification;
// compiles without error but may throw a NullReferenceException at runtime

bool hasCertification = classroom?.GetTeacher()?.HasCertification;
// compile time error: implicit conversion from bool? to bool not allowed

bool? hasCertification = classroom?.GetTeacher()?.HasCertification;
// works just fine, hasCertification will be null if any part of the chain is null

bool hasCertification = classroom?.GetTeacher()?.HasCertification.GetValueOrDefault();
// must extract value from nullable to assign to a value type variable

Use with the Null-Coalescing Operator (??)

You can combine the null-conditional operator with the Null-coalescing Operator (??) to return a default value if the expression resolves to null. Using our example above:

var teacherName = classroom?.GetTeacher()?.Name ?? "No Name";
// teacherName will be "No Name" when GetTeacher() 
// returns null OR classroom is null OR Name is null

Use with Indexers

The null-conditional operator can be used with indexers:

var firstStudentName = classroom?.Students?[0]?.Name;

In the above example:

  • The first ?. ensures that classroom is not null.
  • The second ? ensures that the entire Students collection is not null.
  • The third ?. after the indexer ensures that the [0] indexer did not return a null object. It should be noted that this operation can still throw an IndexOutOfRangeException.

Use with void Functions

Null-conditional operator can also be used with void functions. However in this case, the statement will not evaluate to null. It will just prevent a NullReferenceException.

List<string> list = null;
list?.Add("hi");          // Does not evaluate to null

Use with Event Invocation

Assuming the following event definition:

private event EventArgs OnCompleted;

When invoking an event, traditionally, it is best practice to check if the event is null in case no subscribers are present:

var handler = OnCompleted;
if (handler != null)
{
    handler(EventArgs.Empty);
}

Since the null-conditional operator has been introduced, the invocation can be reduced to a single line:

OnCompleted?.Invoke(EventArgs.Empty);

Limitations

Null-conditional operator produces rvalue, not lvalue, that is, it cannot be used for property assignment, event subscription etc. For example, the following code will not work:

// Error: The left-hand side of an assignment must be a variable, property or indexer
Process.GetProcessById(1337)?.EnableRaisingEvents = true;
// Error: The event can only appear on the left hand side of += or -=
Process.GetProcessById(1337)?.Exited += OnProcessExited;

Gotchas

Note that:

int? nameLength = person?.Name.Length;    // safe if 'person' is null

is not the same as:

int? nameLength = (person?.Name).Length;  // avoid this

because the former corresponds to:

int? nameLength = person != null ? (int?)person.Name.Length : null;

and the latter corresponds to:

int? nameLength = (person != null ? person.Name : null).Length;

Despite ternary operator ?: is used here for explaining the difference between two cases, these operators are not equivalent. This can be easily demonstrated with the following example:

void Main()
{
    var foo = new Foo();
    Console.WriteLine("Null propagation");
    Console.WriteLine(foo.Bar?.Length);

    Console.WriteLine("Ternary");
    Console.WriteLine(foo.Bar != null ? foo.Bar.Length : (int?)null);
}

class Foo
{
    public string Bar
    {
        get
        {
            Console.WriteLine("I was read");
            return string.Empty;
        }
    }
}

Which outputs:

Null propagation
I was read
0
Ternary
I was read
I was read
0

View Demo

To avoid multiple invocations equivalent would be:

var interimResult = foo.Bar;
Console.WriteLine(interimResult != null ? interimResult.Length : (int?)null);

And this difference somewhat explains why null propagation operator is not yet supported in expression trees.

Operator nameof

The nameof operator returns the name of a code element as a string. This is useful when throwing exceptions related to method arguments and also when implementing INotifyPropertyChanged.

public string SayHello(string greeted)
{
    if (greeted == null)
        throw new ArgumentNullException(nameof(greeted));
    
    Console.WriteLine("Hello, " + greeted);
}

The nameof operator is evaluated at compile time and changes the expression into a string literal. This is also useful for strings that are named after their member that exposes them. Consider the following:

public static class Strings
{
    public const string Foo = nameof(Foo); // Rather than Foo = "Foo"
    public const string Bar = nameof(Bar); // Rather than Bar = "Bar"
}

Since nameof expressions are compile-time constants, they can be used in attributes, case labels, switch statements, and so on.


It is convenient to use nameof with Enums. Instead of:

Console.WriteLine(Enum.One.ToString());

it is possible to use:

Console.WriteLine(nameof(Enum.One))

The output will be One in both cases.


The nameof operator can access non-static members using static-like syntax. Instead of doing:

string foo = "Foo";
string lengthName = nameof(foo.Length);

Can be replaced with:

string lengthName = nameof(string.Length);

The output will be Length in both examples. However, the latter prevents the creation of unnecessary instances.


Although the nameof operator works with most language constructs, there are some limitations. For example, you cannot use the nameof operator on open generic types or method return values:

public static int Main()
{   
    Console.WriteLine(nameof(List<>)); // Compile-time error
    Console.WriteLine(nameof(Main())); // Compile-time error
}

Furthermore, if you apply it to a generic type, the generic type parameter will be ignored:

Console.WriteLine(nameof(List<int>));  // "List"
Console.WriteLine(nameof(List<bool>)); // "List"

For more examples, see this topic dedicated to nameof.


Workaround for previous versions (more detail)

Although the nameof operator does not exist in C# for versions prior to 6.0, similar functionality can be had by using MemberExpression as in the following:

6.0

Expression:

public static string NameOf<T>(Expression<Func<T>> propExp)
{
    var memberExpression = propExp.Body as MemberExpression;
    return memberExpression != null ? memberExpression.Member.Name : null;
}

public static string NameOf<TObj, T>(Expression<Func<TObj, T>> propExp)
{
    var memberExpression = propExp.Body as MemberExpression;
    return memberExpression != null ? memberExpression.Member.Name : null;
}

Usage:

string variableName = NameOf(() => variable);
string propertyName = NameOf((Foo o) => o.Bar);

Note that this approach causes an expression tree to be created on every call, so the performance is much worse compared to nameof operator which is evaluated at compile time and has zero overhead at runtime.

String interpolation

String interpolation allows the developer to combine variables and text to form a string.


Basic Example

Two int variables are created: foo and bar.

int foo = 34;
int bar = 42;

string resultString = $"The foo is {foo}, and the bar is {bar}.";

Console.WriteLine(resultString);

Output:

The foo is 34, and the bar is 42.

View Demo

Braces within strings can still be used, like this:

var foo = 34;
var bar = 42;

// String interpolation notation (new style)
Console.WriteLine($"The foo is {{foo}}, and the bar is {{bar}}.");

This produces the following output:

The foo is {foo}, and the bar is {bar}.


Using interpolation with verbatim string literals

Using @ before the string will cause the string to be interpreted verbatim. So, e.g. Unicode characters or line breaks will stay exactly as they've been typed. However, this will not effect the expressions in an interpolated string as shown in the following example:

Console.WriteLine($@"In case it wasn't clear:
\u00B9
The foo
is {foo},
and the bar
is {bar}.");
Output:

In case it wasn't clear:
\u00B9
The foo
is 34,
and the bar
is 42.

View Demo


Expressions

With string interpolation, expressions within curly braces {} can also be evaluated. The result will be inserted at the corresponding location within the string. For example, to calculate the maximum of foo and bar and insert it, use Math.Max within the curly braces:

Console.WriteLine($"And the greater one is: { Math.Max(foo, bar) }");

Output:

And the greater one is: 42

Note: Any leading or trailing whitespace (including space, tab and CRLF/newline) between the curly brace and the expression is completely ignored and not included in the output

View Demo

As another example, variables can be formatted as a currency:

Console.WriteLine($"Foo formatted as a currency to 4 decimal places: {foo:c4}");

Output:

Foo formatted as a currency to 4 decimal places: $34.0000

View Demo

Or they can be formatted as dates:

Console.WriteLine($"Today is: {DateTime.Today:dddd, MMMM dd - yyyy}");

Output:

Today is: Monday, July, 20 - 2015

View Demo

Statements with a Conditional (Ternary) Operator can also be evaluated within the interpolation. However, these must be wrapped in parentheses, since the colon is otherwise used to indicate formatting as shown above:

Console.WriteLine($"{(foo > bar ? "Foo is larger than bar!" : "Bar is larger than foo!")}");

Output:

Bar is larger than foo!

View Demo

Conditional expressions and format specifiers can be mixed:

Console.WriteLine($"Environment: {(Environment.Is64BitProcess ? 64 : 32):00'-bit'} process");

Output:

Environment: 32-bit process


Escape sequences

Escaping backslash (\) and quote (") characters works exactly the same in interpolated strings as in non-interpolated strings, for both verbatim and non-verbatim string literals:

Console.WriteLine($"Foo is: {foo}. In a non-verbatim string, we need to escape \" and \\ with backslashes.");
Console.WriteLine($@"Foo is: {foo}. In a verbatim string, we need to escape "" with an extra quote, but we don't need to escape \");

Output:

Foo is 34. In a non-verbatim string, we need to escape " and \ with backslashes.
Foo is 34. In a verbatim string, we need to escape " with an extra quote, but we don't need to escape \

To include a curly brace { or } in an interpolated string, use two curly braces {{ or }}:

$"{{foo}} is: {foo}"

Output:

{foo} is: 34

View Demo


FormattableString type

The type of a $"..." string interpolation expression is not always a simple string. The compiler decides which type to assign depending on the context:

string s = $"hello, {name}";
System.FormattableString s = $"Hello, {name}";
System.IFormattable s = $"Hello, {name}";

This is also the order of type preference when the compiler needs to choose which overloaded method is going to be called.

A new type, System.FormattableString, represents a composite format string, along with the arguments to be formatted. Use this to write applications that handle the interpolation arguments specifically:

public void AddLogItem(FormattableString formattableString)
{
    foreach (var arg in formattableString.GetArguments())
    {
        // do something to interpolation argument 'arg'
    }

    // use the standard interpolation and the current culture info
    // to get an ordinary String:
    var formatted = formattableString.ToString();

    // ...
}

Call the above method with:

AddLogItem($"The foo is {foo}, and the bar is {bar}.");
For example, one could choose not to incur the performance cost of formatting the string if the logging level was already going to filter out the log item.

Implicit conversions

There are implicit type conversions from an interpolated string:

var s = $"Foo: {foo}";
System.IFormattable s = $"Foo: {foo}";
You can also produce an IFormattable variable that allows you to convert the string with invariant context:
var s = $"Bar: {bar}";
System.FormattableString s = $"Bar: {bar}";

Current and Invariant Culture Methods

If code analysis is turned on, interpolated strings will all produce warning CA1305 (Specify IFormatProvider). A static method may be used to apply current culture.

public static class Culture
{
    public static string Current(FormattableString formattableString)
    {
        return formattableString?.ToString(CultureInfo.CurrentCulture);
    }
    public static string Invariant(FormattableString formattableString)
    {
        return formattableString?.ToString(CultureInfo.InvariantCulture);
    }
}

Then, to produce a correct string for the current culture, just use the expression:

Culture.Current($"interpolated {typeof(string).Name} string.")
Culture.Invariant($"interpolated {typeof(string).Name} string.")
Note: Current and Invariant cannot be created as extension methods because, by default, the compiler assigns type String to interpolated string expression which causes the following code to fail to compile:

$"interpolated {typeof(string).Name} string.".Current();

FormattableString class already contains Invariant() method, so the simplest way of switching to invariant culture is by relying on using static:

using static System.FormattableString;

string invariant = Invariant($"Now = {DateTime.Now}"); string current = $"Now = {DateTime.Now}";


Behind the scenes

Interpolated strings are just a syntactic sugar for String.Format(). The compiler (Roslyn) will turn it into a String.Format behind the scenes:

var text = $"Hello {name + lastName}";

The above will be converted to something like this:

string text = string.Format("Hello {0}", new object[] {
    name + lastName
});

String Interpolation and Linq

It's possible to use interpolated strings in Linq statements to increase readability further.

var fooBar = (from DataRow x in fooBarTable.Rows
          select string.Format("{0}{1}", x["foo"], x["bar"])).ToList();

Can be re-written as:

var fooBar = (from DataRow x in fooBarTable.Rows
          select $"{x["foo"]}{x["bar"]}").ToList();

Reusable Interpolated Strings

With string.Format, you can create reusable format strings:

public const string ErrorFormat = "Exception caught:\r\n{0}";

// ...

Logger.Log(string.Format(ErrorFormat, ex));

Interpolated strings, however, will not compile with placeholders referring to non-existent variables. The following will not compile:

public const string ErrorFormat = $"Exception caught:\r\n{error}";
// CS0103: The name 'error' does not exist in the current context

Instead, create a Func<> which consumes variables and returns a String:

public static Func<Exception, string> FormatError =
    error => $"Exception caught:\r\n{error}";

// ...

Logger.Log(FormatError(ex));

String interpolation and localization

If you’re localizing your application you may wonder if it is possible to use string interpolation along with localization. Indeed, it would be nice to have the possibility to store in resource files Strings like:

"My name is {name} {middlename} {surname}"
instead of the much less readable:

"My name is {0} {1} {2}"

String interpolation process occurs at compile time, unlike formatting string with string.Format which occurs at runtime. Expressions in an interpolated string must reference names in the current context and need to be stored in resource files. That means that if you want to use localization you have to do it like:

var FirstName = "John";

// method using different resource file "strings"
// for French ("strings.fr.resx"), German ("strings.de.resx"), 
// and English ("strings.en.resx")
void ShowMyNameLocalized(string name, string middlename = "", string surname = "")
{
    // get localized string
    var localizedMyNameIs = Properties.strings.Hello;
    // insert spaces where necessary
    name = (string.IsNullOrWhiteSpace(name) ? "" : name + " ");
    middlename = (string.IsNullOrWhiteSpace(middlename) ? "" : middlename + " ");
    surname = (string.IsNullOrWhiteSpace(surname) ? "" : surname + " ");
    // display it
    Console.WriteLine($"{localizedMyNameIs} {name}{middlename}{surname}".Trim());
}

// switch to French and greet John
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("fr-FR");
ShowMyNameLocalized(FirstName);

// switch to German and greet John
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("de-DE");
ShowMyNameLocalized(FirstName);

// switch to US English and greet John
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-US");
ShowMyNameLocalized(FirstName);

If the resource strings for the languages used above are correctly stored in the individual resource files, you should get the following output:

Bonjour, mon nom est John
Hallo, mein Name ist John
Hello, my name is John

Note that this implies that the name follows the localized string in every language. If that is not the case, you need to add placeholders to the resource strings and modify the function above or you need to query the culture info in the function and provide a switch case statement containing the different cases. For more details about resource files, see How to use localization in C#.

It is a good practice to use a default fallback language most people will understand, in case a translation is not available. I suggest to use English as default fallback language.

Recursive interpolation

Although not very useful, it is allowed to use an interpolated string recursively inside another's curly brackets:

Console.WriteLine($"String has {$"My class is called {nameof(MyClass)}.".Length} chars:");
Console.WriteLine($"My class is called {nameof(MyClass)}.");

Output:

String has 27 chars:

My class is called MyClass.

Using an extension method for collection initialization

Collection initialization syntax can be used when instantiating any class which implements IEnumerable and has a method named Add which takes a single parameter.

In previous versions, this Add method had to be an instance method on the class being initialized. In C#6, it can also be an extension method.

public class CollectionWithAdd : IEnumerable
{
    public void Add<T>(T item)
    {
        Console.WriteLine("Item added with instance add method: " + item);
    }

    public IEnumerator GetEnumerator()
    {
        // Some implementation here
    }
}

public class CollectionWithoutAdd : IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        // Some implementation here
    }
}

public static class Extensions
{
    public static void Add<T>(this CollectionWithoutAdd collection, T item)
    {
        Console.WriteLine("Item added with extension add method: " + item);
    }
}

public class Program
{
    public static void Main()
    {
        var collection1 = new CollectionWithAdd{1,2,3}; // Valid in all C# versions
        var collection2 = new CollectionWithoutAdd{4,5,6}; // Valid only since C# 6
    }
}

This will output:

Item added with instance add method: 1
Item added with instance add method: 2
Item added with instance add method: 3
Item added with extension add method: 4
Item added with extension add method: 5
Item added with extension add method: 6

Using static type

The using static [Namespace.Type] directive allows the importing of static members of types and enumeration values. Extension methods are imported as extension methods (from just one type), not into top-level scope.

6.0
using static System.Console;
using static System.ConsoleColor;
using static System.Math;

class Program
{
    static void Main()
    {
        BackgroundColor = DarkBlue;
        WriteLine(Sqrt(2));
    }
}

Live Demo Fiddle

6.0
using System;

class Program
{
    static void Main()
    {
        Console.BackgroundColor = ConsoleColor.DarkBlue;
        Console.WriteLine(Math.Sqrt(2));
    }
}