C# 11 Release
Microsoft claims that the C# 11 version officially replaced the C# 10 version was launched on November 8, 2022.
What's New in C# 11
Microsoft has included some intriguing features in this version of C# 11, including Raw string literals, Generic attributes, UTF-8 string literals, Newlines in string interpolation expressions, List patterns File-local types, Required members, Auto-default structs, Pattern match Span<char> on a constant string, Extended nameof scope, Numeric IntPtr, ref fields, and scoped ref, Improved method group conversion to delegate, Warning wave 7.
In this article, we learn some features with examples.
How to use C#11
Users can download the latest version of Visual Studio 2022. You can also try all these features with the .NET 7 SDK.
Raw string literals
Raw string literals are a new format for string literals. This 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:
//Example: Raw string literals
string longStr = """
Until recently, the prevailing view assumed lorem ipsum was born as a nonsense text.
"It's not Latin, though it looks like it, and it actually says nothing,"
Before & After magazine answered a curious reader,
"Its ‘words’ loosely approximate the frequency with which letters occur in English,
which is why at a glance it looks pretty real."
Some have "quoted text" in them.
""";
Console.WriteLine(longStr);
Output
//Example: Raw string literals
float longitude = 72.579946f, latitude = 23.103942f;
var location = $$"""
You are at {{{longitude}}, {{latitude}}}
""";
Console.WriteLine(location);
Output
//Example: Several lines in string interpolation
int temp = 24;
string message = $"Human sensation to temperature {temp} is {temp switch
{
> 45 => "Death",
> 35 => "Too hot",
> 25 => "Nice",
> -10 => "Cold",
<= -10 => "Death"
}}";
Console.WriteLine(message);
Output
UTF-8 string literals
We 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.
//Example: UTF-8 string literals
var utf8BeforeCS11 = Encoding.UTF8.GetBytes("Hello C#11");
var utf8InCS11 = "Hello C#11"u8.ToArray();
Output
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.
//Eample: List patterns
int[] numbers = { 1, 2, 3 };
var pattern1 = numbers is [1, 2, 3];
var pattern2 = numbers is [1, 2];
var pattern3 = numbers is [1, 2, _];
var pattern4 = numbers is [1, 2, > 2];
var pattern5 = numbers is [0 or 1, <= 2, > 3];
Console.WriteLine($"[{string.Join(',', numbers)}] is [1, 2, 3]: {pattern1}");
Console.WriteLine($"[{string.Join(',', numbers)}] is [1, 2]: {pattern2}");
Console.WriteLine($"[{string.Join(',', numbers)}] is [1, 2, _]: {pattern3}");
Console.WriteLine($"[{string.Join(',', numbers)}] is [1, 2, >2]: {pattern4}");
Console.WriteLine($"[{string.Join(',', numbers)}] is [0 or 1, <= 2, > 3]: {pattern5}");
Output
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.
Public static void FullName(string firstName, string lastName) {
if (firstName is null) {
throw new ArgumentNullException(nameof(firstName));
}
if (lastName is null) {
throw new ArgumentNullException(nameof(lastName));
}
// Body of the method
}
Public static void FullName(string firstName!!, string lastName!!) {
// Body of the method
}
File local types
We can use the file access modifier to create a type whose visibility is scoped to the source file in which it is declared. The file modifier restricts a top-level type's scope and visibility to the file in which it's declared. The file modifier will generally be applied to types written by a source generator. File-local types provide source generators with a convenient way to avoid name collisions among generated types. The file modifier declares a file-local type,
The following example shows a public type that uses a file-local type to provide a worker method. In addition, the public type implements a file-local interface implicitly:
// In ExampleFilelocalTypes1.cs:
file interface IExampleFilelocalTypes1 {
int ProvideAnswer();
}
file class HiddenExampleFilelocalTypes1 {
public int Work() => 42;
}
public class ExampleFilelocalTypes: IExampleFilelocalTypes1 {
public int ProvideAnswer() {
var worker = new HiddenExampleFilelocalTypes1();
return worker.Work();
}
}
In another source file, you can declare types with the same names as file-local types. The file-local types aren't visible:
// In ExampleFilelocalTypes2.cs:
public class ExampleFilelocalTypes2 {
public int ProvideAnswer2() {
var worker = new HiddenExampleFilelocalTypes1();
return worker.Work();
}
}
Output