使用Metalama為VisualStudio "重構"\ LiveTemplate 菜單中動態添加功能 ...
使用基於Roslyn的編譯時AOP框架來解決.NET項目的代碼復用問題
Metalama簡介1. 不止是一個.NET跨平臺的編譯時AOP框架
Metalama簡介2.利用Aspect在編譯時進行消除重覆代碼
Metalama簡介3.自定義.NET項目中的代碼分析
Metalama簡介4.使用Fabric操作項目或命名空間
在Visual Studio
中有提供快速操作
(小燈泡)功能
以及重構
(小刷子)功能
使用它們可以快速進行一些快捷的針對代碼的操作,如提取介面、添加實現、自動屬性、快速重構、刪除引用等。
除官方提供的功能外我們還可以使用很多第三方插件來支持更多地功能。
Metalama
可以通過編寫代碼的形式,讓我們為指定的代碼添加重構
或快速操作
的功能。
自定義一個ToString的實時模板
很多圖形編程或游戲編程中,我們會用到各種自定義類如矩陣、複數、坐標系等,為了方便Debug,我們通常會為這些類增加一個ToString
方法的重寫。
例如
internal class Program
{
private static void Main()
{
var point = new Point { X = 5, Y = 3};
Console.WriteLine($"point = {point}");
}
}
internal class Point
{
public double X;
public double Y;
public override string ToString()
{
return $"({X}, {Y})";
}
}
如果我們不想手寫這個ToString
方法,而想讓VS直接為它生成。
則我們可以使用Metalama
定義一個LiveTemplate,這樣就可以在VS的工具中使用它了。
[LiveTemplate] // 表示當前Aspect為VS添加LiveTempate
internal class ToStringAttribute : TypeAspect
{
[Introduce(WhenExists = OverrideStrategy.Override, Name = "ToString")]
public string IntroducedToString()
{
var stringBuilder = new InterpolatedStringBuilder();
stringBuilder.AddText("{ ");
stringBuilder.AddText(meta.Target.Type.Name);
stringBuilder.AddText(" ");
var fields = meta.Target.Type.FieldsAndProperties.Where(f => !f.IsStatic).ToList();
var i = meta.CompileTime(0);
foreach (var field in fields)
{
if (i > 0)
{
stringBuilder.AddText(", ");
}
stringBuilder.AddText(field.Name);
stringBuilder.AddText("=");
stringBuilder.AddExpression(field.Invokers.Final.GetValue(meta.This));
i++;
}
stringBuilder.AddText(" }");
return stringBuilder.ToValue();
}
}
這樣在,下列代碼中使用重構
功能,即可看到Metalama
給的實時代碼提示。
internal class Point
{
public double X;
public double Y;
}
使用Metalama添加一個VisualStudio的快速操作
我們最終的目的如下,對於標註了[Tostring]
的類,增加一個將[ToString]切換至手動實現
的功能點擊後可實現自動添加一個ToString:
這需要我們在Aspect``ToStringAttribute
中添加一個提示:
public class ToStringAttribute : TypeAspect
{
public override void BuildAspect(IAspectBuilder<INamedType> builder)
{
base.BuildAspect(builder);
// 添加一個建議手動實現的重構提示
if (builder.AspectInstance.Predecessors[0].Instance is IAttribute attribute)
{
builder.Diagnostics.Suggest(
new CodeFix("將 [ToString] 切換至手動實現", codeFixBuilder => this.ImplementManually(codeFixBuilder, builder.Target)),
builder.Target);
}
}
/// <summary>
/// 當點擊手動實現時的操作
/// </summary>
private async Task ImplementManually(ICodeActionBuilder builder, INamedType targetType)
{
await builder.ApplyAspectAsync(targetType, this);
await builder.RemoveAttributesAsync(targetType, typeof(ToStringAttribute));
}
[Introduce(WhenExists = OverrideStrategy.Override, Name = "ToString")]
public string IntroducedToString()
{
// 獲取非靜態欄位
var fields = meta.Target.Type.FieldsAndProperties.Where(f => !f.IsStatic).ToList();
// 構建一個$""字元串
var stringBuilder = new InterpolatedStringBuilder();
stringBuilder.AddText("{ ");
stringBuilder.AddText(meta.Target.Type.Name);
stringBuilder.AddText(" ");
var i = meta.CompileTime(0);
foreach (var field in fields)
{
if (i > 0)
{
stringBuilder.AddText(", ");
}
stringBuilder.AddText(field.Name);
stringBuilder.AddText("=");
stringBuilder.AddExpression(field.Invokers.Final.GetValue(meta.This));
i++;
}
stringBuilder.AddText(" }");
return stringBuilder.ToValue();
}
}
這樣就可以對於已經添加了[ToString]
的類實現以上功能
[ToString]
internal class Point // 在此處觸發 Ctrl+.或右鍵
{
public double X;
public double Y;
}
引用
本章源代碼:https://github.com/chsword/metalama-demo
Metalama官方文檔: https://doc.metalama.net/
Metalama Nuget包: https://www.nuget.org/packages/Metalama.Framework/0.5.13-preview