Discover What’s New in C# 10: Top Features You Should Know

Jul 5, 2021 | .NET

About the possible new features of C# 10

A few days ago Mads Torgersen, the lead designer of the C# language at Microsoft, outlined the cool new things that C# 10 will have.

One of the biggest benefits of open source software is being able to see how your project evolves over time as the days go by. With this we want to refer to the same C#, since we can follow its progress on GitHub and see its main news.

What’s about C# 10

C# is Microsoft’s programming language within the .NET Framework platform. With C# you can develop all kinds of applications. From desktop applications for operating systems such as Windows, Linux or macOS, to mobile applications for Andorid or iOs through APIs, web apps or games with the Unity engine.

C# 10 is the next version of this language that has come to replace C# 9 bringing with it great improvements in terms of features and performance.

C# 10 Release Date

If you are wondering when C# 10 was released, according to Microsoft the C# 10 version was officially released on November 8, 2021.

As always, being an official Microsoft release, C# 10 is officially supported by Microsoft for production applications developed in C# 10.

C# 10 Features – What’s New in C# 10

What can we expect in the future version of C# 10?

Okay, let’s talk about the future version 👇

  • What could bring new? 🤔
  • What would you like me to have? 🤔
  • What is the possibility of it being added?🤔

Note that the upcoming features are still debatable and are not certain to appear in C# 10.

Keep in mind that they are not simply ideas or contributions from the community. These features that I am going to mention are being shuffled by its developers. And although they are not implemented in the next version, today they are being refined so that they come out in C# 10.

Let’s start 👍

Record types can seal ToString

Now in C# 10.0 version, you have the ability to add the sealed modifier when you override ToString in a record type.

Sealing the ToString method evade the compiler from synthesizing a ToString method for any derived record types. This function allows you to ensure all derived record types use the ToString method defined in a common base record type.

Microsoft advises us that this feature requires setting the <LangVersion> element in the csproj file to preview.

What is the Record Keyword?

If you don’t know what I’m talking about, this is normally used to define a reference type that provides built-in functionality for encapsulating data.

A simple example of what can be done with this is that you can create record types with immutable properties using standard property syntax 👇

public record Person(string FirstName, string LastName);

And what better way to understand it than with a simple example from Microsoft 👇

public record Person
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
};

File-scoped namespace declaration

You can now use the new namespace declaration form to declare that all subsequently declared declarations are members of the declared namespace 👇

namespace NamespaceName;

This new syntax, which will be implemented in the new version of C# 10, will save both vertical and horizontal space for the most common namespace declarations.

What is namespace keyword?

To clarify this, the namespace keyword is used to declare a scope that contains a set of related objects. You can use a namespace to organize code elements and to create globally unique types.

namespace SampleNamespace
{
    class SampleClass { }
    interface ISampleInterface { }
    struct SampleStruct { }
    enum SampleEnum { a, b }

    delegate void SampleDelegate(int i);

    namespace Nested
    { 
        class SampleClass2 { }
    } 
}

And sure, but…

What are file-scope namespace declarations?

These declarations in particular, allow you to declare all the types of a file, which are in a single namespace.

To go a little deeper, in this version of C# 10.0, the example is similar to the previous one that has been shown by Microsoft, but uses a file scope namespace declaration 👇

using System;

namespace SampleFileScopedNamespace;

class SampleClass { }
interface ISampleInterface { }
struct SampleStruct { }
enum SampleEnum { a, b }
delegate void SampleDelegate(int i);

Constant interpolated strings

To understand the Constant interpolated strings, first we need to understand String Interpolation.

The $ character identifies a string literal as an interpolated string. An interpolated string is a string literal that might contain interpolation expressions.

When an interpolated string is resolved to a result string, items with interpolation expressions are replaced by the string representations of the expression results.

Perfect, now my question is….

What Constant interpolated strings have in C# 10?

Talking about this new feature that C# version 10.0 will bring is const strings, which can be initialized using string interpolation only if the placeholders are themselves constant strings.

String interpolations can create more readable const strings as the const strings used in the application are constructed. Placeholder expressions cannot be numeric constants because those constants are converted to strings at runtime. The culture currently in place, could affect the string representation.

Extended Property Patterns

First, I will explain what are the Extended property patterns. These patterns allow you to have property subpatterns refer to nested members, for example:

if (e is MethodCallExpression { Method.Name: "MethodName" })

Instead of:

if (e is MethodCallExpression { Method: { Name: "MethodName" } })

Now that this has been clarified….

What Extended Property Patterns have in C# 10?

The next functionality that Microsoft discusses is that nested fields or properties could be referenced within a property pattern. The example Microsoft gives is a pattern of the form 👇

{ Prop1.Prop2: pattern }

