如何通过Roslyn编译时AOP框架优化.NET项目代码复用?

2026-05-25 14:073阅读0评论SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1736个文字,预计阅读时间需要7分钟。

如何通过Roslyn编译时AOP框架优化.NET项目代码复用?

通过利用基于Roslyn的编译时AOP框架,可以有效地解决.NET项目的代码重复问题。该框架允许在项目编译时自动插入指定的代码,从而避免在运行时引入的性能损耗。

代码优化方式:

1.集中管理重复代码:将重复的代码片段集中管理,通过AOP框架在编译时自动插入到需要的位置。

2.提高代码可读性:减少重复代码,使代码结构更加清晰,易于理解和维护。

3.降低维护成本:集中管理重复代码,减少因代码重复带来的维护难度。

团队日常协作:

1.代码审查:定期进行代码审查,确保AOP框架的使用符合项目规范,避免引入不必要的性能损耗。

2.文档共享:建立完善的文档,记录AOP框架的使用方法和注意事项,方便团队成员学习和使用。

3.持续集成:将AOP框架集成到持续集成流程中,确保代码质量和稳定性。

介绍如何通过使用基于Roslyn的编译时AOP框架来解决.NET项目的代码复用问题。 可以在项目编译时自动插入指定代码,从而避免在运行时带来的性能消耗。 理想的代码优化方式

团队日常协作中,自然而然的会出现很多重复代码,根据这些代码的种类,之前可能会以以下方式处理

方式 描述 应用时可能产生的问题 硬编码 多数新手,或逐渐腐坏的项目会这么干,会直接复制之前实现的代码 带来的问题显而易见的多,例如架构会逐渐随时间被侵蚀,例外越来越多 提取函数 提取成为函数,然后复用 提取函数,然后复用,会比直接硬编码好些,但是仍然存在大量因“例外”而导致增加参数、增加函数重载的情况 模板生成器 CodeSmith/T4等 因为是独立进程,所以对于读取用户代码或项目,实现难度较高,且需要现有用户项目先生成成功,再进行生成 ,或者是完全基于新项目 代码片段 VS自带的代码片段功能 无法对复杂的环境或条件做出响应 AOP框架 面向切面编程,可以解决很多于用户代码前后增加操作的事情 但是大多AOP框架都是基于透明代理形式实现的,对于相互调用较多的代码,但形成性能压力,而且因为要符合透明代理的规则,所以要提供相应的子类或接口。 基于Rosyln的编译时插入代码

但以上这几种,AOP算是最理想的方式,但是感觉上还可以有更好的解决方案。

直到读到了这篇文章 Introducing C# Source Generators,文中提供了一种新的解决方案,即通过RoslynSource Generator在编译时直接读取当前项目中的语法树,处理并生成的新代码,然后在编译时也使用这些新代码。

那么如果可以读取现有代码的语法树,通过读取代码中的标记,那么在代码生成过程中是否就能直接生成。
实现如下效果:
项目中的源代码 Program.cs

internal class Program { [Log] private static int Add( int a, int b ) { return a + b; } }

自动根据 LogAttribute 自动编译成的代码 Program.g.cs

internal class Program { [Log] private static int Add( int a, int b ) { Console.WriteLine("Program.Add(int, int) 开始运行."); int result; result = a + b; Console.WriteLine("Program.Add(int, int) 结束运行."); return result; } }

当然LogAttribute中需要去实现插入代码。
然后项目自动使用新生成的Program.g.cs进行编译。这样就实现了基于编译时的AOP。

即实现以下流程

使用Metalama实现以上流程

经过寻找,发现其实已经有框架可以实现我上面说的流程了,也就是在编译时实现代码的插入。
www.postsharp.net/metalama 。

下面作一个简单示例

  1. 创建一个.NET6.0的控制台应用,我这里命名为LogDemo,
    其中的入口文件Program.cs

namespace LogDemo { public class Program { public static void Main(string[] args) { var r = Add(1, 2); Console.WriteLine(r); } // 这里写一个简单的方法,一会对这个方法进行代码的插入 private static int Add(int a, int b) { var result = a + b; Console.WriteLine("Add" + result); return result; } } }

  1. 在项目中使用Metalama

通过引用包 www.nuget.org/packages/Metalama.Framework, 注意Metalama当前是Preview版本,如果通过可视化Nuget管理器引入,需要注意勾选包含预发行版

dotnet add package Metalama.Framework --version 0.5.7-preview

