关于Metalama使用Fabric操作项目或命名空间的问题

 更新时间:2022年04月24日 09:48:13   作者:重典  
Metalama是一个基于微软编译器Roslyn的元编程的库,可以解决我在开发中遇到的重复代码的问题,这篇文章主要介绍了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));
        }
    }
}

image

当然因为当前使用的是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操作各类时间段获取方法汇总

    ASP.NET操作各类时间段获取方法汇总

    这篇文章主要介绍了ASP.NET操作各类时间段获取方法,实例汇总了各类常见的时间段获取方法,需要的朋友可以参考下
    2014-11-11
  • Asp.Net Core利用文件监视进行快速测试开发详解

    Asp.Net Core利用文件监视进行快速测试开发详解

    这篇文章主要给大家介绍了关于Asp.Net Core利用文件监视进行快速测试开发的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-12-12
  • .NET Core中创建和使用NuGet包的示例代码

    .NET Core中创建和使用NuGet包的示例代码

    这篇文章主要介绍了.NET Core中创建和使用NuGet包的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-04-04
  • ASP.NET Core 数据保护(Data Protection)中篇

    ASP.NET Core 数据保护(Data Protection)中篇

    这篇文章主要为大家再一次介绍了ASP.NET Core 数据保护(Data Protection),具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-09-09
  • 使用 ServiceStack.Text 序列化 json的实现代码

    使用 ServiceStack.Text 序列化 json的实现代码

    今天发篇文章总结下自己使用 ServiceStack.Text 来序列化 json。它的速度比 Newtonsoft.Json 快很多,在测试时发现比 fastJson 还快些
    2013-06-06
  • ASP.NET MVC5网站开发之登录、验证和注销管理员篇1(六)

    ASP.NET MVC5网站开发之登录、验证和注销管理员篇1(六)

    这篇文章主要介绍了ASP.NET MVC5网站开发之管理员登录、验证和注销,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-08-08
  • ASP.NET使用正则表达式屏蔽垃圾信息

    ASP.NET使用正则表达式屏蔽垃圾信息

    找资料,看看如何实现使用正则表达式屏蔽垃圾信息,找来找去找出来的都不怎么好,有不详细的,有代码缺失的。最后还是找到了微软,先摘过来,大概思路已经有了,只需把下面微软给的样例代码修改一下即可,具体能不能行还不知道,先睡一觉,起来再慢慢研究。
    2008-09-09
  • ASP.NETCore6开启文件服务允许通过url访问附件的操作方法

    ASP.NETCore6开启文件服务允许通过url访问附件的操作方法

    最近在做一个工作台的文件上传下载功能,主要想实现上传图片之后,可以通过url直接访问,由于url直接访问文件不安全,所以需要手动开启文件服务,这篇文章主要介绍了ASP.NETCore6开启文件服务允许通过url访问附件,需要的朋友可以参考下
    2023-11-11
  • WPF使用VisualTreeHelper进行命中测试

    WPF使用VisualTreeHelper进行命中测试

    这篇文章介绍了WPF使用VisualTreeHelper进行命中测试的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-04-04
  • 异步 HttpContext.Current实现取值的方法(解决异步Application,Session,Cache...等失效的问题)

    异步 HttpContext.Current实现取值的方法(解决异步Application,Session,Cache.

    在一个项目中,为了系统执行效率更快,把一个经常用到的数据库表通过dataset放到Application中,发现在异步实现中每一次都会出现HttpContext.Current为null的异常,后来在网上查了好多资料,发现问这个问题的人多,回答的少
    2009-07-07

最新评论