What's New in C# 11

A look at the key features introduced in C# 11 with the release of .NET 7, including raw string literals, generic math, and required members, and how they improve the developer experience.

With the release of .NET 7 came a new version of the C# language: C# 11. This release didn't bring a single, massive headline feature, but instead delivered a collection of highly requested and practical improvements that make the language more expressive, convenient, and powerful.

Let's dive into some of the most impactful new features in C# 11.

1. Raw String Literals

This is perhaps the most celebrated quality-of-life improvement. Raw string literals make it dramatically easier to work with strings that contain special characters, like JSON, XML, or regular expressions, without having to escape everything.

A raw string literal starts and ends with at least three double-quotes (""").

Before C# 11 (with JSON):

string jsonString = "{\n  \"name\": \"Alice\",\n  \"age\": 30\n}";

With C# 11:

string jsonString = """
{
  "name": "Alice",
  "age": 30
}
""";

No more escaping quotes or backslashes! The number of quotes you use determines how many quotes are needed to escape. For example, if your string contains """, you can start and end your literal with """".

2. Generic Math Support

This is a powerful, more advanced feature that allows you to write generic algorithms that work with any number type. This is enabled by a new feature in .NET 7 called static virtual members in interfaces.

Now, you can define interfaces with static members, like operators (+, -, *, /). The numeric types in .NET 7 (like int, double, decimal) now implement these new interfaces, such as INumber<T>.

Example: A generic Sum method.

// This method can sum an array of any type that implements INumber<T>
public static T Sum<T>(T[] values) where T : INumber<T>
{
    T result = T.Zero; // T.Zero is a static property on INumber<T>
    foreach (var value in values)
    {
        result += value; // The + operator is defined on the interface
    }
    return result;
}

// You can now call it with different number types
int[] ints = { 1, 2, 3 };
Console.WriteLine(Sum(ints)); // Output: 6

double[] doubles = { 1.1, 2.2, 3.3 };
Console.WriteLine(Sum(doubles)); // Output: 6.6

This allows for a new level of abstraction and code reuse when writing mathematical or numerical algorithms.

3. Required Members

This feature helps you create more robust and predictable types by ensuring that certain properties or fields are always initialized.

By adding the required modifier to a property, you are telling the compiler that this property must be set by the caller during object initialization.

public class User
{
    public required int Id { get; set; }
    public required string Username { get; set; }
    public string? Bio { get; set; } // Bio is optional
}

// This will now cause a compile-time error because Username is missing
var user = new User { Id = 1 };

// This is valid
var user2 = new User { Id = 2, Username = "bob" };

This is particularly useful for reducing the need for constructors just to ensure properties are set, and it helps prevent NullReferenceException errors for non-nullable properties.

4. Auto-Default Structs

In older versions of C#, you had to explicitly initialize all fields in a struct's constructor. C# 11 removes this requirement. If you don't assign a value to a field in a struct constructor, the compiler will automatically initialize it to its default value.

Before C# 11 (Compiler Error):

public struct Point
{
    public int X { get; set; }
    public int Y { get; set; }

    public Point(int x)
    {
        X = x; // Error: Y is not assigned
    }
}

With C# 11 (Valid):

public struct Point
{
    public int X { get; set; }
    public int Y { get; set; }

    public Point(int x)
    {
        X = x;
        // Y is automatically initialized to its default value (0)
    }
}

Conclusion

C# 11 is a release focused on developer productivity and code clarity. Features like raw string literals and required members are immediate wins that will make your code cleaner and more robust. While generic math is a more advanced feature, it unlocks new possibilities for writing highly abstract and reusable code. Together, these improvements continue the evolution of C# as a modern, powerful, and enjoyable language to work with.