C# 11 is Here! Revealing ALL C# 11 Features

Dec 22, 2021 | .NET

Microsoft recently released C# 10 and .NET 6 to the world, but they aren’t done yet! The .NET team has already started working on features that will be included in C# 11 and .NET 7. A lot of these new C# 11 features are very focused on pure performance (up to 73.5% faster) and in this article, we’ll take a look at some of the upcoming C# 11 important features. These features will change how you code forever!

C# 11 Release Date

If you are curious and wondering when C# 11 will be officially released, according to microsoft the version of C# 11 has been officially released on November 8, 2022 replacing the previous version of C# 10.

At this time, C# 11 is supported by the “go-live” support policy offered by Microsoft. This means that Microsoft will offer active support for production applications developed with C# 11.

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

Microsoft has been announcing and releasing previews since the C# 11 initial triage. The first couple of features were in development and nothing was officially confirmed at that time.

For some time now Microsoft has published many blog posts in which it has been talking about the possible features (and the official ones) that the major version of C# 11 will bring with it.

In this article we have compiled all the features of C# 11 based on Microsoft release notes and Microsoft Docs. Let’s take a quick tour and discover all the new features of C# 11 (they will blow your mind)!

“Holes” in interpolated chains

Welcome to the first new feature that C# 11 will bring, we have to keep in mind that C# currently supports two types of intepolated strings:

  • Verbatim interpolated: $@""
  • Non-verbatim interpolated: $""

The main difference here is that verbatim interpolated strings can contain new lines of code in their text segments and can only escape a proper quotation mark " “.

This does not happen in non-verbatim interpolated strings; in these cases escape characters (such as /r/n) are used.

When I mention “holes”, I — and Microsoft — mean interpolation expressions.

All this affected (and still affects) all the “holes” in the non-verbatim interpolated strings. Since these holes are not really text, they should not be affected by the escape rules.

Let’s see Microsoft’s example of what could be done with C# 11 that now, with C# 10 would not be possible because it would give error:

var v = $"Count ist: { this.Is.Really.Something()
                            .That.I.Should(
                                be + able)[
                                    to.Wrap()] }.";

List patterns

Here is another new feature: the new list pattern. What it allows us to do in C#11 is to compare with arrays and lists, being able to match different elements or even, to include a cut pattern that matches zero or more elements.

As Kathleen tells us, the slice pattern can go after, for example, another list pattern, such as the var pattern, in order to capture the contents of the slice.

Let’s look at Microsoft’s example:

The pattern [1, 2, .., 10] matches all of the following:

int[] arr1 = { 1, 2, 10 };
int[] arr1 = { 1, 2, 5, 10 };
int[] arr1 = { 1, 2, 5, 6, 7, 8, 9, 10 };

To explore list patterns consider:


public static int CheckSwitch(int[] values)
    => values switch
    {
        [1, 2, .., 10] => 1,
        [1, 2] => 2,
        [1, _] => 3,
        [1, ..] => 4,
        [..] => 50
    };

You can see the example in more depth in the Early peek at C# 11 features.

Parameter Null Checking

This new feature is based on, as we already know, it is common to use variations of boilerplate code to validate if the method arguments are null, for example:

public static void M(string s)
{
    if (s is null)
    {
        throw new ArgumentNullException(nameof(s));
    }
    // Body of the method
}

And now we can abbreviate the intention to check null parameters with !!:

public static void M(string s!!)
{
    // Body of the method
}

This could also be used in checking indexer parameters with get and set:

public string this[string key!!] { get { ... } set { ... } }

Constructors

In this feature there are a few small changes. If at any time an explicit null check change is performed with the !!, null validation syntax, that validation will occur after the field initializers. Before any of these, null-checks using the parameter null-check syntax will be done.

Interaction with Nullable Reference Types

If we apply to the name of a parameter the !! operator we have seen before, it will start as non-null with a nullable state. Let’s check Microsoft example:

void WarnCase<T>(
    string? name!!,     // CS8995   Nullable type 'string?' is null-checked and will throw if null. 
    T value1!!        // Okay
)

