C# 11 features – check what is new!

Mateusz Leszner | Software Development | 21.09.2022

C# 11 features see what is new

A new version of the C# programming language is released every year. C# version 11 will be released in November 2022 together with .NET 7. In this article, we will take a look at some of the important features of C# 11. Are you awaiting this update and wondering what features have been added? Read my mini-guide and see if they are valuable and in what way they could improve developers’ work with code.

The history of C#

  • January 2002 – C# 1.0
  • November 2005 – C# 2.0
  • November 2007 – C# 3.0 (.NET Framework 3.0 and 3.5.)
  • November 2007 – C# 4.0
  • August 2012 – C# 5.0
  • July 2015 – C# 6.0
  • March 2017 – C# 7.0 (.NET Framework 4.7)
  • September 2019 – C# 8.0 
  • November 2020 – C# 9.0 (.NET Framework 5)
  • November 2021 – C# 10.0
C# 11 features – check what is new!C# 11 features – check what is new!

Upcoming update – C# features made available for preview

It seems that not so long ago we welcomed .NET 6 and C# 10, and already a new hand is to come in November 2022. Curious about what new features the latest version of C# will bring? Every developer can check them on their own in Visual Studio 2022 version 17.3 (some of those features are available in earlier VS versions – e.g., Visual Studio 17.1). New features can also be checked in the .NET 7 SDK preview format (available to download on the .NET platform).

  Visual Studio 2022features for preview

  17.3

  17.2

  17.1

Source: Microsoft

Below you will find an overview of, in my view, the most interesting ones. 

C # 11 comes together with NET 7 in November 2022

Generic attributes

C# version 11 has been equipped with a generic attribute feature. This allows you to create a generic class deriving a System.Attribute, which facilitates creating attributes requiring System.Type parameter. In earlier versions of C#, a developer had to construct a more complex solution of attributes with the Type parameter in the constructor. See the following listing:

public class TypeAttribute : Attribute
{
   public TypeAttribute(Type t) => ParamType = t;

   public Type ParamType { get; }
}
[TypeAttribute(typeof(string))]
public string Method() => default;
With C# 11 the same can be achieved simpler:
public class GenericAttribute<T> : Attribute { }
[GenericAttribute<string>()]
public string Method() => default;

Removing the obligatory use of typeof(…) in attributes and replacing it with generic types eliminates the problem of ensuring that type inside typeof() meets attribute requirements. This feature was planned to be released in earlier versions of C#, but due to incompatibility with dev tools, Microsoft decided to postpone it. But now it’s time for generic attributes to show up.

Generic math support

This label hides a few new features which make up the generic math support. There are:

  • static virtual and static abstract members in interfaces,
  • checked user-defined operators,
  • relaxed shift operators,
  • unsigned right-shift operator.

This feature allows developers to use static APIs from generic code. For example, you can implement the sum or average calculator for different numeric types with a single implementation. You can find more details on new syntax in the C# documentation 

Numeric types implement a new interface INumber, which contains properties T.One and T.Zero. This can be used to implement generic sum or average calculators for many numeric types, keeping the DRY principle. The sum method may be implemented as + operator too. You can find it in the following listing:

T Sum<T>(T[] values) where T : INumber<T>
{
  
T result = T.Zero;
  
foreach (var value in values)
  
{
      
result += value;
  
}
  
return result;
}
 
T Avg<T>(T[] values) where T : INumber<T>
{
  
var sum = Sum(values);
  
var count = T.Zero;
  
for (var i = 0; i < values.Length; i++)
  
{
      
count += T.One;
  
}
  
return sum / count;
}
var ints = new[] {1, 2, 3, 4, 5};
var doubles = new[] { 0.1, 0.7, 1.1, 8.3 };
 
var sumOfInts = Sum(ints);
var avgOfInts = Avg(ints);
var sumOfDoubles = Sum(doubles);
var avgOfDoubles = Avg(doubles);

Generic math created other requirements in C# 11:

  • unsigned right-shift operator – using operator >>> you can force any right-shifts to unsigned right-shifts, which means that the high-order empty bit positions are always set to zero, regardless of the type of the left-hand operand
  • relaxed shift operator – starting from C# 11, the second operand of shift doesn’t have to be int or implicitly convertible to int, which allows you to use that operator in generic math interfaces
  • checked (and unchecked) user-defined operators – checked and unchecked arithmetic operators may be defined, and the compiler generates calls to the correct variant based on the current context

New lines in string interpolations

String interpolation was introduced in C# 6 with Visual Studio 2015. String interpolation replaces String.Format(). The format of the interpolated string is a string inside double quotes preceded by $. Inside that string, you can pass the parameters or method executions in brackets {}.

$"{<interpolationExpression>[,<alignment>][:<formatString>]} other text in string”

Now, in C# 11, string interpolation gets a small update – it allows you to contain new lines inside the formulas. Previously, some long formulas, containing LINQ queries might have been unreadable. But no more! With the next release of C#, we will be able to use new lines, and then, complex string interpolated formulas will look much better.

