C# 11 officially released

Produced | OSC Open Source Community (ID: oschina2013)

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, you can now simply append a u8 suffix to your string literals to instantly 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 — in the form of ReadOnlySpan<byte> . 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: https://ift.tt/gDq3mQY

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:

  • Begin and end with a sequence of at least three double quote characters ( """ ). You can start and end sequences with more than three consecutive characters 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” to be part of your content, just use more " s” externally. 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:

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

</element>

Also, Raw string literals support interpolation, read more about it in the documentation: https://ift.tt/qemgI8F

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:

 publicinterfaceIMonoid<TSelf> whereTSelf: IMonoid<TSelf>{ publicstaticabstractTSelfoperator+(TSelf a, TSelf b); publicstaticabstractTSelfZero{ get; }

}

Anyone can now implement this interface by providing implementations for the two static members and passing themselves as TSelf type parameters:

 publicstructMyInt: IMonoid<MyInt>{ intvalue; publicMyInt(int i) => value= i; publicstaticMyIntoperator+(MyInt a, MyInt b) => newMyInt(a.value+ b.value); publicstaticMyIntZero=> newMyInt(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;

}

The type parameter T is constrained by the IMonoid<T> interface, which makes the interface’s static virtual members Zero and + callable on T itself.

Now we can call the generic method with some MyInt , with the correct implementation of + and Zero passed in as a type parameter:

 MyInt sum = AddAll<MyInt>(newMyInt(3), newMyInt(4), newMyInt(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]); // TrueConsole.WriteLine(numbers is [1, 2, 4]); // FalseConsole.WriteLine(numbers is [1, 2, 3, 4]); // FalseConsole.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 disclaimer pattern, or, if you want to capture the element as well, use the 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 at the end of the input sequence, use slice mode .. in list mode, as shown in the following example:

 Console.WriteLine(new[] { 1, 2, 3, 4, 5 } is [> 0, > 0, ..]); // TrueConsole.WriteLine(new[] { 1, 1 } is [_, _, ..]); // TrueConsole.WriteLine(new[] { 0, 1, 2, 3, 4 } is [> 0, > 0, ..]); // FalseConsole.WriteLine(new[] { 1 } is [1, 2, ..]); // FalseConsole.WriteLine(new[] { 1, 2, 3, 4 } is [.., > 0, > 0]); // TrueConsole.WriteLine(new[] { 2, 4 } is [.., > 0, 2, 4]); // FalseConsole.WriteLine(new[] { 2, 4 } is [.., 2, 4]); // TrueConsole.WriteLine(new[] { 1, 2, 3, 4 } is [>= 0, .., 2 or 4]); // TrueConsole.WriteLine(new[] { 1, 0, 0, 1 } is [1, 0, .., 0, 1]); // TrueConsole.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:

 voidMatchMessage(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.voidValidate(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

The text and pictures in this article are from the OSC open source community

loading.gif

This article is reprinted from https://www.techug.com/post/csharp-11-release/
This site is for inclusion only, and the copyright belongs to the original author.