  1. 编写一个AOP的Attribute

在项目中引入 Metalama.Framework后无需多余配置或代码,直接编写一个AOP的Attribute

using Metalama.Framework.Aspects; namespace LogDemo { public class Program { public static void Main(string[] args) { var r = Add(1, 2); Console.WriteLine(r); } // 在这个方法中使用了下面的Attribute [LogAttribute] private static int Add(int a, int b) { var result = a + b; Console.WriteLine("Add" + result); return result; } } // 这里是增加的 Attribute public class LogAttribute : OverrideMethodAspect { public override dynamic? OverrideMethod() { Console.WriteLine(meta.Target.Method.ToDisplayString() + " 开始运行."); var result = meta.Proceed(); Console.WriteLine(meta.Target.Method.ToDisplayString() + " 结束运行."); return result; } } }

  1. 执行结果如下

Program.Add(int, int) 开始运行. Add3 Program.Add(int, int) 结束运行. 3

  1. 生成的程序集进行反编译,得到的代码如下:

using Metalama.Framework.Aspects; namespace LogDemo { public class Program { public static void Main(string[] args) { var r = Add(1, 2); Console.WriteLine(r); } // 在这个方法中使用了下面的Attribute [LogAttribute] private static int Add(int a, int b) { Console.WriteLine("Program.Add(int, int) 开始运行."); int result_1; var result = a + b; Console.WriteLine("Add" + result); result_1 = result; Console.WriteLine("Program.Add(int, int) 结束运行."); return result_1; } } #pragma warning disable CS0067 // 这里是增加的 Attribute public class LogAttribute : OverrideMethodAspect { public override dynamic? OverrideMethod() => throw new System.NotSupportedException("Compile-time-only code cannot be called at run-time."); } #pragma warning restore CS0067 } 总结

这样就完全实现了我之前想要的效果,当然使用Metalama还可以实现很多能极大地提高生产力的功能,它不仅可以对方法进行改写,也可以对属性、字段、事件、甚至是类、命名空间进行一些操作 。

引用

Introducing C# Source Generators:devblogs.microsoft.com/dotnet/introducing-c-source-generators/
Metalama官网:www.postsharp.net/metalama

如何通过Roslyn编译时AOP框架优化.NET项目代码复用?

供大家学习参考,转文章随意--重典

本文共计1736个文字,预计阅读时间需要7分钟。

如何通过Roslyn编译时AOP框架优化.NET项目代码复用?

通过利用基于Roslyn的编译时AOP框架,可以有效地解决.NET项目的代码重复问题。该框架允许在项目编译时自动插入指定的代码,从而避免在运行时引入的性能损耗。

代码优化方式:

1.集中管理重复代码:将重复的代码片段集中管理,通过AOP框架在编译时自动插入到需要的位置。

2.提高代码可读性:减少重复代码,使代码结构更加清晰,易于理解和维护。

3.降低维护成本:集中管理重复代码,减少因代码重复带来的维护难度。

团队日常协作:

1.代码审查:定期进行代码审查,确保AOP框架的使用符合项目规范,避免引入不必要的性能损耗。

2.文档共享:建立完善的文档,记录AOP框架的使用方法和注意事项,方便团队成员学习和使用。

3.持续集成:将AOP框架集成到持续集成流程中,确保代码质量和稳定性。

介绍如何通过使用基于Roslyn的编译时AOP框架来解决.NET项目的代码复用问题。 可以在项目编译时自动插入指定代码,从而避免在运行时带来的性能消耗。 理想的代码优化方式

团队日常协作中,自然而然的会出现很多重复代码,根据这些代码的种类,之前可能会以以下方式处理

方式 描述 应用时可能产生的问题 硬编码 多数新手,或逐渐腐坏的项目会这么干,会直接复制之前实现的代码 带来的问题显而易见的多,例如架构会逐渐随时间被侵蚀,例外越来越多 提取函数 提取成为函数,然后复用 提取函数,然后复用,会比直接硬编码好些,但是仍然存在大量因“例外”而导致增加参数、增加函数重载的情况 模板生成器 CodeSmith/T4等 因为是独立进程,所以对于读取用户代码或项目,实现难度较高,且需要现有用户项目先生成成功,再进行生成 ,或者是完全基于新项目 代码片段 VS自带的代码片段功能 无法对复杂的环境或条件做出响应 AOP框架 面向切面编程,可以解决很多于用户代码前后增加操作的事情 但是大多AOP框架都是基于透明代理形式实现的,对于相互调用较多的代码,但形成性能压力,而且因为要符合透明代理的规则,所以要提供相应的子类或接口。 基于Rosyln的编译时插入代码

但以上这几种,AOP算是最理想的方式,但是感觉上还可以有更好的解决方案。

直到读到了这篇文章 Introducing C# Source Generators,文中提供了一种新的解决方案,即通过RoslynSource Generator在编译时直接读取当前项目中的语法树,处理并生成的新代码,然后在编译时也使用这些新代码。

那么如果可以读取现有代码的语法树,通过读取代码中的标记,那么在代码生成过程中是否就能直接生成。
实现如下效果:
项目中的源代码 Program.cs

internal class Program { [Log] private static int Add( int a, int b ) { return a + b; } }

自动根据 LogAttribute 自动编译成的代码 Program.g.cs

internal class Program { [Log] private static int Add( int a, int b ) { Console.WriteLine("Program.Add(int, int) 开始运行."); int result; result = a + b; Console.WriteLine("Program.Add(int, int) 结束运行."); return result; } }

当然LogAttribute中需要去实现插入代码。
然后项目自动使用新生成的Program.g.cs进行编译。这样就实现了基于编译时的AOP。

即实现以下流程

使用Metalama实现以上流程

经过寻找,发现其实已经有框架可以实现我上面说的流程了,也就是在编译时实现代码的插入。
www.postsharp.net/metalama 。

下面作一个简单示例

  1. 创建一个.NET6.0的控制台应用,我这里命名为LogDemo,
    其中的入口文件Program.cs

namespace LogDemo { public class Program { public static void Main(string[] args) { var r = Add(1, 2); Console.WriteLine(r); } // 这里写一个简单的方法,一会对这个方法进行代码的插入 private static int Add(int a, int b) { var result = a + b; Console.WriteLine("Add" + result); return result; } } }

  1. 在项目中使用Metalama

通过引用包 www.nuget.org/packages/Metalama.Framework, 注意Metalama当前是Preview版本,如果通过可视化Nuget管理器引入,需要注意勾选包含预发行版

dotnet add package Metalama.Framework --version 0.5.7-preview

  1. 编写一个AOP的Attribute

在项目中引入 Metalama.Framework后无需多余配置或代码,直接编写一个AOP的Attribute

using Metalama.Framework.Aspects; namespace LogDemo { public class Program { public static void Main(string[] args) { var r = Add(1, 2); Console.WriteLine(r); } // 在这个方法中使用了下面的Attribute [LogAttribute] private static int Add(int a, int b) { var result = a + b; Console.WriteLine("Add" + result); return result; } } // 这里是增加的 Attribute public class LogAttribute : OverrideMethodAspect { public override dynamic? OverrideMethod() { Console.WriteLine(meta.Target.Method.ToDisplayString() + " 开始运行."); var result = meta.Proceed(); Console.WriteLine(meta.Target.Method.ToDisplayString() + " 结束运行."); return result; } } }

  1. 执行结果如下

Program.Add(int, int) 开始运行. Add3 Program.Add(int, int) 结束运行. 3

  1. 生成的程序集进行反编译,得到的代码如下:

using Metalama.Framework.Aspects; namespace LogDemo { public class Program { public static void Main(string[] args) { var r = Add(1, 2); Console.WriteLine(r); } // 在这个方法中使用了下面的Attribute [LogAttribute] private static int Add(int a, int b) { Console.WriteLine("Program.Add(int, int) 开始运行."); int result_1; var result = a + b; Console.WriteLine("Add" + result); result_1 = result; Console.WriteLine("Program.Add(int, int) 结束运行."); return result_1; } } #pragma warning disable CS0067 // 这里是增加的 Attribute public class LogAttribute : OverrideMethodAspect { public override dynamic? OverrideMethod() => throw new System.NotSupportedException("Compile-time-only code cannot be called at run-time."); } #pragma warning restore CS0067 } 总结

这样就完全实现了我之前想要的效果,当然使用Metalama还可以实现很多能极大地提高生产力的功能,它不仅可以对方法进行改写,也可以对属性、字段、事件、甚至是类、命名空间进行一些操作 。

引用

Introducing C# Source Generators:devblogs.microsoft.com/dotnet/introducing-c-source-generators/
Metalama官网:www.postsharp.net/metalama

如何通过Roslyn编译时AOP框架优化.NET项目代码复用?

供大家学习参考,转文章随意--重典