Microsoft tells us that it will be valid in C# 10.0 and later, and that this is equivalent to 👇

{ Prop1: { Prop2: pattern } }

And this is valid in C# version 8.0 and all subsequent versions

Declaration and assignment in same deconstruction

This new change implemented by the new version, allows to remove the restriction of previous versions of C#. The example that Microsoft gives us in this case is 👇

…Previously, a deconstruction could assign all values to existing variables, or initialize newly declared variables:

// Initialization:
(int x, int y) = point;

// assignment:
int x1 = 0;
int y1 = 0;
(x1, y1) = point;

This restriction is eliminated in C# 10.0 👇

int x = 0; (x, int y) = point;

Global using directives

You can now use the global modifier to any using directive. With this you can tell the compiler that the directive must be applied to all source files in the compilation.

Perfect, but as before…

What is the using directive?

This directive allows you to use types defined in a namespace without specifying the entire namespace of that type.

To summarize, the using directive imports all types from a single namespace, as shown in the following example 👇

using System.Text;

You can apply two modifiers to a using directive:

  • The global modifier has the same effect as adding the same using directive to every source file in your project. This modifier was introduced in C# 10.0.
  • The static modifier imports the static members and nested types from a single type rather than importing all the types in a namespace.

Talking about this new feature that C# version 10.0 will bring is const strings, which can be initialized using string interpolation only if the placeholders are themselves constant strings.

Required properties

Previously, to ensure that objects were created correctly, class constructors were always used. Today we have the possibility of using lighter constructions, such as the self-implemented properties as in this registry 👇

public record Employee
{
    public string Name { get; init; }
    public decimal Salary { get; init; }
    public DateTime Date { get; init; }
}

When instantiating lightweight objects, we always prefer to do it quickly with the object initializer syntax 👇

var theNewGuy = new Employee
{
    Name = "Chris Smith",
    Salary = 1000m,
    Date = DateTime.Now
};

But what if the object doesn’t make sense until some properties are set? 🤔

You could add a constructor, but you would have to add more standard text. Apart from copying parameter values to properties.

In C# 10 this problem disappears 👇

public record Employee
 {
    public required string Name { get; init; }
    public decimal Salary { get; init; }
    public DateTime Date{ get; init; }
 }

Field keyword

After quite some time, the entire C# development team has managed to optimize the code. Self-deployed properties are great, but they can only take you so far.

Many times you are forced to add the backing field to your class and write the property methods as usual.

In the C# 10 new features, there is a new backdoor with the field keyword, which exposes the automatically created backing field 👇

public record Employee
 {
   public required string Name { get; init; }
   public decimal Salary { get; init; }
   public DateTime Date{ get; init => field = value.Date(); }
 }

The cleaning code looks very good, very simple and almost declarative. The best part is that you can use the field keyword to access the backing field in any process, be it set, init, or get.

Let’s see how a property would be valid in an ordinary class 👇

private string _firstName;

public string Name
{
    get
    {
        return _firstName;
    }
    set
    {
        if (value.Trim() == "")
            throw new ArgumentException("No blank strings");
        _firstName = value;
    }
}

Now you can use an autoimplemented property and field 👇

public string tName {get;
    set
    {
        if (value.Trim() == "")
            throw new ArgumentException("No blank strings");        field = value;
    }
}

This is as long as there is no need to change the data type, as there is no need to declare the backing field.

Objects initialization

One of the goals the C# team is focussing on, is making initialisation of objects easier. That is why it will be possible to flag properties of a class, struct, record or record struct as required. It makes those properties mandatory to fill in.

Lets see 👇

class Person
{
  public required string Name { get; set; }
  public DateTime DateOfBirth { get; set; }
}

This can be done via a constructor, or this can be done with object initialisation. The two class definitions below are equivalent. If you write it with the required keyword, you cannot instantiate the Person without setting the Name property.

The compiler will throw errors and fail to compile 👇

class Person
{
  public Person(string name) => Name = name;
  public string Name { get; set; }
  public DateTime DateOfBirth { get; set; }
}

To further improve properties, it will be possible to get rid of backing fields alltogether. The new keyword field will provide access to said backing field.

This will be available for both setters as init only properties.

class Person
{
  public string Name { get; init => field = value.Trim(); }
  public DateTime DateOfBirth { get; set => field = value.Date; }
}

There will be a few nifty little enhancements in the next version as well. One is that the with operator will support anonymous types as well.

var bytehide = new
{
    Name = "bytehide",
    Email = "bytehide@mail.com"
};

var bar = bytehide with { Name = "Bar" };

File-level namespaces

All of us when we started programming in C# we have created a “Hello World” application. Knowing this we also know that C# uses a block structure for namespaces.

