DEV Community

Cover image for New Features in C# 13
Anton Martyniuk
Anton Martyniuk

Posted on • Originally published at antondevtips.com on

New Features in C# 13

.NET 9 and C# 13 were released on November 12, 2024.
In this blog post, I want to show you a list of new features in C# 13.

On my website: antondevtips.com I share .NET and Architecture best practices.
Subscribe to become a better developer.
Download the source code for this blog post for free.

1. Params Collections

Previously, when using params keyword you were able to use only arrays:

PrintNumbers(1, 2, 3, 4, 5);

public void PrintNumbers(params int[] numbers)
{
    // ...
}
Enter fullscreen mode Exit fullscreen mode

C# 13 introduces Params Collections, allowing you to use the following concrete types:

  • Arrays
  • IEnumerable
  • List
  • Span

For example, you can use a list:

List<int>numbers = [ 1, 2, 3, 4, 5 ];
PrintNumbers(numbers);

public void PrintNumbers(params List<int> numbers)
{
    // ...
}
Enter fullscreen mode Exit fullscreen mode

And further in PrintNumbers method you can use, for example, LINQ methods over the params collection.

2. New Lock Object

System.Threading.Lock is a new thread synchronization type in .NET 9 runtime.
It offers better and faster thread synchronization through the API.

How it works:

  • Lock.EnterScope() method enters an exclusive scope and returns a ref struct.
  • Dispose method exits the exclusive scope

When you replace object with a new Lock type inside a lock statement, it starts using a new thread synchronization API.
Rather than using old API through System.Threading.Monitor.

public class LockClass
{
    private readonly System.Threading.Lock _lockObj = new();

    public void Do(int i)
    {
        lock (_lockObj)
        {
            Console.WriteLine($"Do work: {i}");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Partial Properties and Indexers

C# 13 adds support for partial Properties and Indexers.
Partial Properties and Indexers let you split the logic for getters, setters, and index accessors across multiple files.

This feature can be useful particularly for source generators.

public partial class C
{
    // Declaring declaration
    public partial string Name { get; set; }
}

public partial class C
{
    // implementation declaration:
    private string _name;
    public partial string Name 
    {
        get => _name;
        set => _name = value;
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Overload Resolution Priority

With Overload Resolution Priority, you can select the exact order of methods that will be shown in the code editor suggestions.

using System.Runtime.CompilerServices;

public static class MappingExtensions
{
    [OverloadResolutionPriority(1)]
    public static Product ToResponse(this Product entity, Result result)
        => new Product(...);

    [OverloadResolutionPriority(0)]
    public static Product ToResponse(this Product entity)
        => new Product(...);
}

Enter fullscreen mode Exit fullscreen mode

5. Implicit Index Access

C# 13 now allows using "from the end" index operator (^) in an object initializer expression.

For example, you can now initialize an array that counts down from 4 to 0:

var countdown = new TimerRemaining()
{
    buffer =
    {
        [^1] = 0,
        [^2] = 1,
        [^3] = 2,
        [^4] = 3,
        [^5] = 4
    }
};
Enter fullscreen mode Exit fullscreen mode

In previous C# versions, you were not able to use the ^ operator in object initializers.

6. New Escape Sequence

In previous C# versions you needed to use \u001b or \x1b for the ESCAPE character.
But this code had potential for collisions with other sequences.

In C# 13 you can use \e as a character for the ESCAPE character.

var text = "\x1b[1mThis text is bold\x1b[0m";
Console.WriteLine(text);

var text = "\e[1mThis text is bold\e[0m";
Console.WriteLine(text);
Enter fullscreen mode Exit fullscreen mode

7. Ref and Unsafe in Iterators and Async Methods

Before C# 13, the following methods couldn't declare local ref variables or have an unsafe context:

  • async methods
  • iterator methods - methods that use yield return

C# 13 removes these restrictions, and you can use Spans (that are ref structs) inside async methods:

public async Task ProcessTextAsync()
{
    string text = await GetTextAsync(...);

    ReadOnlySpan<char> span = text;
    span = span.Slice(2, 10);

    Console.WriteLine(span.ToString());
}
Enter fullscreen mode Exit fullscreen mode

However, those ref struct and unsafe variables can't be accessed across an await boundary.
Neither can they be accessed across a yield return boundary.

8. Allows Ref Struct

Before C# 13, ref struct types couldn't be declared as the type argument for a generic type or method.

Now, generic type declarations can add an anti-constraint, allows ref struct.
This anti-constraint declares that the type argument supplied for that type parameter can be a ref struct type.
The compiler enforces ref safety rules on all instances of that type parameter.

This enables types such as System.Span<T> and System.ReadOnlySpan<T> to be used with generic algorithms, where applicable.

public class MyClass<T> where T : allows ref struct
{
    // Use T as a ref struct:
    public void Do(scoped T p)
    {
        // The parameter p must follow ref safety rules
    }
}
Enter fullscreen mode Exit fullscreen mode

9. Ref Struct Interfaces

Starting from C# 13, ref struct can now implement interfaces.

However, to ensure ref safety rules, a ref struct type can't be converted to an interface type.
That conversion is a boxing conversion, and could violate ref safety.
From that rule, ref struct types can't declare methods that explicitly implement an interface method.
Also, ref struct types must implement all methods declared in an interface, including those methods with a default implementation.

public interface ICoffee
{
}

public ref struct Coffee : ICoffee
{
}

Coffee coffee = new Coffee();

// This is not allowed
ICoffee coffee2 = (ICoffee) coffee;
Enter fullscreen mode Exit fullscreen mode

On my website: antondevtips.com I share .NET and Architecture best practices.
Subscribe to become a better developer.
Download the source code for this blog post for free.

Top comments (0)