C# scripts using dotnet-script

You know how the python folks can just write some code in a .py file and run it with python3 filename.py, without going through the "new project" and the "public static void main" ceremony that C# users have to? C# can write scripts too – something I learnt just like a year back.

Roslyn made C# scripts possible. There's a Jan 2016 MSDN blogpost by Mark Michaelis titled C# Scripting if you're interested.

The tool that I've been really fascinated by is dotnet-script, which is a cross-platform .NET Core global tool with full intellisense support on VS Code via omnisharp and covers most use-cases for your experimenting needs.


  1. .NET Core SDK 2.1+
  2. VS Code
  3. Official C# Extension for VS Code


First make sure you have the latest version of .NET Core SDK installed from dot.net. The minimum requirement is .NET Core 2.1.

The easiest way to install dotnet-script is installing it as a global tool:

dotnet tool install --global dotnet-script

You should now be able to run dotnet script --version and it should print the version of the dotnet-script tool.

Hello world

  1. Create a new directory to store all your scripts.
  2. Init a new script.
  3. Run.
mkdir console
cd console
dotnet script init hello
dotnet script hello.csx
  • The init command creates a .csx (C# script) file along with an omnisharp.json file for intellisense and a VS Code launch.json file for debug support.
  • The csx script is run using dotnet script filename.

The generated hello.csx file has the following contents:

#!/usr/bin/env dotnet-script

Console.WriteLine("Hello world!");

The first line is a shebang that *nix users must be familiar with. It means we can run it as ./hello.csx and it'll just work! (Also works on windows, but you'll need to associate .csx files with dotnet-script first using dotnet script register.)

Another quick example

A typical use-case for console applications is to experiment with new libraries, and dotnet-script is the perfect tool for the job. Let's say I'm learning about the stateless library. Here's how I'd go about experimenting:

  1. Create a new script using dotnet script init stateless.
  2. Open the folder in VS Code using code ..
  3. Import the nuget package using the roslyn #r directive.
  4. Go to their GitHub page and copy some sample code over.
  5. Add using statements if needed using Cmd+. (or Ctrl+.).
  6. Debug within VS code with F5.

Here's my stateless.csx file:

#!/usr/bin/env dotnet-script
#r "nuget: Stateless, 4.2.1"

// Copied from: https://github.com/dotnet-state-machine/stateless/blob/dev/example/OnOffExample/Program.cs
using Stateless;

const string on = "On";
const string off = "Off";
const char space = ' ';

// Instantiate a new state machine in the 'off' state
var onOffSwitch = new StateMachine<string, char>(off);

// Configure state machine with the Configure method, supplying the state to be configured as a parameter
onOffSwitch.Configure(off).Permit(space, on);
onOffSwitch.Configure(on).Permit(space, off);

Console.WriteLine("Press <space> to toggle the switch. Any other key will exit the program.");

while (true)
    Console.WriteLine("Switch is in state: " + onOffSwitch.State);
    var pressed = Console.ReadKey(true).KeyChar;
    // Check if user wants to exit
    if (pressed != space) break;

    // Use the Fire method with the trigger as payload to supply the state machine with an event.
    // The state machine will react according to its configuration.

Notes for those new to developing on VS Code:

  • If intellisense doesn't work after 3, or no code-actions are available after 5, you might need to restart omnisharp by: Cmd+Shift+P --> Omnisharp: Restart Omnisharp. See filipw/dotnet-script#424 for more information.
  • While running the application you'll most likely see this error: "Cannot read keys when either application does not have a console or when console input has been redirected." because VS Code uses the Debug Console by default which doesn't support console input. You can change that by configuring it to use the integrated terminal instead, by adding a "console": "integratedTerminal" property to .vscode/launch.json. More information here.

That's it. If you ask me I think it's pretty slick.

What I love about dotnet-script is:

  1. All relevant code, including required nuget packages, can exist in a single file.
  2. Full and reliable intellisense.
  3. Debugger support.
  4. No public static void main or .csproj files.