「A」兼容Core3.0后 Natasha 的隔离域与热编译操作

文章转载授权级别:A 预计阅读时间:15分钟
文章转载自公众号【NCC开源社区】 ,作者 【】
一、 2.0预览版本增加了哪些功能
大部分为底层的升级优化,例如:
构建方面的强化 , 例如:
二、我们经历了哪些实践
深度克?。?com/night-moon-/
本项目由 、Vito、、白开水组队开发,可在运行时动态生成克隆方法 。深度克隆作为基础项目 , 锻炼了开源工作者的类型辨识技能 , 趟过了坑为以后的封装之路打下基础 。
【「A」兼容Core3.0后 Natasha 的隔离域与热编译操作】快速调用:.com/night-moon-/
本项目由 AzulX 和 * 开发,可以对运行时实体类、静态类的字段/属性进行动态调用和赋值,目前有两个主要分支,哈希二叉查找算法动态实现以及 * 的指针二叉查找算法动态实现,在算法的动态实现上 ,  表现出了相当强大的优势 。
三、谈一谈‘热更新’
‘热更新’是 Core3.0 的亮点特性之一 , 不少小伙伴在看到译文的时候可能就已经想到了N多场景 , 历经两代 .NET 的洗礼,‘热更新’现在发展到什么样子了?下面简单谈一谈:
.NET开荒时期有域之隔离术,包括有创建、加载程序集、卸载等方法,囊括百家程序集在以下哪个场景中推荐使用wisp,一刀以斩之 。对于前辈们来说谈到可以口若悬河滔滔不绝,可惜我进入 C# 时间比较晚,对的印象并不是很深,在应用上也没有什么造诣在以下哪个场景中推荐使用wisp,仅此泛泛而言 。
时间进入了 . 时代,在升级大潮中受到了致命打击 , 方法和方法经岁月升级后的源码中充斥着 throw 和 throw,完全丧失了功能,取而代之的是 ALC(),Core3.0 的 ALC 是一个更为完善的操作类,官方为其定义了三大洪荒场景:
1、插件编程
2、动态编译 , 运行/刷新代码,网站/脚本引擎
3、外部程序集的一次性内?。ㄎ腋鋈死斫饩褪抢嗟男畔?,,这种元数据只读属性)
据描述: 之前一直用, 每个测试都腰酸背痛相当慢,自从换了 ALC( A blue Ca.) 一口气上5楼不费劲!官方画了大饼:未来分析器执行编译时也都在ALC里进行,用完就卸载,卸磨就杀驴 。
当初被定位在高性能、安全,历史证明这个定位跟 GPS 一样不准,ASP.NET 深受其害,历史车轮碾过了 ASP.NET 迎来了 ASP.NET Core,在域功能被阉割的期间 , ASP.NET Core 转向了相对静态的模型,增加了若干学习成本,详见watch 命令 。还有 Razor , 它从 . 编译到 .dll 的环境就是 ALC,自建了一个名为 Razor- 的域环境 。
另外还涉及到和 Prism 框架, 精力有限,谁有兴趣就去研究研究吧 。
ALC 的场景和案例可能激起了您的好奇心,下面讲一下 ALC 的应用:
我们可以在程序里创建多个 ALC 实例 , 但前提是你需要继承并实现它 。每一个 ALC 的实例都是一个域(这里我就不叫它上下文了) 。程序刚跑起来的时候是在域中的,这个域属于系统域卸不了,又称为共享域,不同域之间是无法访问和引用的不同域中信息的,却共用域中的信息,这个域至关重要,所以尽量避免向其中加载乱七八糟的程序集 。
ALC 的使用需要注意以下几点:
1、子类继承时需指定 ALC 的构造参数,base() , 这个参数可以赋予 ALC 卸载的能力 。
2、时刻注意反射信息的引用,只有清除引用,才能保证 ALC 实例被 GC 回收 。
3、在针对不同域的编程时可使用 ction 方法锁住域内上下文,ction 方法是放在 using 里的,这样你的花括号内就是一个域 , 并用属性来获取当前操作域 。
4、注意 ALC 被线程占用的情况 , 被占用的对象是无法被回收的,如果你在测试中没有达到预期,除了排除代码问题之外你还需要注意函数是否被内联进入主线程或一个带有阻塞功能的线程 , 如果你不确定 , 可以在方法上使用 [(.)] 阻止代码内联优化 , 正常情况下优化功能是开启的。
5、插件加载要注意与插件 dll 同目录的依赖文件,3.0 提供了操作类自动解析依赖,建议使用带有.deps.json文件的完整插件 。
6、当你的外部文件引用并使用了 / 等(测试日期9月3日),会造成不可回收的情况,不是你的代码出问题了 , 而是库本身的问题(待解决,3.1或者5.0) 。
对 ALC 封装的一些建议:
1、如果没有非托管代码,尽量不要在析构函数里折腾代码 。
2、如果你的域管理代码有些复杂 , 建议对外给个接口,以便清除对该域的程序集、元数据等信息的引用 。
3、肉眼观测内存时,测试代码中尽量不要在 Main 函数里做元数据的相关操作 , 主线程是 GC 的一个干扰点 。
4、若对内存的开销比较敏感,请尽可能分域,并结合弱引用实现创建与销毁 。
5、有时显式调用方法会报异常,可以在里清除完引用之后再使用,实测你不用方法也能回收 。
Core3.0 中随 ALC 一起的还有反射的自省信息 。
例如:.、.等元数据,它将告诉你它是否能被回收,当然了这种自省的信息都是只读的 。说到只读 , .NET 中还存有一条进化路线即 : ->->(感谢提供的信息), 只读元数据,相比 ALC 可执行,可调用,MLC (在包 .. 中) 的是元数据只读操作,它并不能执行程序集的内容 , 仅仅反射出元数据,配套使用的是.
对于无法卸载的情况,官方建议使用sos 组件进行调试,新版 sos 将独立出来,各位可以使用以下命令进行安装(建议开源工作者在封装此功能时添加UT测试检测卸载功能,尽可能保证在正常的情况下不需要用户自己去调试) 。
$ dotnet tool install -g dotnet-sos --version 3.0.0-preview8.19412.1
$ dotnet-sos install
更多的实践还需要大家去探索 。
四、是如何实现‘热更新’的
据以上信息,.0 中动态构建遵循以下结构 。
这两幅图说展示了中自定义编译域的结构,如果在创建程序集时不指定名字,程序集名将以 GUID 形式创建,故名随机程序集 。在编译时未被移除的引用都将参与编译 , 该引用的来源:1、共享域;2、当前域;
//创建一个域DomainManagment.Create("MyDomain");//移除一个域,移除将无法进行DomainManagment的其他任何操作DomainManagment.Remove("MyDomain");//判断域是否被卸载(被GC回收)DomainManagment.IsDeleted("MyDomain");//获取一个ALC上下文DomainManagment.Get("MyDomain");

