C# 脚本引擎CS-Script的使用

 更新时间:2020年12月31日 09:23:02   作者:波多尔斯基  
这篇文章主要介绍了C#脚本引擎CS-Script的使用,帮助大家更好的理解和学习,感兴趣的朋友可以了解下

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

比较

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

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

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

当然也可以自己基于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

以上就是C#脚本引擎CS-Script的使用的详细内容,更多关于C#脚本引擎CS-Script的资料请关注脚本之家其它相关文章!

相关文章

  • C#如何通过匿名类直接使用访问JSON数据详解

    C#如何通过匿名类直接使用访问JSON数据详解

    这篇文章主要给大家介绍了关于C#如何通过匿名类直接使用访问JSON数据的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起看看吧。
    2018-02-02
  • 关于C#反射 你需要知道的

    关于C#反射 你需要知道的

    这篇文章主要介绍了C#反射的相关知识,文中讲解的非常详细,代码帮助大家更好的参考学习,感兴趣的朋友可以了解下
    2020-06-06
  • 使用C#实现Windows组和用户管理的示例代码

    使用C#实现Windows组和用户管理的示例代码

    这篇文章主要介绍了使用C#实现Windows组和用户管理的示例代码,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下
    2021-01-01
  • C# 多项目打包时如何将项目引用转为包依赖(最新推荐)

    C# 多项目打包时如何将项目引用转为包依赖(最新推荐)

    这篇文章主要介绍了C#多项目打包时如何将项目引用转为包依赖,本文给大家介绍的非常详细,感兴趣的朋友一起看看吧
    2025-04-04
  • C#连续任务Task.ContinueWith方法

    C#连续任务Task.ContinueWith方法

    这篇文章介绍了C#中的连续任务Task.ContinueWith方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-04-04
  • C# datagridview、datagrid、GridControl增加行号代码解析

    C# datagridview、datagrid、GridControl增加行号代码解析

    今天这篇文章小编就来给大家分享关于C# datagridview、datagrid、GridControl增加行号的介绍,主要包括WinForm中datagridview增加行号、WPF中datagrid增加行号、WPF dev控件GridControl增加行号三个内容,感兴趣等我小伙伴可以参考一下
    2021-10-10
  • C#导入导出EXCEL文件的代码实例

    C#导入导出EXCEL文件的代码实例

    这篇文章主要介绍了C#导入导出EXCEL文件代码实例,代码的流程和方法都很详细,需要的朋友可以参考下
    2014-04-04
  • C#中的IDisposable模式用法详解

    C#中的IDisposable模式用法详解

    这篇文章主要介绍了C#中的IDisposable模式用法,讲述了垃圾资源回收机制的实现,并对比分析了Dispose()方法、~DisposableClass()析构函数、虚方法Dispose(bool disposing)的原理,需要的朋友可以参考下
    2014-09-09
  • C#如何给新建的winform程序添加资源文件夹Resources

    C#如何给新建的winform程序添加资源文件夹Resources

    这篇文章主要介绍了C#如何给新建的winform程序添加资源文件夹Resources,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-09-09
  • C#6 null 条件运算符

    C#6 null 条件运算符

    本文主要对比C# 6 null运算符与老版本的不同,并且用代码实例测试,发现新语法性能提高,语法简化了。希望看到的同学对你有所帮助
    2016-07-07

最新评论