本系列其它文章

使用基于Roslyn的编译时AOP框架来解决.NET项目的代码复用问题

Metalama简介1. 不止是一个.NET跨平台的编译时AOP框架

Metalama简介2.利用Aspect在编译时进行消除重复代码

代码分析

这里所说的代码分析,是可以通过一些自定义的方法,在使用不符合条件的代码时产生错误或警告。

如果配合CI并在每次持续集成时,都向团队分发警告和错误。团队也在开发时遵守谁产生的警告,谁解决的团队约定,那么团队将不断减少技术债务,这样可以避免架构持续性的腐坏。

当然.NET自身及一些三方工具如Resharper已经提供了很多的代码分析功能,包括但不限于命名、代码调用等。但是有时想要更近一步地为团队增加更加定制化地代码分析,却没有对应的办法。

Metalama中也提供了代码分析功能。

下面我们以几个示例来演示Metalama中如何使用代码分析。

通用自定义代码分析示例Logger

(源码见最后)

以我们最初的Log示例为例,假设我们当前要引入ILogger来记录日志,来替换当前的Console.WriteLine

interface ILogger
{
void Info(string message);
}
public class ConsoleLogger : ILogger
{
public void Info(string message)
{
Console.WriteLine(message);
}
}

那么Program也要做出修改。

class Program
{
ILogger _logger = new ConsoleLogger();
public static void Main(string[] args)
{
var r = new Program().Add(1, 2);
Console.WriteLine(r);
}
// 在这个方法中使用了下面的Attribute
[LogAttribute]
private int Add(int a, int b)
{
var result = a + b;
return result;
}
}

LogAttribute也要进行修改。

public class LogAttribute : OverrideMethodAspect
{
public override dynamic? OverrideMethod()
{
meta.This._logger.Info(meta.Target.Method.ToDisplayString() + " 开始运行.");
var result = meta.Proceed();
meta.This._logger.Info(meta.Target.Method.ToDisplayString() + " 结束运行.");
return result;
}
}

接下来我们可以为LogAttribute添加代码分析,要求LogAttribute的方法的所在的类上,必须有_logger且类型必须为ILogger

public class LogAttribute : OverrideMethodAspect
{
static DiagnosticDefinition<(INamedType DeclaringType, IMethod Method)> _loggerFieldNotFoundError = new(
"DEMO01",
Severity.Error,
"类型'{0}'必须包含ILogger类型的字段 '_logger'因为使用了[Log]Aspect在'{1}'上."); // Entry point of the aspect.
public override void BuildAspect(IAspectBuilder<IMethod> builder)
{
// 此处必须调用,否则目标方法将不会被覆盖,因为这里Override与BuildAspect共同使用了
base.BuildAspect(builder); // 验证字段
var loggerField = builder.Target.DeclaringType.Fields.OfName("_logger").SingleOrDefault();
if (loggerField == null || !loggerField.Type.Is(typeof(ILogger)) || loggerField.IsStatic)
{
// 报告异常
builder.Diagnostics.Report(_loggerFieldNotFoundError.WithArguments((builder.Target.DeclaringType, builder.Target)), builder.Target.DeclaringType);
// 不执行Aspect
builder.SkipAspect();
return;
}
}
public override dynamic? OverrideMethod()
{
meta.This._logger.Info(meta.Target.Method.ToDisplayString() + " 开始运行.");
var result = meta.Proceed();
meta.This._logger.Info(meta.Target.Method.ToDisplayString() + " 结束运行.");
return result;
}
}

这样当我们代码中有错误,将会触发提示。

如果没有_logger_logger类型不对或为static时则有以下提示

同时也可以在Aspect中定义Eligibility,在编译时检查Aspect作用的目标是否符合要求。

下面的代码加到LogAttribute就会检查Add方法是否为非static

    public override void BuildEligibility( IEligibilityBuilder<IMethod> builder )
{
base.BuildEligibility( builder );
builder.MustBeNonStatic();
}

此时若将Add修改为static则会提示

error LAMA0037: The aspect 'Log' cannot be applied to 'Program.Add(int, int)' because 'Program.Add(int, int)' must be non-static.

自定义一个代码分析:要求当前方法只能在符合规则的命名空间中使用

当一个团队存在多个项目时,我们会约定这里的某些项目的命名必须符合某一规则。

例如,当我们构建一个微服务项目时,我们会要求所有的数据库调用,都发生在指定的命名空间中。

此时我们可以使用一个自定义的Aspect构造一个方法的代码验证规则。

下面这个示例是要求调用函数的命名空间必须符合以.Tests结尾的规则,否则给出警告

using Metalama.Framework.Aspects;
using Metalama.Framework.Code;
using Metalama.Framework.Diagnostics;
using Metalama.Framework.Validation;
namespace LogWithWarning
{
class ForTestOnlyAttribute : Aspect, IAspect<IDeclaration>
{
private static readonly DiagnosticDefinition<IDeclaration> _warning = new(
"DEMO02",
Severity.Warning,
"'{0}' 只能在一个以 '.Tests'结尾的命名空间中使用"); public void BuildAspect(IAspectBuilder<IDeclaration> builder)
{
builder.WithTarget().RegisterReferenceValidator(this.ValidateReference, ReferenceKinds.All);
} private void ValidateReference(in ReferenceValidationContext context)
{
if (!context.ReferencingType.Namespace.FullName.EndsWith(".Tests"))
{
context.Diagnostics.Report(_warning.WithArguments(context.ReferencedDeclaration));
}
}
}
}

