csharp
Use when building C# applications on modern .NET — command-line tools with System.CommandLine, desktop games with Raylib-cs, xUnit tests, self-contained single-file publishing for Windows and Linux, a
SKILL.mdskills/languages/csharp/SKILL.md
GitHubC# on .NET
Default: the current LTS .NET SDK, nullable reference types on, warnings as errors, xUnit, dotnet publish self-contained.
Setup
dotnet new console -n Appanddotnet new xunit -n App.Tests; add both to a solution.- In the project file:
<Nullable>enable</Nullable>,<TreatWarningsAsErrors>true</TreatWarningsAsErrors>,<ImplicitUsings>enable</ImplicitUsings>. - Pin the SDK with
global.jsonso CI and every machine build with the same compiler. - A machine without admin rights can install the SDK per user with the official
dotnet-installscript; put it onPATHonly where it is used.
Command-line tools
- Use
System.CommandLinefor parsing; it gives help, validation and tab completion. - Keep the core logic in a library project and the CLI as a thin layer; test the library.
- Return non-zero exit codes on failure; write data to stdout and messages to stderr.
Desktop games with Raylib-cs
- Separate the simulation (plain classes, deterministic, testable with xUnit) from drawing.
- Measure frame time with a real clock over a window (e.g. 1 s) and report mean and worst; a single fps number hides stutter. Add a
--statsflag rather than guessing. - Provide a headless mode (
--bench,--screenshot <scene>) so balance and images can be produced without playing. - Load fonts with the exact set of code points the UI uses (collect them from all string tables at start-up). Loading only ASCII makes every Japanese or accented character render as
?. - Font collections (
.ttc) cannot be passed directly to loaders that expect a single font; extract the face you need first, then verify every required glyph exists in itscmap.
Persistence
- Give save data a version number and migrate forward step by step (
v1 → v2 → v3). Test each migration with a stored fixture of the old format. - Write saves atomically (temp file +
File.Move(..., overwrite: true)).
Publishing
dotnet publish -c Release -r win-x64 --self-contained -p:PublishSingleFile=true
dotnet publish -c Release -r linux-x64 --self-contained -p:PublishSingleFile=true
- Self-contained avoids "install the runtime first" support issues; check the output size and keep it in the release notes.
- Native libraries (raylib) must be present for each runtime identifier; test the published binary on a clean machine or VM.
Verification loop
dotnet buildwith zero warnings.dotnet test— unit tests plus a deterministic benchmark (fixed seed → known outcome ranges).- Run the published binary, not
dotnet run, before release.
Pitfalls
- Culture-sensitive formatting:
double.ParseandToString()follow the OS locale. UseCultureInfo.InvariantCulturefor files and protocols. async voidswallows exceptions except in event handlers.- Floating-point determinism differs across platforms and JIT settings; do not rely on bit-identical physics for replays across machines without fixed-point or careful ordering.
- Disposing native resources: textures, sounds and fonts from native libraries are not garbage-collected promptly; unload them explicitly.
- WSL or remote desktops may render through a software path; measure performance on the target hardware before tuning.