Csharp coding
Skill nguyenthdat/opencode-manager/registry/skills/csharp-coding
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.From its SKILL.md
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.
One thing to look at
- 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.
SKILL.md
28.7 KB, ~7.4k tokens by cl100k_base, 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)
What ships with it: 186 files
410.4 KB alongside SKILL.md
rules/
- anti-async-void.md1.7 KB
- anti-boxing-generic-collections.md1.4 KB
- anti-catch-exception-broad.md1.6 KB
- anti-datetime-now-untestable.md2.2 KB
- anti-empty-catch-block.md1.4 KB
- anti-god-class.md2.1 KB
- anti-linq-multiple-enumeration.md1.5 KB
- anti-magic-strings-numbers.md1.6 KB
- anti-mutable-public-fields.md1.4 KB
- anti-over-mocking.md1.8 KB
- anti-primitive-obsession.md1.4 KB
- anti-region-abuse.md2.1 KB
- anti-singleton-static-state.md1.9 KB
- anti-sync-over-async.md1.6 KB
- anti-throw-ex-loses-stack.md1.1 KB
- api-builder-fluent.md2.6 KB
- api-collection-expressions.md2.0 KB
- api-expose-interfaces-not-impls.md2.4 KB
- api-extension-methods.md2.3 KB
- api-generic-constraints.md2.5 KB
- api-init-only-properties.md2.1 KB
- api-interface-segregation.md2.5 KB
- api-optional-parameters-vs-overloads.md2.2 KB
- api-out-var-pattern.md2.2 KB
- api-primary-constructor.md2.7 KB
- api-record-value-data.md2.5 KB
- api-required-members.md2.3 KB
- api-sealed-by-default.md2.2 KB
- api-static-factory-methods.md2.6 KB
- api-with-expression-nondestructive.md2.2 KB
- async-async-lambda-void.md2.3 KB
- async-avoid-task-run-server.md2.3 KB
- async-cancellationtoken-propagate.md2.5 KB
- async-channels-producer-consumer.md2.7 KB
- async-configureawait-false-lib.md2.3 KB
- async-iasyncenumerable-streaming.md2.4 KB
- async-lock-not-monitor-async.md2.1 KB
- async-name-suffix.md1.8 KB
- async-no-async-void.md2.3 KB
- async-no-sync-over-async.md2.2 KB
146 more files not listed here. See all 186 in the repository.