# Modern C# Cheat Sheet: C# 10 to C# 14

> Source: <https://blog.devgenius.io/modern-c-cheat-sheet-c-10-to-c-14-1793a8e612a8?source=rss----4e2c1156667e---4>
> Published: 2026-11-04 16:17:54+00:00

*Note: this article was drafted with AI assistance and reviewed by Softinbit Technology before publishing. It is part of the series* *The .NET Field Guide**.*

***TL;DR*** *— Every C# feature from C# 10 (.NET 6) through C# 14 (.NET 10) condensed into single-purpose before/after snippets tested on .NET 10 SDK (10.0.300). — Fast lookups for top idioms: file-scoped namespaces, raw strings, required properties, primary constructors, collection expressions, Lock, and field-backed properties. — Clear guidance on target framework defaults and compiler diagnostics (**#error version) without fragile* *LangVersion overrides.*

***Level:*** *Intermediate ·* ***Prerequisites:*** *Working knowledge of C# 9 / .NET 5 or earlier ·* ***Reading time:*** *13 min*

Your team just moved Store.Api to .NET 10, and a reviewer comments "use field here" on a property you wrote the C# 9 way. This sheet is for developers who learned C# a few versions ago and want the short version of C# 10 (.NET 6) through C# 14 (.NET 10). Every snippet was compiled with the .NET 10 software development kit (SDK), version 10.0.300; outputs and error messages are real.

The lookups people make most:

The compiler picks the default C# version from your target framework (the “Default for” column above). You do not need LangVersion in the project file.

```
error CS8304: Compiler version: '5.6.0-2.26230.102 (caa81fa4...)'. Language version: 14.0.
```

Using a C# 14 feature when the language version is 13 (for example net9.0 built with the .NET 10 SDK):

```
error CS9260: Feature 'field keyword' is not available in C# 13.0. Please use language version 14.0 or greater.
```

**Global usings.** Import a namespace once for the whole project.

```
// Before: repeated at the top of every fileusing Store.Api.Orders;
// After: once, in GlobalUsings.cs (or <Using Include="Store.Api.Orders" /> in the .csproj)global using Store.Api.Orders;
```

**File-scoped namespaces.** One less indentation level per file.

```
// Beforenamespace Store.Api.Orders{    public sealed class OrderService { }}
// Afternamespace Store.Api.Orders;
public sealed class OrderService { }
```

**Record structs.** Value-type records with generated equality, ToString, and with.

```
// Before: a struct with hand-written Equals, GetHashCode, ==, and ToString// Afterpublic readonly record struct Money(decimal Amount, string Currency);
js
var a = new Money(10m, "TRY");var b = a with { Amount = 12m };Console.WriteLine(a);                           // Money { Amount = 10, Currency = TRY }Console.WriteLine(a == new Money(10m, "TRY"));  // True
```

**Extended property patterns.** Reach into nested members with a dot.

```
// Beforeif (order is { Lines: { Count: > 0 } }) { }
// Afterif (order is { Lines.Count: > 0 }) { }
```

**Constant interpolated strings.** Build const strings, for example route templates, with $"".

``` js
// Beforepublic const string Products = Base + "/products";
js
// Afterpublic const string Base = "/api";public const string Products = $"{Base}/products";   // "/api/products", usable in [Route(Products)]
```

**Lambda improvements.** Lambdas get a natural type, an optional explicit return type, and attributes.

``` js
// BeforeFunc<string, int> parse = s => int.Parse(s);
js
// Aftervar parse = (string s) => int.Parse(s);          // inferred as Func<string, int>var pick = object (bool b) => b ? 1 : "one";     // explicit return typeapp.MapGet("/api/health", [AllowAnonymous] () => Results.Ok());   // Microsoft.AspNetCore.Authorization
```

**Raw string literals.** Multi-line text with quotes and braces, no escaping.

```
// Beforevar json = "{ \"id\": " + id + ", \"name\": \"Mouse\" }";
bash
// After: $$ means {{x}} interpolates and a single { is literalvar json = $$"""    { "id": {{id}}, "name": "Mouse" }    """;// { "id": 42, "name": "Mouse" }
```

**Required members.** The compiler forces object initializers to set these properties.

```
public sealed class CreateProductRequest{    public required string Name { get; init; }    public required decimal Price { get; init; }}
js
var request = new CreateProductRequest { Name = "Mouse" };// error CS9035: Required member 'CreateProductRequest.Price' must be set in the object initializer or attribute constructor.
```

Mark a constructor that sets them all with [SetsRequiredMembers] from System.Diagnostics.CodeAnalysis.

