C# 脚本引擎CS-Script的使用
最近想要在程序中嵌入一个C#脚本引擎,在.NETFramework时代用过一个叫做CS-Script的东西,感觉还是不错,发现现在也支持.NETCore了,试着嵌入一下。
比较
要说能够运行C#脚本的解决方案,有Roslyn和Mono,与他们相比,CS-Script能够提供的封装更为高级,它底层是通过Roslyn之类的引擎运行的,在此基础上,提供了一些额外功能:
- 执行完整的C#文件
- 通过外部进程执行C#文件
- 在运行过程中链接多个c#文件,并集成运行
- 提供简便的方法进行链接
- 脚本调试功能
注:由于技术发展,很多功能可能已经被Roslyn支持了。同时基于web有Try.NET和SharpLab等优秀方案。
当然也可以自己基于Roslyn去实现这些功能,不过CS-Script提供了更加简单的封装,适用于懒人。
使用
程序基于.NET5的开发,尝试引用CS-Script包,发现不太好用,一直提示System.Reflection.TargetInvocationException:“Exceptionhasbeenthrownbythetargetofaninvocation.”。支持.NETCore的实际上是CS-Script.Core这个包,安装即可。
Install-PackageCS-Script.Core
CS-Script实际上底层支持Mono/Roslyn/CodeDom三种脚本引擎,由于.NETCORE的特殊性,CS-Script.Core做了删减,只能支持Roslyn一种引擎了,支持的C#语言版本由Roslyn版本决定。
旁的不说,直接上代码:
usingCSScriptLib;
usingSystem;
usingSystem.Reflection;
namespaceConsoleApp3
{
publicclassProgram
{
staticvoidMain(string[]args)
{
//vareval=CSScript.Evaluator.ReferenceAssemblyByNamespace("System.Text");
//varp=eval.ReferenceAssemblyByNamespace("ConsoleApp3");
Assemblycompilemethod=CSScript.RoslynEvaluator.CompileMethod(
@"usingSystem;
publicstaticvoidCompileMethod(stringgreeting)
{
Console.WriteLine(""CompileMethod:""+greeting);
}");
varp=compilemethod.GetType("css_root+DynamicClass");
varme=p.GetMethod("CompileMethod");
me.Invoke(null,newobject[]{"1"});
//eval=CSScript.Evaluator.ReferenceAssembly(sqr);
dynamicloadmethod=CSScript.Evaluator.LoadMethod(@"usingSystem;
publicvoidLoadMethod(stringgreeting)
{
Console.WriteLine(""LoadMethod:""+greeting);
}");
loadmethod.LoadMethod("HelloWorld!");
dynamicloadcode=CSScript.Evaluator
.LoadCode(@"usingSystem;
usingConsoleApp31;
usingSystem.Text;
publicclassScriptCC
{
publicvoidLoadCode(stringgreeting)
{
Console.WriteLine(""LoadCode:""+greeting);
}
}");
loadcode.LoadCode("111");
vareval=CSScript.Evaluator.ReferenceDomainAssemblies(DomainAssemblies.AllStaticNonGAC);
varass=eval
.CompileCode(@"usingSystem;
publicstaticclassScriptCCStatic
{
publicstaticvoidLoadCodeStatic(stringgreeting)
{
Console.WriteLine(""LoadCodeStatic:""+greeting);
}
}");
vartp=eval.CreateDelegate(@"intSqr(inta)
{
returna*a;
}");
Console.WriteLine(tp(3));
eval=eval.ReferenceDomainAssemblies(DomainAssemblies.AllStaticNonGAC);
Assemblycompilecode=eval
.CompileCode(@"usingSystem;
usingConsoleApp31;//含有这个namespace的文件包含在本项目中。
usingSystem.Text;
usingConsoleApp3;
publicclassScriptLC
{
publicvoidCompileCode(stringgreeting)
{
Console.WriteLine(""CompileCode:""+greeting+Encoding.ASCII.IsBrowserDisplay);
Program.Write();
Test.Send();
}
}");
varps=compilecode.GetType("css_root+ScriptLC");
varobj=compilecode.CreateInstance("css_root+ScriptLC");
varmes=ps.GetMethod("CompileCode");
mes.Invoke(obj,newobject[]{"1"});
Console.WriteLine();
//查看evaluator的引用程序集
varww=eval.GetReferencedAssemblies();
foreach(varninww)
{
if(n.GetName().Name.Contains("System"))continue;
if(n.GetName().Name.Contains("Microsoft"))continue;
if(n.GetName().Name.Contains("CS"))continue;
Console.WriteLine("AseemblyName:"+n.GetName());
foreach(varwninn.GetTypes())
{
Console.WriteLine("Types:"+wn.Name);
}
}
Console.WriteLine();
//查看当前AppDomain加载的程序集
foreach(varninAppDomain.CurrentDomain.GetAssemblies())
{
if(n.GetName().Name.Contains("System"))continue;
if(n.GetName().Name.Contains("Microsoft"))continue;
if(n.GetName().Name.Contains("CS"))continue;
Console.WriteLine("AseemblyName:"+n.GetName());
foreach(varwninn.GetTypes())
{
Console.WriteLine("Types:"+wn.Name);
}
}
Console.ReadKey();
}
publicstaticvoidWrite()
{
Console.WriteLine("REFERENCEOK");
}
}
}
总结
使用CS-Script.Core的时候,所有加载/编译的方法与类型都动态加入了CurrentAppDomain,可以在主程序中进行调用(注意using和usingstatic)。通过Evaluator.ReferenceAssembly等函数添加引用,不支持引用其他动态编译的代码段。
可以一次性将当前AppDomain的程序集引用加入Evaluator,但是一样,只能调用在文件中定义的程序集,无法加载其他动态程序集,调用Evaluator.ReferenceDomainAssemblies(DomainAssemblies.All)将提示错误。
这个限制是Roslyn导致的,暂时无法解决。如果需要实现多个代码段的互相调用,可以直接将代码段进行拼接,或者将公用的代码段存成文件,从文件中进行调用。
CompileMethod
编译方法,并返回动态生成的程序集,方法被默认加载到DynamicClass类中,该Type完全限定名称为css_root+DynamicClass,定义的静态方法需要使用以下方式调用。
varp=compilemethod.GetType("css_root+DynamicClass");
varme=p.GetMethod("CompileMethod");
me.Invoke(null,newobject[]{"1"});
LoadMethod
加载方法,并返回默认类(DynamicClass)的一个对象,通过定义返回对象为dynamic类型,可以直接调用实例方法。
loadmethod.LoadMethod("HelloWorld!");
LoadCode
加载类,并返回代码段中的第一个类的实例,通过定义返回对象为dynamic类型,可以直接调用实例方法。
loadcode.LoadCode("111");
CompileCode
编译类,并返回动态生成的程序集,定义的实例方法可以使用以下方式调用。
varps=compilecode.GetType("css_root+ScriptLC");
varobj=compilecode.CreateInstance("css_root+ScriptLC");
varmes=ps.GetMethod("CompileCode");
mes.Invoke(obj,newobject[]{"1"});
Console.WriteLine();
CreateDelegate
生成一个委托,同样定义在DynamicClass中,可以直接调用。
vartp=eval.CreateDelegate(@"intSqr(inta)
{
returna*a;
}");
Console.WriteLine(tp(3));
参考资料
附上直接通过Roslyn使用脚本的方法:RoslynScripting-API-Samples.md
以上就是C#脚本引擎CS-Script的使用的详细内容,更多关于C#脚本引擎CS-Script的资料请关注毛票票其它相关文章!