Use C# as a Scripting language — in VSCode!

ilias shaikh
7 min readJun 28, 2020

--

(Note: To skip to the absolute bare-bones of getting up and running with C# scripting go to the TD;LR section…or stay on — because “the journey is the reward ”.)

Yes, it’s true, your favourite programming language is also a scripting language. Well, C# has had native scripting support, powered by Roslyn, for a while now.

In this article, however, we will not look into the details of the C# scripting language. Instead, we will look at how you can use VSCode as an efficient and productive environment to write C# scripts in.

Before we get into the ‘how’ let's look at the ‘why’.

Let us write the simplest of applications we can in C# — a console application using a dotnet core template.

We need to do the following on the console

PS C:\code\dotnet> dotnet new console -n HelloWorld                                                                     The template "Console Application" was created successfully.Processing post-creation actions...
Running 'dotnet restore' on HelloWorld\HelloWorld.csproj...
Determining projects to restore...
Restored C:\code\dotnet\HelloWorld\HelloWorld.csproj (in 197 ms).
Restore succeeded.PS C:\code\dotnet> cd .\HelloWorld\PS C:\code\dotnet\HelloWorld> dotnet run Hello World!

After running the application the folder structure looks like —

PS C:\code\dotnet\helloworld> tree /F                                                                                   Folder PATH listing for volume Windows
Volume serial number is A872-217C
C:.
│ helloworld.csproj
│ Program.cs

├───bin
│ └───Debug
│ └───netcoreapp3.1
│ helloworld.deps.json
│ helloworld.dll
│ helloworld.exe
│ helloworld.pdb
│ helloworld.runtimeconfig.dev.json
│ helloworld.runtimeconfig.json

└───obj
│ helloworld.csproj.nuget.dgspec.json
│ helloworld.csproj.nuget.g.props
│ helloworld.csproj.nuget.g.targets
│ project.assets.json
│ project.nuget.cache

└───Debug
└───netcoreapp3.1
.NETCoreApp,Version=v3.1.AssemblyAttributes.cs
helloworld.AssemblyInfo.cs
helloworld.AssemblyInfoInputs.cache
helloworld.assets.cache
helloworld.csproj.CoreCompileInputs.cache
helloworld.csproj.FileListAbsolute.txt
helloworld.csprojAssemblyReference.cache
helloworld.dll
helloworld.exe
helloworld.genruntimeconfig.cache
helloworld.pdb

Say I want to change the text “Hello world!” to “Hello universe!”, I need to open the file Program.cs in a text editor and edit the file, then compile and run.

using System;namespace helloworld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello Universe!");
}
}
}

If we want IntelliSense then we need to open the project in Visual Studio and make our changes. Seems a bit like using a sledgehammer to crack a nut, especially if all we want to do is to write a snippet of throwaway code, to say, try out a new language feature.

Ideally, we want to be able to write code snippets with the following features

  1. Quick lightweight editing
  2. Intellisense
  3. No or little boilerplate code
  4. Easy to redistribute
  5. Cross-platform
  6. Free!

…and yes, we can!

The recipe

We can achieve all of the above using —

  1. A scripting engine — dotnet-script
  2. An editor — VS code
  3. Language services — Omnisharp

Scripting engine

C# can be used as a scripting language using the compiler services from Roslyn. This has existed since Visual Studio 2015 Update 1. Canonically C# script files have been written with a .csx extension.

