Skip to content

C# 12

It combines readability improvements with some targeted performance-oriented tools.

Modernization Overview

Primary constructors bring constructor parameters directly into the type declaration.

public class Worker(ILogger<Worker> logger)
{
public void Run() => logger.LogInformation("Running");
}

Legacy style:

public class Worker
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
public void Run() => _logger.LogInformation("Running");
}

This feature is concise, but explicit constructors can still be clearer in more complex types.

Collection expressions provide a uniform syntax for arrays and many collection targets.

int[] values = [1, 2, 3];
List<string> names = ["Ada", "Grace"];
var combined = [..names, "Linus"];

Legacy style:

int[] values = new[] { 1, 2, 3 };
List<string> names = new List<string> { "Ada", "Grace" };

Inline arrays are aimed at specialized low-level scenarios where a small fixed-size buffer is useful.

[System.Runtime.CompilerServices.InlineArray(8)]
public struct Buffer8
{
private byte _element0;
}

This is not a general-purpose replacement for arrays, but it is relevant in tight performance-sensitive code.

Alias any type makes complex tuple or array signatures easier to name.

using Measurement = (double Value, string Unit);
Measurement length = (12.5, "cm");

This can improve readability when the same complex type appears in multiple places.

ref readonly parameters allow passing large structs efficiently while preventing mutation.

public static decimal Sum(in Money value1, in Money value2)
{
return value1.Amount + value2.Amount;
}

This is most useful for larger value types or performance-sensitive APIs.