$npx -y skills add wshaddix/dotnet-skills --skill csharp-scriptsRun single-file C# programs as scripts for quick experimentation, prototyping, and concept testing. Use when the user wants to write and execute a small C# program without creating a full project.
| 1 | # C# Scripts |
| 2 | |
| 3 | ## When to Use |
| 4 | |
| 5 | - Testing a C# concept, API, or language feature with a quick one-file program |
| 6 | - Prototyping logic before integrating it into a larger project |
| 7 | |
| 8 | ## When Not to Use |
| 9 | |
| 10 | - The user needs a full project with multiple files or project references |
| 11 | - The user is working inside an existing .NET solution and wants to add code there |
| 12 | - The program is too large or complex for a single file |
| 13 | |
| 14 | ## Inputs |
| 15 | |
| 16 | | Input | Required | Description | |
| 17 | |-------|----------|-------------| |
| 18 | | C# code or intent | Yes | The code to run, or a description of what the script should do | |
| 19 | |
| 20 | ## Workflow |
| 21 | |
| 22 | ### Step 1: Check the .NET SDK version |
| 23 | |
| 24 | Run `dotnet --version` to verify the SDK is installed and note the major version number. File-based apps require .NET 10 or later. If the version is below 10, follow the [fallback for older SDKs](#fallback-for-net-9-and-earlier) instead. |
| 25 | |
| 26 | ### Step 2: Write the script file |
| 27 | |
| 28 | Create a single `.cs` file using top-level statements. Place it outside any existing project directory to avoid conflicts with `.csproj` files. |
| 29 | |
| 30 | ```csharp |
| 31 | // hello.cs |
| 32 | Console.WriteLine("Hello from a C# script!"); |
| 33 | |
| 34 | var numbers = new[] { 1, 2, 3, 4, 5 }; |
| 35 | Console.WriteLine($"Sum: {numbers.Sum()}"); |
| 36 | ``` |
| 37 | |
| 38 | Guidelines: |
| 39 | |
| 40 | - Use top-level statements (no `Main` method, class, or namespace boilerplate) |
| 41 | - Place `using` directives at the top of the file |
| 42 | - Place type declarations (classes, records, enums) after all top-level statements |
| 43 | |
| 44 | ### Step 3: Run the script |
| 45 | |
| 46 | ```bash |
| 47 | dotnet hello.cs |
| 48 | ``` |
| 49 | |
| 50 | Builds and runs the file automatically. Cached so subsequent runs are fast. Pass arguments after `--`: |
| 51 | |
| 52 | ```bash |
| 53 | dotnet hello.cs -- arg1 arg2 "multi word arg" |
| 54 | ``` |
| 55 | |
| 56 | ### Step 4: Add NuGet packages (if needed) |
| 57 | |
| 58 | Use the `#:package` directive at the top of the file to reference NuGet packages. Always specify a version: |
| 59 | |
| 60 | ```csharp |
| 61 | #:package Humanizer@2.14.1 |
| 62 | |
| 63 | using Humanizer; |
| 64 | |
| 65 | Console.WriteLine("hello world".Titleize()); |
| 66 | ``` |
| 67 | |
| 68 | ### Step 5: Clean up |
| 69 | |
| 70 | Remove the script file when the user is done. To clear cached build artifacts: |
| 71 | |
| 72 | ```bash |
| 73 | dotnet clean hello.cs |
| 74 | ``` |
| 75 | |
| 76 | ## Unix shebang support |
| 77 | |
| 78 | On Unix platforms, make a `.cs` file directly executable: |
| 79 | |
| 80 | 1. Add a shebang as the first line of the file: |
| 81 | |
| 82 | ```csharp |
| 83 | #!/usr/bin/env dotnet |
| 84 | Console.WriteLine("I'm executable!"); |
| 85 | ``` |
| 86 | |
| 87 | 2. Set execute permissions: |
| 88 | |
| 89 | ```bash |
| 90 | chmod +x hello.cs |
| 91 | ``` |
| 92 | |
| 93 | 3. Run directly: |
| 94 | |
| 95 | ```bash |
| 96 | ./hello.cs |
| 97 | ``` |
| 98 | |
| 99 | Use `LF` line endings (not `CRLF`) when adding a shebang. This directive is ignored on Windows. |
| 100 | |
| 101 | ## Source-generated JSON |
| 102 | |
| 103 | File-based apps enable native AOT by default. Reflection-based APIs like `JsonSerializer.Serialize<T>(value)` fail at runtime under AOT. Use source-generated serialization instead: |
| 104 | |
| 105 | ```csharp |
| 106 | using System.Text.Json; |
| 107 | using System.Text.Json.Serialization; |
| 108 | |
| 109 | var person = new Person("Alice", 30); |
| 110 | var json = JsonSerializer.Serialize(person, AppJsonContext.Default.Person); |
| 111 | Console.WriteLine(json); |
| 112 | |
| 113 | var deserialized = JsonSerializer.Deserialize(json, AppJsonContext.Default.Person); |
| 114 | Console.WriteLine($"Name: {deserialized!.Name}, Age: {deserialized.Age}"); |
| 115 | |
| 116 | record Person(string Name, int Age); |
| 117 | |
| 118 | [JsonSerializable(typeof(Person))] |
| 119 | partial class AppJsonContext : JsonSerializerContext; |
| 120 | ``` |
| 121 | |
| 122 | ## Converting to a project |
| 123 | |
| 124 | When a script outgrows a single file, convert it to a full project: |
| 125 | |
| 126 | ```bash |
| 127 | dotnet project convert hello.cs |
| 128 | ``` |
| 129 | |
| 130 | ## Fallback for .NET 9 and earlier |
| 131 | |
| 132 | If the .NET SDK version is below 10, file-based apps are not available. Use a temporary console project instead: |
| 133 | |
| 134 | ```bash |
| 135 | mkdir -p /tmp/csharp-script && cd /tmp/csharp-script |
| 136 | dotnet new console -o . --force |
| 137 | ``` |
| 138 | |
| 139 | Replace the generated `Program.cs` with the script content and run with `dotnet run`. Add NuGet packages with `dotnet add package <name>`. Remove the directory when done. |
| 140 | |
| 141 | ## Validation |
| 142 | |
| 143 | - [ ] `dotnet --version` reports 10.0 or later (or fallback path is used) |
| 144 | - [ ] The script compiles without errors (can be checked explicitly with `dotnet build <file>.cs`) |
| 145 | - [ ] `dotnet <file>.cs` produces the expected output |
| 146 | - [ ] Script file and cached artifacts are cleaned up after the session |
| 147 | |
| 148 | ## Common Pitfalls |
| 149 | |
| 150 | | Pitfall | Solution | |
| 151 | |---------|----------| |
| 152 | | `.cs` file is inside a directory with a `.csproj` | Move the script outside the project directory, or use `dotnet run --file file.cs` | |
| 153 | | `#:package` without a version | Specify a version: `#:package PackageName@1.2.3` or `@*` for latest | |
| 154 | | Reflection-based JSON serialization fails | Use source-generated JSON with `JsonSerializerContext` (see [Source-generated JSON](#source-generated-json)) | |
| 155 | | Unexpected build behavior or version errors | File-based apps inherit `global.json`, `Directory.Build.props`, `Directory.Build.targets`, and `nuget.config` from parent directories. Move the script to an |