What's New In C# 10?

C# has been around for a while since 2002 to be precise. As we wait for the 20th birthday of this popular programming language, we got its new version as well. C# 10 was released in November 2021 along with the much awaited .Net 6. In this article, we will see some cool features of C# 10.  

Following are some of the features in the new version of C#.

Global Using Directive

After the top level statements feature in C# 9, which allows us to exclude writing namespace, class and Main method declaration, this global using directive takes it to the next level and even removes the clutter of using statements by making that global in some other file.

In C# 9 you need to import the namespaces of the packages you use in your code in every file wherever required. 

using System;
using System.Linq;
using System.Text.Json;
using CSharp10Features;

var names = new[] { "Amit", "Aman" };
int count = names.Count();
var serialized = JsonSerializer.Serialize(names);

Console.WriteLine(serialized);

In C# 10, now you have the flexibility of keeping the namespaces in a single file.

// Moved using statements to a different file with global keyword
var names = new[] { "Amit", "Aman" };
int count = names.Count();
var serialized = JsonSerializer.Serialize(names);

Console.WriteLine(serialized);

We declare all the using statements in another file and add global keyword to it.

global using System;
global using System.Linq;
global using System.Text.Json;
global using CSharp10Features;

File Scoped Namespaces

Along with minimizing the vertical clutter with global using statements, C# 10 also allows us to reduce the horizontal clutter with file scoped namespaces.

Till C# 9 the namespace declarations used to be block scoped.

namespace CSharp10Features {
    internal class A {
        public string Description {
            get;
            set;
        }
    }
    internal class B {
        public string Description {
            get;
            set;
        }
    }
}

In C# 10 the namespace declarations are now file scoped.

namespace CSharp10Features;
internal class A {
    public string Description {
        get;
        set;
    }
}
internal class B {
    public string Description {
        get;
        set;
    }
}

The compiler provides quick actions to allow us to convert block scoped namespace declaration to file scoped namespaces.

Lambda improvements

C# 10 brings many improvements to the lambda expressions. One of them being natural type lambda expressions

In C# 9 working with lambda expressions required to be a bit more complex with more code needed to be written. We needed to explicitly define delegate types.

public void Calculate()
{
    Func<int, int, int> Sum = (int x, int y) => x + y;
    Console.WriteLine("Sum is " + Sum(5, 10));
}

With C# 10, compiler can infer the natural type from the expression at the compile time which means that we can use var.

var Sum = (int x, int y) => x + y;

You can even return explicit type from lambda expression in C# 10 which wasn’t possible earlier.

var NamesList = IList<string>() => new string[] { "Ram", "Aman", "Tania" };

If return type wasn’t specified then C# compiler would have assigned it as Func<string[]>

But now it will assign it Func<Ilist<string>>

Deconstructor improvements

In the earlier versions of C# you need to add custom logic to deconstruct an object of a type, but now there is a deconstruct method available on reference types that makes the task easy.

Before C# 10 this is how we used to break an object

var person = new Person {
    Name = "Harkirat Singh",
        DOB = new DateOnly(1992, 08, 30),
        Address = "Chandigarh",
        Qualification = "Btech",
        PhoneNumber = "123456785"
};
var name = person.Name;
var dob = person.DOB;
var address = person.Address;
var qual = person.Qualification;
var phone = person.PhoneNumber;

Deconstructors are the real game changers in C# 10 and were much awaited since its inception in the javascript world. Also now assignment and declaration is possible in the same deconstruction. Tuples don’t require Deconstruct method

public(string name, DateOnly dob) GetPerson() {
    var person = (Name: "Harkirat Singh", DOB: new DateOnly(1992, 08, 30));
    (string name, DateOnly dob) = person;
    return person;
}

But other types such as classes do require Deconstruct method for deconstruction.

public void Deconstruct(out string name, out DateOnly dob, out string qual, out string phone) {
    name = Name;
    dob = DOB;
    qual = Qualification;
    phone = PhoneNumber;
}
public Person GetDetails() {
    var person = new Person {
        Name = "Harkirat Singh",
            DOB = new DateOnly(1992, 08, 30),
            Address = "Chandigarh",
            Qualification = "Btech",
            PhoneNumber = "123456785"
    };
    (string firstName, DateOnly dob, string qualifications, string phoneNumber) = person;
    //Can console write any of the above variables
    return person;
}

Extended property patterns

Sometimes we need nested properties in our code i.e property of a property. Using pattern matching on these values is possible in previous versions of the C# as well, but code looks a bit weird.

if (person is { Name: { Length: > 10 } })
{
    Console.WriteLine("You should use a nickname");
}

C# 10 provides dot operator to solve this problem with much cleaner code.

if (person is { Name.Length: > 10 })
{
    Console.WriteLine("You should use a nickname");
}

Please check MS docs for all list of features.


Similar Articles