namespace HelloWorld
 {
 class Hello
 { 
     static void Main(string[] args)
     {
         System.Console.WriteLine("Hello World!");
     }
 }
 }

The best thing about this is that namespaces can be overlaid very easily, simply by nesting blocks. In the same way that a single file can contain types in any combination of namespaces and multiple files can share the same namespace between them.

If we want to scratch the negative part a bit, this system adds a bit of indentation if we compare it with bracket languages such as JavaScript or Java.

The question we ask ourselves at this point is:

Is it possible to keep that functionality, but at the same time reduce excess indentation? 🤔

Yes ✅

How is it possible? 🤔

It simply opened that entering namespaces with file scope, this would allow to establish a default namespace that would be applied automatically to the entire file eliminating the indentation.

namespace HelloWorld; public class Hello
{
    static void Main (string[] args)
    {
        System.Console.WriteLine("Hello World!");
    }
}

It is normal to only have one file scoped namespace per file, so there would be no problem. Likewise, most C# code files do not include more than one namespace.

If for example we add a namespace block to a file that uses a file-scoped namespace, a nested namespace is simply created.

Let’s see a quick example 👇

namespace Company.Product;
Company.Product.Component
namespace Component
{
}

It is clear that it is not a very big feature, but it is preferable that the more improvements there are, the easier and more intuitive the task of programming will be.

Primary constructors

In the latest released versions of C#, the topic of boilerplate code has been reduced considerably with features like automatic properties.

The main improvement of this is not simply reducing the amount of code that is written, but reducing the amount of code that has to be read. It makes navigating code bases easier and reduces the most common places where errors can occur.

Primary constructors are a very good implementation that would again reduce the amount of code that is written. We can see it with this simple example that has a class that has a constructor and two read-only properties.

public class DataSlice
 {
   public string DataLabel { get; }
   public float DataValue { get; }
 
   public DataSlice(string dataLabel, float dataValue)
   {
      DataLabel = dataLabel;
      DataValue = dataValue;
   }
 }

What the statistics tell us, is that 70% of its classes have constructors, and more than 90% of all of them simply do nothing more than copy parameters into properties.

If you haven’t written any kind of constructor code yet, don’t worry as we can still create and use the class in the same way.

var adultData = new DataSlice("Vaccinated adults", 741);

By using the main constructor, property validation is not excluded. In the same way, its rules can be enforced in a property setter.

Let’s see an example 👇

public class DataSlice(string dataLabel, float dataValue)
 {
 public string DataLabel
 {
   get => dataLabel;
   set
   {
     if (value < 0) throw new ArgumentOutOfRangeException();
     dataLabel = value;
   }
 }
 public float DataValue { get => dataValue; } 
 }

Other details are also possible (calling the base constructor in a derived class, adding constructors). The main downside to all of this is that the primary constructors could collide with the position registers.

Raw string literals

We already know that the ordinary strings that C# has, tend to be quite messy since they need quotation marks (‘’), newlines (\ n) and backslashes (\). What C# offers before this little problem is the use of special characters.

For example, we can prefix a string with @ and have free rein to add all these details without any problem 👇

string path = "c:\\path\\backslashes";
string path = @"c:\pathh\backslashes";

What the raw string literal string allows is to create new paths to avoid escaping problems. Using the delimiter of a series of quotes followed by a line break to start, and a line break followed by the same number of quotes to close.

To understand it more simply, I leave you this example below 👇

string xml = """
          <part number="2021">
            <name>year</name>
            <description>this is the actual year
             <ref part="2020">year</ref> actual year.
            </description>
          </part>
          """;

If your concern is that there is a possibility of a triple quote sequence within the string, you can simply extend the delimiter so that you can use all the quotes you want, as long as the beginning and end are respected.

string xml = """" 
             Now """ is safe to use in your raw string.
             """";

In the same way as @ strings, newlines and whitespace are preserved in a raw string. What happens is that the common white space, that is, the amount that is used to bleed, is cut off.

Let’s see more simply with an example 👇

<part number="2021">
            <name>year</name>
            <description>this is the actual year
             <ref part="2020">year</ref> actual year.
            </description>
          </part>

To this 👇

<part number="2021">
  <name>year</name>
  <description>this is the actual year
   <ref part="2021">year</ref> actual year.
  </description>
</part>

With this I want to explain to you that the raw strings are not intended to replace the @ strings that you are using right now.

Rather, they are prepared for the specific moments when you need a marked block or arbitrary code and in turn you need a coding approach that is guaranteed to be safe.

You May Also Like

Sign up For Our Newsletter

Weekly .NET Capsules: Short reads for busy devs.

  • NLatest .NET tips and tricks
  • NQuick 5-minute reads
  • NPractical code snippets
.