此时当我们在非.Tests结尾的命名空间中调用时。

则会发生如下提示。

引用

Metalama简介3.自定义.NET项目中的代码分析的更多相关文章

  1. Roslyn 入门:使用 Roslyn 静态分析现有项目中的代码

    Roslyn 是微软为 C# 设计的一套分析器,它具有很强的扩展性.以至于我们只需要编写很少量的代码便能够分析我们的项目文件. 作为 Roslyn 入门篇文章,你将可以通过本文学习如何开始编写一个 R ...

  2. 使用SonarCloud对.NET Core项目进行静态代码分析

    本文将介绍如何使用SonarCloud进行.NET Core项目的静态代码分析.SonarCloud是SonarQube提供的基于云的版本,特别针对于开源项目是免费的. 首先,在sonarcloud. ...

  3. linux内核中链表代码分析---list.h头文件分析(一)【转】

    转自:http://blog.chinaunix.net/uid-30254565-id-5637596.html linux内核中链表代码分析---list.h头文件分析(一) 16年2月27日17 ...

  4. linux内核中链表代码分析---list.h头文件分析(二)【转】

    转自:http://blog.chinaunix.net/uid-30254565-id-5637598.html linux内核中链表代码分析---list.h头文件分析(二) 16年2月28日16 ...

  5. 使用eslint将项目中的代码修改统一的缩进

    背景 继承了组里师兄师姐写的项目的前端代码,但是是两个人写的,有两格缩进的,有四格缩进的,有字符串外用单引号的,有用双引号的. 于是搜索了一下,可以用eslint强制转化. eslint在github ...

  6. [git]安装git-pylint-commit-hook提高python项目中的代码质量

    什么是'git-pylint-commit-hook' 我在工作中,团队为了保证代码和提高代码的质量,要求每个项目都要求安装git-pylint-commit-hook,它是个钩子,会在你提交代码到本 ...

  7. 吐槽一下项目中的代码坏味道:滥用java常量

    我们的项目中是否充斥着类似以下的代码呢?定义一个专门存放常量的java类(接口),非常多其它类依赖该常量类. public interface IConstant { int ZERO = 0; St ...

  8. Maven项目中pom文件分析

    pom英文全称: project object model 1.概述 pom.xml文件描述了maven项目的基本信息,比如groupId,artifactId,version等.也可以对maven项 ...

  9. 最新广商小助手 项目进展 OpenGL ES 3D在我项目中引用 代码太多只好选重要部分出来

    package com.example.home; import java.io.IOException; import java.io.InputStream; import javax.micro ...

随机推荐

  1. java-计算机

    计算机 硬件 装机:CPU 内存 主板 IO设备(input output) 冯诺依曼体系结构 CPU读取数据在运算器中运算传输到存储器,控制器控制输出结果. 软件

  2. spring——整合Mybatis

    一.Mybatis整合spring 步骤: 导入相关jar包 junit mybatis mysql数据库 spring-webmvc aop织入 mybatis-spring spring-jdbc ...

  3. RabbitMQ Go客户端教程3——发布/订阅

    本文翻译自RabbitMQ官网的Go语言客户端系列教程,本文首发于我的个人博客:liwenzhou.com,教程共分为六篇,本文是第三篇--发布/订阅. 这些教程涵盖了使用RabbitMQ创建消息传递 ...

  4. mycat的基本介绍 看这一篇就够了

    1.前置知识 1.分布式系统 ​ 分布式系统是指其组件分布在网络上,组件之间通过传递消息进行通信和动作协调的系统.它的核心理念是让多台服务器协同工作,完成单台服务器无法处理的任务,尤其是高并发或者大数 ...

  5. S120通讯报文应用类型及区别,PZD格式及意义 | 标准报文1/2/3/4/5/6/7/9/20/81/82/83,西门子报文

    一.报文功能图 1.1标准报文 适用于驱动器 SINAMICS S120 S150 参数手册 章节3.9 PROFIdrive 图3-36 功能图2415标准报文和过程数据1 SINAMICS S12 ...

  6. UNIX网络编程--学习日记

    今天在学习accept函数的时候,在执行服务器程序的时候,碰到了如下的出错信息: bind error: Address already in use 其原因在于服务器程序使用了13号的端口; 然而在 ...

  7. jvm-learning-运行时数据区-整体

    在jdk8之后之前的方法区有叫做元数据. 每个JVM只有一个Runtime实例,即为运行时环境,相当于内存结构种的运行时数据区 线程 线程是一个程序里的运行单元,JVM允许一个应用有多个线程并行的执行 ...

  8. 二、mycat15种分片规则

    一.分片枚举 通过在配置文件中配置可能的枚举 id,自己配置分片,本规则适用于特定的场景,比如有些业务需要按照省份或区县来做保存,而全国省份区县固定的,这类业务使用本条规则,配置如下: <tab ...

  9. Java 中,嵌套公共静态类与顶级类有什么不同?

    类的内部可以有多个嵌套公共静态类,但是一个 Java 源文件只能有一个顶级公 共类,并且顶级公共类的名称与源文件名称必须一致.

  10. Demo示例——Bundle打包和加载

    Unity游戏里面的场景.模型.图片等资源,是如何管理和加载的? 这就是本文要讲的资源管理方式--bundle打包和加载. 图片 Unity游戏资源管理有很多方式: (1)简单游戏比如demo,可以直 ...