As we can see, the compiler gives a warning when !! syntax on parameters is used with an explicitly nullable type on the parameter.

Generic attributes

This is the first feature talked about in one of the first C# issues on GitHub. Microsoft has had a couple of issues with this promising feature as it encountered some incompatibilities with other tools at the last minute (including crashes and problems compiling). For this reason Microsoft did not officially release this feature in C# 10, but in a preview. Let’s see how it works:

As Ashmind has explained with his words in his Proposal:

“For some attributes (e.g. TypeConverterAttribute) that are used to reference types, the current usage looks like this:”

[TypeConverter(typeof(X))]

According to him, this problem has 2 disadvantages:

  • You can’t guarantee that the type matches the requirements of the attribute — e.g. has an empty constructor or inherits TypeConverter.
  • The syntax is a bit verbose.

Ashmind suggested that the generic attributes should be supported, including any generic constraints. Followed by an example:

[TypeConverter<X>]

This very good proposal you suggest has a couple of advantages. In his own words:

  • Support for type constraints in type-referencing attributes.
  • Shorter syntax.
  • As far as I know, generic attributes are already supported in IL.

Field Keyword

The favorite feature of many C# developers. We have been analyzing this feature in the previews of C# 10 but it was not officially released (according to Microsoft) due to lack of time, but it seems that they are convinced that in C# 11 it will be released and we will be able to use it with enthusiasm.

To understand the Field Keyword, I will use the explanation of Lachbaer, the creator of this proposal, as it seemed to me to be the best way to understand the Field Keyword:

“Create auto-properties that can also have getters and setters. These can access the automatically created backing field with the field keyword, that acts like a variable, as value does for properties.”

Below, Lachbaer explains how this feature works with a very simple example:

A semi-auto-property with an automatic backing-field is created under the follwing cases:

  • there must be either a get; or set; statement,
  • the property has an initializer

Constraint

public string PropertyConstraint {
    get;
    set => field = value ?? throw new ArgumentNullException();
} = "";

The setter defines a constraint. field represents the automatically created backing field. As possible with auto-properties, the backing field is initialized by = "".

Getter logic

public T PropertyAssertedGet {
    int getCounter = 0;   // property scoped field #133
    get 
    {
        getCounter++;
        Debug.Assert (getCounter <= 3,
              "In my code this prop is only called 3 times, somethings terribly wrong.");
        return field;
    }
    set;
}

Event raising

public T PropertyEvent {
    get;
    set
    {
        var oldValue = field;
        OnPropertyChanging(
            new PropertyChangingEventArgs(/* current value */ oldValue, /* new value */ value));
        field = value;
        OnPropertyChanged(
            new PropertyChangedEventArgs(/* current value */ oldValue, /* new value */value));
    }
} = default(T);

In my opinion it is one of the most promising features and the most awaited by all developers in C# 10. It is not known exactly when it will be officially released but we will follow it closely.

Static abstracts in interfaces

According to MadsTorgersen— the contributor to this proposal — specifying abstract static members in an interface obligates the classes and structs that implement the interface to include those members, either explicitly or implicitly, in their implementations of the interface in question. It is possible to get access to the members by specifying type parameters that are not prohibited by the interface’s restrictions.

The inability to abstract over static members and build generic code that works across types that specify those static members is a major limitation of the present state of the art. This is especially troublesome for member types that only exist in a static form, such as operators, which are particularly difficult to deal with.

Static abstracts in interfaces allows generic algorithms over numeric types to be implemented, which are represented by interface constraints that indicate the existence of certain operators. As a result, the algorithms may be stated in terms of the following operators:

// Interface specifies static properties and operators
interface IAddable<T> where T : IAddable<T>
{
    static abstract T Zero { get; }
    static abstract T operator +(T t1, T t2);
}

// Classes and structs (including built-ins) can implement interface
struct Int32 : …, IAddable<Int32>
{
// Explicit
    static Int32 I.operator +(Int32 x, Int32 y) => x + y; 
// Implicit
    public static int Zero => 0;                          
}

