当前位置:网站首页>.NET静态代码织入——肉夹馍(Rougamo) 发布1.1.0
.NET静态代码织入——肉夹馍(Rougamo) 发布1.1.0
2022-08-04 00:21: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这条路,如果有时间可以尝试一下,也希望肉夹馍这个项目能给你带来一些参考价值。
边栏推荐
猜你喜欢

After building the pytorch environment, the pip and conda commands cannot be used

The super perfect layout has shortcut keys and background replacement

Salesforce's China business may see new changes, rumors may be closing

【性能优化】MySQL常用慢查询分析工具

绕任意轴旋转矩阵推导

JVM垃圾回收总结(未完待续)

带你造轮子,自定义一个随意拖拽可吸边的悬浮View组件

Go编译原理系列7(Go源码调试)

面试必问的HashCode技术内幕

C# wpf使用ffmpeg命令行实现录屏
随机推荐
Nanoprobes Mono- Sulfo -NHS-Nanogold的使用和应用
2015年开源大事件汇总
教你如何定位不合理的SQL?并优化之
动态内存二
初始 List 接口
手撕Gateway源码,今日撕工作流程、负载均衡源码
Apple told Qualcomm: I bought a new campus for $445 million and may plan to speed up self-development of baseband chips
Shell 用法梳理总结
corn表达式 具体详解与案例
YOLOv7改进之二十二:涨点神器——引入递归门控卷积(gnConv)
Jmeter-断言
带你造轮子,自定义一个随意拖拽可吸边的悬浮View组件
代码重构:面向单元测试
【杂项】如何将指定字体装入电脑然后能在Office软件里使用该字体?
Sqlnet. Ora file with the connection of authentication test
【杂项】通过Excel为字符串产生条码
【超详细】手把手教你搭建MongoDB集群搭建
学习笔记 | uiautomation(如何)实现自动化
分布式事务框架 seata
FastDFS 一文读懂