Introduction
C# 6.0, released with Visual Studio 2015 and .NET Framework 4.6, brought several small but powerful features that made code more concise, readable, and expressive.
While these weren’t groundbreaking syntax changes, they were aimed at improving developer productivity and reducing boilerplate code.
In this post, we’ll go through all the key features introduced in C# 6.0 with clear examples.
Auto-Property Initializers
Before C# 6.0, initializing auto-properties required a constructor. Now you can initialize them directly in the property declaration.
Example (Before C# 6.0):
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person()
{
Name = "Unknown";
Age = 18;
}
}
C# 6.0 and later:
public class Person
{
public string Name { get; set; } = "Unknown";
public int Age { get; set; } = 18;
}
This simplifies object creation and improves readability.
Expression-Bodied Members
C# 6.0 introduced a shorthand syntax for simple methods and properties using the lambda arrow (=>).
Example:
public class Circle
{
public double Radius { get; }
public Circle(double radius) => Radius = radius;
public double Area => Math.PI * Radius * Radius;
}
This makes small methods or property getters more elegant.
Null-Conditional Operator (?.)
This feature reduces the need for repetitive null-checks.
Before:
if (person != null && person.Address != null)
{
Console.WriteLine(person.Address.City);
}
In C# 6.0
Console.WriteLine(person?.Address?.City);
If any value in the chain is null, the result will be null instead of throwing a NullReferenceException.
String Interpolation ($"")
This is one of the most popular features from C# 6.0 — it makes string formatting much cleaner.
Before:
Console.WriteLine(string.Format("Name: {0}, Age: {1}", name, age));
In C# 6.0
Console.WriteLine($"Name: {name}, Age: {age}");
nameOf Operator
This operator returns the string name of a variable, type, or member — extremely useful for refactoring and logging.
Example:
public void Validate(string name)
{
if (string.IsNullOrEmpty(name))
throw new ArgumentException($"Argument cannot be null or empty: {nameof(name)}");
}
If you later rename name in your code, the compiler automatically updates the nameof reference.
Using Static Members
You can import static members from a class, so you don’t need to qualify them with the class name every time.
Example:
using static System.Math;
public class Geometry
{
public double GetHypotenuse(double a, double b) => Sqrt(a * a + b * b);
}
This helps make mathematical or utility code cleaner.
Exception Filters
You can now catch exceptions based on conditions.
Example:
try
{
// code
}
catch (Exception ex) when (ex.Message.Contains("Timeout"))
{
Console.WriteLine("Operation timed out!");
}
This avoids multiple catch blocks and adds more control over exception handling.
Index Initializers
C# 6.0 simplified how you can initialize dictionaries or other index-based collections.
Example:
var numbers = new Dictionary<int, string>
{
[1] = "One",
[2] = "Two",
[3] = "Three"
};