modern-csharp
codewithmukesh/dotnet-claude-kit
Modern C# 14 and .NET 10 language features: primary constructors, records, pattern matching, spans, and the field keyword.
What is modern-csharp?
Covers C# 14 and .NET 10 best practices including primary constructors, collection expressions, records, pattern matching, spans, raw string literals, the field keyword, and extension members. Load this skill when writing new C# code or modernizing existing code to use current language features.
- Use primary constructors to eliminate field assignments and simplify dependency injection
- Create immutable data types with records and readonly record structs
- Apply pattern matching with switch expressions and list patterns for cleaner control flow
- Leverage the field keyword for validation and lazy initialization in auto-properties without manual backing fields
- Define extension properties and static extension members via extension blocks
- Write zero-allocation code with Span<T> for string and array slicing
How to install modern-csharp
npx skills add https://github.com/codewithmukesh/dotnet-claude-kit --skill modern-csharpHow to use modern-csharp
- 1.Review the Core Principles section to understand the philosophy: newest features, readability, value types, and immutability by default
- 2.Consult the Well-Known Features Quick Reference table to identify which C# 14 feature solves your current problem
- 3.For property validation or lazy initialization, use the field keyword examples instead of manual backing fields
- 4.For DTOs and immutable data, prefer record or readonly record struct over classes or tuples
- 5.Use pattern matching with switch expressions and list patterns for type checking and deconstruction
- 6.Apply collection expressions [] and spread operators for all collection initialization
- 7.Use raw string literals for multi-line SQL, JSON, or XML without escaping
- 8.Reference the Decision Guide table to choose the right construct for your scenario
Use cases
- Modernizing legacy C# codebases to use C# 14 syntax and reduce boilerplate
- Building DTOs and value objects with records for API contracts and domain models
- Implementing validation logic in properties using the field keyword without manual backing fields
- Writing high-performance code paths with Span<T> to reduce garbage collection pressure
- Creating fluent, readable control flow with pattern matching instead of nested if-else chains
- C# developers writing new code targeting .NET 10 or later
- Teams modernizing existing C# codebases to current language standards
- Backend engineers building high-performance services with reduced GC overhead
- API and domain-driven design practitioners using records for immutable data structures
modern-csharp FAQ
Use record (reference type) for DTOs, API contracts, and larger objects. Use readonly record struct for small value objects (2–3 fields) that benefit from stack allocation and reduced GC pressure.
The field keyword (C# 14) accesses the auto-generated backing field in property accessors without declaring it manually. Use it for validation, lazy initialization, or change notification in auto-properties.
Extension blocks (C# 14) support extension properties and static extension members, with the receiver declared once for the whole block. Classic this-parameter extension methods still work and coexist; use extension blocks when you need properties or multiple members on the same receiver.
Use pattern matching when it improves readability—switch expressions, list patterns, and property patterns are cleaner than nested if-else. Avoid deeply nested patterns that are hard to read; extract to methods or use sequential checks instead.
Use Span<T> for zero-allocation slicing of strings and arrays in performance-critical code paths. It avoids creating intermediate copies and reduces garbage collection overhead.
Full instructions (SKILL.md)
Source of truth, from codewithmukesh/dotnet-claude-kit.
name: modern-csharp description: > Modern C# language features for .NET 10 and C# 14. Covers primary constructors, collection expressions, the field keyword, extension members, records, pattern matching, spans, and raw string literals. Load this skill when writing any new C# code, reviewing existing code for modernization, using "modern C#", "C# 14", "primary constructor", "collection expression", "records", "pattern matching", "span", "field keyword", or "extension members". Always loaded as the baseline for all agents.
Modern C# (C# 14 / .NET 10)
Core Principles
- Use the newest stable features — C# 14 is the target. Prefer language-level constructs over library workarounds.
- Readability over cleverness — Pattern matching and expression-bodied members improve readability when used appropriately; deeply nested patterns do not.
- Value types where possible — Prefer
record struct,Span<T>, and stack allocation to reduce GC pressure. - Immutability by default — Use
record,readonly,init, andrequiredto make illegal states unrepresentable.
Patterns
Well-Known Features Quick Reference
| Feature | Usage | Example |
|---|---|---|
| Primary constructors | DI injection, eliminate field assignments | public class OrderService(IOrderRepo repo, TimeProvider clock) { } |
| Collection expressions | [] for all collection types + spread | List<string> names = ["Alice", "Bob"]; / int[] all = [..a, ..b, 99]; |
| Records | DTOs, value objects, immutable data | public record CreateOrderRequest(string CustomerId, List<OrderItem> Items); |
readonly record struct | Small stack-allocated value types | public readonly record struct Money(decimal Amount, string Currency); |
| Pattern matching | Switch expressions, list/property patterns | order switch { { Total: > 1000 } => "Premium", _ => "Standard" }; |
| List patterns | Deconstruct arrays/lists | items switch { [] => "Empty", [var x] => $"One: {x}", [var f, .., var l] => $"{f}..{l}" }; |
Span<T> | Zero-allocation slicing | ReadOnlySpan<char> trimmed = input.Trim(); int.TryParse(trimmed[4..], out id); |
| Raw string literals | Multi-line SQL, JSON, XML | var sql = """ SELECT ... """; / interpolated: $$""" {"id": "{{id}}"} """; |
required members | Enforce initialization | public required string ConnectionString { get; init; } |
is pattern + extraction | Null/type/property check | if (result is { IsSuccess: true, Value: var order }) { ... } |
The field Keyword (C# 14)
Access the auto-generated backing field in property accessors without declaring it manually.
// GOOD — field keyword for validation in auto-property
public class Product
{
public string Name
{
get => field;
set => field = value?.Trim() ?? throw new ArgumentNullException(nameof(value));
}
public decimal Price
{
get => field;
set => field = value >= 0 ? value : throw new ArgumentOutOfRangeException(nameof(value));
}
}
Lazy Initialization with field
public class ProductCatalog
{
// Lazy-load on first access — no manual Lazy<T> or backing field
public IReadOnlyList<Product> Products
{
get => field ??= LoadProducts();
}
private static List<Product> LoadProducts() => /* expensive load */;
}
Change Notification with field
// INotifyPropertyChanged without manual backing fields
public class OrderViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
public string CustomerName
{
get => field;
set
{
if (field == value) return;
field = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CustomerName)));
}
} = "";
public decimal Total
{
get => field;
set
{
if (field == value) return;
field = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Total)));
}
}
}
Extension Members (C# 14)
C# 14 adds extension blocks inside static classes. Unlike classic extension methods, they support extension properties and static extension members — the receiver is declared once for the whole block.
// GOOD — extension block (shipped C# 14 syntax)
public static class OrderExtensions
{
extension(Order order)
{
public decimal TotalWithTax => order.Total * 1.2m;
public bool IsHighValue => order.Total > 1000m;
public string ToSummary() =>
$"Order #{order.Id}: {order.Total:C} ({order.Items.Count} items)";
}
// Static extension members use the type (no receiver instance)
extension(Order)
{
public static Order Empty => Order.Create("none", [], DateTimeOffset.MinValue);
}
}
// Callers see them as if declared on Order
if (order.IsHighValue) { /* ... */ }
Classic this-parameter extension methods still work and coexist — use extension blocks when you need properties or several members on the same receiver.
Anti-patterns
Don't Use Obsolete Patterns When Modern Alternatives Exist
// BAD — manual backing field when field keyword works
private string _name;
public string Name
{
get => _name;
set => _name = value ?? throw new ArgumentNullException();
}
// BAD — old-style collection initialization
var list = new List<int>() { 1, 2, 3 };
// BAD — Tuple instead of record for domain types
(string Name, decimal Price) product = ("Widget", 9.99m);
// GOOD — record
public record Product(string Name, decimal Price);
Don't Over-pattern-match
// BAD — deeply nested pattern that's hard to read
if (order is { Customer: { Address: { Country: { Code: "US" } } } })
// GOOD — extract to a clear method or use sequential checks
if (order.Customer.Address.Country.Code == "US")
Don't Use var When the Type Is Not Obvious
// BAD — what type is this?
var result = Process(order);
// GOOD — explicit type when not obvious
Result<Order> result = Process(order);
// Also GOOD — var is fine when type is apparent
var orders = new List<Order>();
Decision Guide
| Scenario | Recommendation |
|---|---|
| DTO / API contract | record (reference type) |
| Small value object (2-3 fields) | readonly record struct |
| Service with DI | Primary constructor |
| Collection creation | Collection expression [] |
| Property with validation | field keyword |
| Multi-line string (SQL, JSON) | Raw string literal """ |
| Slicing strings/arrays | Span<T> |
| Type checking + extraction | Pattern matching with is / switch |
| Enforced initialization | required modifier |
| Adding methods to external types | Extension members |
Related skills
More from codewithmukesh/dotnet-claude-kit and the wider catalog.

openapi
Built-in OpenAPI support for .NET 10 with document generation, transformers, and security schemes—no Swashbuckle needed.

opentelemetry
Distributed tracing, metrics, and logs for .NET 10 with OpenTelemetry and OTLP export.

outdated
Audit .NET NuGet packages for vulnerabilities, staleness, and commercial-license traps.

plan
Architecture-aware planning for .NET projects before implementation.

project-setup
Tech-stack selection advisor for .NET projects with recommended defaults and rationale.

project-structure
Organize .NET solutions with central package management, shared build properties, and modern conventions.