Interactive C# Development

Filip W

not PM. not architect. not developer advocate.

dotnet watch

dotnet watch is a dotnet CLI tool that runs a specific dotnet CLI command, with a specified set of parameters, as soon as any source file changes in the project.

Adding dotnet watcher

    <DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" 
        Version="2.0.0" />

Using dotnet watcher

C:\app>dotnet watch run

C:\app>dotnet watch run -f net462

C:\app>dotnet watch test

C:\app>dotnet watch script foo.csx

Running dotnet watcher

C:\code\IdentityWebApp>dotnet watch run
watch : Started
Hosting environment: Production
Content root path: C:\code\IdentityWebApp
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.
watch : Exited with error code 1
watch : File changed: C:\code\IdentityWebApp\Translation\JwtTranslationGrant.cs
watch : Started
Hosting environment: Production
Content root path: C:\code\IdentityWebApp
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.

Excluding items from watch

  <ProjectReference Include="..\DataAccessLibrary\DataAccessLibrary.csproj" Watch="false" />

  <Watch Include="**\*.csx" Exclude="Codegen\**\*.cs;$(DefaultExcludes)" />

  <EmbeddedResource Update="Translations.resx" Watch="false" />

Edit and Continue

Edit-and-Continue was already present in "Visual Studio .NET 2003", but only for C/C++ code. Since Visual Studio 2005 you can make changes to code in break mode while debugging.

They are applied to the CLR directly without a need to restart the debugging session.

Roslyn introduced a ton of improvements to Edit-and-Continue, as the entire compiler part of that functionality was rewritten.

🦄 Allowed changes

  • Within Method/Property Bodies
    most changes
  • Within Types
    add methods, fields, constructors, nested types
  • Lambda expressions
    add/edit delegates, LINQ expressions (with limitations)
  • Async/await expressions & Iterators
    add/edit (with limitations)

💩 Prohibited changes

  • Add
    abstract/virtual/override member or type, using statements, imports, generics
  • Edit
    method signatures, interfaces, generics, enums
  • Remove
    members, types, namespaces, bodies
  • Rename

Visual Studio 2017 Version 15.3 introduces Edit-and-Continue for C# 7 features.

Edit And Continue with Roslyn (before)

class Program
    public static void Main() 

Edit And Continue with Roslyn (after)

class Program
    public static void Main() 

// read metadata from the original compilation or using metadata reader
var originalMetadata = AssemblyMetadata.CreateFromStream(originalCompilationStream);
var baseline = EmitBaseline.CreateInitialBaseline(originalMetadata.GetModules()[0], 
    handle => default(EditAndContinueMethodDebugInformation));

MemoryStream mdStream = new MemoryStream(), ilStream = new MemoryStream(), pdbStream = new MemoryStream();
var updatedMethods = new List<MethodDefinitionHandle>();

var edits = new[] {
    new SemanticEdit(SemanticEditKind.Update,
var emitDifferenceResult = changedCompilation.EmitDifference(baseline, edits, mdStream, ilStream, pdbStream, updatedMethods);
// ilStream and mdStream can be now applied to the CLR

IL produced by EmitDifference

  // Code size        7 (0x7)
  .maxstack  8
  IL_0000:  ldc.i4.2
  IL_0001:  call       0x0A000006
  IL_0006:  ret


A Read–Eval–Print Loop (REPL), also known as an interactive toplevel or language shell, is a simple, interactive computer programming environment that takes single user inputs (i.e. single expressions), evaluates them, and returns the result to the user; a program written in a REPL environment is executed piecewise.


  • CSI (C# Interactive)
    Official Roslyn script runner and REPL
  • scriptcs
    Rich and extensible command infrastructure
  • dotnet-script
    C# REPL for .NET Core
  • Online C# REPL

CSI is bundled with MSBuild Tools. It supports Desktop .NET only, but works cross platform via Mono.

~/Documents$ csi
Microsoft (R) Visual C# Interactive Compiler version
Copyright (C) Microsoft Corporation. All rights reserved.

Type "#help" for more information.
> #r "System.Net.Http"
> using System.Net.Http;
> var client = new HttpClient();
> await client.GetStringAsync("");
"Hello from a gist"

Source code kinds in Roslyn

public enum SourceCodeKind 

Creating a REPL compilation

public static CSharpCompilation Create(string assemblyName, 
        IEnumerable<SyntaxTree> syntaxTrees = null, 
        IEnumerable<MetadataReference> references = null, 
        CSharpCompilationOptions options = null);

public static CSharpCompilation CreateScriptCompilation(string assemblyName,
        SyntaxTree syntaxTree = null, 
        IEnumerable<MetadataReference> references = null, 
        CSharpCompilationOptions options = null, 
        CSharpCompilation previousScriptCompilation = null, 
        Type returnType = null, 
        Type globalsType = null);

C# Interactive Window

C# Interactive Window shares most of the code with CSI (the command line REPL).

C# Interactive Window is built into Visual Studio. It can be found under View > Other Windows menu.

Main C# Interactive Window Features

  • 📝 Intellisense & Colorization
    Completion services - same as those powering Visual Studio - are available
  • 📜 History navigation
    Cycle through submissions
  • 🏓 Integration With Open Solution
    Send references and code into C# Interactive Window
  • 🌽 Seeding
    C# Interactive Window can be seeded with references/using statements from a file

At the moment C# Interactive Window does not support .NET Core.


// REPL global variables are compiled into fields
var numbers = new[] { 1, 2, 3, 4 };

// this is legal in a C# REPL
// in "regular" C# such shadowing produces CS0136
var x = numbers.Where(x => x % 2 == 0);

Xamarin Workbooks

Xamarin Workbooks provide a fantastic mix of a C# REPL and documentation. Workbook files are stored as Markdown files.

title: "UrhoSharp: Planet Earth in C#"
- Console
- id: UrhoSharp
  version: 1.5.22

# UrhoSharp: Planet Earth in C#

[UrhoSharp]( is a powerful 3D game engine for Xamarin and .NET developers. Start by loading URho into the workbook and import some namespaces that we’ll want to consume.

#r "Urho"

using Urho;
using Urho.Actions;
using Urho.Shapes;

var app = SimpleApplication.Show(new ApplicationOptions("Data") { Width = 600, Height = 600, TouchEmulation = true });

We have created a window containing a `ApplicationOptions`. Let’s add a new `Node` to the scene, representing the Earth.

var earthNode = app.RootNode.CreateChild(name: "Earth");
var earth = earthNode.CreateComponent<Sphere>();

Main Xamarin Workbooks Features

  • 📝 Intellisense & Colorization
    Completion services powered by Roslyn
  • 🎁 Nuget package integration
    Reference Nuget packages from within the workbook
  • 🌈 Multi-platform and GUI support
    Console, Android, iOS, Mac, WPF
  • 📇 Rich Text Editor for Markdown
    Using the CommonMark spec

Internally, Xamarin Workbooks uses Roslyn scripting to provide its REPL-like features.

Live Unit Testing

Live Unit Testing was introduced in Visual Studio 2017. At the moment it is available only for Visual Studio Enterprise.

Starting Live Unit Testing globally

Starting Live Unit Testing per project

Everything OK

As soon as Filip addded some logic

Visual Studio 2017 Version 15.3 introduces Live Unit Testing for .NET Core projects.

Live Code Analyzers

Roslyn Features API

Find issue Change code Diagnostic Distribution
Analyzer 🕵️ 🦄
Code fix 🔧 🦄
Refactoring 🔧

public class RemoveRegionAnalyzer : DiagnosticAnalyzer
    public const string DiagnosticId = "RemoveRegionAnalyzer";
    private const string Category = "Naming";
    private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, "Regions are banned", 
        "Region '{0}' can't be used. In fact no regions can.", "Sanity", DiagnosticSeverity.Error, 
        isEnabledByDefault: true);
    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
    public override void Initialize(AnalysisContext context)
        context.RegisterSyntaxNodeAction(ctx => {
            var diagnostic = Diagnostic.Create(Rule, context.Node.GetLocation());
        }, ImmutableArray.Create(SyntaxKind.RegionDirectiveTrivia, SyntaxKind.EndRegionDirectiveTrivia));