{"slug": "modern-c-cheat-sheet-c-10-to-c-14", "title": "Modern C# Cheat Sheet: C# 10 to C# 14", "summary": "Softinbit Technology published a Modern C# cheat sheet condensing every language feature from C# 10 (.NET 6) through C# 14 (.NET 10) into before/after snippets compiled and tested on the .NET 10 SDK version 10.0.300. The guide covers file-scoped namespaces, raw string literals, required properties, primary constructors, collection expressions, the Lock type and field-backed properties, and notes that the compiler selects the default C# version from the target framework so LangVersion overrides are unnecessary. It documents real diagnostics, including CS9260 when the C# 14 field keyword is used under language version 13.0.", "body_md": "*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**.*\n\n***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.*\n\n***Level:*** *Intermediate ·* ***Prerequisites:*** *Working knowledge of C# 9 / .NET 5 or earlier ·* ***Reading time:*** *13 min*\n\nYour 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.\n\nThe lookups people make most:\n\nThe compiler picks the default C# version from your target framework (the “Default for” column above). You do not need LangVersion in the project file.\n\n```\nerror CS8304: Compiler version: '5.6.0-2.26230.102 (caa81fa4...)'. Language version: 14.0.\n```\n\nUsing a C# 14 feature when the language version is 13 (for example net9.0 built with the .NET 10 SDK):\n\n```\nerror CS9260: Feature 'field keyword' is not available in C# 13.0. Please use language version 14.0 or greater.\n```\n\n**Global usings.** Import a namespace once for the whole project.\n\n```\n// Before: repeated at the top of every fileusing Store.Api.Orders;\n// After: once, in GlobalUsings.cs (or <Using Include=\"Store.Api.Orders\" /> in the .csproj)global using Store.Api.Orders;\n```\n\n**File-scoped namespaces.** One less indentation level per file.\n\n```\n// Beforenamespace Store.Api.Orders{    public sealed class OrderService { }}\n// Afternamespace Store.Api.Orders;\npublic sealed class OrderService { }\n```\n\n**Record structs.** Value-type records with generated equality, ToString, and with.\n\n```\n// Before: a struct with hand-written Equals, GetHashCode, ==, and ToString// Afterpublic readonly record struct Money(decimal Amount, string Currency);\njs\nvar 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\n```\n\n**Extended property patterns.** Reach into nested members with a dot.\n\n```\n// Beforeif (order is { Lines: { Count: > 0 } }) { }\n// Afterif (order is { Lines.Count: > 0 }) { }\n```\n\n**Constant interpolated strings.** Build const strings, for example route templates, with $\"\".\n\n``` js\n// Beforepublic const string Products = Base + \"/products\";\njs\n// Afterpublic const string Base = \"/api\";public const string Products = $\"{Base}/products\";   // \"/api/products\", usable in [Route(Products)]\n```\n\n**Lambda improvements.** Lambdas get a natural type, an optional explicit return type, and attributes.\n\n``` js\n// BeforeFunc<string, int> parse = s => int.Parse(s);\njs\n// 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\n```\n\n**Raw string literals.** Multi-line text with quotes and braces, no escaping.\n\n```\n// Beforevar json = \"{ \\\"id\\\": \" + id + \", \\\"name\\\": \\\"Mouse\\\" }\";\nbash\n// After: $$ means {{x}} interpolates and a single { is literalvar json = $$\"\"\"    { \"id\": {{id}}, \"name\": \"Mouse\" }    \"\"\";// { \"id\": 42, \"name\": \"Mouse\" }\n```\n\n**Required members.** The compiler forces object initializers to set these properties.\n\n```\npublic sealed class CreateProductRequest{    public required string Name { get; init; }    public required decimal Price { get; init; }}\njs\nvar request = new CreateProductRequest { Name = \"Mouse\" };// error CS9035: Required member 'CreateProductRequest.Price' must be set in the object initializer or attribute constructor.\n```\n\nMark a constructor that sets them all with [SetsRequiredMembers] from System.Diagnostics.CodeAnalysis.\n\n**List patterns.** Match arrays and lists by shape, with .. for \"the rest\".\n\n```\n// 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\"\n```\n\n**Generic math (static abstract interface members).** Write one numeric algorithm for every number type.\n\n```\nusing System.Numerics;\n// 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\n```\n\nYour own interfaces can declare static abstract TSelf Zero { get; } the same way.\n\n**UTF-8 string literals.** Get bytes at compile time.\n\n```\n// Beforebyte[] ok = Encoding.UTF8.GetBytes(\"OK\");\n// AfterReadOnlySpan<byte> ok = \"OK\"u8;   // 2 bytes, no runtime encoding\n```\n\n**File-local types.** A type visible only inside its own file.\n\n```\n// Before: internal helper visible to the whole assembly// Afterfile sealed class SkuFormatter{    public static string Format(int id) => $\"SKU-{id:D5}\";   // SKU-00042}\n```\n\n**scoped.** Promise that a span parameter does not escape, so callers can pass stack memory.\n\n```\npublic ref struct ReceiptWriter{    private Span<char> _dest;    public int Written;    public ReceiptWriter(Span<char> dest) { _dest = dest; Written = 0; }\npublic void Append(scoped ReadOnlySpan<char> text)    {        text.CopyTo(_dest[Written..]);        Written += text.Length;    }}// Without \"scoped\", w.Append(stackallocBuffer) fails with error CS8350.\n```\n\n**Primary constructors for classes.** Constructor parameters in the class header, in scope everywhere.\n\n```\n// Beforepublic sealed class PriceCalculator{    private readonly decimal _vatRate;    public PriceCalculator(decimal vatRate) => _vatRate = vatRate;}\n// Afterpublic sealed class PriceCalculator(decimal vatRate){    public decimal WithVat(decimal net) => net * (1 + vatRate);   // 100 -> 120.00}\n```\n\n**Collection expressions.** One syntax for arrays, lists, spans, and sets, plus .. spread.\n\n``` js\n// Beforevar ids = new int[] { 1, 2, 3 };var tags = new List<string> { \"new\", \"sale\" };var none = Array.Empty<Product>();\n// Afterint[] ids = [1, 2, 3];List<string> tags = [\"new\", \"sale\"];string[] all = [.. tags, \"featured\"];   // new, sale, featuredProduct[] none = [];\n```\n\n**Default lambda parameters.** Optional parameters on lambdas.\n\n```\n// 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\n```\n\n**Alias any type.** using aliases for tuples, arrays, and pointer types.\n\n```\n// Before: only named types could be aliased// Afterusing LineTotal = (int ProductId, decimal Total);\nLineTotal line = (5, 59.90m);Console.WriteLine(line.Total);   // 59.90\n```\n\n**Inline arrays.** A fixed-size buffer inside a struct, without unsafe.\n\n```\nusing System.Runtime.CompilerServices;\n[InlineArray(3)]public struct LastThreePrices{    private decimal _element0;}\njs\nvar last = new LastThreePrices();last[0] = 10m; last[1] = 20m; last[2] = 30m;   // indexable and foreach-able\n```\n\n**Experimental attribute.** Mark APIs that callers must opt into.\n\n```\nusing System.Diagnostics.CodeAnalysis;\n[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.\n```\n\nSuppress with #pragma warning disable STORE001 or <NoWarn>STORE001</NoWarn>. Also new in C# 12: ref readonly parameters, and interceptors as a preview feature.\n\n**params collections.** params works with spans and collection interfaces, not only arrays.\n\n```\n// Before: allocates an array on every callpublic static string Tags(params string[] tags) => string.Join(\"|\", tags);\njs\n// 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\"\n```\n\n**System.Threading.Lock.** A dedicated lock type; lock uses its API instead of Monitor.\n\n```\n// Beforeprivate readonly object _gate = new();\n// Afterprivate readonly Lock _gate = new();\nlock (_gate)   // compiles to _gate.EnterScope(){    _reserved += qty;}\n```\n\n**\\e escape.** The ESCAPE character (U+001B) for terminal colors.\n\n```\n// BeforeConsole.WriteLine(\"\\u001b[32mPaid\\u001b[0m\");\n// AfterConsole.WriteLine(\"\\e[32mPaid\\e[0m\");\n```\n\n**Partial properties and indexers.** Declare in one part, implement in another (source generators use this).\n\n```\npublic partial class ProductView{    public partial string Sku { get; set; }   // declaring part}\npublic partial class ProductView{    private string _sku = \"\";    public partial string Sku                 // implementing part    {        get => _sku;        set => _sku = value.ToUpperInvariant();    }}\n```\n\n**Implicit index access in object initializers.** ^ (from the end) works inside initializers.\n\n```\npublic sealed class Shelf { public int[] Slots { get; } = new int[5]; }\n// Before: front indexes only, { Slots = { [4] = 99 } }// Aftervar shelf = new Shelf { Slots = { [^1] = 99, [0] = 1 } };   // 1,0,0,0,99\n```\n\n**OverloadResolutionPriority.** Library authors steer callers to a better overload without breaking them.\n\n```\nusing System.Runtime.CompilerServices;\njs\npublic static class Logging{    public static string Log(string message) => \"string overload\";\n[OverloadResolutionPriority(1)]    public static string Log(ReadOnlySpan<char> message) => \"span overload\";}// Logging.Log(\"hello\") -> \"span overload\"\n```\n\n**ref struct interfaces and** **allows ref struct.** Span-holding types can implement interfaces and flow through generics.\n\n```\npublic interface ISkuSource { int Count { get; } }\npublic ref struct SpanSkuSource : ISkuSource{    private readonly ReadOnlySpan<string> _skus;    public SpanSkuSource(ReadOnlySpan<string> skus) => _skus = skus;    public int Count => _skus.Length;}\njs\nstatic int CountOf<T>(T source) where T : ISkuSource, allows ref struct => source.Count;\n```\n\nAlso in C# 13: ref locals and unsafe blocks inside async methods and iterators (not across await or yield), and smarter method group overload resolution.\n\n**Extension members.** Extension blocks add properties, methods, and static members to types you do not own.\n\n``` js\n// Before: extension methods onlypublic static bool InStock(this Product p) => p.Stock > 0;\n// Afterpublic static class ProductExtensions{    extension(Product product)    {        public bool InStock => product.Stock > 0;        public decimal PriceWithVat(decimal rate) => product.Price * (1 + rate);    }\njs\n    extension(Product)    {        public static Product Placeholder => new() { Name = \"(none)\" };    }}// p.InStock -> True    p.PriceWithVat(0.20m) -> 120.00    Product.Placeholder.Name -> (none)\n```\n\n**The** **field keyword.** Use the compiler-generated backing field inside an accessor.\n\n``` js\n// Beforeprivate string _email = \"\";public string Email{    get => _email;    set => _email = value.Trim().ToLowerInvariant();}\njs\n// Afterpublic string Email{    get;    set => field = value.Trim().ToLowerInvariant();} = \"\";// \"  Ali@Example.COM \" -> \"ali@example.com\"\n```\n\n**Null-conditional assignment.** ?. and ?[] on the left side of = and compound assignments.\n\n```\n// Beforeif (order is not null) order.Status = OrderStatus.Paid;\n// Afterorder?.Status = OrderStatus.Paid;   // right side runs only when order is not nullorder?.Total += 10m;// order?.Total++;  -> error CS1059 (++ and -- are not allowed here)\n```\n\n**Implicit span conversions.** Arrays convert to spans for extension receivers and generic inference.\n\n```\npublic static decimal Total(this ReadOnlySpan<decimal> prices){    decimal sum = 0;    foreach (var p in prices) sum += p;    return sum;}\ndecimal[] prices = [10m, 20m];prices.Total();// C# 13: error CS1929: 'decimal[]' does not contain a definition for 'Total' ...// C# 14: compiles and returns 30\n```\n\n**nameof with unbound generics.** No dummy type argument needed.\n\n```\n// Beforenameof(List<int>)        // \"List\"\n// Afternameof(List<>)           // \"List\"nameof(Dictionary<,>)    // \"Dictionary\"\n```\n\n**Simple lambda parameters with modifiers.** out, ref, in, scoped without spelling out types (params still needs them).\n\n```\npublic delegate bool TryParser<T>(string text, out T result);\njs\n// BeforeTryParser<decimal> p1 = (string text, out decimal result) => decimal.TryParse(text, out result);\njs\n// AfterTryParser<decimal> p2 = (text, out result) => decimal.TryParse(text, out result);\n```\n\n**Partial constructors and partial events.** One defining and one implementing declaration, like partial methods.\n\n```\npublic partial class Cart{    public partial Cart(int id);    public partial event EventHandler? Changed;}\npublic 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;    }}\n```\n\n**User-defined compound assignment.** Overload += and ++ as instance operators that mutate in place.\n\n```\n// 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; }\npublic void operator +=(decimal price) { Total += price; Items++; }    public void operator ++() => Items++;}\njs\nvar basket = new Basket();basket += 50m; basket += 25m; basket++;   // Total 75, Items 3\n```\n\nC# 14 also adds preprocessor directives for file-based apps (dotnet run app.cs).\n\n**Solution-wide defaults.** Put this next to your .sln; keep global usings in Store.Api.csproj so test projects still build.\n\n``` php\n<!-- 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>\nphp\n<!-- File: Store.Api.csproj (inside <Project>) --><ItemGroup>  <Using Include=\"Store.Api.Orders\" />  <Using Include=\"Store.Api.Products\" /></ItemGroup>\n```\n\n**A request data transfer object (DTO) with** **required and** **field validation.**\n\n```\n// File: Products/CreateProductRequest.csnamespace Store.Api.Products;\npublic sealed class CreateProductRequest{    public required string Name    {        get;        init => field = value.Trim();    }\njs\n    public required decimal Price    {        get;        init => field = value >= 0m            ? value            : throw new ArgumentOutOfRangeException(nameof(Price), \"Price cannot be negative.\");    }\npublic int Stock { get; init; }}\n```\n\n**A service with a primary constructor, collection expressions, and patterns.**\n\n```\n// File: Orders/OrderPricing.csnamespace Store.Api.Orders;\npublic sealed class OrderPricing(TimeProvider clock){    private static readonly HashSet<DayOfWeek> Weekend = [DayOfWeek.Saturday, DayOfWeek.Sunday];\njs\n    public decimal ShippingFee(Order order) => order switch    {        { Lines.Count: 0 } => 0m,        { Total: >= 500m } => 0m,        _ when Weekend.Contains(clock.GetUtcNow().DayOfWeek) => 29.90m,        _ => 49.90m    };\njs\n    public static int[] ProductIds(Order order) =>        [.. order.Lines.Select(l => l.ProductId).Distinct()];}\n```\n\nRegister it with builder.Services.AddSingleton(TimeProvider.System); and builder.Services.AddScoped<OrderPricing>();.\n\n**An HTML email body with a raw interpolated string.**\n\n```\n// File: Infrastructure/Email/OrderEmailTemplates.csusing System.Globalization;using System.Net;using Store.Api.Orders;\nnamespace Store.Api.Infrastructure.Email;\npublic static class OrderEmailTemplates{    private static readonly CultureInfo Turkish = CultureInfo.GetCultureInfo(\"tr-TR\");\njs\n    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>        \"\"\";}\n```\n\nOutput for order 1001 with a total of 1200 and the name Ayşe <VIP>:\n\n```\n<h1>Thank you, Ayşe &lt;VIP&gt;</h1><p>Order #1001 is Paid.</p><p>Total: ₺1.200,00</p>\n```\n\nPass the result to IEmailService.SendAsync(to, subject, htmlBody, ct).\n\n*Tags: csharp, dotnet, csharp-14, dotnet-10, cheat-sheet*\n\n*Originally published at* *www.softinbit.com**.*\n\n*New articles by email:* *subscribe**.*\n\n[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.", "url": "https://wpnews.pro/news/modern-c-cheat-sheet-c-10-to-c-14", "canonical_source": "https://blog.devgenius.io/modern-c-cheat-sheet-c-10-to-c-14-1793a8e612a8?source=rss----4e2c1156667e---4", "published_at": "2026-11-04 16:17:54+00:00", "updated_at": "2026-11-04 16:49:25.664515+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["Softinbit Technology", "C#", ".NET", "Microsoft", ".NET 10 SDK"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/modern-c-cheat-sheet-c-10-to-c-14", "markdown": "https://wpnews.pro/news/modern-c-cheat-sheet-c-10-to-c-14.md", "text": "https://wpnews.pro/news/modern-c-cheat-sheet-c-10-to-c-14.txt", "jsonld": "https://wpnews.pro/news/modern-c-cheat-sheet-c-10-to-c-14.jsonld"}}