Practical .NET

Making Your Life Easier with C# Scripting

Test projects give you a way to exercise your code to see if it works but they're also part of your project's "permanent record." If all you want to do is try something out with some throwaway code, C# Scripting in Visual Studio might be a better choice.

C# Scripting (hereafter, CS-Script) lets you write and execute individual lines of C# code without having to define classes or namespaces. This statement, all by itself, is a perfectly good CS-Script script (and, in the right environment, will even display "Hello, World"):

Console.WriteLine("Hello, World");

And, if you've installed Visual Studio, then you already have a perfectly good CS-Script environment: CSI.EXE (I found my copy in C:\Users\<user name>\Source\ExchangeControl.WebService\bin\roslyn). You can create a CS-Script command environment by just opening the Developer Command Prompt for Visual Studio and typing CSI. Once the window has re-displayed the command prompt, you can start entering and executing CS-Script.

You're not limited to single C# statements with the CSI prompt: Statements that you enter in the CSI environment build on previous statements you've entered. The following statements, entered one right after another, will work together to build a Customer class, for example (the CSI environment detects when you complete a line and keeps extending the Customer class I begin with in this example until I get to the final closing curly brace):

> class Customer {
. public string firstName {get; set;}
. public string lastName {get; set; }
. public string fullName() {
. return firstName + " " + lastName;
. }
. }

Now I can take advantage of that Customer object with some more CS-Script statements:

> Customer cust = new Customer();
> cust.firstName = "Peter";
> cust.lastName = "Vogel";
> string fname = cust.fullName();

If enter just an expression (some combination of operators, members, variables and values that reduce to a single value), the environment echoes that value back to me:

> fname
"Peter Vogel"

Of course, the chances of you entering all that code without an error is vanishingly small (though you can use the arrow keys to return to earlier standalone lines and fix them up). The smart thing, then, is to put all your code into a file with the .csx extension and execute that file. You can use the csi command to run that script file also, like this:

csi <name of your script file>.csx

Sadly, the CSI command prompt doesn't support any folder navigation and management commands (e.g. cd, dir, etc.) so there's no built-in way to navigate to the folder holding your.csx file. Mark Michealis has provided a set of extensions for the CSI environment that will let you get some of that functionality back.

Here's another option: Associate the .csx file extension with CSI.EXE. Now you can execute a .csx file just by double clicking on it in File Explorer.

C# Scripting and Visual Studio
And that's all very interesting, but it's the integration with Visual Studio that could simplify some parts of your code development process. The most obvious example of this integration appears when you right-click on C# statement in your code and select Execute in interactive from the popup menu: Visual Studio treats the statement as if it were CS-Script and executes it.

Of course, that probably won't work because the line of code you clicked on almost certainly depends on work done by previous lines of code. Typically, then, you'll select multiple lines of code and right-click on the whole block before picking Execute in interactive. A block of code like this will execute just fine, for example:

CustomerRepo cr = new CustomerRepo();
Customer cust = cr.GetCustomer("A123");
string fName = cust.GetFirstName();

Even more useful is the CS-Script REPL window (Read-Evaluate-Print-Loop) that you can open by going to Visual Studio's View | Other Windows menu choice and selecting C# Interactive. In that window you can now enter CS-Script code and just hit the <Enter> key to have it execute.

There are a couple of disappointments here, though. It might make sense to try out classes and their members from the interactive window. However, just because the window has opened in Visual Studio while your solution is open, it doesn't mean the window knows anything about the classes defined in the current solution. In fact, the window's default folder isn't even your current solution's folder (at least in Visual Studio 2019).

You're not completely without resources: There are some basic .NET DLLs loaded for you and some system directives (using statements) executed for you when the window opens. This means that Console.WriteLine works right out of the box, for example. But for anything beyond some basic class libraries, you'll need to add references to those libraries yourself.

