C# 11 is out now. “With each release, the community has become increasingly engaged, contributing everything from suggestions, insights, and bug reports to the entire feature implementation. This is really C# for everyone,” the announcement said.

Some of the highlight updates in the new release include:

UTF-8 String Literals

By default, C# strings are hardcoded as UTF-16, while the prevailing string encoding on the Internet is UTF-8.To minimize conversion hassle and performance overhead, it is now possible to simply append au8suffix to immediately convert them to UTF-8:


var u8 = "This is a UTF-8 string!"u8;

UTF-8 string literals just return you a block of bytes — starting withReadOnlySpan<byte>form. For those scenarios that require UTF-8 encoding, this may be more useful than some specialized new UTF-8 string type.

Read the documentation on UTF-8 string literals.

Raw string literals

Starting with C# 11, it is easier to create multi-line strings using raw string literals, or any character that requires an escape sequence. Raw string literals do not need to use escape sequences. You can write the string, including the space format, and how you want the string to appear in the output. Raw string literals:

  • start with a sequence of at least three double-quote characters (""") at the beginning and end. Sequences of more than three consecutive characters can be used to start and end to support string literals that contain three (or more) repeating quote characters.
  • Single-line raw string literals require the opening and closing quote characters to be on the same line.
  • Multi-line raw string literals require the opening and closing quote characters to be on separate lines.
  • In a multi-line raw string literal, any spaces to the left of the closing quote are removed.

Raw string literals are separated by at least three double quotes:


var raw1 = """This\is\all "content"!""";
Console.WriteLine(raw1);

This prints:


This\is\all "content"!

If you need three or more"s becomes part of your content, just inexternaluse more"s. The beginning and end must match.


var raw2 = """""I can do ", "", """ or even """" double quotes!""""";

This makes it very easy to paste, maintain, and read at a glance what the text contains.

Multiline raw string literals can also truncate leading whitespace: the position of the trailing quote determines where the whitespace begins to be included in the output:


var raw3 = """
    <element attr="content">
      <body>
        This line is indented by 4 spaces.
      </body>
    </element>
    """;
//  ^white space left of here is removed

Since there are four spaces to the left of the closing quote, four spaces are removed from the beginning of each line of content, resulting in the following output:


<element attr="content">
  <body>
    This line is indented by 4 spaces.
  </body>
</element>

also,Raw string literals returnInterpolation is supported, read more about it in the documentation.

abstract static members

Support for static virtual members in interfaces was released in C# 11 and is in preview in C# 10. With it, you can now define a very simple mathematical interface:


public interface IMonoid<TSelf> where TSelf : IMonoid<TSelf>
{
    public static abstract TSelf operator +(TSelf a, TSelf b);
    public static abstract TSelf Zero { get; }
}

Anyone can now do this by providing an implementation for the two static members and putting themselves asTSelfType parameters are passed to implement this interface:


public struct MyInt : IMonoid<MyInt>
{
    int value;
    public MyInt(int i) => value = i;
    public static MyInt operator +(MyInt a, MyInt b) => new MyInt(a.value + b.value);
    public static MyInt Zero => new MyInt(0);
}

Importantly, how do you use these abstract operations? How do you call a virtual member when there is no instance to call? The answer is via generics:


T AddAll<T>(params T[] elements) where T : IMonoid<T>
{
    T result = T.Zero;
    foreach (var element in elements)
    {
        result += element;
    }
    return result;
}

type parameterTbyIMonoid<T>interface constraints, which make static virtual members of the interfaceZeroand+allowableTcalled on itself.

Now we can use someMyIntto call the generic method, while+andZeroThe correct implementation is passed in via the type parameter:


MyInt sum = AddAll<MyInt>(new MyInt(3), new MyInt(4), new MyInt(5));

In fact, .NET 7 provides a new namespace System.Numerics which is full of mathematical interfaces representing different combinations of operators and other static members you want to use: the “grown- up” version. All numeric types in .NET now implement these new interfaces — you can also add these interfaces for your own types.

It’s also worth noting that static virtual members are also useful for things other than math. More details can be found in the documentation on static abstract interface methods and general mathematics.Even if you don’t create interfaces with static virtual members, you can benefit from the improvements they make to the .NET library now and in the future.

List patterns

Pattern matching was introduced in C# 7, and since then it has grown into one of the most important and powerful control structures in the language; C# 11 added the list pattern.Starting with C# 11, an array or list can be matched against a sequence of patterns, as shown in the following example:


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

As shown in the previous example, a list pattern matches when each nested pattern matches the corresponding element of the input sequence. Any of the list modes can be used.To match any element, use the discard pattern, or, if you want to capture the element as well, use var pattern, as shown in the following example:


List<int> numbers = new() { 1, 2, 3 };

if (numbers is [var first, _, _])
{
    Console.WriteLine($"The first element of a three-item list is {first}.");
}
// Output:
// The first element of a three-item list is 1.

The preceding example matches the entire input sequence against a list pattern.To match only elements at the beginning or/and the end of the input sequence, use in list modeslice mode..as shown in the following example:


Console.WriteLine(new[] { 1, 2, 3, 4, 5 } is [> 0, > 0, ..]);  // True
Console.WriteLine(new[] { 1, 1 } is [_, _, ..]);  // True
Console.WriteLine(new[] { 0, 1, 2, 3, 4 } is [> 0, > 0, ..]);  // False
Console.WriteLine(new[] { 1 } is [1, 2, ..]);  // False

Console.WriteLine(new[] { 1, 2, 3, 4 } is [.., > 0, > 0]);  // True
Console.WriteLine(new[] { 2, 4 } is [.., > 0, 2, 4]);  // False
Console.WriteLine(new[] { 2, 4 } is [.., 2, 4]);  // True

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

Slice pattern matches zero or more elements. At most one slice mode can be used in list mode.

Subpatterns can also be nested within slice patterns, as shown in the following example:


void MatchMessage(string message)
{
    var result = message is ['a' or 'A', .. var s, 'a' or 'A']
        ? $"Message {message} matches; inner part is {s}."
        : $"Message {message} doesn't match.";
    Console.WriteLine(result);
}

MatchMessage("aBBA");  // output: Message aBBA matches; inner part is BB.
MatchMessage("apron");  // output: Message apron doesn't match.

void Validate(int[] numbers)
{
    var result = numbers is [< 0, .. { Length: 2 or 4 }, > 0] ? "valid" : "not valid";
    Console.WriteLine(result);
}

Validate(new[] { -1, 0, 1 });  // output: not valid
Validate(new[] { -1, 0, 0, 1 });  // output: valid

More details can be found in the relevant documentation.

Required members

The development team has worked on improving object creation and initialization in several releases, and C# 11 continues these improvements with required members.

When creating types that use object initializers, it was once impossible to specify that certain properties must be initialized.Now, lets say an attribute or field isrequired. This means that when an object of that type is created, it must be initialized by an object initializer:


public class Person
{
    public required string FirstName { get; init; }
    public string? MiddleName { get; init; }
    public required string LastName { get; init; }
}

Now create one without initializing the two required propertiesPersonit’s wrong:


var person = new Person { FirstName = "Ada" }; // Error: no LastName!

Check required members documentation for more information.

More details can be found in the official announcement.

#officially #released #News Fast Delivery

Leave a Comment

Your email address will not be published. Required fields are marked *