THIS DOCUMENT IS UNDER DEVELOPMENT
Because of the large scale of the CS-Script online documentation it can be challenging to find a specific information within it. Thus the all major details about hosting the script engine in applications are described on the single page: Script Hosting Guideline.
Hosted script execution
CS-Script can be hosted by any CLR application. The best way to bring scripting in your application is to add the corresponding NuGet package to your Visual Studio project :Install-Package CS-Script.bin or Install-Package CS-Script
'CS-Script.bin' package contains only binaries (e.g. CSScriptLibrary.dll) and 'CS-Script' contains binaries and sample files. The code samples in this article are consistent with the samples distributed with the NuGet package.
CSScriptLibrary.dll is the actual assembly implementing CS-Script as a class library. It is targeting .NET v4.0/4.5. However the package from the CodePlex Releases page always contains CSScriptLibrary.dll builds for earlier versions of .NET (<cs-script>\lib\Bin).
Setting up Evaluator
Any application hosting CS-Script engine can execute C# code containing either fully defined types or code fragments (e.g. methods). It is important to note that CS-Script is neither a compiler nor evaluator. It is rather a runtime environment that relies for the code compilation by the standard compiling toolset available with any .NET or Mono installation. The first compiling services CS-Script integrated was CodeDom. It is the very original compiler-as-service that was available starting from the first release of .NET.
While many may not consider CodeDom as 'true compiler-as-service', in reality, it actually is. The problem with CodeDom API is that it's inconvenient in require a great deal of manual runtime configuration. And it is exactly what CS-Script is addressing. Later on some alternative proprietary compiler-as-service solutions became available. First Mono Evaluator and later Roslyn (currently still Beta). Both demonstrated a completely different approach towards what good scripting API should look like. Thus all three compiling platforms are mutually inconsistent and only partially overlapping in with respect to functionality.
And this is where CS-Script comes to the rescue. It provides one unified generic interface transparent to the underlying compiler technology. You can have your script code executed by ether of three supported compilers without affecting your hosting code.
The code below demonstrates creating (compiling) a delegate.
var sqr = CSScript.Evaluator .CreateDelegate(@"int Sqr(int a) { return a * a; }"); var r = sqr(3);
By default CSScript.Evaluator references Mono compiler. However you can change this behavior globally by re-configuring the default compiler:
CSScript.EvaluatorConfig.Engine = EvaluatorEngine.Mono; CSScript.EvaluatorConfig.Engine = EvaluatorEngine.Roslyn; CSScript.EvaluatorConfig.Engine = EvaluatorEngine.CodeDom;
Alternatively you can access the corresponding compiler via a dedicated member property:
CSScript.MonoEvaluator.CreateDelegate(... CSScript.RoslynEvaluator.CreateDelegate(... CSScript.CodeDomEvaluator.CreateDelegate(...
Every time a CSScript.*Evaluator property is accessed a new instance of the corresponding evaluator is created and returned to the caller. Though this can be changed by re-configuring the evaluator access type to return the reference to the global evaluator object:
CSScript.EvaluatorConfig.Access = EvaluatorAccess.Singleton;
Executing the scripts
The evaluator allows executing code containing definition a type (or multiple types):
Assembly script = CSScript.Evaluator
.CompileCode(@"using System;
public class Script
{
public int Sum(int a, int b)
{
return a+b;
}
}");
Alternativelly you can compile code containing only method(s). In this case Evaluator will wrap the method code into a class definition with name DynamicClass:
Assembly script = CSScript.Evaluator
.CompileMethod(@"int Sum(int a, int b)
{
return a+b;
}");
Further access to the script members can be simplified by using Evaluator.Load*, which compiles the code and returns the instance of the first class in compiled assembly:
dynamic script = CSScript.MonoEvaluator .LoadMethod(@"int Product(int a, int b) { return a * b; }"); int result = script.Product(3, 2);
Note that in the code below the method Product is invoked with 'dynamic' keyword, meaning that the host application cannot do any compile-time checking in the host code. In many cases it is oOK, though sometimes it is desirable that the script object was strongly typed. The easiest way to achieve this is to use interfaces:
publicinterface ICalc { int Sum(int a, int b); } ... ICalc script = (ICalc)CSScript.Evaluator .LoadCode(@"using System; public class Script : ICalc { public int Sum(int a, int b) { return a+b; } }"); int result = script.Sum(1, 2);
Not that you can also use an Interface alignment (duck-typing) technique, which allows 'aligning' the script object to the interface even if it is not implementing this interface. It is achieved by evaluator wrapping the script object into dynamically generated proxy of the interface (e.g. ICals) type:
ICalc script = CSScript.Evaluator
.LoadCode<ICalc>(@"using System;
public class Script
{
public int Sum(int a, int b)
{
return a+b;
}
}");
Evaluator also allows compiling method scripts into class-less delegates:
var sqr = CSScript.Evaluator .CreateDelegate<int>(@"int Sqr(int a) { return a * a; }"); int result = sqr(3);
In the code above CreateDelegate returns MethodDelegate<T>, which is semi-dynamic by nature. It is strongly typed by return type and dynamically typed (thanks to 'params') by method parameters:
publicdelegate T MethodDelegate<T>(paramsobject[] paramters);
Though if strongly typed delegate is preferred then you can use LoadDelegate instead:
Func<int, int, int> product = CSScript.Evaluator .LoadDelegate<Func<int, int, int>>( @"int Product(int a, int b) { return a * b; }"); int result = product(3, 2);