Latest Features of C# 13 - What You Need to Know!
- Raghav Kumar
- Mar 18
- 3 min read
Updated: Apr 6

C# 13 includes a wealth of changes and improvements. This article will walk you through the most impactful and broadly applicable features in .NET 9 and C# 13.
New Lock Object
The .NET 9 runtime includes a new type for thread synchronization, the System.Threading.Lock type. This type provides better thread synchronization through its API. The Lock.EnterScope() method enters an exclusive scope. The ref struct returned from that supports the Dispose() pattern to exit the exclusive scope.
// Before
public class LockExample
{
private readonly object _lock = new();
public void DoStuff()
{
lock (_lock)
{
Console.WriteLine("We're inside old lock");
}
}
}
// .NET 9
public class LockExample
{
private readonly Lock _lock = new();
public void DoStuff()
{
lock (_lock)
{
Console.WriteLine("We're inside .NET 9 lock");
}
}
}
params Collections
The params modifier isn't limited to array types. You can now use params with any recognized collection type.
// Before
static void WriteNumbersCount(params int[] numbers)
=> Console.WriteLine(numbers.Length);
// .NET 9
static void WriteNumbersCount(params ReadOnlySpan<int> numbers) =>
Console.WriteLine(numbers.Length);
static void WriteNumbersCount(params IEnumerable<int> numbers) =>
Console.WriteLine(numbers.Count());
static void WriteNumbersCount(params HashSet<int> numbers) =>
Console.WriteLine(numbers.Count);
Task.WhenEach
The Task.WhenEach method enables developers to handle asynchronous tasks more elegantly. The Task.WhenEach method allows us to iterate through tasks as they complete, without having to call Task.WaitAny repeatedly in order to select the next task that completes. Task.WhenEach will be beneficial, particularly in scenarios where you are waiting for tasks that differ in their execution times.
var tasks = Enumerable.Range(1, 5)
.Select(async i =>
{
await Task.Delay(new Random().Next(1000, 5000));
return $"Task {i} done";
})
.ToList();
// Before
while(tasks.Count > 0)
{
var completedTask = await Task.WhenAny(tasks);
tasks.Remove(completedTask);
Console.WriteLine(await completedTask);
}
// .NET 9
await foreach (var completedTask in Task.WhenEach(tasks))
Console.WriteLine(await completedTask);
Semi-Auto Properties
Semi-auto properties introduce the field keyword, allowing developers to add custom logic while still benefiting from the concise syntax of auto-properties. field keyword allowing you to access the backing field directly without having to define it manually.
// Before
public class MagicNumber
{
private int _number;
public int Number
{
get => _number * 10;
set {
_number = value;
}
}
}
// .NET 9
public class MagicNumber
{
public int Number
{
get => field;
set {
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(value), "Number cannot be negative");
field = value;
}
}
}
New escape sequence
C# 13 introduces a new escape sequence for the character you know as ESCAPE or ESC. You previously had to type this as a variation of \u001b. This new sequence is especially convenient when interacting with terminals with the VT100/ANSI escape codes to System.Console.
// Prior to C# 13
Console.WriteLine("\u001b[1mThis is a bold text\u001b[0m");
// With C# 13
Console.WriteLine("\e[1mThis is a bold text\e[0m");
Implicit index access
The implicit "from the end" index operator, ^, is now allowed in an object initializer expression. For example, you can now initialize an array in an object initializer as shown in the following code:
public class TimerRemaining
{
public int[] buffer { get; set; } = new int[5];
}
var countdown = new TimerRemaining()
{
buffer =
{
[^1] = 0,
[^2] = 1,
[^3] = 2,
[^4] = 3,
[^5] = 4
}
};
The TimerRemaining class includes a buffer array initialized to a length of 10. The preceding example assigns values to this array using the "from the end" index operator (^), effectively creating an array that counts down from 4 to 0.
Partial Properties
C# 13 adds partial properties. Like partial methods, their primary purpose is to support source generators. Partial methods have been available for many releases with additional improvements in C# 9. Partial properties are much like their partial method counterparts.
// before C# 13
[GeneratedRegex("abc|def")]
private static partial Regex AbcOrDefMethod();
if (AbcOrDefMethod().IsMatch(text))
{
// Take action with matching text
}
// C# 13
[GeneratedRegex("abc|def")]
private static partial Regex AbcOrDefProperty { get; }
if (AbcOrDefProperty.IsMatch(text))
{
// Take action with matching text
}
Additional helpful articles
For more C# 13 and .Net 9 features:
Happy coding and learning!
Comments