C#脚本引擎CS-Script

要说能够运行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这个包,安装即可。

dotnet add package  CS-Script.Core

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

旁的不说,直接上代码:

using System;
using System.Reflection;
using CSScriptLib;

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");
        }
    }
}

namespace ConsoleApp31
{
    public class Test
    {
        public static void Send()
        {
            Console.WriteLine("ConsoleApp31 Test Send");
        }
    }
}

总结#

使用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

作者: 波多尔斯基

出处:https://www.cnblogs.com/podolski/p/14192599.html

版权:本文采用「署名 4.0 国际」知识共享许可协议进行许可。

欢迎转载,转载请保留原文链接~喜欢的观众老爷们可以点下推荐或者右下角关注不迷路~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值