Skip to content

C# 9

This release is where many modern data modeling patterns became much easier to express.

Modernization Overview

Records are a concise way to model immutable, value-like data.

public record Money(decimal Amount, string Currency);

Legacy equivalent:

public sealed class Money
{
public decimal Amount { get; }
public string Currency { get; }
public Money(decimal amount, string currency)
{
Amount = amount;
Currency = currency;
}
}

Records also provide value-based equality by default.

Init-only setters allow properties to be assigned during object initialization, but not mutated later.

public class Person
{
public string FirstName { get; init; } = "";
public string LastName { get; init; } = "";
}

Usage:

var person = new Person
{
FirstName = "Ada",
LastName = "Lovelace"
};

This is often a better fit than fully mutable setters on data models.

Target-typed new removes repeated type names when the target type is already clear.

Dictionary<string, List<int>> lookup = new();

Legacy style:

Dictionary<string, List<int>> lookup = new Dictionary<string, List<int>>();

Relational and logical patterns make range checks more readable.

if (percentage is >= 0 and <= 100)
{
Console.WriteLine("Valid");
}

Legacy style:

if (percentage >= 0 && percentage <= 100)
{
Console.WriteLine("Valid");
}

Top-level statements reduce ceremony for small programs, demos, and utilities.

using System;
Console.WriteLine("Hello, modern C#");

This feature is convenient for small tools, but many larger applications still prefer explicit Program types for structure and discoverability.