关于Metalama使用Fabric操作项目或命名空间的问题
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)); } } }
当然因为当前使用的是NamespaceFabric
所以该规则只应用于当前命名空间如,我们如果在另外一个命名空间中定义一个违反规则的字段的话,并不会有警告。
namespace FabricCamelCase; internal class OtherNamespace { int count = 0; int _total = 0; public int Add() { count++; _total++; return count + _total; } }
使用TypeFabric为类型动态添加方法
开始前伪造一个需求,假设我有一个类AddUtils
专门处理加法操作,它里面应该有从2个到15个参数的Add方法15个(当然我知道,可以使用params
等方法实现,所以这里是个伪需求)。
最终效果为
public class AddUtils { public int Add2(int x1, int x2) { var result = 0; result += x1; result += x2; return 2; } public int Add3(int x1, int x2, int x3) { var result = 0; result += x1; result += x2; 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
到此这篇关于Metalama使用Fabric操作项目或命名空间的文章就介绍到这了,更多相关Metalama操作项目或命名空间内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
ASP.NET Core 数据保护(Data Protection)中篇
这篇文章主要为大家再一次介绍了ASP.NET Core 数据保护(Data Protection),具有一定的参考价值,感兴趣的小伙伴们可以参考一下2016-09-09使用 ServiceStack.Text 序列化 json的实现代码
今天发篇文章总结下自己使用 ServiceStack.Text 来序列化 json。它的速度比 Newtonsoft.Json 快很多,在测试时发现比 fastJson 还快些2013-06-06ASP.NET MVC5网站开发之登录、验证和注销管理员篇1(六)
这篇文章主要介绍了ASP.NET MVC5网站开发之管理员登录、验证和注销,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下2016-08-08ASP.NETCore6开启文件服务允许通过url访问附件的操作方法
最近在做一个工作台的文件上传下载功能,主要想实现上传图片之后,可以通过url直接访问,由于url直接访问文件不安全,所以需要手动开启文件服务,这篇文章主要介绍了ASP.NETCore6开启文件服务允许通过url访问附件,需要的朋友可以参考下2023-11-11异步 HttpContext.Current实现取值的方法(解决异步Application,Session,Cache.
在一个项目中,为了系统执行效率更快,把一个经常用到的数据库表通过dataset放到Application中,发现在异步实现中每一次都会出现HttpContext.Current为null的异常,后来在网上查了好多资料,发现问这个问题的人多,回答的少2009-07-07
最新评论