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

Natasha 4.0 探索之路系列(三) 基本的动态编译的更多相关文章

  1. Natasha 4.0 探索之路系列(一) 概况

    Natasha 简介 Natasha 是一个基于 Roslyn 的动态编译类库, 它以极简的 API 完成了动态编译的大部分功能, 使用它可以在程序运行时编译出新的程序集. Natasha 允许开发人 ...

  2. Natasha 4.0 探索之路系列(二) "域"与插件

    域与ALC 在 Natasha 发布之后有不少小伙伴跑过来问域相关的问题, 能不能兼容 AppDomain, 如何使用 AppDomain, 为什么 CoreAPI 阉割了 AppDomain 等一系 ...

  3. Natasha 4.0 探索之路系列(四) 模板 API

    Natasha 模板 Natasha 在编译单元的基础上进行了封装整理, 并提供了多种模板帮助开发者构建功能. 使用此篇的 API 前提是您对 C# 非常熟悉, 对系统的一些类型足够了解. 据此 Na ...

  4. Microsoft Enterprise Library 5.0 系列(三)

    一.简介及用途 在实际的项目开发中,我们总会需要对数据进行验证,以保证数据的可靠性,而为了使这些验证可以在不同的地方进行复用(如winform.web.WPF等),就需要将验证进行封装,EntLib的 ...

  5. 【Android Studio探索之路系列】之六:Android Studio加入依赖

    作者:郭孝星 微博:郭孝星的新浪微博 邮箱:allenwells@163.com 博客:http://blog.csdn.net/allenwells github:https://github.co ...

  6. Web 开发人员和设计师必读文章推荐【系列三十】

    <Web 前端开发精华文章推荐>2014年第9期(总第30期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各类能够提升网站用户体验的优秀 jQuery 插件,展示前沿的 HTML5 ...

  7. WCF 4.0 进阶系列 -- 随笔汇总

    WCF4.0 进阶系列–前言 WCF4.0 进阶系列--第一章 WCF简介 WCF4.0进阶系列--第二章 寄宿WCF服务 WCF4.0进阶系列--第三章 构建健壮的程序和服务 WCF4.0进阶系列- ...

  8. MyBatis学习系列三——结合Spring

    目录 MyBatis学习系列一之环境搭建 MyBatis学习系列二——增删改查 MyBatis学习系列三——结合Spring MyBatis在项目中应用一般都要结合Spring,这一章主要把MyBat ...

  9. MySQL并发复制系列三:MySQL和MariaDB实现对比

    http://blog.itpub.net/28218939/viewspace-1975856/ 并发复制(Parallel Replication) 系列三:MySQL 5.7 和MariaDB ...

随机推荐

  1. 在Winform项目和Web API的.NetCore项目中使用Serilog 来记录日志信息

    在我们常规的调试或者测试的时候,喜欢把一些测试信息打印在控制台或者记录在文件中,对于.netframework项目来说,我们输出控制台的日志信息习惯的用Console.WriteLine来输出查看,不 ...

  2. win10 1909+ vs2015up3 使用fmt概述(fmt version 7.0.1)

    !!版权声明:本文为博主原创文章,版权归原文作者和博客园共有,谢绝任何形式的 转载!! 作者:mohist fmt 源码: https://github.com/fmtlib/fmt fmt官方文档: ...

  3. win10使用cmake配置fmt生成vs2015解决方案(fmt version 7.0.1)

    !!版权声明:本文为博主原创文章,版权归原文作者和博客园共有,谢绝任何形式的 转载!! 作者:mohist 本文仅为参考,请以实际情况为准, fmt版本: 7.0.1 准备 下载源码fmt : htt ...

  4. windows串口之虚拟串口和Access port

    关于 本文将介绍自己常用的串口工具,演示环境为Windows. 1. 虚拟串口 1.1 没有那么多的物理串口, 用虚拟串口来凑.需要软件Configure Virtual Serial Port Dr ...

  5. 【Android开发】问答机器人,聊天类App的开发制作过程记录

    缘起 很久没写项目了,所以单纯的想练练手,正好看到有问答机器人的接口,想到之前也做过聊天项目,为什么不实验一下呢.当然也是简单调用接口的项目,并没有真正的完成问答的算法等等.业余项目,功能不齐全,只实 ...

  6. 【LeetCode】970. Powerful Integers 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 暴力搜索 日期 题目地址:https://leetc ...

  7. 【LeetCode】497. Random Point in Non-overlapping Rectangles 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地址: https://leetcode.com/problems/random-p ...

  8. 【剑指Offer】10- II. 青蛙跳台阶问题 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 个人微信公众号:负雪明烛 目录 题目描述 解题方法 动态规划 日期 题目地址:https: ...

  9. 【LeetCode】105. Construct Binary Tree from Preorder and Inorder Traversal 从前序与中序遍历序列构造二叉树(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 递归 日期 题目地址:https://leetcod ...

  10. @Transactional 注解实现

    @Transactional注解简介 @Transactional是spring中声明式事务管理的注解配置方式,相信这个注解的作用大家都很清楚.@Transactional注解可以帮助我们把事务开启. ...