**List patterns.** Match arrays and lists by shape, with .. for "the rest".

```
// Before: if (ids.Length == 0) ... else if (ids.Length == 1) ...// Afterstatic string Describe(int[] ids) => ids switch{    [] => "empty",    [var only] => $"one: {only}",    [var first, .., var last] => $"{first}..{last}",};// Describe([1, 2, 3]) -> "1..3"
```

**Generic math (static abstract interface members).** Write one numeric algorithm for every number type.

```
using System.Numerics;
// Before: Sum(IEnumerable<int>), Sum(IEnumerable<decimal>), ...// Afterstatic T Sum<T>(IEnumerable<T> values) where T : INumber<T>{    T total = T.Zero;    foreach (var v in values) total += v;    return total;}// Sum([10.5m, 20m]) -> 30.5    Sum([1, 2, 3]) -> 6
```

Your own interfaces can declare static abstract TSelf Zero { get; } the same way.

**UTF-8 string literals.** Get bytes at compile time.

```
// Beforebyte[] ok = Encoding.UTF8.GetBytes("OK");
// AfterReadOnlySpan<byte> ok = "OK"u8;   // 2 bytes, no runtime encoding
```

**File-local types.** A type visible only inside its own file.

```
// Before: internal helper visible to the whole assembly// Afterfile sealed class SkuFormatter{    public static string Format(int id) => $"SKU-{id:D5}";   // SKU-00042}
```

**scoped.** Promise that a span parameter does not escape, so callers can pass stack memory.

```
public ref struct ReceiptWriter{    private Span<char> _dest;    public int Written;    public ReceiptWriter(Span<char> dest) { _dest = dest; Written = 0; }
public void Append(scoped ReadOnlySpan<char> text)    {        text.CopyTo(_dest[Written..]);        Written += text.Length;    }}// Without "scoped", w.Append(stackallocBuffer) fails with error CS8350.
```

**Primary constructors for classes.** Constructor parameters in the class header, in scope everywhere.

```
// Beforepublic sealed class PriceCalculator{    private readonly decimal _vatRate;    public PriceCalculator(decimal vatRate) => _vatRate = vatRate;}
// Afterpublic sealed class PriceCalculator(decimal vatRate){    public decimal WithVat(decimal net) => net * (1 + vatRate);   // 100 -> 120.00}
```

**Collection expressions.** One syntax for arrays, lists, spans, and sets, plus .. spread.

``` js
// Beforevar ids = new int[] { 1, 2, 3 };var tags = new List<string> { "new", "sale" };var none = Array.Empty<Product>();
// Afterint[] ids = [1, 2, 3];List<string> tags = ["new", "sale"];string[] all = [.. tags, "featured"];   // new, sale, featuredProduct[] none = [];
```

**Default lambda parameters.** Optional parameters on lambdas.

```
// Before: a local function or two overloads// Aftervar discount = (decimal price, decimal rate = 0.10m) => price * (1 - rate);discount(100m);          // 90.00discount(100m, 0.25m);   // 75.00
```

**Alias any type.** using aliases for tuples, arrays, and pointer types.

```
// Before: only named types could be aliased// Afterusing LineTotal = (int ProductId, decimal Total);
LineTotal line = (5, 59.90m);Console.WriteLine(line.Total);   // 59.90
```

**Inline arrays.** A fixed-size buffer inside a struct, without unsafe.

```
using System.Runtime.CompilerServices;
[InlineArray(3)]public struct LastThreePrices{    private decimal _element0;}
js
var last = new LastThreePrices();last[0] = 10m; last[1] = 20m; last[2] = 30m;   // indexable and foreach-able
```

**Experimental attribute.** Mark APIs that callers must opt into.

```
using System.Diagnostics.CodeAnalysis;
[Experimental("STORE001")]public static class Recommendations{    public static string[] For(int userId) => ["Mouse", "Keyboard"];}// Calling it from Store.Api.Products without suppression:// error STORE001: 'Store.Api.Products.Recommendations' is for evaluation purposes only and is// subject to change or removal in future updates. Suppress this diagnostic to proceed.
```

Suppress with #pragma warning disable STORE001 or <NoWarn>STORE001</NoWarn>. Also new in C# 12: ref readonly parameters, and interceptors as a preview feature.

**params collections.** params works with spans and collection interfaces, not only arrays.

```
// Before: allocates an array on every callpublic static string Tags(params string[] tags) => string.Join("|", tags);
js
// Afterpublic static string Tags(params ReadOnlySpan<string> tags) => string.Join("|", tags);public static int CountIds(params IEnumerable<int> ids) => ids.Count();// Tags("new", "sale") -> "new|sale"
```