There are actually, at least 3 ways of running scripts in C# —

  1. dotnet-script
  2. script-cs
  3. csi (C# interactive)

In this article, we will use the dotnet-script tool, which I believe offers the best support for our requirements. It’s cross-platform, extremely straightforward to set up and has support for debugging using VSCode.

Installation

You will need .NET Core SDK (at least .NET Core 2.1) on your PC to start with. dotnet-script installs as a global dotnet tool — and support for dotnet global tools was introduced with .NET Core 2.1.

Once you have installed the SDK, install the tool using the command

PS C:\code\dotnet> dotnet tool install -g dotnet-scriptYou can invoke the tool using the following command: dotnet-script
Tool 'dotnet-script' (version '0.53.0') was successfully installed.

Because this is a .NET Core tool, the process is the same across all platforms — Windows, Linux, macOS

That pretty much is all you need.

Running scripts

Once we have dotnet-installed there is very little ceremony to get to our Hello world!

Open an editor and create a new file called ‘helloworld.csx’ and type in

Console.WriteLine("Hello world!");

….and that’s all the editing that is needed.

We run the script using the dotnet-script tool

C:\code\dotnet>dotnet script helloworld.csx
Hello World!

Editor & Language services

Although we can use any text editor, using VSCode can supercharge our editing experience.

In the unlikely event of you not having VSCode, download and install it from https://code.visualstudio.com/

Using the extension ‘C# for VSCode’ (Omnisharp) we get IntelliSense, and we also get integrated debugging.

Installing Omnisharp/ C# Extension

You can get C# for Visual Studio Code in a couple of different ways.

  • When you open any CSharp file — with a .cs extension, VSCode will prompt you automatically on the bottom right corner of the status bar.

Click on ‘Install’ and you will have what we need.

The other option is to directly add the extensions using the Extensions view by clicking on the Extensions icon in the Activity Bar on the side of VS Code or the View: Extensions command (Ctrl+Shift+X) and searching for C#

Remember to Restart VSCode!

That’s really it, now we have Intellisense.

Running the code

To run the code we can of course just open the VS Code terminal window and run the file using dotnet-script, as we had done before.

C:\code\dotnet\csx>dotnet script helloworld.csx
hello world
1
2
3
4
5
6
7
8
9
10

Debugging

With VSCode we have a lightweight debugger for our scripts. This requires us to have an appropriatelaunch.json file, but we can letdotnet script set up the scaffolding for us.

Create a folder on your system and issue a command

C:\code\dotnet\helloworld>dotnet script init

This creates the following folder structure for me with the right setup for debugging and any custom OmniSharp settings.

C:\code\dotnet\helloworld>tree /f
Folder PATH listing for volume Windows
Volume serial number is A872-217C
C:.
│ main.csx
│ omnisharp.json

└───.vscode
launch.json

Note that the default name for the script file is main.csx You can pass in the argument for a filename to the init command, to say create ahelloworld.csx file instead.

C:\code\dotnet\helloworld>dotnet script init helloworld

Set a breakpoint anywhere — by using F9 or clicking on the editor margin— and run the debugger by either running ‘Debug: Start Debugging’ from the Command Palette or using the F5 key.

You can inspect variables and set breakpoints, add watches, view the call stack etc. Console output can be seen on the DEBUG CONSOLE window.

Tasks

We have looked at how to run a script using the debugger, but what if we want to run the script from VSCode — same as ‘Start Without Debugging(Ctrl+F5)’ in Visual Studio. We can accomplish this using Tasks.

VSCode Tasks allow us to run external programs. VSCode auto-detect tasks for some systems but we will create a custom task to run our script.

(We won’t go into the details VSCode Tasks, you can look it up at https://code.visualstudio.com/docs/editor/tasks)

Create a file called tasks.json in the .vscodefolder that the dotnet script init command created and copy the following text into it.

{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Run csx",
"type": "shell",
"command": "dotnet script",
"args": [
"${file}"
],
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "dedicated",
"showReuseMessage": true,
"clear": false
},
"windows": {
"options": {
"shell": {
"executable": "cmd.exe",
"args": [
"/d", "/c"
]
}
}
}
}
]
}

Your folder now looks like this

C:\code\dotnet\helloworld>tree /f
Folder PATH listing for volume Windows
Volume serial number is A872-217C
C:.
│ helloworld.csx
│ omnisharp.json

└───.vscode
launch.json
tasks.json

This tasks file sets us up so that the default build tool runs the code in a terminal window. Run the code by using Ctrl+Shift+B or “Tasks: Run Build Task” from the Command Palette (Ctrl+Shift+P)

Note: We are essentially hijacking the default Build Task, but then since there is no ‘build’ involved and the keybinding is already set, this doesn't seem like a bad solution.

TL;DR

That is essentially how you can use Visual Studio Code (VSCode) for scripting and quick testing/ prototyping of C# code snippets. To recap, this is, in a nutshell, is what we need to do —

  • Install .NET Core (upwards from 2.1)
  • Run dotnet tool install -g dotnet-script
  • Install VSCode
  • Install the C# extension
  • Create a script file by running dotnet script init myscriptto create a file called myscript.csx
  • Add the file .vscode\tasks.json from the previous section.
  • Run the script from VSCode using Ctrl + Shift + B

….Happy scripting!

--

--