Microsoft released C# 11 on November 2022. C# 11 has great new features and enhancements. You will like these features, especially Required Member and Raw String Literals.
You can find the source code for this post on my GitHub repository.
Let's discover C# 11.
1 - Raw String Literals
I have been waiting for this feature for a long time. No more escaping strings.
You can create multiline strings as below.
string rawStringLiteralDelimiter = """"
Raw string literals are delimited
by a string of at least three double quotes,
like this: """
"""";
A raw string literal starts with at least three double-quote (""") characters. It 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.
string singleLine = """Friends say "hello" as they pass by.""";
Embedding XML and JSON string is a piece of cake now.
string embeddedXML = """
<element attr = "content">
<body style="normal">
Here is the main text
</body>
<footer>
Excerpts from "An amazing story"
</footer>
</element >
""";
string jsonString = """
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot",
"DatesAvailable": [
"2019-08-01T00:00:00-07:00",
"2019-08-02T00:00:00-07:00"
],
"TemperatureRanges": {
"Cold": {
"High": 20,
"Low": -10
},
"Hot": {
"High": 60,
"Low": 20
}
},
"SummaryWords": [
"Cool",
"Windy",
"Humid"
]
}
""";
The following examples demonstrate the compiler errors reported based on these rules:
// CS8997: Unterminated raw string literal.
var multiLineStart = """This
is the beginning of a string
""";
// CS9000: Raw string literal delimiter must be on its own line.
var multiLineEnd = """
This is the beginning of a string """;
// CS8999: Line does not start with the same whitespace as the closing line
// of the raw string literal
var noOutdenting = """
A line of text.
Trying to outdent the second line.
""";
2 - Required Members
You can add the required modifier to properties and fields to enforce constructors and callers to initialize those values.
Previously, I was using constructors to ensure obligatory properties were set. However, this method increased the complexity and maintainability of my code. Now, with the help of the required modifier, this is easier.
public class Book
{
public required string Title { get; set; }
public required string Author { get; set; }
public int? Pages { get; set; }
}
// This will compile
var solito = new Book { Author = "Javier Zamora", Title = "Solito: A Memoir" };
// This will not compile
var solitoError = new Book { Author = "Javier Zamora" };
You will also see the required properties on IntelliSense.
3 - Auto Default Structs
The C# 11 compiler ensures that all fields of a struct type are initialized to their default value as part of executing a constructor.
public struct Coordinate
{
public double Longitude { get; set; }
public double Latitude { get; set; }
public override string ToString()
{
return $"Longitude : {Longitude}, Latitude : {Latitude}";
}
}
Console.WriteLine(new Coordinate());
// Output is as below
// Longitude : 0, Latitude : 0
4 - File Local Type
Beginning in C# 11, you can use the file access modifier to create a type whose visibility is scoped to the source file in which it is declared. This feature helps source generator authors avoid naming collisions.
5 - Generic Attributes
You can declare a generic class whose base class is System.Attribute. This feature provides a more convenient syntax for attributes that require a System.Type parameter.
public class GenericAttribute<T> : Attribute {
public T Name { get; private set; }
public GenericAttribute(T name)
{
Name = name;
}
}
[Generic<int>(10)]
public class GenericClass1
{
}
[Generic<string>("Hello World")]
public class GenericClass2
{
}
// Below will not compile
[Generic<T>()]
public class GenericClass3<T>
{
}
6 - List Patterns
In previous versions of C#, pattern matching was introduced. In C#11 pattern matching was extended to use lists and arrays.
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
// Discard pattern "_"
// Slice pattern ".."
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
7 - Utf8 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.
string s1 = "hello"u8; // Error
var s2 = "hello"u8; // Okay and type is ReadOnlySpan<byte>
ReadOnlySpan<byte> s3 = "hello"u8; // Okay.
byte[] s4 = "hello"u8; // Error - Cannot implicitly convert type 'System.ReadOnlySpan<byte>' to 'byte[]'.
byte[] s5 = "hello"u8.ToArray(); // Okay.
Span<byte> s6 = "hello"u8; // Error - Cannot implicitly convert type 'System.ReadOnlySpan<byte>' to 'System.Span<byte>'.
Conclusion
These are several features of C# 11. If you want to learn more, you can go to "What's new in C# 11".
Happy Coding!
References
https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/file
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/strings