csharp-coding

Mandatory patterns and idioms for writing modern C#. All items in this skill are requirements, not suggestions. Use for new code; opportunistically refactor existing code when revisiting.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "csharp-coding" with this command: npx skills add rcdailey/dotfiles/rcdailey-dotfiles-csharp-coding

C# Coding Skill

Mandatory patterns and idioms for writing modern C#. All items in this skill are requirements, not suggestions. Use for new code; opportunistically refactor existing code when revisiting.

Framework Detection

Before writing code, check TargetFramework in the project's csproj to determine the available C# language version. If the csproj has no TargetFramework , look for a Directory.Build.props (or other *.props file) that defines it; these files spread common properties across multiple projects. Only use features whose minimum version tag (e.g., C# 12+) is satisfied by the project. When a mandated feature is unavailable, fall back to the idiomatic alternative for that version.

Required Language Features

  • File-scoped namespaces: namespace MyApp.Core;

  • Primary constructors (C# 12+): class Service(IDep dep, ILogger logger)

  • Collection expressions (C# 12+): [] , [item] , [..spread]

  • NEVER use new[] , new List<T>() , Array.Empty<T>()

  • For type inference, prefer [new T { }, new T { }] over casts

  • Use T[] x = [...] only when simpler forms fail

  • Records for DTOs, init setters

  • Pattern matching: is not null , switch expressions, property patterns

  • Property patterns: obj is Type { Prop: value } over obj is Type t && t.Prop == value

  • Recursive/nested: obj is Type { Outer: { Inner: value } }

  • Extended property pattern: obj is { Outer.Inner: value } (C# 10)

  • Empty property pattern: { } name matches non-null and binds (e.g., is Type { Prop: { } x } )

  • Spread operator for collections (C# 12+): [..first, ..second]

  • field keyword in properties (C# 14+): public string Name { get; set => field = value ?? throw; } = "";

  • NEVER use explicit backing fields when field suffices

  • Extension blocks for extension methods and properties (C# 14+): extension(T src) { public bool IsEmpty => !src.Any(); }

  • NEVER use static class with this parameter for new extension methods

  • Null-conditional assignment (C# 14+): obj?.Prop = value; over null checks wrapping assignment

  • Lambda modifiers without types (C# 14+): (text, out result) => int.TryParse(text, out result)

  • NEVER add redundant parameter types when modifiers alone suffice

Required Idioms

Visibility

  • Use internal for implementation classes (CLI apps, service implementations)

  • Use public only for genuine external APIs

  • Concrete classes implementing public interfaces should be internal

Data Modeling

  • Records for data models

  • Favor immutability where reasonable

  • Use immutable collections: IReadOnlyCollection , IReadOnlyDictionary

JSON Serialization

  • Configure naming policy, converters, and style via JsonSerializerOptions (or source-generated JsonSerializerContext ) so conventions apply uniformly

  • Check for existing options configuration before creating new instances

  • Reserve per-property attributes ([JsonPropertyName] , etc.) for exceptions to the convention

UsedImplicitly Attribute

Mark runtime-used members (deserialization, reflection, DI):

  • [UsedImplicitly]

  • type instantiated implicitly (DI, empty marker records)

  • [UsedImplicitly(ImplicitUseKindFlags.Assign)]

  • properties set via deserialization

  • [UsedImplicitly(..., ImplicitUseTargetFlags.WithMembers)]

  • applies to type AND all members

  • Common for DTOs: [UsedImplicitly(ImplicitUseKindFlags.Assign, ImplicitUseTargetFlags.WithMembers)]

Warning Suppression

  • NEVER use #pragma warning disable

  • Use [SuppressMessage] with Justification on class/method level

  • Prefer class-level when multiple members need same suppression

LINQ

  • LINQ method chaining over loops

  • LINQ method syntax only; NEVER use query syntax (from/where/select keywords)

Method Calls

  • Named arguments for boolean literals: new Options(SendInfo: false, SendEmpty: true)

  • Named arguments for consecutive same-type parameters to clarify intent

Async

  • ValueTask for hot paths

  • CancellationToken everywhere (use ct for variable name)

Interfaces

  • Avoid interface pollution: not every service class must have an interface

  • Add interfaces when justified (testability, more than one implementation)

Local Functions

  • Local functions go after return /continue statements

  • Add explicit return; or continue; if needed to separate main logic from local function defs

Design Principles

Quality Gates

  • Zero warnings/analysis issues - treat warnings as errors

  • All code must pass static analysis before commit

Abstraction

  • Prefer polymorphism over enums when modeling behavior or extensibility

  • Propose enum vs polymorphism tradeoffs for discussion rather than defaulting to enums

  • Every abstraction must justify its existence with concrete current needs

Dependency Injection

  • MUST use DI for all dependencies; NEVER manually new service objects in production code

  • Concrete implementations get injected; tests can substitute

  • Search existing registrations before adding new ones

Comment Guidelines

Comments must earn their place by reducing cognitive load. When to comment:

  • LINQ chains (3+ operations): Brief comment stating transformation goal

  • Conditional blocks with non-obvious purpose: One-line comment (e.g., // Explicit: user specified )

  • Private methods: Block comment if name + parameters don't make purpose self-evident

  • Early returns/continues: Include reason if not obvious from context

  • Complex algorithms: Comment explaining approach at top, not line-by-line

  • Null-suppression operator (! ): Every use MUST have an inline comment explaining why null is impossible at that point (e.g., // non-null: validated above , // non-null: dict always contains key after init ). The comment documents the runtime guarantee so reviewers can verify it and future maintainers can detect if the invariant breaks.

  • General: Any code where a reader would pause and wonder "why?" or "what's happening here?"

NEVER:

  • XML doc comments (unless public API library)

  • Commented-out code

  • Restating what code literally does

Tooling

Formatting

  • CSharpier is the ONLY formatting tool

  • NEVER use dotnet format or other formatters

  • Run pre-commit hooks on all changed files

dotnet CLI

  • Use dotnet CLI for: adding/removing packages, adding projects to solution

  • Central package management via Directory.Packages.props

  • specify versions there, not in csproj

  • Avoid --no-build or --no-restore flags; dotnet test handles restore + build automatically

  • Quiet verbosity for build/test: dotnet build -v q , dotnet test -v q

  • For verbose debugging, pipe to file: dotnet test -v d 2>&1 > /tmp/test.log then search with rg

Project Structure

  • SLNX format preferred over traditional SLN

  • One Autofac/DI module per library to keep registration modular

  • Dotnet tools configured in .config/dotnet-tools.json

  • Keep source files flat in their project directory; create subdirectories only when file count makes navigation difficult

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

code-quality

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

frontend-development

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

codex-config-optimizer

No summary provided by upstream source.

Repository SourceNeeds Review