In this recipe, we wrote a simple set of C# interactive script commands to perform a bunch of operations that are common in regular C# code, but without having to declare a stub type/main method or having to create a source file/project. The operations performed during the interactive session were:
- Evaluating an expression that outputs a string to the console (Console.WriteLine(...)).
- Declaring a local variable for the lifetime of the interactive session and initialize it with a collection initializer (myList).
- Accessing the preceding declared variable in a subsequent linq statement and evaluating the resultant value (myList.Where(...)).
- Accessing environment variables in C# expression evaluations (Environment.CurrentDirectory).
- Importing namespace in the session through a using declaration (using System.Threading.Tasks;).
- Awaiting an async expression (await Task.Delay(10000)).
- Declaring a C# class with a method (class C and method M) for the lifetime of the interactive session.
- Instantiating the preceding declared class and invoking the method in a subsequent statement (new C().M()).
Let's briefly walk through the implementation of the C# interactive compiler that enables all the preceding regular C# operations in the interactive mode.
Csi.main (http://source.roslyn.io/#csi/Csi.cs,14) is the primary entry point into the C# interactive compiler. After initialization of the compiler, the control eventually reaches CommandLineRunner.RunInteractiveLoop (http://source.roslyn.io/#Microsoft.CodeAnalysis.Scripting/Hosting/CommandLine/CommandLineRunner.cs,7c8c5cedadd34d79), which is, the REPL, or read-evaluate-print-loop, that reads interactive commands and evaluates them in a loop until the user exits by pressing Ctrl + C.
For each entered line, the REPL loop executes ScriptCompiler.ParseSubmission (http://source.roslyn.io/#Microsoft.CodeAnalysis.Scripting/ScriptCompiler.cs,54b12302e519f660) to parse the given source text into a syntax tree. If the submission is incomplete (for example, if the first line of a class declaration has been entered), then it outputs . and continues waiting for more text for the submission. Otherwise, it creates a script using the current submission text chained to the end of the prior submissions and runs the new submission by invoking into the core C# compiler APIs. The result of the submission is output to the interactive window.