The features I reach for most, grouped by the C# version that introduced them.
// Records – value equality, immutability, with-expressions
public record Person(string Name, int Age);
var older = person with { Age = person.Age + 1 };
// Init-only setters
public class Options { public string Url { get; init; } = ""; }
// File-scoped namespace + global usings (C# 10)
namespace MyApp.Orders;
global using System.Text.Json;
// Target-typed new
List<string> names = new();
// Required members
public class User { public required string Email { get; init; } }
// Raw string literals
var json = """
{ "name": "Jeff", "role": "Architect" }
""";
// List patterns
if (args is [var first, .., var last]) { /* ... */ }
// Primary constructors for classes
public class OrderService(IOrderRepository repo, ILogger<OrderService> log)
{
public Task<Order?> Get(int id) => repo.FindAsync(id);
}
// Collection expressions
int[] numbers = [1, 2, 3];
List<int> more = [..numbers, 4, 5];
// Alias any type
using Point = (int X, int Y);
// params collections (C# 13)
void Log(params ReadOnlySpan<string> parts) { }
// The field keyword (C# 14) – no manual backing field
public string Name
{
get;
set => field = value?.Trim() ?? "";
}
// Null-conditional assignment (C# 14)
customer?.Address = newAddress;
string Describe(object o) => o switch
{
null => "null",
int n when n < 0 => "negative int",
int => "int",
string { Length: 0 } => "empty string",
Person { Age: >= 18 } p => $"adult {p.Name}",
_ => "something else"
};
<!-- .csproj -->
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
string? may be null; string should not bevalue! is the null-forgiving operator (use sparingly)ArgumentNullException.ThrowIfNull(arg);Which C# version you get depends on your target framework: .NET 8 → C# 12, .NET 9 → C# 13, .NET 10 → C# 14.