Csharp coding
Skill nguyenthdat/opencode-manager/registry/skills/csharp-coding
Project-scoped OpenCode TUI plugin for grouping and managing MCP servers, custom agent skills, and pinned vendor skill registries.
npx -y skills add nguyenthdat/opencode-manager --skill csharp-codingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 13 days oldThe repository was created 13 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Comprehensive idiomatic C#/.NET guidance: 186 prioritized rules across 15 categories covering memory/resource management, error handling, async/await, API and record design, LINQ, naming, nullability, dependency injection, testing, documentation, performance, project structure, analyzers, and anti-patterns. Use when writing, reviewing, refactoring, optimizing, or debugging C# or .NET code (`.cs`, `.csproj`, `Directory.Build.props`). Targets modern C# 12/13 and .NET 8/9 idioms (primary constructors, collection expressions, required members, nullable reference types, records) while respecting the project's declared language version and target framework.
SKILL.md
28.7 KB, as published. Nobody here has run it
C# / .NET Best Practices
Comprehensive guide for writing high-quality, idiomatic C# and .NET code. Contains 186 rules across 15 categories, prioritized by impact. Project constraints override generic defaults: preserve the declared LangVersion, TargetFramework, and nullable/analyzer settings unless the user explicitly requests a migration or upgrade.
When to Apply
Reference these guidelines when:
- Writing new C# classes, records, methods, or minimal API endpoints
- Implementing error handling or async/await code
- Designing public APIs for libraries or NuGet packages
- Reviewing code for nullability, disposal, or concurrency issues
- Optimizing memory usage or reducing allocations
- Configuring dependency injection lifetimes and registrations
- Writing or reviewing unit/integration tests
- Refactoring existing C#/.NET code
- Setting up analyzers,
.editorconfig, or CI formatting checks
Modern C#/.NET (C# 12/13, .NET 8/9)
For an existing project, preserve its LangVersion and TargetFramework unless a migration is explicitly in scope. For new projects, target the latest LTS/STS release actually supported by your deployment environment. Key modern idioms to prefer when the project's language version supports them:
- Primary constructors (C# 12+). Reduce constructor boilerplate for simple field capture and DI - see
api-primary-constructor. - Collection expressions (
[1, 2, 3], C# 12+). One consistent literal syntax across arrays,List<T>,Span<T>, and custom collection-builder types, including the spread operator (..) - seeapi-collection-expressions. requiredmembers (C# 11+). Compiler-enforced mandatory properties without a long constructor - seeapi-required-members.record/record struct. Value-based equality,withexpressions, and deconstruction generated for you - seeapi-record-value-dataandimmut-record-struct-small-value.- Nullable reference types (
<Nullable>enable</Nullable>, C# 8+, foundational for modern code). Turns a huge class ofNullReferenceExceptions into compile-time warnings - seetype-nullable-reference-types. - Pattern matching enhancements: list patterns, property patterns, logical (
and/or/not) patterns - seetype-pattern-matching-is. ref structs andSpan<T>/Memory<T>. Stack-only, allocation-free data access for hot paths - seemem-span-zero-allocandmem-ref-struct-stack.- Source generators over reflection for serialization, logging, and mapping - see
perf-source-generators-over-reflection. - Frozen collections (
FrozenDictionary/FrozenSet, .NET 8+) for build-once/read-many lookups - seeimmut-frozen-collections. - Generic math (
INumber<T>, .NET 7+) for numeric-agnostic algorithms - seetype-generic-math. - Keyed DI services (.NET 8+) for multiple implementations of one interface - see
di-keyed-services.
For the authoritative, complete list of language and runtime changes, consult the official What's New in C# and .NET release notes. Everything below applies across supported versions; prefer the modern forms above where the project's LangVersion/TargetFramework allow them.
Rule Categories by Priority
| Priority | Category | Impact | Prefix | Rules |
|---|---|---|---|---|
| 1 | Memory & Resource Management | CRITICAL | mem- | 14 |
| 2 | Error Handling | CRITICAL | err- | 13 |
| 3 | Async/Await & Concurrency | CRITICAL | async- | 17 |
| 4 | API & Type Design | HIGH | api- | 15 |
| 5 | Records & Immutability | HIGH | immut- | 10 |
| 6 | LINQ & Collections | HIGH | linq- | 12 |
| 7 | Naming Conventions | MEDIUM | name- | 14 |
| 8 | Type Safety & Nullability | MEDIUM/HIGH | type- | 12 |
| 9 | Dependency Injection & Configuration | MEDIUM | di- | 10 |
| 10 | Testing | MEDIUM | test- | 13 |
| 11 | Documentation | MEDIUM | doc- | 9 |
| 12 | Performance Patterns | MEDIUM | perf- | 12 |
| 13 | Project Structure | LOW | proj- | 10 |
| 14 | Analyzers & Linting | LOW | lint- | 10 |
| 15 | Anti-patterns | REFERENCE | anti- | 15 |
Quick Reference
1. Memory & Resource Management (CRITICAL)
mem-using-declaration- Useusingdeclarations for deterministic disposalmem-await-using-async- Useawait usingforIAsyncDisposablemem-dispose-pattern- Implement the fullIDisposabledispose patternmem-finalizer-rare- Only add a finalizer when holding raw unmanaged handlesmem-span-zero-alloc- UseSpan<T>/ReadOnlySpan<T>for zero-allocation slicingmem-memory-async-boundary- UseMemory<T>across async boundariesmem-arraypool-rent- Rent fromArrayPool<T>for short-lived large buffersmem-readonly-struct- Mark immutable structsreadonly structmem-ref-struct-stack- Useref structfor stack-only typesmem-struct-vs-class- Choose struct vs class by size/mutability/identitymem-avoid-boxing- Avoid boxing value types in hot pathsmem-stackalloc-small- Usestackallocfor small, bounded buffersmem-object-pooling- Pool expensive objects withObjectPool<T>mem-large-object-heap- Understand LOH allocations for large arrays/objects
2. Error Handling (CRITICAL)
err-exceptions-exceptional- Reserve exceptions for exceptional conditionserr-custom-hierarchy- Design a custom exception hierarchyerr-argumentnull-throwifnull- UseArgumentNullException.ThrowIfNullerr-no-catch-exception- Don't catch the baseExceptionbroadlyerr-exception-filters-when- Use exception filters (when)err-preserve-stack-trace- Usethrow;notthrow ex;err-finally-cleanup- Usefinallyfor guaranteed cleanuperr-wrap-with-innerexception- Wrap withinnerExceptionpreservederr-async-exception-propagation- Understand async exception flowerr-aggregateexception-flatten- FlattenAggregateExceptionerr-result-pattern-domain- UseResult<T>for expected failureserr-exception-message-quality- Write actionable exception messageserr-dont-swallow- Never swallow exceptions silently
3. Async/Await & Concurrency (CRITICAL)
async-configureawait-false-lib- UseConfigureAwait(false)in librariesasync-no-async-void- Avoidasync voidexcept event handlersasync-task-vs-valuetask- ChooseValueTask<T>only for proven hot pathsasync-cancellationtoken-propagate- PropagateCancellationTokenasync-no-sync-over-async- Never block on async with.Result/.Wait()async-iasyncenumerable-streaming- UseIAsyncEnumerable<T>for streamingasync-semaphoreslim-lock- UseSemaphoreSlimfor async-safe lockingasync-whenall-parallel- UseTask.WhenAllfor parallel operationsasync-whenany-timeout- UseTask.WhenAnyfor timeouts/racingasync-taskcompletionsource-bridge- Bridge callbacks withTaskCompletionSource<T>async-name-suffix- Suffix async methods withAsyncasync-return-task-directly- Return the innerTaskdirectly when possibleasync-avoid-task-run-server- AvoidTask.Runfor server request pathsasync-valuetask-single-await- Await aValueTaskexactly onceasync-async-lambda-void- Avoid async lambdas assigned toActionasync-channels-producer-consumer- UseSystem.Threading.Channelsasync-lock-not-monitor-async- Never holdlock/Monitoracrossawait
4. API & Type Design (HIGH)
api-primary-constructor- Use primary constructors to reduce boilerplateapi-init-only-properties- Useinit-only propertiesapi-required-members- Userequiredmembers instead of throwing constructorsapi-record-value-data- Model immutable value data withrecordapi-sealed-by-default- Seal classes by defaultapi-interface-segregation- Keep interfaces small and role-basedapi-extension-methods- Use extension methods for types you don't ownapi-builder-fluent- Use a fluent builder for complex constructionapi-collection-expressions- Use collection expressions ([1, 2, 3])api-expose-interfaces-not-impls- Return abstractions, not concrete collectionsapi-optional-parameters-vs-overloads- Prefer overloads over long optional-parameter listsapi-generic-constraints- Constrain generics withwhereclausesapi-static-factory-methods- Use static factory methods for validated constructionapi-out-var-pattern- Useout var/deconstruction for multiple returnsapi-with-expression-nondestructive- Usewithfor non-destructive mutation
5. Records & Immutability (HIGH)
immut-record-equality- Understand records give value-based equalityimmut-record-struct-small-value- Userecord structfor small valuesimmut-readonly-fields- Mark fieldsreadonlyunless mutation is requiredimmut-immutable-collections- UseSystem.Collections.Immutableimmut-avoid-mutable-public-fields- Never expose mutable public fieldsimmut-with-nondestructive-mutation- Usewithinstead of manual copy constructorsimmut-defensive-copy-collections- Defensively copy internal collectionsimmut-frozen-collections- UseFrozenDictionary/FrozenSetfor read-heavy lookupsimmut-positional-record-deconstruct- Positional records for deconstructionimmut-value-object-record- Model value objects as records, entities as classes
6. LINQ & Collections (HIGH)
linq-deferred-execution-aware- Understand deferred executionlinq-avoid-multiple-enumeration- Avoid enumerating a query multiple timeslinq-avoid-hot-path- Avoid LINQ allocations in hot pathslinq-any-vs-count- Use.Any()not.Count() > 0linq-firstordefault-vs-first- ChooseFirstOrDefaultvsFirstdeliberatelylinq-select-then-where-order- Filter before projectinglinq-avoid-linq-in-loop-alloc- Hoist invariant LINQ queries out of loopslinq-collection-choice- Choose collections by access patternlinq-span-linq-alternative- Replace LINQ with loops only when profiledlinq-groupby-lookup- UseGroupBy/ToLookupover manual groupinglinq-orderby-stable- Rely onOrderBy's stability; chainThenBylinq-iqueryable-vs-ienumerable- Know when work is pushed to the database
7. Naming Conventions (MEDIUM)
name-pascalcase-public-PascalCasefor types, methods, propertiesname-camelcase-locals-camelCasefor locals and parametersname-underscore-private-fields- Prefix private fields with_camelCasename-interface-i-prefix- Prefix interfaces withIname-async-suffix- Suffix async methods withAsyncname-generic-type-param-t- UseT/TKey/TValueconventionsname-boolean-is-has-can- UseIs/Has/Canfor booleansname-constants-pascalcase-PascalCasefor constants, notSCREAMING_CASEname-namespace-matches-folder- Match namespace to folder structurename-avoid-hungarian- Avoid Hungarian notationname-event-naming- Name events with a verb phrase; handlers withOnname-avoid-abbreviations- Avoid unclear abbreviationsname-file-matches-type- Name a file after its single public typename-plural-collections- Name collections with plural nouns
8. Type Safety & Nullability (MEDIUM/HIGH)
type-nullable-reference-types- Enable nullable reference typestype-pattern-matching-is- Use pattern matching over type-casting chainstype-switch-expression-exhaustive- Exhaustive switch expressionstype-avoid-dynamic- Avoiddynamictype-enum-design- Explicit enum values;[Flags]only when combinabletype-nullable-value-types- UseT?for optional value typestype-notnullwhen-attributes- Annotate your own nullable-flow APIstype-record-for-equality- Use records instead of manual equalitytype-generic-math- Use generic math for numeric-agnostic algorithmstype-avoid-object-typed- Avoidobject-typed APIstype-strongly-typed-ids- Wrap primitive IDs in strongly-typed structstype-null-forgiving-sparingly- Use!sparingly, with justification
9. Dependency Injection & Configuration (MEDIUM)
di-constructor-injection- Prefer constructor injectiondi-lifetime-choice- Choose Singleton/Scoped/Transient deliberatelydi-avoid-captive-dependency- Avoid captive dependenciesdi-options-pattern- Use the Options pattern for configurationdi-avoid-service-locator- Avoid the service-locator anti-patterndi-register-interfaces- Register interfaces, not concrete implementationsdi-validate-on-start- Validate the DI graph eagerly at startupdi-httpclientfactory- UseIHttpClientFactorydi-keyed-services- Use keyed DI services for multiple implementationsdi-avoid-property-injection- Avoid property/method injection
10. Testing (MEDIUM)
test-xunit-theory-inlinedata- Use[Theory]/[InlineData]test-arrange-act-assert- Structure tests as Arrange/Act/Asserttest-fluentassertions- Use FluentAssertions for readable assertionstest-mock-interfaces-not-concretes- Mock interfaces, not concretestest-nsubstitute-moq- Use NSubstitute/Moq for isolationtest-descriptive-test-names- Name tests descriptivelytest-webapplicationfactory-integration- UseWebApplicationFactory<T>test-avoid-shared-mutable-state- Avoid shared mutable state across teststest-collection-fixture- Use fixtures for expensive shared setuptest-avoid-thread-sleep- Never useThread.Sleepto synchronize teststest-testcontainers-integration- Use Testcontainers for real dependenciestest-one-assert-concept- Test one logical concept per test methodtest-avoid-testing-private- Test through the public API
11. Documentation (MEDIUM)
doc-xml-summary-public- Document all public members with<summary>doc-param-returns-tags- Document parameters and return valuesdoc-exception-tags- Document exceptions with<exception>doc-example-code- Include<example>/<code>for non-obvious APIsdoc-generate-xml-docfile- EnableGenerateDocumentationFiledoc-see-cref-links- Cross-reference with<see cref="..."/>doc-remarks-for-nuance- Use<remarks>for nuance/caveatsdoc-inheritdoc- Use<inheritdoc/>to avoid duplicationdoc-readme-nuget-metadata- Fill NuGet package metadata
12. Performance Patterns (MEDIUM)
perf-stringbuilder-concat- UseStringBuilderfor loop concatenationperf-string-interpolation-vs-concat- Know how interpolation compilesperf-span-parsing- Parse withSpan<T>to avoid substring allocationsperf-source-generators-over-reflection- Source generators over reflectionperf-record-struct-hot-data-record structfor small, hot dataperf-avoid-linq-hot-path- Avoid LINQ in proven hot pathsperf-cache-regex- Compile/cacheRegex, or use[GeneratedRegex]perf-frozen-lookup-startup- Frozen collections for startup-built lookupsperf-value-task-hot-path- ReturnValueTaskfrom hot async methodsperf-string-comparison-ordinal- UseStringComparison.Ordinalperf-avoid-unnecessary-async-state-machine- Avoid needless async state machinesperf-json-source-gen- UseSystem.Text.Jsonsource generation
13. Project Structure (LOW)
proj-directory-build-props- Centralize settings inDirectory.Build.propsproj-central-package-management- Central package managementproj-solution-folder-layout- Organize solution folders by layerproj-internal-visibility- Default tointernalvisibilityproj-internalsvisibleto-tests- Expose internals to tests viaInternalsVisibleToproj-nullable-enable-solution-wide- Enable nullable solution-wideproj-namespace-folder-structure- Match namespaces to foldersproj-separate-test-projects- Keep test projects separateproj-file-scoped-namespaces- Use file-scoped namespacesproj-implicit-usings- UseImplicitUsingsandGlobalUsings.cs
14. Analyzers & Linting (LOW)
lint-treat-warnings-as-errors- EnableTreatWarningsAsErrorsin CIlint-nullable-warnings-errors- Promote nullable warnings to errorslint-editorconfig-enforce- Enforce style with.editorconfiglint-roslyn-analyzers- EnableMicrosoft.CodeAnalysis.NetAnalyzerslint-stylecop-analyzers- Use StyleCop.Analyzerslint-code-analysis-enforce-latest- SetAnalysisLeveltolatestlint-format-verify-ci- Rundotnet format --verify-no-changesin CIlint-suppress-with-justification- Justify every suppressionlint-banned-api-analyzer- Ban dangerous APIs at compile timelint-nuget-audit- EnableNuGetAuditfor vulnerable packages
15. Anti-patterns (REFERENCE)
anti-async-void- Don't useasync voidoutside event handlersanti-sync-over-async- Don't block on async with.Result/.Wait()anti-catch-exception-broad- Don't catchExceptionbroadlyanti-empty-catch-block- Don't leave empty catch blocksanti-god-class- Don't build God classesanti-magic-strings-numbers- Don't scatter magic strings/numbersanti-mutable-public-fields- Don't expose mutable public fieldsanti-linq-multiple-enumeration- Don't enumerate a query multiple timesanti-over-mocking- Don't over-mockanti-primitive-obsession- Don't pass primitive swarms instead of typesanti-throw-ex-loses-stack- Don't usethrow ex;anti-datetime-now-untestable- Don't callDateTime.Nowdirectlyanti-singleton-static-state- Don't hide dependencies behind staticsanti-boxing-generic-collections- Don't box into non-generic collectionsanti-region-abuse- Don't use#regionto hide poor organization
Recommended .csproj / Directory.Build.props Settings
<!-- Directory.Build.props (repository root) -->
<Project>
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AnalysisLevel>latest</AnalysisLevel>
<AnalysisMode>Recommended</AnalysisMode>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NuGetAudit>true</NuGetAudit>
<NuGetAuditMode>all</NuGetAuditMode>
</PropertyGroup>
</Project>
<!-- Directory.Packages.props (repository root) -->
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
</Project>
# .editorconfig
root = true
[*.cs]
indent_style = space
indent_size = 4
csharp_style_namespace_declarations = file_scoped:warning
dotnet_style_namespace_match_folder = true:warning
dotnet_style_require_accessibility_modifiers = always:warning
dotnet_diagnostic.CA2007.severity = warning # ConfigureAwait(false) in libraries
How to Use
This skill provides rule identifiers for quick reference. When generating or reviewing C#/.NET code:
- Check relevant category based on task type
- Apply rules with matching prefix
- Prioritize CRITICAL > HIGH > MEDIUM > LOW
- Read rule files in
rules/for detailed examples
Rule Application by Task
| Task | Primary Categories |
|---|---|
| New class/method | api-, err-, name- |
| New record/DTO | immut-, api-, type- |
| Async code | async-, mem- |
| Error handling | err-, api- |
| Memory optimization | mem-, perf- |
| LINQ/collections | linq-, perf- |
| Dependency injection | di- |
| Writing tests | test- |
| Performance tuning | perf-, mem-, linq- |
| Code review | anti-, lint- |
Related Skills
- design-patterns - choosing and implementing GoF and idiomatic patterns; apply alongside this skill's API and naming rules for pattern-heavy C# design.
- security-review - security-focused audit checklists; apply alongside this skill's error-handling and interop rules when reviewing C# code for vulnerabilities.
Sources
This skill synthesizes best practices from:
- Microsoft C# Coding Conventions
- .NET Framework Design Guidelines (Cwalina & Abrams)
- .NET Naming Guidelines
- What's New in C# (C# 12/13 language reference)
dotnet/roslyn-analyzersanddotnet/runtimesource and analyzer documentation- Production codebases:
dotnet/runtime,dotnet/aspnetcore,dotnet/efcore - Community conventions and tooling (StyleCop.Analyzers, xUnit, FluentAssertions, Testcontainers) (2024-2025)