**System.Threading.Lock.** A dedicated lock type; lock uses its API instead of Monitor.

```
// Beforeprivate readonly object _gate = new();
// Afterprivate readonly Lock _gate = new();
lock (_gate)   // compiles to _gate.EnterScope(){    _reserved += qty;}
```

**\e escape.** The ESCAPE character (U+001B) for terminal colors.

```
// BeforeConsole.WriteLine("\u001b[32mPaid\u001b[0m");
// AfterConsole.WriteLine("\e[32mPaid\e[0m");
```

**Partial properties and indexers.** Declare in one part, implement in another (source generators use this).

```
public partial class ProductView{    public partial string Sku { get; set; }   // declaring part}
public partial class ProductView{    private string _sku = "";    public partial string Sku                 // implementing part    {        get => _sku;        set => _sku = value.ToUpperInvariant();    }}
```

**Implicit index access in object initializers.** ^ (from the end) works inside initializers.

```
public sealed class Shelf { public int[] Slots { get; } = new int[5]; }
// Before: front indexes only, { Slots = { [4] = 99 } }// Aftervar shelf = new Shelf { Slots = { [^1] = 99, [0] = 1 } };   // 1,0,0,0,99
```

**OverloadResolutionPriority.** Library authors steer callers to a better overload without breaking them.

```
using System.Runtime.CompilerServices;
js
public static class Logging{    public static string Log(string message) => "string overload";
[OverloadResolutionPriority(1)]    public static string Log(ReadOnlySpan<char> message) => "span overload";}// Logging.Log("hello") -> "span overload"
```

**ref struct interfaces and** **allows ref struct.** Span-holding types can implement interfaces and flow through generics.

```
public interface ISkuSource { int Count { get; } }
public ref struct SpanSkuSource : ISkuSource{    private readonly ReadOnlySpan<string> _skus;    public SpanSkuSource(ReadOnlySpan<string> skus) => _skus = skus;    public int Count => _skus.Length;}
js
static int CountOf<T>(T source) where T : ISkuSource, allows ref struct => source.Count;
```

Also in C# 13: ref locals and unsafe blocks inside async methods and iterators (not across await or yield), and smarter method group overload resolution.

**Extension members.** Extension blocks add properties, methods, and static members to types you do not own.

``` js
// Before: extension methods onlypublic static bool InStock(this Product p) => p.Stock > 0;
// Afterpublic static class ProductExtensions{    extension(Product product)    {        public bool InStock => product.Stock > 0;        public decimal PriceWithVat(decimal rate) => product.Price * (1 + rate);    }
js
    extension(Product)    {        public static Product Placeholder => new() { Name = "(none)" };    }}// p.InStock -> True    p.PriceWithVat(0.20m) -> 120.00    Product.Placeholder.Name -> (none)
```

**The** **field keyword.** Use the compiler-generated backing field inside an accessor.

``` js
// Beforeprivate string _email = "";public string Email{    get => _email;    set => _email = value.Trim().ToLowerInvariant();}
js
// Afterpublic string Email{    get;    set => field = value.Trim().ToLowerInvariant();} = "";// "  Ali@Example.COM " -> "ali@example.com"
```

**Null-conditional assignment.** ?. and ?[] on the left side of = and compound assignments.

```
// Beforeif (order is not null) order.Status = OrderStatus.Paid;
// Afterorder?.Status = OrderStatus.Paid;   // right side runs only when order is not nullorder?.Total += 10m;// order?.Total++;  -> error CS1059 (++ and -- are not allowed here)
```

**Implicit span conversions.** Arrays convert to spans for extension receivers and generic inference.

```
public static decimal Total(this ReadOnlySpan<decimal> prices){    decimal sum = 0;    foreach (var p in prices) sum += p;    return sum;}
decimal[] prices = [10m, 20m];prices.Total();// C# 13: error CS1929: 'decimal[]' does not contain a definition for 'Total' ...// C# 14: compiles and returns 30
```

**nameof with unbound generics.** No dummy type argument needed.

```
// Beforenameof(List<int>)        // "List"
// Afternameof(List<>)           // "List"nameof(Dictionary<,>)    // "Dictionary"
```

**Simple lambda parameters with modifiers.** out, ref, in, scoped without spelling out types (params still needs them).