// Generic algorithms can use static members on T
public static T AddAll<T>(T[] ts) where T : IAddable<T>
{
// Call static operator    
    T result = T.Zero;
// Use `+`                   
    foreach (T t in ts) { result += t; } 
    return result;
}

// Generic method can be applied to built-in and user-defined types
int sixtyThree = AddAll(new [] { 1, 2, 4, 8, 16, 32 });

You can see the complete example and more information in the Static abstracts in interfaces proposal on GitHub.

MadsTorgesen has also proposed a couple of alternatives, such as Structural constraint:

“An alternative approach would be to have “structural constraints” directly and explicitly requiring the presence of specific operators on a type parameter. The drawbacks of that are:”

  • This would have to be written out every time. Having a named constraint seems better.
  • This is a whole new kind of constraint, whereas the proposed feature utilizes the existing concept of interface constraints.
  • It would only work for operators, not (easily) other kinds of static members.

Declarations under or patterns

This is a feature suggested again by Alrz and is detailed in this way:

“Allow pattern variables to be declared in different mutually exclusive patterns. This is the part of pattern-matching proposal that did not make it into C# 9.0.”

If each mutually exclusive pattern creates a different set of variables with different types, then the variables will not be firmly assigned in that specific code path, which is what is desired. This contains the or patterns, as well as each occurrence of a switch section, among other things.

A pattern variable may be definitively assigned in a whenclause but not in the body of a switch section, for example, as a result of this:

case (int x, 0) a when Use(x, a): // ok
  case (0, int x) b when Use(x, b): // ok
     Use(x); // ok
     Use(a); // error; not definitely assigned
     Use(b); // error; not definitely assigned
     break;

Pattern variables may be defined on both sides of the equation in a recursive way by multiplying them together as follows:

if (e is { A: (int x, 0) or (0, int x) } or
         { B: (int x, 0) or (0, int x) })
{
    Use(x);
}

Declarations under or patterns might be one of the most missing features in C# 11 but I think that is a very cool feature.

Again, I recommend reading the Declarations Under or Patterns Proposal in depth on GitHub if you want to know it perfectly.

New Raw string literals

This new upcoming feature is an enhancement of string literals. Microsoft has now added a new format called raw string literals.

As Microsoft says, this new format allows different types of special characters without needing to escape them. Apart from special characters, it also allows:

  • Arbitrary text
  • Embedded quotes
  • New lines
  • Whitespaces

The main feature of raw string literals is that they always open with (at least) 3 double-quotes and must close with the same number of double-quotes. With this practical example Microsoft shows us how to use it:

string longMessage = """
     This is a long message.
     It has several lines.
         Some are indented
                 more than others.
     Some should start at the first column.
     Some have "quoted text" in them.
     """;

In addition, Microsoft reminds us that we can combine string interpolation with raw string literals by adding multiple dollar symbols $$ .

To learn more about this feature I recommend you to consult the original post from Microsoft.

Newlines in string interpolations

Previously, the text of a string interpolation {text} allowed only one line. Now in C# 11 this text can allow more than one line.

For those who do not know, this is the structure of an item with an interpolation expression:

{<interpolationExpression>[,<alignment>][:<formatString>]}

The main advantages of this feature is to have a clearer reading and compression of string interpolations (especially those using long expressions).

Method Group conversion to Delegate (updated)

Previously, in versions of the standard, when creating a delegate object for a method group conversion, the compiler could not reuse it.

Now, as Microsoft explains:

“The conversion is permitted (but not required) to use an existing delegate instance that already contains these references.”

To understand what happens, the compiler of the new version of C# has the ability to reuse the delegate object by storing it previously in cache.

Generic math support

Generic Math is a feature that Microsoft introduced in .NET 6, but now with the arrival of .NET 7 they have implemented many improvements and that is why we are going to talk about it. With Generic Math you can take full advantage of operators and static APIs by combining static virtuals and the power of generics.

One of its major advantages is that it allows you to restrict the input to number like types avoiding having many similar implementations (if not almost identical). Besides, now you will be able to use different operators from generic contexts.

