codewithmukesh/dotnet-claude-kit

modern-csharp

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.

All-time #9504 Trending #2800 First seen Mar 13, 2026
8-week activity · all time api

Installation

$ npx skills add codewithmukesh/dotnet-claude-kit --skill modern-csharp

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from codewithmukesh/dotnet-claude-kit · top by installs.

npx skills add codewithmukesh/dotnet-claude-kit

Browse all from codewithmukesh/dotnet-claude-kit

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 700
License LICENSE
Default branch main
Open issues 6
Status Active

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 6,937 B
  • docs SUMMARY.md 3,750 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 1,100 installs

SKILL.md

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.

// 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