```
public delegate bool TryParser<T>(string text, out T result);
js
// BeforeTryParser<decimal> p1 = (string text, out decimal result) => decimal.TryParse(text, out result);
js
// AfterTryParser<decimal> p2 = (text, out result) => decimal.TryParse(text, out result);
```

**Partial constructors and partial events.** One defining and one implementing declaration, like partial methods.

```
public partial class Cart{    public partial Cart(int id);    public partial event EventHandler? Changed;}
public partial class Cart{    private EventHandler? _changed;    public int Id { get; }    public partial Cart(int id) { Id = id; }    public partial event EventHandler? Changed    {        add => _changed += value;        remove => _changed -= value;    }}
```

**User-defined compound assignment.** Overload += and ++ as instance operators that mutate in place.

```
// Before: static operator + creates a new object on every +=// Afterpublic sealed class Basket{    public decimal Total { get; private set; }    public int Items { get; private set; }
public void operator +=(decimal price) { Total += price; Items++; }    public void operator ++() => Items++;}
js
var basket = new Basket();basket += 50m; basket += 25m; basket++;   // Total 75, Items 3
```

C# 14 also adds preprocessor directives for file-based apps (dotnet run app.cs).

**Solution-wide defaults.** Put this next to your .sln; keep global usings in Store.Api.csproj so test projects still build.

``` php
<!-- File: Directory.Build.props --><Project>  <PropertyGroup>    <TargetFramework>net10.0</TargetFramework>    <Nullable>enable</Nullable>    <ImplicitUsings>enable</ImplicitUsings>    <!-- No LangVersion: net10.0 gives you C# 14 -->  </PropertyGroup></Project>
php
<!-- File: Store.Api.csproj (inside <Project>) --><ItemGroup>  <Using Include="Store.Api.Orders" />  <Using Include="Store.Api.Products" /></ItemGroup>
```

**A request data transfer object (DTO) with** **required and** **field validation.**

```
// File: Products/CreateProductRequest.csnamespace Store.Api.Products;
public sealed class CreateProductRequest{    public required string Name    {        get;        init => field = value.Trim();    }
js
    public required decimal Price    {        get;        init => field = value >= 0m            ? value            : throw new ArgumentOutOfRangeException(nameof(Price), "Price cannot be negative.");    }
public int Stock { get; init; }}
```

**A service with a primary constructor, collection expressions, and patterns.**

```
// File: Orders/OrderPricing.csnamespace Store.Api.Orders;
public sealed class OrderPricing(TimeProvider clock){    private static readonly HashSet<DayOfWeek> Weekend = [DayOfWeek.Saturday, DayOfWeek.Sunday];
js
    public decimal ShippingFee(Order order) => order switch    {        { Lines.Count: 0 } => 0m,        { Total: >= 500m } => 0m,        _ when Weekend.Contains(clock.GetUtcNow().DayOfWeek) => 29.90m,        _ => 49.90m    };
js
    public static int[] ProductIds(Order order) =>        [.. order.Lines.Select(l => l.ProductId).Distinct()];}
```

Register it with builder.Services.AddSingleton(TimeProvider.System); and builder.Services.AddScoped<OrderPricing>();.

**An HTML email body with a raw interpolated string.**

```
// File: Infrastructure/Email/OrderEmailTemplates.csusing System.Globalization;using System.Net;using Store.Api.Orders;
namespace Store.Api.Infrastructure.Email;
public static class OrderEmailTemplates{    private static readonly CultureInfo Turkish = CultureInfo.GetCultureInfo("tr-TR");
js
    public static string Confirmation(Order order, string customerName) => $$"""        <h1>Thank you, {{WebUtility.HtmlEncode(customerName)}}</h1>        <p>Order #{{order.Id}} is {{order.Status}}.</p>        <p>Total: {{order.Total.ToString("C", Turkish)}}</p>        """;}
```

Output for order 1001 with a total of 1200 and the name Ayşe <VIP>:

```
<h1>Thank you, Ayşe &lt;VIP&gt;</h1><p>Order #1001 is Paid.</p><p>Total: ₺1.200,00</p>
```

Pass the result to IEmailService.SendAsync(to, subject, htmlBody, ct).

*Tags: csharp, dotnet, csharp-14, dotnet-10, cheat-sheet*

*Originally published at* *www.softinbit.com**.*

*New articles by email:* *subscribe**.*

[Modern C# Cheat Sheet: C# 10 to C# 14](https://blog.devgenius.io/modern-c-cheat-sheet-c-10-to-c-14-1793a8e612a8) was originally published in [Dev Genius](https://blog.devgenius.io) on Medium, where people are continuing the conversation by highlighting and responding to this story.
