理想的代碼優化方式
團隊日常協作中,自然而然的會出現很多重復代碼,根據這些代碼的種類,之前可能會以以下方式處理
| 方式 | 描述 | 應用時可能產生的問題 |
|---|---|---|
| 硬編碼 | 多數新手,或逐漸腐壞的專案會這么干,會直接復制之前實作的代碼 | 帶來的問題顯而易見的多,例如架構會逐漸隨時間被侵蝕,例外越來越多 |
| 提取函式 | 提取成為函式,然后復用 | 提取函式,然后復用,會比直接硬編碼好些,但是仍然存在大量因“例外”而導致增加引數、增加函式多載的情況 |
| 模板生成器 | CodeSmith/T4等 | 因為是獨立行程,所以對于讀取用戶代碼或專案,實作難度較高,且需要現有用戶專案先生成成功,再進行生成 ,或者是完全基于新專案 |
| 代碼片段 | VS自帶的代碼片段功能 | 無法對復雜的環境或條件做出回應 |
| AOP框架 | 面向切面編程,可以解決很多于用戶代碼前后增加操作的事情 | 但是大多AOP框架都是基于透明代理形式實作的,對于相互呼叫較多的代碼,但形成性能壓力,而且因為要符合透明代理的規則,所以要提供相應的子類或介面, |
基于Rosyln的編譯時插入代碼
但以上這幾種,AOP算是最理想的方式,但是感覺上還可以有更好的解決方案,
直到讀到了這篇文章 Introducing C# Source Generators,文中提供了一種新的解決方案,即通過Roslyn的Source 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實作以上流程
經過尋找,發現其實已經有框架可以實作我上面說的流程了,也就是在編譯時實作代碼的插入,
https://www.postsharp.net/metalama ,
下面作一個簡單示例
- 創建一個.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;
}
}
}
- 在專案中使用Metalama
通過參考包 https://www.nuget.org/packages/Metalama.Framework, 注意Metalama當前是Preview版本,如果通過可視化Nuget管理器引入,需要注意勾選包含預發行版
dotnet add package Metalama.Framework --version 0.5.7-preview
- 撰寫一個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;
}
}
}
- 執行結果如下
Program.Add(int, int) 開始運行.
Add3
Program.Add(int, int) 結束運行.
3
- 生成的程式集進行反編譯,得到的代碼如下:
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:https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/
Metalama官網:https://www.postsharp.net/metalama
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/456944.html
標籤:.NET Core
下一篇:C#多執行緒下的調優
