Skip to footer content
Iron Academy Logo
Learn C#
Learn C#

Other Categories

File-Based C# Execution in .NET 10

Tim Corey
13m 36s

C# has always required a project file to run code. Even the simplest "Hello World" needed a .csproj, a Program.cs, and a build step before anything executed. With .NET 10, that changes. You can now write a single .cs file and run it directly, the same way you'd execute a Python script or a Node.js file.

In his video "File-Based C# Execution in .NET 10", Tim Corey walks through every aspect of this feature: running a standalone file, passing command line arguments, adding NuGet packages inline, publishing to a native executable, and converting a file into a full project when the scope outgrows the single-file format. If you've been using console apps for quick scripts and prototypes, this changes the workflow significantly.

Running a Single C# File

[0:40 - 1:48] Tim starts in VS Code with an empty folder. No solution, no project file, no boilerplate. He creates a single file called demo.cs with one line of code:

Console.WriteLine("Hello World");
Console.WriteLine("Hello World");

To execute it:

dotnet run file demo.cs
dotnet run file demo.cs

That's the full workflow. The dotnet run file command compiles and executes the .cs file in one step. There's no intermediate .csproj generated, no bin or obj folders created. The mental model is closer to scripting than traditional C# development: write a file, run it, see output.

Tim draws a direct comparison to Python and JavaScript, where single-file execution has always been the default. .NET 10 brings C# into that same territory while keeping the type safety and performance that make C# attractive in the first place.

Command Line Arguments and Implicit Usings

[1:48 - 3:26] The args array is available automatically, just like it would be in a standard Program.cs with top-level statements. Tim modifies the file to accept a name argument:

Console.WriteLine($"Hello {args[0]}");
Console.WriteLine($"Hello {args[0]}");

Running it with an argument:

dotnet run file demo.cs Tim
dotnet run file demo.cs Tim

This prints "Hello Tim". Tim points out that Console.WriteLine works without a using System; statement because file-based execution includes implicit usings by default. This is the same behavior that top-level statements introduced in C# 9, now extended to standalone files.

The broader pattern Microsoft has been following is trimming ceremony from C# one release at a time. Global usings, top-level statements, and now standalone .cs files are all steps toward making C# viable for quick tasks that previously required Python or bash scripts.

Adding User Input

[3:26 - 5:43] Tim replaces the argument approach with interactive input to demonstrate that file-based execution supports the full range of console application patterns:

Console.Write("What is your name? ");
string name = Console.ReadLine();
Console.WriteLine($"Hello {name}");
Console.Write("What is your name? ");
string name = Console.ReadLine();
Console.WriteLine($"Hello {name}");

Running this prompts for input, reads the response, and prints the greeting. Standard I/O works identically to a full project.

One limitation Tim calls out: this mode is strictly single-file. You can't have two .cs files referencing each other. If your code grows to the point where you need multiple files, it's time to convert to a project (covered later in the video).

Bringing in NuGet Packages

[5:43 - 7:49] This is where the single-file approach gets genuinely useful for scripting. You can reference NuGet packages directly inside the .cs file using a #r directive at the top:

#r "nuget:Spectre.Console, 0.54.0"
using Spectre.Console;

Console.Write("What is your name? ");
string name = Console.ReadLine();
AnsiConsole.MarkupLine($"Hello [red]{name}[/]");
#r "nuget:Spectre.Console, 0.54.0"
using Spectre.Console;

Console.Write("What is your name? ");
string name = Console.ReadLine();
AnsiConsole.MarkupLine($"Hello [red]{name}[/]");

The #r "nuget:..." syntax tells the runtime to download and reference the specified package before compilation. Tim uses Spectre.Console to colorize the output, turning the greeting text red.

No dotnet add package command, no .csproj to edit, no restore step. The package reference lives in the source file itself. For scripts that need an HTTP client, a JSON serializer, or a formatting library, this eliminates the overhead of creating a project just to pull in one dependency.

Publishing to a Native Executable

[7:49 - 8:59] When you want to distribute a file-based script as a standalone binary, the publish command handles it:

dotnet publish file demo.cs
dotnet publish file demo.cs

This produces an executable in the artifacts folder. The compiled binary includes everything needed to run, with no .NET SDK required on the target machine. Tim notes that file-based builds use Native AOT by default, which means the output is a single, self-contained binary with fast startup times.

If Native AOT causes compatibility issues with a specific library, you can disable it by adding a property directive at the top of the file (similar to how #r works for packages). For most scripting use cases, though, AOT is the right default.

Converting to a Full Project

[8:59 - 10:45] The escape hatch for when a script outgrows the single-file format is the convert command:

dotnet project convert file demo.cs
dotnet project convert file demo.cs

This generates a .csproj file that includes the NuGet package references from the #r directives and moves the code into a standard project structure. From that point forward, you can add multiple files, configure build settings, and use the full .NET project system.

Tim frames this as a natural progression: start with a single file for quick experiments, and when the scope expands, promote it to a project without rewriting anything. The #r directives translate cleanly into <PackageReference> entries in the generated .csproj.

Native AOT and Platform Notes

[10:45 - 12:42] By default, file-based execution compiles with Native AOT, producing the fastest possible startup times. Tim notes this is ideal for CLI tools and scripts where cold start performance matters. If a library isn't AOT-compatible, the feature can be disabled with a file-level property.

On Linux and macOS, you can also add a hashbang (#!/usr/bin/dotnet run file) at the top of the .cs file, making it directly executable from the shell without typing the dotnet run file prefix. This brings C# fully into scripting territory alongside bash, Python, and Ruby.

Wrapping Up: C# as a Scripting Language

[12:42 - 13:05] The feature Tim highlights as most impactful is the elimination of project overhead for small tasks. Console apps have always been the go-to for quick experiments, automation scripts, and one-off tools. File-based execution removes the ceremony that made those feel heavier than they needed to be, while keeping every advantage (type safety, performance, NuGet ecosystem) that makes C# worth choosing over a scripting language.

Conclusion

[13:05 - 13:36] To sum it up: .NET 10's file-based execution lets you run a single .cs file with dotnet run file, reference NuGet packages with #r directives, publish to a native binary with dotnet publish file, and convert to a full project with dotnet project convert file when you outgrow the single-file format. Native AOT is on by default, and Linux/macOS users get hashbang support for shell-level execution.

For anything you currently use a console app to prototype or automate, this is worth trying as a lighter-weight alternative.

Example Tip: If you find yourself creating console apps just to test a NuGet package or debug an API call, try file-based execution instead. Create a .cs file, add #r "nuget:PackageName, Version" at the top, write your test code, and run it with dotnet run file. When you're done, delete the file. No project cleanup required.

Watch full video on his YouTube Channel and gain more insights on modern C# development workflows.

Hero Worlddot related to File-Based C# Execution in .NET 10
Hero Affiliate related to File-Based C# Execution in .NET 10

Earn More by Sharing What You Love

Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!

Iron Support Team

We're online 24 hours, 5 days a week.
Chat
Email
Call Me