What's New in C# 13

By FoxLearn 11/19/2024 8:19:01 AM   138
C# 13, released with .NET 9, brings several new features designed to boost developer productivity and improve code efficiency.

Key Features of C# 13

1. Enhanced params Collections:

The `params` keyword in C# 13 now supports any collection type, not just arrays. This allows developers to pass a variable number of arguments using types like `List<T>`, `Span<T>`, and `IEnumerable<T>`, enhancing method signatures and usability.

For example:

internal class Program
{
    static void Main(string[] args)
    {
        Print(1, 2, 3, 4, 5);
    }

    // using params
    static void Print(params int[] numbers)
    {
        Console.WriteLine("Numbers: " + string.Join(",", numbers));
    }
}

2. New Escape Sequence:

C# 13 introduces the `\e` escape sequence for the ESCAPE character, making it easier and less error-prone to work with ANSI escape codes, particularly in terminal applications.

3. Partial Properties and Indexers:

C# 13 allows developers to declare partial properties and indexers, improving code organization across multiple files. This feature is especially helpful in large projects or when working with auto-generated code.

Partial properties in C# 13 allow you to define a property in one part of a partial class and implement it in another, enabling better code organization across multiple files.

// File: FileDownload.cs
public partial class FileDownload
{
    public partial int Size { get; set; }
}

// File: FileDownload.Implementation.cs
public partial class FileDownload
{
    const int _minSize = 1024;
    int _size;

    public partial int Size
    {
        get => _size < _minSize ? _minSize : _size;
        set => _size = value;
    }
}

This separation enables better code organization, particularly when managing generated code, by allowing different parts of a class to be defined and implemented separately.

Partial indexers in C# 13 work similarly to partial properties, allowing you to declare an indexer in one part of a class and implement it in another, improving code organization.

// File: VList.cs
public partial class VList
{
    public partial int this[int index] { get; set; }
}

// File: VList.Implementation.cs
public partial class VList
{
    int[] _items = new int[10];

    public partial int this[int index]
    {
        get => _items [index];
        set => _items [index] = value;
    }
}

The logic for getting and setting values can be provided in another file, ensuring clarity and a clear separation of concerns.

4. Overload Resolution Priority Attribute:

C# 13 introduces a feature that allows library authors to designate one method overload as preferred, improving method resolution in complex scenarios.

The `OverloadResolutionPriorityAttribute`, found in the `System.Runtime.CompilerServices` namespace, can be applied to methods, properties, and constructors to indicate their priority during overload resolution.

How to use the OverloadResolutionPriorityAttribute in C#?

using System;
using System.Runtime.CompilerServices;

public class MyMath
{
    [OverloadResolutionPriority(1)]
    public static void Calculate(double number = 15)
    {
        Console.WriteLine($"Calculate value with: {number}");
    }

    [OverloadResolutionPriority(2)]
    public static void Calculate(int number = 5)
    {
        Console.WriteLine($"Calculate value with: {number}");
    }
}

//Program.cs
Math.Calculate(5);
Math.Calculate(7);

The `OverloadResolutionPriorityAttribute` helps resolve ambiguities in method calls, particularly with numeric types that can implicitly convert (e.g., `int` to `double`). It allows developers to assign lower priorities to new overloads, ensuring legacy methods remain preferred and avoiding breaking changes.

If multiple overloads share the same priority, developers may encounter ambiguity warnings or errors during compilation. While the `OverloadResolutionPriorityAttribute` improves usability in specific cases, overusing it can lead to confusion about which method will be invoked, so it should be applied judiciously.

5. Allowing Ref Structs:

C# 13 introduces a major enhancement allowing `ref struct` types to be used as type arguments in generics. Previously, `ref struct` types were not permitted in generics, but with the new `allow ref struct` constraint, developers can now specify that a generic type or method can accept a `ref struct`. This change enhances flexibility and enables the compiler to enforce reference safety rules for all instances of the type parameter.

For example:

public ref struct ConversionRequest
{
    public int Value;

    public ConversionRequest(int value)
    {
        Value = value;
    }

    public void Display()
    {
        Console.WriteLine($"My value: {Value}");
    }
}

It enables developers to create more versatile and reusable components without compromising performance or safety. The compiler enforces safety rules, preventing issues like heap allocations and boxing. Since `ref structs` are allocated on the stack, they ensure efficient memory usage, making them particularly valuable in performance-critical applications.

6. New Lock Type:

C# 13 introduces a new `Lock` type in `System.Threading` to improve thread synchronization. The `Lock.EnterScope()` method simplifies entering critical sections by automatically releasing the lock when the scope ends, even in the event of an exception.

This reduces boilerplate code and minimizes the risk of deadlocks or resource contention. The `Lock` type integrates with the existing `lock` statement, making the syntax more intuitive and enhancing code readability and maintainability.

For example:

Lock lock = new Lock();

using (lock.EnterScope()) {   
    //Thread-safe code here, only one thread can execute this at a time.
}

C# 13 enhances flexibility, performance, and usability for developers, aiming to streamline coding practices, reduce errors, and improve overall efficiency. Developers are encouraged to explore these features with Visual Studio 2022 or the .NET 9 Preview SDK to fully benefit from the improvements in this release.