iTranslated by AI
[C#] Running source files directly with `dotnet run file.cs` is coming
What does this mean?
- In .NET 10, it seems you will be able to execute C# using
dotnet run file.cs.- It is available as of .NET SDK 10 preview 4.
dotnet run file.cs
-
Unlike
dotnet runordotnet build,/binand/objfolders are not generated.- No build artifacts or intermediate products are created.
-
You can later convert it to the standard source + project file format.
dotnet project convert file.cs
-
New syntax seems to be coming in C# 14.0?
#!/usr/bin/dotnet run
#:sdk Microsoft.NET.Sdk.Web
#:property TargetFramework net10.0
#:property LangVersion preview
#:package System.CommandLine 2.0.0-*
Console.WriteLine("Hello, World!");
- This is different from the long-standing "C# Script (the ones with the
*.csxextension)."
dotnet run file.cs
The conventional dotnet run
The existing dotnet run command is a convenient tool that handles dependency resolution, compilation, and execution for C#[1] all at once.
While C# is strongly associated with sharing compiled executable files, the dotnet run command allows you to distribute and use source code directly, similar to script distribution (as long as the SDK is installed in the environment).
However, until now, the dotnet run command required a project file (*.csproj) in addition to the source file (*.cs).
Even though dotnet new console creates it automatically from a template, having multiple files is inconvenient for script-like usage.
Directly Specifying Source Files (*.cs)
In .NET 10 and later, the dotnet run command will allow you to execute by specifying a source file. A project file (*.csproj) is not required[2].
dotnet run file.cs
The way to specify command-line arguments is the same as dotnet run; arguments other than the source file specification are passed to args.
dotnet run file.cs --option=xxx
If no source file is specified, it behaves as before (searching for and using a project file).
No Build Artifacts or Intermediate Products (bin/obj) are Generated
In regular dotnet run or dotnet build, build results (like .dll or .exe) and intermediate artifacts are generated in /bin and /obj within the same directory.
However, with this dotnet run file.cs, no build results or intermediate products are generated.
Neither an .exe nor a .dll exists; it runs directly. It feels a bit like a script.
That said, it's not that it isn't being built; the first run takes a fair amount of time because it also checks dependencies (similar to when you run dotnet run for the first time). Unfortunately, it's not as fast as a script!
Since /bin and /obj are not created, it also prevents mistakes like accidentally committing them because you forgot to specify them in .gitignore[4]!
Which Source Files Can Be Specified?
The source files that can be executed are files that can serve as entry points.
Speaking of entry points, since modern C# has Top-level statements, essentially any source file where code is written directly like a script will serve as an entry point.
Console.WriteLine("Hello, World!");
If you specify a .cs file that is not an entry point, it will fail.
CSC : error CS5001:
Program does not contain a static 'Main' method suitable for an entry point
Build of project "lib.csproj" finished -- FAILED.
The build failed. Fix the build errors and run again.
// This is not an entry point
public class Lib {}
New C# Syntax: ignored directives
New syntax is planned to be added to C# as well.
To be precise, there are two types, and lines starting with the following combinations of symbols are ignored as C# code.
-
shebang directive starting with
#! - ignored directive starting with
#:
#!/usr/bin/dotnet run
#:sdk Microsoft.NET.Sdk.Web
#:property TargetFramework net11.0
#:property LangVersion preview
#:package System.CommandLine 2.0.0-*
Console.WriteLine("Hello, World!");
No characters can be written before these lines (BOM is also not allowed).
#!/usr/bin/dotnet run
#:sdk Microsoft.NET.Sdk.Web
#:property TargetFramework net11.0
#:property LangVersion preview
#:package System.CommandLine 2.0.0-*
Console.WriteLine("Hello, World!");
error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line
Build of project "badignore.csproj" finished -- FAILED.
The build failed. Fix the build errors and run again.
These notations have no meaning as C# code and are intended to provide information to the compiler, MSBuild, etc. By writing the contents previously described in the .csproj here, the .csproj becomes unnecessary.
#! (shebang)
You are likely familiar with the shebang directive from shell scripts and the like.
#!/usr/bin/dotnet run
In other words, by specifying the path in a Unix environment and using chmod, you can execute the .cs file on its own.
[Update] For macOS Sequoia 15.5
#!/usr/bin/dotnet run does not work.
Rewrite the first line in the sample source as follows:
- #!/usr/bin/dotnet run
+ #!/usr/bin/env -S dotnet run
After that, if you grant execution permissions (chmod 755), you can run it just by specifying the .cs file.
% chmod 755 file.cs
% ./file.cs
Hello, World!
#:
In terms of syntax, only #: at the beginning of the line has been added.
However, in practice, you will write the contents that were previously in the .csproj, such as #:sdk or #:property, following the #:.
| Syntax | .csproj |
|---|---|
#:sdk *** |
<Project Sdk="***"> |
#:property XX YY |
<PropertyGroup><XX>YY</XX></PropertyGroup> |
#:package XX 1.0.0-* |
<ItemGroup><PackageReference Include="XX" Version="2.0.0-*" /></ItemGroup> |
Currently, it seems that only three options can be specified: sdk, property, and package.
Can be converted to a standard .csproj
You can convert it to a standard format consisting of a regular source file (*.cs) and a project file (*.csproj) using the following command:
dotnet project convert file.cs
file.cs
↓
file/
├ file.cs
└ file.csproj
The sections containing ignored directives are removed from the source file and converted into the .csproj file.
#!/usr/bin/dotnet run
#:sdk Microsoft.NET.Sdk.Web
#:property TargetFramework net11.0
#:property LangVersion preview
#:package System.CommandLine 2.0.0-*
↓
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net11.0</TargetFramework>
<LangVersion>preview</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.CommandLine" Version="2.0.0-*" />
</ItemGroup>
</Project>
How is it different from C# Script / REPL?
C# has long had a script execution mechanism called C# Script (Roslyn Script)[6].
- dotnet interactive
- Polyglot Notebooks for VSCode
- External tools installable via
dotnet tool
As the name implies, C# Script is for script execution, so it has the following differences from this new feature:
- Requires a dedicated extension (
.csx) for files. - Requires special syntax like
#rfor referencing external libraries. -
dotnet run file.csxis not supported.
On the other hand, the C# in dotnet run file.cs is standard C# source code.
You don't need to write ignored directives if you don't need them; you can use existing C# source code as-is.
Since building takes a bit of time, it doesn't run quite as fast as a script[7], making it less suitable for REPL use.
Other Trivia
- Although the documentation states that
dotnet restore file.csanddotnet build file.csare also possible, they did not work in .NET SDK 10.0-preview 4.- Will they be included in the final release?
- There are no plans to support
dotnet pack file.csordotnet watch run file.cs.- I feel like people might eventually want
watchsupport...
- I feel like people might eventually want
- It seems only files with the
*.csextension are supported[8].- There might be no intention to support F# or VB.NET.
- Then again, F# already has
dotnet fsi, so it might not be necessary.
- Then again, F# already has
- There might be no intention to support F# or VB.NET.
- The option to output binary logs with
-blappears to be enabled. - Update: Improvements to build performance are being considered.
- Support for execution via
dotnet file.cs(omitting the explicitrunsubcommand) is also under consideration.
Summary
- (Probably) In .NET 10, you will be able to run C# from a single file using
dotnet run file.cs, making project files (.csproj) unnecessary. - Build artifacts and intermediate files (
/binand/obj) are not generated. - Execution is the same as regular C# code (not a script), so existing C# knowledge can be applied directly.
- Newly introduced
#:and#!directive syntax allows for specifying dependencies and the SDK within the .cs file. - Can be converted to a regular project structure using
dotnet project convert file.cs. - It is different from existing C# Script (
.csx). - Currently[9], available in the preview version of .NET SDK 10.
Update
References
- sdk/documentation/general/dotnet-run-file.md at main · dotnet/sdk
- csharplang/proposals/ignored-directives.md at main · dotnet/csharplang
-
More accurately, F# and others as well ↩︎
-
Internally, it behaves as if a virtual project file is created ↩︎
-
dotnet new gitignorewould solve that anyway, though... ↩︎ -
Currently not implemented as of preview 4 Support multiple files in file-based programs by jjonescz · Pull Request #48782 · dotnet/sdk ↩︎
-
Note that while it's confusing, Unity's C# is also called "C# Script," but that is something else entirely ↩︎
-
That said,
dotnet-scriptalso takes about the same time for the first run ↩︎ -
2025-05-23 ↩︎
Discussion