11

C#脚本引擎CS-Script

 3 years ago
source link: https://www.cnblogs.com/podolski/p/14192599.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

最近想要在程序中嵌入一个C#脚本引擎,在.NET Framework时代用过一个叫做CS-Script的东西,感觉还是不错,发现现在也支持.NET Core了,试着嵌入一下。

比较#

要说能够运行C#脚本的解决方案,有Roslyn和Mono,与他们相比,CS-Script能够提供的封装更为高级,它底层是通过Roslyn之类的引擎运行的,在此基础上,提供了一些额外功能:

  • 执行完整的C#文件
  • 通过外部进程执行C#文件
  • 在运行过程中链接多个c#文件,并集成运行
  • 提供简便的方法进行链接
  • 脚本调试功能

注:由于技术发展,很多功能可能已经被Roslyn支持了。同时基于web有Try.NETSharpLab等优秀方案。

当然也可以自己基于Roslyn去实现这些功能,不过CS-Script提供了更加简单的封装,适用于懒人。

使用#

程序基于.NET 5的开发,尝试引用CS-Script包,发现不太好用,一直提示System.Reflection.TargetInvocationException:“Exception has been thrown by the target of an invocation.”。支持.NET Core的实际上是CS-Script.Core这个包,安装即可。

Install-Package CS-Script.Core

CS-Script实际上底层支持Mono/Roslyn/CodeDom三种脚本引擎,由于.NET CORE的特殊性,CS-Script.Core做了删减,只能支持Roslyn一种引擎了,支持的C#语言版本由Roslyn版本决定。

旁的不说,直接上代码:

using CSScriptLib;
using System;
using System.Reflection;

namespace ConsoleApp3
{
    public class Program
    {
        static void Main(string[] args)
        {
            //var eval =  CSScript.Evaluator.ReferenceAssemblyByNamespace("System.Text");
            //var p = eval.ReferenceAssemblyByNamespace("ConsoleApp3");
            Assembly compilemethod = CSScript.RoslynEvaluator.CompileMethod(
                        @"using System;
                          public static void CompileMethod(string greeting)
                          {
                              Console.WriteLine(""CompileMethod:"" + greeting);
                          }");
            var p = compilemethod.GetType("css_root+DynamicClass");
            var me = p.GetMethod("CompileMethod");
            me.Invoke(null, new object[] { "1" });


            //eval = CSScript.Evaluator.ReferenceAssembly(sqr);
            dynamic loadmethod = CSScript.Evaluator.LoadMethod(@"using System;
                          public void LoadMethod(string greeting)
                          {
                              Console.WriteLine(""LoadMethod:"" +greeting);
                          }");
            loadmethod.LoadMethod("Hello World!");


            dynamic loadcode = CSScript.Evaluator
             .LoadCode(@"using System;
using ConsoleApp31;
using System.Text;
public class ScriptCC
{
    public void LoadCode(string greeting)
    {
        Console.WriteLine(""LoadCode:"" + greeting);
    }
}");
            loadcode.LoadCode("111");

            var eval = CSScript.Evaluator.ReferenceDomainAssemblies(DomainAssemblies.AllStaticNonGAC);

            var ass = eval
 .CompileCode(@"using System;
public static class ScriptCCStatic
{
    public static void LoadCodeStatic(string greeting)
    {
        Console.WriteLine(""LoadCodeStatic:"" + greeting);
    }
}");
            var tp = eval.CreateDelegate(@"int Sqr(int a)
                                               {
                                                   return a * a;
                                               }");
            Console.WriteLine(tp(3));

            eval = eval.ReferenceDomainAssemblies(DomainAssemblies.AllStaticNonGAC);
            Assembly compilecode = eval
            .CompileCode(@"using System;
using ConsoleApp31;//含有这个namespace的文件包含在本项目中。
using System.Text;
using ConsoleApp3;
public class ScriptLC
{
    public void CompileCode(string greeting)
    {
        Console.WriteLine(""CompileCode:"" + greeting + Encoding.ASCII.IsBrowserDisplay);
        Program.Write();
        Test.Send();
    }
}");
            var ps = compilecode.GetType("css_root+ScriptLC");
            var obj = compilecode.CreateInstance("css_root+ScriptLC");
            var mes = ps.GetMethod("CompileCode");
            mes.Invoke(obj, new object[] { "1" });
            Console.WriteLine();

            //查看evaluator的引用程序集
            var ww = eval.GetReferencedAssemblies();
            foreach (var n in ww)
            {
                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 (var wn in n.GetTypes())
                {
                    Console.WriteLine("Types: " + wn.Name);
                }
            }
            Console.WriteLine();

            //查看当前AppDomain加载的程序集
            foreach (var n in AppDomain.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 (var wn in n.GetTypes())
                {
                    Console.WriteLine("Types: " + wn.Name);
                }
            }
            Console.ReadKey();
        }

        public static void Write()
        {
            Console.WriteLine("REFERENCE OK");
        }
    }
}

总结#

使用CS-Script.Core的时候,所有加载/编译的方法与类型都动态加入了CurrentAppDomain,可以在主程序中进行调用(注意using和using static)。通过Evaluator.ReferenceAssembly等函数添加引用,不支持引用其他动态编译的代码段。

可以一次性将当前AppDomain的程序集引用加入Evaluator,但是一样,只能调用在文件中定义的程序集,无法加载其他动态程序集,调用Evaluator.ReferenceDomainAssemblies(DomainAssemblies.All)将提示错误。

这个限制是Roslyn导致的,暂时无法解决。如果需要实现多个代码段的互相调用,可以直接将代码段进行拼接,或者将公用的代码段存成文件,从文件中进行调用。

CompileMethod#

编译方法,并返回动态生成的程序集,方法被默认加载到DynamicClass类中,该Type完全限定名称为css_root+DynamicClass,定义的静态方法需要使用以下方式调用。

var p = compilemethod.GetType("css_root+DynamicClass");
var me = p.GetMethod("CompileMethod");
me.Invoke(null, new object[] { "1" });

LoadMethod#

加载方法,并返回默认类(DynamicClass)的一个对象,通过定义返回对象为dynamic类型,可以直接调用实例方法。

loadmethod.LoadMethod("Hello World!");

LoadCode#

加载类,并返回代码段中的第一个类的实例,通过定义返回对象为dynamic类型,可以直接调用实例方法。

loadcode.LoadCode("111");

CompileCode#

编译类,并返回动态生成的程序集,定义的实例方法可以使用以下方式调用。

var ps = compilecode.GetType("css_root+ScriptLC");
var obj = compilecode.CreateInstance("css_root+ScriptLC");
var mes = ps.GetMethod("CompileCode");
mes.Invoke(obj, new object[] { "1" });
Console.WriteLine();

CreateDelegate#

生成一个委托,同样定义在DynamicClass中,可以直接调用。

var tp = eval.CreateDelegate(@"int Sqr(int a)
                                    {
                                        return a * a;
                                    }");
Console.WriteLine(tp(3));

参考资料#

附上直接通过Roslyn使用脚本的方法:Roslyn Scripting-API-Samples.md


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK