高性能动态编译库Natasha发布1.0版本!

一、 前言


        对于开源贡献者,Emit和表达式树不是陌生的字眼,IL的动态特性为封装工作带来了极大的方便,会Emit的开发者可以说驾驭了大部分的高性能、高动态的编程技巧。纵观ef、dapper、json.net等第三方常用库,哪个能脱离emit而独善其身,也正因如此,幸福了一批批懒癌患者,包括我这个懒癌中晚期患者(这里给各位病友问好),与此同时本人对封装有着莫名其妙的执念,就在两支怪力的驱使下走上了对emit的不归路.


旧版Natasha始于2016年,当时是对Emit进行的封装,中途经有柠檬的提醒完善了UT和兼容性等工作,后由Victor.X.Qu补充了文档,后经ORM实战。


二、Emit非银弹


        经历过重重思考和实践,Emit不是动态的最佳实践,简单的从以下几个角度来讲:


  • 调优:


  • dup : emit中的dup指令优化在是由开发者控制的,在熟悉指令操作的同时又给开发者带来了额外的优化工作。


  • if/while/for :不得不说IL可以透过代码看本质,指令就是这样的,在条件分支上,标签跳转的形式使得逻辑执行灵活多变。这样除了栈的操作之外,还要关注标签的位置和跳转语句的优化,另外还要清晰的记得你的各个分支。


  • 并发字典与算法优化 :这一点是出自我的极端,在对象成员的赋值/加载等操作面前,并发字典像是一场灾难,卖尽气力优化的动态执行,却被某些数据结构所糟蹋。至于算法与动态编译结合起来,应该没几个病友做过,各位如果有兴趣的话可以慢慢体会。



  • 兼容性:


  • 结构体 : 类与结构体在操作指令上有着诸多的不同,开发者不仅仅要熟悉对类的操作指令,还要对结构体做出兼容,诸如ldflda、 ldloca、Constrained等指令,对于开发者来说并不是一件省心的事。


  • 类型转换 : .NET中的类型转换不仅仅有指令级的转换,standard还提供了诸多方法支持不同类型之间的转换,因此你还需要花一些功夫去处理这些。


  • 语法糖 : 一切语法糖在emit面前都要还原,比如可空类型语法糖,对象比较语法糖,类型比较语法糖等等,无疑会大大增加兼容工作的负担(core3.0的可空引用我还没有做测试)。



  • 构建难度:


  • 深度克隆 : 深度克隆是动态编程的一个典型实战,如果各位病友坚持用EMIT挑战的话,可以没病走两步,走两步。


  • 深度构建 :一旦遇到了动态构建动态场景,那么这个复杂度难以想象。


  • 猜错误 : Emit并没有很好的友情提示,没有语法检查,而被程序锻炼成老猎手一定要付出很多代价。



  • 维护升级:


  • 后续开发 :接手emit代码是一件令人纠结的事,当量变引起质变的时候,从兴奋到苦不堪言这种事情并不是没有发生过,尤其是现在.NET开源工作者都比较独立,没有凝聚力和氛围,人的生命以及精力是有限的。


  • 传承  :由上面诸多信息也可见,在新技术的冲击下,在令人不安的环境下,在孤独的夜里,传承也是个问题。




尽管表达式树已经帮我们做了一些工作,但复杂场景和使用习惯仍然封印着开发者的大脑。


三、狙击暴君


        Roslyn到如今已经耳熟能详了,编译被当作成服务对外开放,让不少开发者从中受益,但由于文档不全,实例不充分,从开始一直到2018年期间,对于懒癌开发者来说,基于Roslyn开发都是一件憋手的事情(例如一些必备操作文档,在2019年今年5月份才提上日程)。Natasha使用Roslyn做为编译引擎,不仅仅在动态构建上进行了人性化升级,还在功能上进行了简化。您不仅可以使用Natasha轻松的构建类、结构体、方法、接口、抽象类,还可以轻松的继承类、重载方法、实现接口、抽象类等等,技术较新,仅支持.standard2.0。


        项 目 地 址:https://github.com/dotnetcore/Natasha

        Nuget索引:DotNetCore.Natasha  (正式版1.0.0.0)


(娜塔莎)(原型苏联红军第25步兵师的中尉柳德米拉·帕夫利琴科,一名出色的女狙击手)


使用Natasha你需要关注:


  1. 在您的工程文件里添加这个节点:<PreserveCompilationContext>true</PreserveCompilationContext>


  2. 了解wiki中反解器的概念及使用。    


  3. 注意命名空间,自动补充命名空间目前尚未支持,需要您手动操作,使用using方法添加。


  4. 想尽一切办法拼接字符串,目前符合CSharp7.3或以下C#版本的都行。


  5. 编译模式有区分:StreamComplier内存流编译/FileComplier文件流编译, 文件流编译的内容,可以被动态调用。当你想动态编译类B的时候使用类A,那类A就需要使用文件流编译,相当于dll动态加载到运行时。


  6. 使用Natasha中的Operator来构建你的动态内容。

四、性能


        这几年随着.NET架构引擎的不断升级,dynamic、emit执行性能已经得到了大幅度提升,roslyn也不例外,之前官方给过性能测试截图,上面显示是比emit快一点,个人的基准测试要等下一个benchmark版本,从耗时的角度来说roslyn <= emit (roslyn有指定release模式编译),所以大家根本不用关心性能问题。


五、使用案例


        使用之前需要注意的是,方法操作都是基于内存流编译,类和其他都基于文件流编译。但有特殊情况,比如有些方法需要被复用,这时可以在编译选项中如此操作:

var tempBuilder = FastMethodOperator.New;	
tempBuilder.ComplierOption.UseFileComplie();	//可根据需求选择编译方式	var builder = new ClassBuilder();	builder.ComplierOption.UseFileComplie();	builder.ComplierOption.UseMemoryComplie();


  • 实现抽象类与接口


public abstract class TestAbstract	
{	public int Name;	public int Age;	public abstract int GetAge();	public abstract string GetName();	
}	OopOperator<TestAbstract> abstractBuilder = new OopOperator<TestAbstract>();	
abstractBuilder.ClassName("UTestClass");	
abstractBuilder["GetName"] = "return Name;";	
abstractBuilder["GetAge"] = "return Age;";	
abstractBuilder.Compile();	
TestAbstract test = abstractBuilder.Create("UTestClass");	public interface ITest 	
{	int MethodWidthReturnInt();	string MethodWidthReturnString();	void MethodWidthParamsRefInt(ref int i);	string MethodWidthParamsString(string str);	string MethodWidthParams(int a,string str,int b);	
}	OopOperator<ITest> interfaceBuilder = new OopOperator<ITest>();	
interfaceBuilder.ClassName("UTestClass");	
interfaceBuilder["MethodWidthReturnInt"] = "return 123456;";	
interfaceBuilder["MethodWidthReturnString"] = "return \"test\";";	
interfaceBuilder["MethodWidthParamsRefInt"] = "i+=10;";	
interfaceBuilder["MethodWidthParamsString"] = "return str+\"1\";";	
interfaceBuilder["MethodWidthParams"] = "return a.ToString()+str+b.ToString();";	
interfaceBuilder.Compile();	
ITest test = interfaceBuilder.Create("UTestClass");	



  • 快速编写委托


public delegate string GetterDelegate(int value);	//方法一	
var action = DelegateOperator<GetterDelegate>.Create("value += 101; return value.ToString();");	
//action(1); result: "102"	//方法二	
var action = "value += 101; return value.ToString();".Create<GetterDelegate>();	
//action(1); result: "102"



  • 定制方法


var action = FastMethodOperator.New	.Param<string>("str1")	.Param(typeof(string),"str2")	.MethodBody("return str1+str2;")	.Return<string>()	.Complie<Func<string,string,string>>();	string result = action("Hello ","World!");    	
//result:   "Hello World!"	



  • 伪造方法


//这里只为演示,实际使用请用下面的静态构造,拿到委托后可以直接运行。	public class TestB	
{	public void TestMethod(){}	
}	var action = FakeMethodOperator.New	.UseMethod<TestB>("TestMethod")	.MethodContent($@"Console.WriteLine(""Hello World!"");")	.Complie<Action>();	//The class script :	
//	
//   using System;	
//   public class N20d26dcba7e6451eaf4c4a6f4753e243	
//   {	
//          public void TestMethod()	
//         {	
//               Console.WriteLine("Hello World!");	
//          }	
//   }	var action = FakeMethodOperator.New	.UseMethod<TestB>("TestMethod")	.StaticMethodContent($@"Console.WriteLine(""Hello World!"");")	.Complie<Action>();	//The class script :	
//	
//    using System;	
//    public static class Neae0b5f6b8b94f4b9418ebc68813760b	
//    {	
//         public static void TestMethod()	
//         {	
//              Console.WriteLine("Hello World!");	
//         }	
//    }



  • 自定制一个类


ClassBuilder builder = new ClassBuilder();	
var script = builder	.Namespace("TestNamespace")	.ClassAccess(AccessTypes.Private)	.ClassModifier(Modifiers.Abstract)	.ClassName("TestUt2")	.ClassBody(@"public static void Test(){}")	.PublicStaticField<string>("Name")	.PrivateStaticField<int>("_age")	.Builder().Script;	Assert.Equal(	
@"using System;namespace TestNamespace{private abstract class TestUt2{public static String Name;private static Int32 _age;public static void Test(){}}}"	
, script);	


  • 自编译一个类


string text = @"using System;	
using System.Collections;	
using System.Linq;	
using System.Text;	namespace HelloWorld	
{	public class TestIndex1	{	public string Name;	public int Age{get;set;}	}	public class TestIndex2	{	public string Name;	public int Age{get;set;}	}	public class TestIndex3	{	public string Name;	public int Age{get;set;}	}	
}	namespace HelloWorld{	public struct TestStruct1{}	public struct TestStruct2{}	public class TestIndex4	{	public string Name;	public int Age{get;set;}	}	
}";	//根据脚本创建动态类	
//寻找第二个命名空间中的第一个类	
Type type = RuntimeComplier.GetClassType(text, 1,2);	
Assert.Equal("TestIndex4", type.Name);	//寻找第二个命名空间中的第二个结构体	
type = RuntimeComplier.GetStructType(text, 2, 2);	
Assert.Equal("TestStruct2", type.Name);


六、方便的扩展


  • 使用Natasha的类扩展:


Example:          	typeof(Dictionary<string,List<int>>[]).GetDevelopName();	//result:  "Dictionary<String,List<Int32>>[]"	typeof(Dictionary<string,List<int>>[]).GetAvailableName();	//result:  "Dictionary_String_List_Int32____"	typeof(Dictionary<string,List<int>>).GetAllGenericTypes(); 	//result:  [string,list<>,int]	typeof(Dictionary<string,List<int>>).IsImplementFrom<IDictionary>(); 	//result: true	typeof(Dictionary<string,List<int>>).IsOnceType();         	//result: false	typeof(List<>).With(typeof(int));                          	//result: List<int>


  • 使用Natasha的方法扩展:


Example:  	Using : Natasha.Method; 	public delegate int AddOne(int value);	var action = "return value + 1;".Create<AddOne>();	var result = action(9);	//result : 10	var action = typeof(AddOne).Create("return value + 1;");	var result = action(9);	//result : 10


  • 使用Natasha的克隆扩展: 


Example:	Using : Natasha.Clone;	var instance = new ClassA();	var result = instance.Clone();


  • 使用Natasha的快照扩展:


Example:	Using : Natasha.Snapshot;	var instance = new ClassA();	instance.MakeSnapshot();	// ********	//  do sth	// ********	var result = instance.Compare();

640?wx_fmt=png



七、实战


  • 深度克隆:Natasha中提供了类遍历器,结合多层动态编译,可以支持复杂数据结构的克隆操作。


  • NCaller是Natasha的实战项目,采用动态原生操作+动态优化查找算法,可以对动/静态类初始化以及字段和属性的常规操作,耗时仅为原生的2.5倍以下。


八、调试


        由于核心编译引擎为Roslyn,因此语法检查、词法检查、语义检查等都支持,这样可以很好的为开发者提供错误提示, Natasha为此增加日志模块(NScriptLog), 在编译流程中,捕获编译信息并记录,另需注意的是,Natasha脚本的格式化操作与VS的格式化一样,所以需要开发者在构建脚本的时候就要多多注意换行的问题。Natasha的编译日志共有3个种类:成功日志、错误日志、警告日志。

  

  • 成功日志:

640?wx_fmt=png

  •  错误日志:

640?wx_fmt=png

  •  警告日志:

640?wx_fmt=png



九、寄语


       随着.NETCore的不断升级,Natasha还有很多新的特性在等待开发,希望各位多多支持。https://github.com/night-moon-studio  NMS是一个基于Natasha的开源项目“孵化”组,项目成熟且通过审核之后(或改名)推荐进入NCC,大家可以积极参与。


        Natasha如果说是一个库不如说是一个时机,动态封装已经不是学习成本高、开发效率低的工作了,希望各位能积极参与尝试开源项目,共同打造.NET Core新生态。




https://github.com/dotnetcore640?wx_fmt=gif

打赏一杯酒,削减三分愁。
跟着我们走,脱发包你有。

640?wx_fmt=png

640?wx_fmt=png

组织打赏账户为柠檬的账户,请标注「NCC」,并留下您的名字,以下地址可查看收支明细:https://github.com/dotnetcore/Home/blob/master/Statement-of-Income-and-Expense.md640?wx_fmt=png640?wx_fmt=pngOpenNCC,专注.NET技术的公众号https://www.dotnetcore.xyz640?wx_fmt=png640?wx_fmt=png微信ID:OpenNCC640?wx_fmt=png长按左侧二维码关注


欢迎打赏组织

给予我们更多的支持


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/314644.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Codeforces Round #245 (Div. 1) E. Points and Segments 欧拉回路 + 建模

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 考虑对于线段&#xff0c;如何建模。 我们考虑先将线段转换成左闭右开的形式&#xff0c;将左右点连起来。 再考虑每个点&#xff0c;将所有离散化后的点拿出来&#xff0c;每个点都有一个…

微软.Net Core 3.0 预览版7发布:大幅减少 SDK 空间大小

据悉&#xff0c;这个预览版是 .Net Core 3 中重要的版本&#xff0c;可以视为原计划在 7 月发布的 RC 版本 (引自微软 .NET Core 首席 Program Manager Richard 先生原话&#xff09;&#xff0c;故可在生产环境进行开发和部署。Windows, macOS 和 Linux 版本的Download .NET …

5门可能衰落的编程语言

专注于为北美地区的科技专业人士提供行业见解和分析&#xff0c;以及提供求职消息的技术职业消息服务网站 Dice Insights 近日发表了一篇题为《5 Programming Languages That Are Probably Doomed》的文章。作者主要根据 TIOBE 和 RedMonk 这两个编程语言排行榜&#xff0c;以及…

.NET加水印/验证码的NuGet包

.NET加水印/验证码的NuGet包 我的在前两篇文章中&#xff0c;我介绍了使用Direct2D给图片加水印/验证码&#xff0c;今天我将其进行了封装&#xff0c;发布了一个NuGet包Sdcb.Imaging&#xff1a;<PackageReference Include"Sdcb.Imaging" Version"1.1.0&quo…

hdu1686:KMP板子

题目链接 题目大意就是给你两个字符串&#xff0c;求出第一个字符串在第二个字符串中出现的次数。 如果我们暴力匹配的话&#xff0c;复杂度是 len(first) * len(second) 对于题目给的 1e4 * 1e6 显然暴力不可取&#xff0c; 这里就用到 KMP 。 说到 KMP 最难理解的就是 next…

集群故障处理之处理思路以及健康状态检查(三十三)

前言 按照笔者的教程&#xff0c;大家应该都能够比较顺畅的完成k8s集群的部署&#xff0c;不过由于环境、配置以及对Linux、k8s的不了解会导致很多问题、异常和故障&#xff0c;这里笔者分享一些处理技巧和思路&#xff0c;以及部分常见的问题&#xff0c;以供大…

Trie初步

简单的描述就是一个字典树&#xff0c; 我们用下图来简单描述一下。 上述的字典树代表着 ab abc abk cd ca b 这六个单词&#xff0c;我们不难发现其中标记是红色的代表从一个伪根节点到这是一个完整的单词。不同的单词有重复的部分&#xff0c;例如accepted&#xff0c;accep…

使用Kubeadm创建k8s集群之节点部署(三十二)

前言 由于上次忘开申明原创&#xff0c;特再发一次。本篇部署教程将讲述k8s集群的节点&#xff08;master和工作节点&#xff09;部署&#xff0c;请先按照上一篇教程完成节点的准备。本篇教程中的操作全部使用脚本完成&#xff0c;并且对于某些情况&#xff08;比如镜…

Trie:hdu 4825、1251、1247、Poj 3764

hdu 4825链接 题目意思很简单&#xff0c;就是要求最大异或值的数。 我们可以从二进制的最高位开始选择&#xff0c;不断的排除一些数。我们先假设存在某些数字的二进制数是与当前查找的数不一样的&#xff0c;我们进入这一部分数进行查找&#xff0c;以此重复&#xff0c;不断…

ASP.NET Core on K8S深入学习(2)部署过程解析与部署Dashboard

上一篇《K8S集群部署》中搭建好了一个最小化的K8S集群&#xff0c;这一篇我们来部署一个ASP.NET Core WebAPI项目来介绍一下整个部署过程的运行机制&#xff0c;然后部署一下Dashboard&#xff0c;完成可视化管理。本篇已加入了《.NET Core on K8S学习实践系列文章索引》&#…

