Exploring Useful Features in C# 11

Shehan Vanderputt
4 min readMar 16, 2024

C# 11 was released in 2022 as part of the .NET 7, bringing a host of new features and improvements. In this blog, we’ll explore some notable additions and improvements introduced in C# 11.

  1. Raw string literals

Raw string literals in C# 10 introduce a new format for string literals, allowing them to contain arbitrary text, including whitespace, new lines, embedded quotes, and other special characters without requiring escape sequences.

A raw string literal begins with at least three double-quote (`”””`) characters and ends with the same number of double-quote characters. Typically, a raw string literal uses three double quotes on a single line to start the string and three double quotes on a separate line to end the string. The newlines following the opening quote and preceding the closing quote are not included in the final content. This feature provides a cleaner and more readable way to represent strings with special characters or multiline content.

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.
""";

Any whitespace to the left of the closing double quotes will be removed from the string literal. Raw string literals can be combined with string interpolation to include braces in the output text. Multiple $ characters denote how many consecutive braces start and end the interpolation:

var Longitude = 34.5433;
var Latitude = -75.53212;

var location = $$"""
You are at {{{Longitude}}, {{Latitude}}}
""";

The preceding example specifies that two braces start and end an interpolation. The third repeated opening and closing brace are included in the output string.

2. Unsigned right shift operator

Unsigned right shift operator (>>>), Before C# 11, performing an unsigned right shift required casting signed integers to unsigned types. With C# 11, the introduction of the >>> operator allows for unsigned right shifts directly without the need for casting.

uint x = 0b_1111_0000_0000_0000_0000_0000_0000_0000; // Unsigned integer
uint result = x >>> 4; // Right shift by 4 bits
Console.WriteLine(Convert.ToString(result, 2)); // Output: 1111

In this example, x is an unsigned integer, and we perform an unsigned right shift by 4 bits using the >>> operator. The resulting value is printed in binary format, showing the expected output 1111.

3. Newlines in string interpolations

Beginning with C# 11, we can use newlines within an interpolation expression to enhance the readability of the expression’s code. The following example demonstrates how newlines can improve the readability of an expression involving pattern matching, such as the is expression, the switch statement, and the switch expression.

string message = $"The usage policy for {safetyScore} is {
safetyScore switch
{
> 90 => "Unlimited usage",
> 80 => "General usage, with daily safety check",
> 70 => "Issues must be addressed within 1 week",
> 50 => "Issues must be addressed within 1 day",
_ => "Issues must be addressed before continued use",
}
}";

4. List patterns

List patterns extend pattern matching to match sequences of elements in a list or an array. For example, the sequence [1, 2, 3] is true when the sequence is an array or a list of three integers (1, 2, and 3). You can match elements using any pattern, including constant, type, property, and relational patterns. The discard pattern (`_`) matches any single element, and the new range pattern (`..`) matches any sequence of zero or more elements.

int[] numbers = { 1, 2, 3 }; 
Console.WriteLine(numbers is [1, 2, 3]); // True
Console.WriteLine(numbers is [1, 2, 4]); // False
Console.WriteLine(numbers is [1, 2, 3, 4]); // False
Console.WriteLine(numbers is [0 or 1, <= 2, >= 3]); // True

5. Auto-default struct

The C# 11 compiler ensures that all fields of a struct type are initialized to their default value as part of executing a constructor. This change means any field or auto property not initialized by a constructor is automatically initialized by the compiler. Structs where the constructor doesn’t definitely assign all fields now compile, and any fields not explicitly initialized are set to their default value. You can read more about how this change affects struct initialization in the article on structs.

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 ()
}

6. UTF-8 string literals

You can specify the u8 suffix on a string literal to specify UTF-8-character encoding. If your application needs UTF-8 strings, for HTTP string constants or similar text protocols, you can use this feature to simplify the creation of UTF-8 strings.

ReadOnlySpan<byte> AuthWithTrailingSpace = new byte[] 
{ 0x41, 0x55, 0x54, 0x48, 0x20 };
ReadOnlySpan<byte> AuthStringLiteral = "AUTH "u8;

--

--

Shehan Vanderputt

Azure | AWS | .NET | C# | Django | Python | MongoDB | MSSQL