当前位置:网站首页>.NET静态代码织入——肉夹馍(Rougamo) 发布1.1.0
.NET静态代码织入——肉夹馍(Rougamo) 发布1.1.0
2022-08-09 16:09:00 【dotNET跨平台】
肉夹馍是什么
肉夹馍(https://github.com/inversionhourglass/Rougamo)通过静态代码织入方式实现AOP的组件。.NET常用的AOP有Castle DynamicProxy、AspectCore等,以上两种AOP组件都是通过运行时生成一个代理类执行AOP代码的,肉夹馍则是在代码编译时直接修改原始方法IL代码,在原始方法内织入AOP代码的。.NET静态AOP的组件或许有人使用过PostSharp,这是一个功能完善且强大的静态代码织入组件,Postsharp有社区版,但可惜的是社区版不支持异步方法,肉夹馍的实现方式与Postsharp类似,同时也支持了异步方法,如果你仅仅使用了Postsharp方法层级的AOP代码织入功能,可以尝试使用肉夹馍来替代Postsharp。
在 上一篇文章 中介绍了1.0.0版本肉夹馍的功能,1.0.0版本能够进行的AOP操作主要是日志记录以及APM操作,给出的示例项目也是OpenTelemetry的APM项目。在上一篇文章的评论以及github issue中都有朋友询问是否能处理异常以及修改返回值等操作,最终拖了较长一段时间于近期发布了1.1.0版本实现了这些功能。
快速开始
# 添加NuGet引用dotnet add package Rougamo.Fodypublic class TestService{
[Fact] public async void Test1()
{ var v1 = await M1();
Assert.Null(v1); var v2 = Sum(1, null);
Assert.Equal(-1, v2); var v3 = await M2();
Assert.Empty(v3);
}
[MuteException] public async Task<string> M1()
{ throw new NotImplementedException();
}
[ArgNullCheck] public int Sum(int? a, int? b)
{ return a.Value + b.Value;
}
[ReturnNullCheck] public async Task<string> M2()
{ await Task.Yield(); return null;
}
}public class MuteExceptionAttribute : MoAttribute{ public override void OnException(MethodContext context)
{ if (context.RealReturnType == typeof(string))
{
context.HandledException(this, null);
}
}
}public class ArgNullCheckAttribute : MoAttribute{ public override void OnEntry(MethodContext context)
{ foreach (var arg in context.Arguments)
{ if (arg == null)
{
context.ReplaceReturnValue(this, -1);
}
}
}
}public class ReturnNullCheckAttribute : MoAttribute{ public override void OnSuccess(MethodContext context)
{ if (context.ReturnValue == null)
{
context.ReplaceReturnValue(this, string.Empty);
}
}
}折叠在上面的示例代码中MuteExceptionAttribute重写了OnException通过MethodContext.HandledException表明异常已处理并将返回值设置为null;ArgNullCheckAttribute重写了OnEntry通过MethodContext.ReplaceReturnValue设置了返回值,由于OnEntry是在执行方法前调用,这种方式会在OnEntry执行完毕之后直接将ReplaceReturnValue设置的返回值作为方法的返回值直接返回,一般参数验证、缓存逻辑会用到;ReturnNullCheckAttribute重写了OnSuccess通过MethodContext.ReplaceReturnValue修改了实际的返回值,示例中通过这种方式避免返回null值。
注意事项
如果方法是
async Task那么MethodContext.RealReturnType取值为typeof(void),如果是async Task<T>那么取值为typeof(T),但如果返回值为Task或Task<T>但并没有使用async写法,那么其值就是typeof(Task)或typeof(Task<T>),这样设定的好处是,你设置的返回值类型与该属性的值相同即可,不用考虑方法是否异步不论是异常处理还是设置/修改返回值,设置的返回值类型必须与方法定义的返回类型(
MethodContext.RealReturnType)相同,类型不同时运行时会报错OnExit中调用MethodContext.ReplaceReturnValue无法修改返回值
补充说明
在 上一篇文章 中由于是第一篇文章,介绍的东西较多,部分功能并没有在文章中详细说明,本篇由于篇幅较短,所以会补上一些说明,不过这里也不会介绍全部的,详细的介绍可以移步 github(https://github.com/inversionhourglass/Rougamo)
Iterator / AsyncIterator 不支持修改返回值和异常处理
Iterator和AsyncIterator也就是下面的写法
public IEnumerable<int> Iterator(int count){ yield return 1; yield return 2; yield return 3;
}public async IAsyncEnumerable<int> AsyncIterator(int count){ yield return 3; await Task.Yield(); yield return 2; await Task.Yield(); yield return 1;
}之所以不支持,是因为它们并不直接返回一个集合,而是返回一个状态机(StateMachine),使用foreach迭代时实际每次迭代执行状态机的MoveNext方法获取本次迭代的返回值,考虑到实现这种特殊机制的复杂性以及平时使用的频率,当前对此种类型不进行支持。
Iterator / AsyncIterator 不支持记录返回值
同样的,Iterator和AsyncIterator默认也无法通过MethodContext.ReturnValue获取方法的返回值,但可以通过FodyWeavers.xml的Rougamo节点增加属性配置enumerable-returns="true"来记录Iterator和AsyncIterator的返回值到MethodContext.ReturnValue。
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<Rougamo enumerable-returns="true" /></Weavers>这个设定是因为状态机并没有保存所有的元素到一个集合中,每个元素都是一次一次调用MoveNext执行代码返回的,如果你使用foreach遍历Iterator或AsyncIterator,并且对每次遍历的元素使用玩之后并没有进行保存,那么上一个元素可能在你遍历下一个元素时被GC回收。记录它们的返回值的实现方式是额外建立一个集合保存每次迭代的元素值,这种方式对上面说的的foreach遍历的情况来说会产生额外的内存消耗,而如果迭代器的元素很多,或者每个元素本身很占内存,那么这种方式可能会额外占用大量内存空间,所以开启这个开关前需要考虑一番。
最后
如果在使用肉夹馍的过程中遇到了什么问题,或者希望增加一些什么样的功能,欢迎到github(https://github.com/inversionhourglass/Rougamo)里提issue,不过对于新功能,可能会有一个较长的周期才能完成并发布正式版。
随着SourceGenerator的应用越来越广泛,Mono.Cecil的应用场景被进一步压缩,一开始提到的动态代理现在也能通过SourceGenerator在编译时生成代理类,这是一件好事,相比晦涩易错的IL,SourceGenerator提供的语法树更加方便易懂且不易出错,但这并不代表Mono.Cecil应该退场了(至少现在不是),Mono.Cecil虽然门槛高,但他的功能也同样强大,直接修改IL是SourceGenerator和`Emit所无法做到的(至少现在是这样),如果在以后的编程之路中遇到了SourceGenerator和`Emit无法解决的问题,希望你能想起还有Mono.Cecil和Fody这条路,如果有时间可以尝试一下,也希望肉夹馍这个项目能给你带来一些参考价值。
边栏推荐
- Fees and inquiry methods of futures account opening exchanges
- 央企施工企业数字化转型的灵魂是什么
- .NET 6学习笔记(4)——解决VS2022中Nullable警告
- 小家电控制板开发——未来小家电行业的发展方向
- 中科院打脸谷歌:普通电脑追上量子优越性,几小时搞定原本要一万年的计算...
- QuickSort(快速排序)&&MergeSort(归并排序)的效率比较[搭配LeetCode例题]
- 测试/开发程序员喜欢跳槽?跳了就能涨工资吗?
- 2022年中国第三方证券APP创新专题分析
- 2.1、基于并行上下文注意网络的场景文本图像超分辨率
- B48 - 基于51单片机的学生管理门禁系统设计
猜你喜欢
随机推荐
什么是硬件集成开发?硬件集成开发的核心有哪些?
对象模型-虚指针虚表
面试官:Redis 大 key 要如何处理?
利用C#传输Json数据
ceph2
Vim practical skills_2. Normal mode and insert mode
如何仿造一个websocket请求?
MySQL 5.5 series installation steps tutorial (graphical version)
B50 - 基于51单片机的儿童成长管理系统
B49 - 基于STM32单片机的心率血氧检测与远程定位报警装置
The Chinese Academy of Sciences slaps Google in the face: ordinary computers catch up with quantum superiority, and can solve calculations that would have taken 10,000 years in a few hours...
ABP详细教程——模块类
Smart Tool Management System
产品结构设计优化模具简化结构总结
【开源教程4】疯壳·开源编队无人机-OPENMV 脚本烧写
B43 - 基于STM32单片机的自动视力检测仪
Sigrity PowerSI 特征阻抗和耦合度仿真
现在,怎么挑选舞台租赁LED显示屏?
WeChat developer tools error, prompt did not find the entrance to the app. The json file
央企施工企业数字化转型的灵魂是什么








