Scripting in C # or dynamic execution at runtime

Hello, Habr!





I think few people know that C # has something like eval from other languages. Thanks to the Roslyn API, you can compile and execute your C # code at runtime. You can see an example of usage in my REPL implementation for C #.





I first met such a thing as REPL when I touched python. There is a similar thing in the .NET world called C # Interactive (CSI). Quite a handy thing, but it has one big drawback - it is included in the Visual Studio tools, so without installing VS, you will not be able to use it, and in order to run it without starting VS, you have to climb into its depths (or rather, run through the console C: \ Program Files (x86) \ Microsoft Visual Studio \ 2019 \ Community \ Common7 \ Tools \ VsDevCmd.bat), which may not be a convenient enough solution.





There are also projects like dotnet-script and cs-script (they work through Microsoft.CodeAnalysis.CSharp.Scripting ), but they have a fatal flaw - they were not written by me. So the idea came to write your own clumsy bike, but with its own features (which also work clumsily)!. After a short search, my eyes fell on this miracle: Microsoft.CodeAnalysis.CSharp.Scripting . One of the advantages is a convenient API, the ability to execute code without classes and namespaces.





First, you need to install the Microsoft.CodeAnalysis.CSharp.Scripting package and do using





using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
      
      



CSharpScript is a static class that will help us create a script, includes 3 methods:





  • Create - Script ,





  • RunAsync - , ScriptState





  • EvaluateAsync -





CSharpScript.Create , .





var script = CSharpScript.Create("System.Console.WriteLine(\"Hello from script\")");
script.Compile();
await script.RunAsync();
      
      



E Compile(), .





ScriptOptions, namespace- reference- ( , using static).





  var options = ScriptOptions.Default
            .AddImports("System", "System.IO", "System.Collections.Generic",
                "System.Console", "System.Diagnostics", "System.Dynamic",
                "System.Linq", "System.Text",
                "System.Threading.Tasks")
            .AddReferences("System", "System.Core", "Microsoft.CSharp");

 CSharpScript.Create("Console.WriteLine(\"Hello from script\")", options);
      
      



— ScriptOptions - namespace-. whitelist, , , .





CSharpScript.RunAsync ScriptState, ContinueWithAsync, , ScriptState. , Script. , ReturnValue.





ScriptState state = await CSharpScript.RunAsync("int x = 5;");
state = await state.ContinueWithAsync<int>("x + 1");
Console.WriteLine(state.ReturnValue); // 6
      
      



state , exception





foreach(var variable in state.Variables)
{
	Console.WriteLine($"{variable.Name} - {variable.Value}");
}
      
      



CSharpScript.Create ,





var script = CSharpScript.Create<Func<int,int>>("x => x+1");
Console.WriteLine(await script.CreateDelegate().Invoke(1)); // 2
      
      



, - , CSharpScript.EvaluateAsync ( , )





var deleg = await CSharpScript.EvaluateAsync<Func<int, int>>("x => x * 2");
Console.WriteLine(deleg(5)); // 10
      
      



- ( , , ).





CSharpScript globals globalsType (globalsType , globals), , ( CSharpScript.Create globalsType, script.RunAsync() globals).





var res = await CSharpScript.EvaluateAsync<int>("X+Y", globals: new GlobalValues());
Console.WriteLine(res); // 100

public class GlobalValues 
{
	public int X = 25;
	public int Y = 75;
}
      
      



:





, , . .





Microsoft.CodeAnalysis.CSharp.Scripting, , runtime C# . , , .net .





5 github, :





:





https://github.com/dotnet/roslyn/blob/main/docs/wiki/Scripting-API-Samples.md





https://github.com/dotnet/roslyn/tree/a7319e2bc8cac34c34527031e6204d383d29d4ab/src/Scripting





I hope my first article did not seem too boring, and I was able to help you somehow.





Have a nice day!








All Articles