Other modern C# features
C# has evolved significantly, adding features that improve conciseness, readability and safety. Some have their own notes: pattern matchingPattern matchingTest the shape of a value and pull data out of it in one step.Opens in a new tab, switch expressionsSwitch expressionsA switch that returns a value.Opens in a new tab, nullable reference typesNullable reference typesReference types are non-nullable by default, and the compiler warns when that might not hold.Opens in a new tab, recordsRecordsA class for immutable data, with value-based equality generated for you.Opens in a new tab and init-only settersInit-only settersA property you can set while creating the object, and never again.Opens in a new tab. These are the others.
- Top-level statements (C# 9.0+) simplify writing simple console applications or scripts. You can put code directly at the file level, without a
Programclass andMainmethod. - File-scoped namespaces (C# 10.0+) reduce indentation and boilerplate. You declare a namespace for the whole file with
namespace MyNamespace;at the top. - Global using directives (C# 10.0+) apply
usingdirectives project-wide. You prefix them withglobalin any.csfile, often a dedicatedGlobalUsings.cs. - Required properties (C# 11.0+) must be initialized by the caller when an object is created, if they are marked with the
requiredmodifier. That keeps essential properties from being left unassigned, and makes object construction safer. - Raw string literals (C# 11.0+) simplify multi-line strings, and strings with characters that would normally need escaping, such as JSON, XML or regex patterns. You write them with triple quotes,
"""...""". - Collection expressions (C# 12+) are a concise, unified syntax for creating common collection types like arrays,
List<T>andSpan<T>, using square brackets. - Primary constructors (C# 12+) declare class and struct constructors more concisely, by listing the parameters directly in the type declaration. The parameters are in scope throughout the class body, and can be used to initialize properties or fields.
public class Point(int x, int y) { public int X { get; } = x; public int Y { get; } = y; }At a glance
| Feature | C# version | Core benefit | Brief example or use case |
|---|---|---|---|
| Pattern matching | 7.0+ | More expressive and concise conditional logic based on data shape. | if (obj is string s), value switch { > 0 => "Positive", _ => "Non-positive" } |
| Nullable reference types | 8.0+ | Helps prevent NullReferenceException through compile-time analysis. |
string? name = null; string title = "Mr"; |
| Switch expressions | 8.0+ | Concise, expression-based switch logic. | var desc = obj switch { Circle c => $"Circle r={c.R}", _ => "Other" }; |
| Records | 9.0+ | Concise syntax for immutable data types with value-based equality. | public record Point(int X, int Y); (DTOs, value objects) |
| Init-only setters | 9.0+ | Allows property assignment only during object initialization. | public int Id { get; init; } (for immutable properties) |
| Top-level statements | 9.0+ | Simplifies simple programs and scripts by removing the Program class and Main boilerplate. |
Console.WriteLine("Hello!"); directly in a .cs file. |
| File-scoped namespaces | 10.0+ | Reduces nesting for namespace declarations. | namespace MyProject.MyFeature; at the top of a file. |
| Global using directives | 10.0+ | Applies using directives to all files in a project. |
global using System.Linq; |
| Record structs | 10.0+ | Value-type semantics for records. | public record struct Coordinates(double Lat, double Lon); |
| Required properties | 11.0+ | Ensures properties are initialized by the caller. | public required string Name { get; set; } |
| List patterns | 11.0+ | Pattern matching for elements in lists and arrays. | if (arr is [1, var x, ..]) { /*... */ } |
| Raw string literals | 11.0+ | Easier creation of multi-line strings or strings with special characters. | string json = """{ "name": "test" }"""; |
| Collection expressions | 12+ | Concise syntax for creating collections. | — |
| Primary constructors | 12+ | Concise syntax for class and struct constructors and parameter capture. | public class User(int id, string name) { /*... */ } |
The steady evolution of C#, with features like records, init-only setters, nullable reference types and advanced pattern matching, shows a strong trend toward more robust, concise and immutable code. That improves developer productivity, and it helps reduce common bugs from null references and mutable state.
Records come in two kinds, a class and a struct: see the records noteRecordsA class for immutable data, with value-based equality generated for you.Opens in a new tab and choosing a typeChoosing a typeClass, struct, interface, record class or record struct.Opens in a new tab.
Sources
- milanm/csharp-cheatsheet, GitHub · accessed May 16, 2025
- Nullable reference types, C#, Microsoft Learn · accessed May 16, 2025