目录
Metalama中的Fabric可以做什么使用Fabric为指定的方法添加Aspect使用Fabric添加代码分析使用TypeFabric为类型动态添加方法引用Metalama中的Fabric可以做什么
Metalama是一个基于微软编译器Roslyn的元编程的库,可以解决我在开发中遇到的重复代码的问题。但是其实Metalama不止可以提供编译时的代码转换,更可以提供自定义代码分析、与IDE结合的自定义代码修复与代码重构功能 等功能。
经过面对文档的学习,发现Metalama可以做到很多非常神奇的事情。
Fabric通过修改项目、命名空间、类型来达到一些效果,这引起修改包括:添加Aspect或添加代码分析
使用Fabric为指定的方法添加Aspect
前文中我们写过一个简单的Aspect:
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; }}当我们使用它时,我们要在对应的方法上添加这个Attribute:
[Log]private static int Add(int a, int b) //... ...
那么当我们有一个Aspect要在项目中大量使用时,在每个方法上添加这个Aspect当然是一种方法,但是这种方法有2个缺点:
[Log]对于原代码的入侵性太强此时我们就可以使用Fabric为所有符合要求的方法添加指定的Aspect:
internal class Fabric : ProjectFabric{ // 这个是重写项目的Fabric中修改项目的方法 public override void AmendProject(IProjectAmender amender) { // 添加 LogAttribute 到符合规则的方法上 // 为名为 Add 且 private 的方法添加 LogAttribute amender.WithTargetMembers(c => c.Types.SelectMany(t => t.Methods) .Where(t => t.Name == "Add" && t.Accessibility == Metalama.Framework.Code.Accessibility.Private) ).AddAspect(t => new LogAttribute()); }}
这样就可以在不入侵现有代码的情况下为指定的方法添加Aspect。
使用Fabric添加代码分析
上文中我们提到,我们可以通过Aspect为代码添加代码分析,当我们要将一个包含(且仅包含)代码分析的Aspect应用于一批代码时,当然我们可以按本文示例1中的方法,直接使用Fabric将包含代码分析的Aspect应用于指定代码。
但还有另外一种方法,我们可以直接在Fabric中定义应用于指定代码的代码分析。
下面示例,我们验证所有类中的私有字段必须符合 _camelCase,并且使用一个NamespaceFabric来实现:
namespace FabricCamelCaseDemo;class Fabric : NamespaceFabric{ private static readonly DiagnosticDefinition<string> _warning = new( "DEMO04", Severity.Warning, "'{0}'必须使用驼峰命名法并以'_'开头"); // 这个是命名空间的Fabric中修改命名空间规则 的方法 public override void AmendNamespace(INamespaceAmender amender) { // 取所有非static 的private的字段,并添加代码分析 amender.WithTargetMembers(c => c.AllTypes.SelectMany(t=>t.Fields) .Where(t => t.Accessibility == Accessibility.Private && !t.IsStatic ) ) //preview 0.5.8之前为 RegisterFinalValidator .Validate(this.FinalValidator); } private void FinalValidator(in DeclarationValidationContext context) { var fullname = context.Declaration.ToDisplayString(); var fieldName = fullname.Split('.').LastOrDefault(); if (fieldName!=null && (!fieldName.StartsWith("_") || !char.IsLower(fieldName[1]))) { context.Diagnostics.Report(_warning.WithArguments(fieldName)); } }}result += x3; return 3; }// 以此类推... 下面省去若干方法}
那么我们可以用Metalama如此实现
using System.Reflection.Emit;using Metalama.Framework.Aspects;using Metalama.Framework.Fabrics; public class AddUtils{ private class Fabric : TypeFabric { // 实现的方法体 [Template] public int MethodTemplate() { var num = (int) meta.Tags["nums"]!; var result = 0; foreach (var targetParameter in meta.Target.Parameters) { result += targetParameter.Value; } return num; } public override void AmendType(ITypeAmender amender) for (var i = 2; i < 15; i++) // 生成一个方法 var methodBuilder = amender.Advices.IntroduceMethod( amender.Type, nameof(this.MethodTemplate), tags: new TagDictionary { ["nums"] = i }); // 方法名 methodBuilder.Name = "Add" + i; // 添加参数 for (int parameterIndex = 1; parameterIndex <= i; parameterIndex++) { methodBuilder.AddParameter($"x{parameterIndex}", typeof(int)); } }}引用
本章源代码:https://github.com/chsword/metalama-demo
Metalama官方文档: https://doc.metalama.net/
Metalama Nuget包: https://www.nuget.org/packages/Metalama.Framework/0.5.11-preview








