• C#脚本引擎CS-Script


    最近想要在程序中嵌入一个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

  • 相关阅读:
    media query 开发总结
    整屏滚动
    移动端reset样式
    中国天气网 城市代码 sql语句
    php文章tag标签的增删
    oracle的分号和斜杠/
    php 操作 oracle lob 数据2
    php 操作 oracle lob 数据
    oracle创建用户
    php进度条
  • 原文地址:https://www.cnblogs.com/podolski/p/14192599.html
Copyright © 2020-2023  润新知