In addition to this new feature, Generic Math support has been added with different features such as:

  • Members that are static and virtual in interfaces
  • Verified user-defined operators
  • Relaxed right-shift requirements
  • Operator with an unsigned right shift

Thanks to this, it will now be possible to add static abstract members with overloadable operators (or static properties or members) to define interfaces.

And that’s not all, now some different requirements have also been created due to generic math. These requirements are divided into 3:

  • Unsigned right shift operator: Now with C# 11 you will simply have to use >>> to force any unasigned rigth-shift.
  • Relaxed shift operator requirements: Thanks to C# 11, the requirement that the second operand must be an int is eliminated and thanks to this, types that implement generic math interfaces can be used in these cases.
  • Checked and unchecked user defined operators: Now both checked and unchecked arithmetic operators can be defined. This allows the compiler, always depending on the context, to generate calls to the correct variable.

Numeric IntPtr and UIntPtr

This new feature adds to the nint and nuint integral numeric types as aliases System.IntPtr and System.UlntPtr.

The two types nint and nuint are native-sized integers. The thing is that they are native if, for example, they are 64-bit integers and run in a 64-bit process. The same as for 32-bit scenario.

Another advantage is the performance optimization in scenarios where integer math is used and they can also be used in low-level libraries and interop scenarios.

In the following table you can find all the Integral numeric types:

c# 11 features integral types
Characteristics of the integral types (Source: Microsoft)

Auto-default struct

This C# 11 feature is a bit more compiler focused. For those who don’t know, a variable of a struct type contains all the data of that struct. So you can easily distinguish if a struct is initialized, if it is uninitialized or if it has a default value. Let’s see the Microsoft example:

public readonly struct Measurement
{
    public Measurement()
    {
        Value = double.NaN;
        Description = "Undefined";
    }

    public Measurement(double value, string description)
    {
        Value = value;
        Description = description;
    }

    public double Value { get; init; }
    public string Description { get; init; }

    public override string ToString() => $"{Value} ({Description})";
}

public static void Main()
{
    var m1 = new Measurement();
    Console.WriteLine(m1);  // output: NaN (Undefined)

    var m2 = default(Measurement);
    Console.WriteLine(m2);  // output: 0 ()

    var ms = new Measurement[2];
    Console.WriteLine(string.Join(", ", ms));  // output: 0 (), 0 ()
}

The structure-type array instantiation generates an array with the default values of a structure type while ignoring the parameterless costructor.

Now in C# 11, the new compiler in this version takes care to ensure that fields of any struct type are always initialized to their default value. The result of this is that if an automatic property or field is not initialized by the constructor, it will be initialized by the compiler.

Pattern match Span<char> or ReadOnlySpan<char> on a constant string

As Microsoft tells us, since a couple of C# versions ago, using pattern matching you could check if a particular string has a specific constant value. Now in C# 11 opens the possibility to use the same logic for pattern matching ReadOnlySpan<char> and Span<char> variables.

Extended nameof scope

The function of nameof expressions is to generate the name of a type, variable or member as a string constant. Besides, it does not affect in any way at run time since it is evaluated and executed at compile time.

One of the utilities of the nameof expression is to maintain the argument checking code. Let’s look at the following Microsoft example:

public string Name
{
    get => name;
    set => name = value ?? throw new ArgumentNullException(nameof(value), $"{nameof(Name)} cannot be null");
}

With the new version of C# it will be possible to specify the name of any method parameter in an attribute of the method declaration using the nameof operators.

This is because when both parameter names and Type parameter names are used in a nameof expression, they are always in scope.

This makes it possible to easily add attributes for nullable analysis.


All these new features in C# 11 and all the work Microsoft is doing for C# is amazing. This is not a minor release, this is a very big release and at least for me, we are in front of one of the biggest updates of the C# language.

If we remember the principles of C# until today, that we can develop from desktop applications, through web applications to mobile. And let’s not forget that C# is also a popular game developing programming language (Unity).

Of course, there are still a couple of months to go before the official release of C# 11 and knowing Microsoft, we are sure that it has something else to surprise us until then. What do you think in general about C# 11? Was it what you expected? Do you miss any feature or better?

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
.