• Natasha 4.0 探索之路系列(三) 基本的动态编译


    Natasha 的设计

    动态编译

    Roslyn 为开发者提供了动态编译的接口, 允许我们以 C# 代码来编写 Emit 或 表达式树生成的程序集, 但是完成一个编译需要诸多步骤, 用户参与的操作也很多, 例如: 格式化整理语法树, 创建编译选项, 填充对应的引用程序集来支持语义检查和编译, 控制输出流等. 其中除了第一个语法树相对简单, 后面都需要开发者摸索完成. 毕竟 Roslyn 的文档不全, 甚至关于它的文档散落在其他边角章节, 比七龙珠都散. 那么在这种情况下使用 Natasha 无疑是非常好的选择.

    Natasha 的便捷之处

    Natasha 自发版以来,便集成有引用管理, 全局 Using 管理, 域管理, 这让开发者极大的减少了开发前的准备工作, 在便捷编译过程中, Natasha 支持引用覆盖, Using 覆盖,编译流到域的输出, 有了这三大保证, 开发者可更多的关注于动态功能逻辑的开发.
    新版 Natasha 新增了语义过滤委托 API 以方便用户根据语义信息定制/重组自己的语法树, 并提供方法支持开发者管理引用版本, 另外保证了3种流的对外输出,即

    • dll : 程序集输出文件
    • pdb : 元数据调试信息
    • xml : 元数据结构及注释

    整个编译过程中将会分3阶段抛出异常:

    1. 语法构建阶段,如果出错则抛出异常;
    2. 编译阶段, 如果编译失败则会抛出异常;
    3. 元数据转换阶段, 有些 API 是支持从 Assembly 到其他元数据获取和转换的, 转换失败则抛出异常.

    Natasha 基本编译单元

    Natasha 的基本编译单元为 AssemblyCSharpBuilder , 该单元整合了编译流程所需要的基本功能, 相比 Natasha 的模板而言, 它则是轻量级,底层的工作单元.
    以下是使用方法:

    首先引入 DotNetCore.Natasha.CSharp

    最基本的编译操作
    
    //Natasha 预热
    NatashaInitializer.Preheating(/*...引用添加过滤器...例如:(item, name) => name!.Contains("IO")*/);
    
    string code = @"public class A{public string Name=""HelloWorld"";}";
    
    //在花括号范围内圈定域,using 内的方法锁定了域的作用范围.
    //Natasha 所有关于 Name 的 Api 如果不指定,默认为 GUID.
    using (DomainManagement.Create(domainName)/Random().CreateScope())
    {
      AssemblyCSharpBuilder builder = new( /*....assenblyName....*/ );
      builder.Add(code);
      var type = builder.GetTypeFromShortName("A");  
      //...do sth...        
    }
    
    
    //手动指定域
    AssemblyCSharpBuilder builder = new();
    builder.Domain = DomainManagement.Random();
    builder.Add(code);
    var assembly = builder.GetAssembly();  
    //...do sth...        
    
    
    //直接定位到委托
    string code = @"public class A{public string Name=""HelloWorld""; public static string Get(){  return (new A()).Name; }}";
    using (DomainManagement.Create("myDomain").CreateScope())
    {
       AssemblyCSharpBuilder builder = new("myAssembly");
       builder.Add(code);
       var func = builder.GetDelegateFromShortName<Func<string>>("A","Get");
       Assert.Equal("HelloWorld", func()); // √
    }
    
    其他 API
    //设置输出 dll 文件路径
    builder.SetDllFilePath(mydll);
    //设置输出 pdb 文件路径
    builder.SetPdbFilePath(mypdb);
    //设置输出 xml 文件路径
    builder.SetXmlFilePath(myxml);
    //使用 Natasha 自带的输出路径(请在域和程序集名确定之后调用).
    builder.UseNatashaFileOut();
    
    //配置编译选项
    builder.ConfigCompilerOption(opt=>opt);
    //配置语法树选项
    builder.ConfigSyntaxOptions(opt=>opt);
    
    //给编译单元添加语义过滤
    builder.AddSemanticAnalysistor();
    //启/禁用语义过滤
    builder.Enable/DisableSemanticCheck();
    
    
    //添加日志事件
    builder.LogCompilationEvent += (log) => { if(log.HasError) Console.WriteLine(log.ToString()); };
    
    //编译事件
    builder.CompileSucceedEvent //编译成功触发事件
    builder.CompileFailedEvent //编译失败触发事件
    
    
    //引用行为与程序集加载行为控制
    var assembly = builder
    
         //委托过滤: 如果发现默认域的引用与定制域中的引用有同名情况,则进入委托处理. 返回一个枚举结果给程序处理.
         //PassToNextHandler 结果表示将进入到引用版本行为控制继续处理
        .CompileWithReferencesFilter((defaultAssemblyName,domainAssemblyName)=> LoadVersionResultEnum.PassToNextHandler)
    
         //引用行为控制, None/UseHighVersion/UseLowVersion/UseDefault(默认使用)/UseCustom 四种控制方法
        .CompileWithReferenceLoadBehavior(referenceLoadBehavior)
    
         //程序集编译成功后,在域中加载的行为控制,默认为 LoadBehaviorEnum.None (全加载);
        .CompileWithAssemblyLoadBehavior(LoadBehaviorEnum.UseDefault)
    
        .GetAssembly();
    

    注意: 主域的引用文件和自己创建域的引用文件可能存在同名,但不同版本,此时编译需要 CompileWithReferenceLoadBehavior 来控制引用加载的行为, 举例: RefA(v1.0) 和 RefA(v2.0) 相比, v2.0 中比 v1.0 多了几个功能,几个类,几个接口.... 那么在管理引用的时候, 你就要根据自身的代码情况进行管理, 比如你的代码用到了 v2.0 的新类,新功能, 那么就要屏蔽掉 v1.0.

    覆盖全局 using
    //-------------------主域 using -------------------- 定制域 using ------------------------------- 代码脚本 ---------------
    string code = DefaultUsing.UsingScript +  builder.Domain.UsingRecorder.ToString() + "namespace{ public class xx.....  }";
    

    域中的 UsingRecorder 会记录编译之后产生的 using, 自动管理.

    结尾

    大家在使用动态编译时, 要尽可能做到"隔离", 一旦依赖和引用版本多了, 对于动态开发来讲,就是一场灾难.
    以上是使用 Natasha 关于动态编译的最基本使用方法, 下一篇将讲解 Natasha 高级 API 的使用.

  • 相关阅读:
    Atitit.木马病毒websql的原理跟个设计
    Atitit.cateService分类管理新特性与设计文档说明v1
    Atitit.cateService分类管理新特性与设计文档说明v1
    Atitit.iso格式蓝光 BDMV 结构说明
    Atitit.iso格式蓝光 BDMV 结构说明
    Atitit.复合文档的格式 标准化格式
    Atitit.复合文档的格式 标准化格式
    Atitit.木马病毒强制强行关闭360 360tray.exe的方法
    Atitit.木马病毒强制强行关闭360 360tray.exe的方法
    Atitit.复合文档的格式 标准化格式
  • 原文地址:https://www.cnblogs.com/NMSLanX/p/15813760.html
Copyright © 2020-2023  润新知