Building Test Scripts
That means, if you want to try out the objects in your solution, you'll need to have (a) compiled your code at least once, and (b) have added a reference one or more of your projects' DDLs. The CS-Script #r directive will let you reference a DLL, provided you pass the #r directive the full path to the DLL you want or are referencing your bin/debug folder through the ReferencePaths collection.

I find adding the DLL's file path to my projects' bin/debug folders to ReferencePaths is the easiest solution. That may not be obvious because the code to do that looks something like this for a typical project:

ReferencePaths.Add(@"C:\Users\<user name>\Source\Repos\<solution name>\<project name>\bin\debug\");

I agree: That's not something you want to type in more than once (or at all, for that matter). To facilitate exercising the objects in my project from the C# Interactive window, I first add a file called utilities.csx to my project and then add that ReferencePaths statement to the file (I can get the path name for my Debug folder by right-clicking on the folder in Solution Explorer and selecting Copy Full Path). When I need to add my reference path, I just open my utilities.csx file, right-click on that ReferencePaths line, and select Execute in interactive.

With that done, I can now load the DLLs in my Debug folder by name (and set their namespaces to save some typing) with code like this in the interactive window:

> #r "<project name>.dll"
> using <project namespace>;

However, you'll probably find yourself running the same sets of code over and over again as you get your code working. Once you've got some script code to work in the C# interactive window, the smart thing to do is copy that code into another .csx file in your project and run that file to exercise your classes.

To run that .csx file, you use CS-Script's #load directive. Unfortunately, the #load directive also requires a full path, this time to the .csx file. Again, I can get that full filename by right-clicking on my .csx file and picking Copy Full Path. I then add a #load statement with that file path to my utilities.csx file and use Execute in interactive to run it. A typical #load command in my utilities.csx file to run a script looks like this:

#load "C:\Users\peter\Source\Repos\SampleClassLibrary\SampleClassLibrary\ExerciseAddCustomer.csx"

Keeping this script around in .csx files saves me a lot of typing when I return to this project and want to use that code again.

Gotchas and Watchouts
The C# Interactive window, like any good REPL window, lets you scroll back through previous commands, modify them and re-execute them. Which brings me to this: There are a couple of places where your finger's habits from other REPL windows can mislead you.

In the C# Interactive REPL window, using the <Up Arrow> won't let you scroll back through previous commands: You'll need to use <Alt>+<Up Arrow>. Hitting the <Enter> key in the middle of a statement won't execute the statement but will, instead, add a line break in the statement: <Ctrl>+<Enter> always executes the current statement.

You can clear the interactive window with the clear window icon in the C# Interactive window's menu bar or using the #cls or #clear directives in the window itself. You can start a new session either by clicking on the reset icon in the window's menu or by using the #reset directive inside the window. If you're working with .NET Core, you'll want to use the #reset directive with the core parameter so that CS-Script will understand the format of your DLLs, like this:

#reset core

So, with a little bit of setup (and a utilities.csx file of useful statements), CS-Script can give you an easy way to try out new code and exercise existing code without having to pollute your test classes with temporary trials. And we're all opposed to polluting things.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

  • Simulated Annealing Optimization Using C# or Python

    Dr. James McCaffrey of Microsoft Research shows how to implement simulated annealing for the Traveling Salesman Problem (find the best ordering of a set of discrete items).

  • Microsoft Offers Windows ML Samples Gallery

    Microsoft has created a Windows ML Samples Gallery of interactive projects to showcase Windows machine learning functionality in both managed and native scenarios.

  • New Lightweight JetBrains Editor Draws VS Code Comparisons

    Software development tooling vendor JetBrains debuted a new lightweight code editor that can act like a full-fledged IDE, immediately drawing comparisons to Microsoft's Visual Studio Code.

  • WinUI 3 Team Asked About Delayed Open Source Plans: 'What Happened?'

    A developer asked Microsoft's WinUI 3 dev team about delayed open source plans for the nearly two-year-old project that serves as the company's next-gen UI library for creating Windows apps.

Upcoming Events