//锁住已存在的域上下文using(DomainManagment.Lock("MyDomain")){ var domain = DomainManagment.CurrentDomain; //code in 'MyDomain' domain }//创建并锁定一个域上下文using(DomainManagment.CreateAndLock("MyDomain")){ var domain = DomainManagment.CurrentDomain; //code in 'MyDomain' domain }
//向域中注入插件 string dllPath = @"1/2/3.dll";var domain = DomainManagment.Get/Create("MyDomain");var assembly = domain.LoadFile(dllPath);

//锁域与插件解构操作string dllPath = @"1/2/3.dll";using(DomainManagment.CreateAndLock("MyDomain")){ var (Assembly,TypeCache) = dllPath; //Assembly: Assembly //TypeCache: ConcurrentDictionary }

//将引用从当前域内移除,下次编译将不会带着该程序集的信息//下面方法三选一均可实现引用移除操作domain.RemoveDll(dllPath);domain.RemoveAssembly(assembly);domain.RemoveType(type);
//从指定域创建一个程序集操作实例var asm = domain.CreateAssembly("MyAssembly");

//向程序集中添加一段已经写好的类/结构体/接口/枚举asm.AddScript(@"using xxx; namespace xxx{xxxx}");asm.AddFile(@"Class1.cs");

//使用Natasha内置的操作类asm.CreateEnum(name=);asm.CreateClass(name=);asm.CreateStruct(name=);asm.CreateInterface(name=);

//使用Natasha内置的方法操作类//并不是很推荐使用这两个方法//建议在一个单独的程序集内编译方法 asm.CreateFastMethod(name=);asm.CreateFakeMethod(name=);

//使用程序集进行编译并获得程序集var assembly = asm.Complier;asm.GetType(name);
using(DomainManagment.CreateAndLock("MyDomain")){ var domain = DomainManagment.CurrentDomain; var assembly = domain.CreateAssembly("MyAssembly"); //创建一个接口 assembly .CreateInterface("InterfaceTest") .Using("System") .OopAccess(AccessTypes.Public) .OopBody("string ShowMethod(string str);"); //创建一个类并实现接口 assembly .CreateClass("TestClass") .Using("System") .OopAccess(AccessTypes.Public) .Inheritance("InterfaceTest") .Method(method => method .MemberAccess(AccessTypes.Public) .Name("ShowMethod") .Param("str") .Body("return str+" World!";") .Return); //编译并获取类型 var result = assembly.Complier; var type = assembly.GetType("TestClass"); //Operator默认单独创建一个程序集 var @delegate = FastMethodOperator.New .Using(type) .MethodBody(@" TestClass obj = new TestClass; return obj.ShowMethod(arg);") .Complie<Func>; @delegate("Hello"); //result = "Hello World!"; domain.Dispose; //卸磨杀驴}
不要从公众号里复制代码到VS , 会有意外字符 。
五、Bug有缘人
访问以下链接:.com///blob//src/../src///..cs
自右向左选中, 如果页面崩溃了, 老铁握爪 。
.com/
打赏一杯酒,削减三分愁 。跟着我们走,脱发包你有 。
组织打赏账户为柠檬的账户,请标注「NCC」 , 并留下您的名字,以下地址可查看收支明细:.com//Home/blob//-of–and-.md
,专注.NET技术的公众号
.xyz
微信ID:
欢迎打赏组织
给予我们更多的支持
本文到此结束,希望对大家有所帮助 。