02 | 健康之路 kubernetes(k8s) 实践之路 : 生产可用环境及验证

上一篇《 01 | 健康之路 kubernetes(k8s) 实践之路 : 开篇及概况 》我们介绍了我们的大体情况&#xff0c;也算迈出了第一步。今天我们主要介绍下我们生产可用的集群架设方案。涉及了整体拓补图&#xff0c;和我们采用的硬件配置&#xff0c;目前存在的问题等内容。遵循上一篇提…

[开源] .NETCore websocket 即时通讯组件---ImCore

ImCore 是一款 .NETCore 下利用 WebSocket 实现的简易、高性能、集群即时通讯组件&#xff0c;支持点对点通讯、群聊通讯、上线下线事件消息等众多实用性功能。开源地址&#xff1a;https://github.com/2881099/im &#xff0c;求 star~~dotnet add package ImCoreIM服务端publ…

【译】在 Linux 上不安装 Mono 构建 .NET Framework 类库

在这篇文章中&#xff0c;我展示了如何在 Linux 上构建针对 .NET Framework 版本的.NET项目&#xff0c;而不使用 Mono。通用使用微软新发布的 Mocrosoft.NETFramework.ReferenceAssemblies NuGet 包&#xff0c;您将不需要安装除 .NET Core SDK 之外的任何其他软件包&#xff…

DevOps书单:调研了101名专家,推荐这39本必读书籍

任何一个领域都遵循从新人到熟手&#xff0c;从熟手到专家的路径。在成长过程中&#xff0c;DevOps人经常会陷入没人带&#xff0c;没人管&#xff0c;找不到职业方向的迷茫。DevOps是在商业演进与企业协作的进化过程中诞生的一个全新职业&#xff0c;被很多人看成是一个“全栈…

面对人性,有的选择向左,有的向右

这里是Z哥的个人公众号每周五11&#xff1a;45 按时送达有时也会有感而发&#xff0c;来加个餐&#xff5e;我的第「84」篇原创敬上前天早上&#xff0c;在36kr看到一篇文章《一个负能量的人&#xff0c;可以轻易搞垮周围人的生活》&#xff0c;讲述了可能我们每个人身边都存在…

10分钟了解分布式CAP、BASE理论

CAP理论2000年7月&#xff0c;Eric Brewer教授提出CAP猜想&#xff1b;2年后&#xff0c;Seth Gilbert和Nancy Lynch从理论上证明了CAP&#xff1b;之后&#xff0c;CAP理论正式成为分布式计算领域的公认定理。CAP定律说的是在一个分布式计算机系统中&#xff0c;一致性&#x…

ASP.NET Core Web Api之JWT刷新Token(三)

本节我们进入JWT最后一节内容&#xff0c;JWT本质上就是从身份认证服务器获取访问令牌&#xff0c;继而对于用户后续可访问受保护资源&#xff0c;但是关键问题是&#xff1a;访问令牌的生命周期到底设置成多久呢&#xff1f;见过一些使用JWT的童鞋会将JWT过期时间设置成很长&a…

简单dfs序 + 树链剖分

树链剖分 DFS序 先来讲一讲DFS序是什么东西&#xff0c;直接上图&#xff0c;方便理解。 估计巨巨们应该知道了DFS序的两个重要的东西&#xff0c;in,outin,outin,out数组。 ininin数组就是这个点进入DFS的时间。outoutout数组就是这个点退出DFS递归栈的时间。这个时间要注意…

WeihanLi.Npoi 导出支持自定义列内容啦

WeihanLi.Npoi 导出支持自定义列内容啦Intro之前也有网友给提出过希望列合并或者自定义列内容的 issue 或请求&#xff0c;起初因为自己做 WeihanLi.Npoi 这个扩展的最初目的是导入导出的简单化&#xff0c;使用这个扩展导出的 Excel/csv 可以直接导入成 List<TEntity>&a…

让 .Net 更方便的导入导出 Excel

让 .Net 更方便的导入导出ExcelIntro因为前一段时间需要处理一些 excel 数据&#xff0c;主要是导入/导出操作&#xff0c;将 Excel 数据转化为对象再用程序进行处理和分析&#xff0c;没有找到比较满意的库&#xff0c;于是就自己造了一个轮子&#xff0c;屏蔽掉了 xlsx 与 xl…