var list = new[] {"Apple", "Banana", "Orange", "Grapefruit"};
var str = $"Third letters of fruits starts with B: {list
   .Where(fruit => fruit.StartsWith("B"))
   .Select(fruit => fruit[2])
   .FirstOrDefault()}";

List patterns

Pattern matching means for example matching an object to a type in switch cases, but not only. For more general details about pattern matching see the Microsoft documentation. Pattern matching was introduced with  C# 7 with the is keyword allowing you to map an object to a type and when keyword to be used in switch statements.

Then, C# 7.1 extended type patterns to support generic types. C# 8 have improved pattern matching by adding property patterns allowing you to match type and property. The next release of C# brought relational patterns. So since C# 9 we can use >, <, >=, <= in property matching. C# 9 enabled use of logical operators and, or, not, is null, is not null in pattern matching and var keyword for creating variables. C# 10 have extended property patterns and now it’s time for C# 11. List patterns – matching a sequence of elements. You can match a list using some items as a pattern. Discard pattern (_) is used for matching any single elements, and the range pattern (..) is used to match any sequence of zero or more elements.

var list = new[] {
    new[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, // A
    new[] {1, 2, 3, 5, 7, 10}, // B
    new[] {0, 2, 3, 8, 10, 10}, // C
    new[] {3, 4, 10}, // D
    new[] {1, 4, 10, 15, 21, 3, 10}, // E
    new[] {-1, 2, 3, 5, -1, 10} // F
};

•	Match complete list:
list.Where(x => x is [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); // matches list A
•	Match some elements:
list.Where(x => x is [_, 2, 3, _, _, 10]); // matches lists B, C, F
•	Match range:
list.Where(x => x is [1, 2, .., 10]); // matches lists A, B
•	Mixed match for elements and range:
list.Where(x => x is [1, _, .., 10]); // matches lists A, B, E
•	Match list starting with negative number:
list.Where(x => x is [< 0, ..]); // matches list F

Raw string literals

Raw string literals could be multiline, contain quotes, and other special characters without escape sequence and they can contain string interpolations. Here you can find some examples:

var line2 = "line2 from another variable";
 
var str1 = """
line1
{line2} - won't be replaced
line3
       line4 
       
 line6
""";
 
var str2 = $"""
line1 from second string
{line2}
line3
""";
 
var str3 = $$"""
using { in line 1}
{{line2}}
{line2} this won't be replaced
{{{line2}}} you can use it like this
""" ;

Required members

Developers gain the option to mark properties and fields using the modifier required. This lets us ensure those values are initialized in the constructor. Then you have to set attribute SetsRequiredMembers to the constructor, which should initialize the values. You can have constructors without the attribute too, and those constructors don’t have to initialize required values.

Required members must be initialized, but they may be initialized to null. If the type is a non-nullable reference type, the compiler issues a warning if you initialize the member to null. The compiler issues an error if the member isn’t initialized at all.

public required string RequiredString { get; set; }

[SetsRequiredMembers]
public RequiredDemo()
{
   
    RequiredString = String.Empty;
}

Parameter null-checking – postponed after preview

There was a plan to add a parameter null checking feature, but it has been postponed for now due to developers’ objections on the C# Language Design Meeting on April 6th 2022. The feature was made available for preview and widely discussed. While many developers were satisfied, others raised objections of all kinds.

Auto-default struct

Before the release of C# 11, programmers had to create struct constructors with parameters to ensure that all fields are initialized. But C# 11 will make it different. For now, you can create a struct with parameterless constructor, that doesn’t explicitly initialize structs’ members.

Then the compiler takes its role and ensures that all not yet initialized fields are initialized to their default value. The compiler adds necessary code at the beginning of the constructor body, before the fields are accessed. If this is accessed before the initialization of all fields, the struct is initialized to the default value before the constructor body executes. You can read more about struct initialization and default values on the Microsoft C# documentation webpage.

public readonly struct AutoDefaultStruct
{
    public int IntegerValue { get; }
    public string StringValue { get; }
    public double DoubleValue { get; }

    public AutoDefaultStruct()
    {
        //IntegerValue = default;
        //StringValue = default;
        //DoubleValue = default;
        // doesn't have to set values for all fields, default initializations will be added at the beginning of constructor by compiler
    }
}

var structDemo = new AutoDefaultStruct(); 

To sum up: developer, brace yourself for more C# 11 features! 

The above-mentioned are the most important C# 11 features that you can use in the preview version even today. Of course, the full list of changes is longer including the extended nameof scope, pattern matching for Span<char> and ReadOnlySpan<char>, aliases for IntPtr and UIntPtr, and more. You can find the technical documentation on GitHub.  

The author of the post is:

.NET Developer

Mateusz is a graduate of computer science at Poznań University of Technology. Mainly involved in C# programming, in recent years a web developer who has entered the world of the frontend (Angular). Privately, the happy husband and father of a wonderful daughter. Mateusz waits for the baby to grow up so that she could join family motorcycle trips.

Add comment: