modern-csharp — Claude Code skill for C# 14 and .NET 10
Teaches the agent C# 14 syntax: primary constructors, collection expressions, the field keyword, extension blocks. Useful when you write new C# or modernize old code.
npx skills add codewithmukesh/dotnet-claude-kit --skill modern-csharp -a claude-codeWhat modern-csharp does
The skill gives the agent a baseline for modern C#: primary constructors, collection expressions [] with spread, records and readonly record struct, pattern matching, Span<T> slicing, raw string literals, and the required modifier. Inside there is a quick-reference table of features, an anti-patterns section, and a decision table — which construct to pick for a DTO, which for a DI service, which for a small value object.
Two sections stand on their own: the field keyword from C# 14 and extension blocks. The first removes manual backing fields, so validation, lazy loading and INotifyPropertyChanged live right in the accessors. The second adds extension properties and static extension members that classic extension methods never had. The old this-parameter syntax still works and coexists with the new blocks.
The target is C# 14 and .NET 10, so on a project pinned to an older language version some examples will not compile at all. The skill is meant to load as the baseline for every agent, which means you wire it up once and it colours all C# generation afterwards.
What it helps with
- write DTOs and value objects as records instead of tuples
- drop manual backing fields from properties with validation
- build lists and arrays with collection expressions
- slice strings through
Span<T>without extra allocations - move old code onto primary constructors and
required
Example prompts for the agent
- Rewrite this class for C# 14: primary constructor instead of fields, DI through the constructor
- Replace the manual backing fields with the
fieldkeyword and a value check - Add a
TotalWithTaxextension property toOrderusing anextensionblock
How to install modern-csharp
1Skills CLI (Vercel)
npx skills add codewithmukesh/dotnet-claude-kit --skill modern-csharp -a claude-code2Manually: put the files in place
~/.claude/skills/modern-csharp/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/modern-csharp/
1Skills CLI (Vercel)
npx skills add codewithmukesh/dotnet-claude-kit --skill modern-csharp -a codex2Manually: put the files in place
.agents/skills/modern-csharp/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add codewithmukesh/dotnet-claude-kit --skill modern-csharp -a cursor2Manually: put the files in place
.cursor/skills/modern-csharp/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install codewithmukesh/dotnet-claude-kit modern-csharp2Manually: put the files in place
.github/skills/modern-csharp/SKILL.md1Skills CLI (Vercel)
npx skills add codewithmukesh/dotnet-claude-kit --skill modern-csharp -a gemini-cli2Platform CLI
gemini skills install https://github.com/codewithmukesh/dotnet-claude-kit --path skills/modern-csharpManually: put the files in place
.devin/skills/modern-csharp/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add codewithmukesh/dotnet-claude-kit --skill modern-csharp -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about modern-csharp
How do I install modern-csharp in Claude?
Run npx skills add codewithmukesh/dotnet-claude-kit --skill modern-csharp -a claude-code. The skill file lands at ~/.claude/skills/modern-csharp/SKILL.md.
What does modern-csharp do?
It hands the agent a reference for C# 14 and .NET 10 features: primary constructors, collection expressions, field, extension blocks, records, pattern matching, Span<T> and raw string literals. Anti-patterns and a decision table come with it.
Which C# version does the skill target?
C# 14 and .NET 10. On a project pinned to an older language version, the field and extension block examples will not compile.
Source text
SKILL.md · MIT
---
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
1. **Use the newest stable features** — C# 14 is the target. Prefer language-level constructs over library workarounds.
2. **Readability over cleverness** — Pattern matching and expression-bodied members improve readability when used appropriately; deeply nested patterns do not.
3. **Value types where possible** — Prefer `record struct`, `Span<T>`, and stack allocation to reduce GC pressure.
4. **Immutability by default** — Use `record`, `readonly`, `init`, and `required` to 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.
```csharp
// 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`
```csharp
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`
```csharp
// 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.
```csharp
// 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
```csharp
// 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
```csharp
// 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
```csharp
// 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 |
© codewithmukesh · MIT · codewithmukesh/dotnet-claude-kit