当前位置:首页 > C# > 正文

打造高质量C#代码(使用Roslyn分析器实现自定义代码规则)

在现代C#开发中,保持代码的一致性、可读性和安全性至关重要。借助Roslyn分析器,开发者可以轻松创建自定义代码规则,在编译时自动检测不符合规范的代码,并提供修复建议。本文将手把手教你从零开始构建一个简单的Roslyn分析器,即使你是初学者也能轻松上手。

打造高质量C#代码(使用Roslyn分析器实现自定义代码规则) Roslyn分析器  C#代码分析 自定义代码规则 .NET静态分析 第1张

什么是Roslyn分析器?

Roslyn 是 .NET 编译器平台,它不仅负责编译 C# 和 VB.NET 代码,还提供了丰富的 API,允许开发者对源代码进行分析和修改。Roslyn分析器就是基于这些 API 构建的工具,用于在开发过程中实时检查代码质量。

通过编写自定义分析器,你可以:

  • 禁止使用某些不安全的API
  • 强制命名规范(如接口必须以“I”开头)
  • 检测潜在的性能问题或内存泄漏
  • 统一团队编码风格

准备工作

你需要安装以下工具:

  • Visual Studio 2022(或更高版本)
  • .NET SDK 6.0 或更高版本
  • NuGet 包:Microsoft.CodeAnalysis.CSharp.Workspaces

第一步:创建分析器项目

打开 Visual Studio,选择“创建新项目”,搜索“Analyzer”,选择“Analyzer with Code Fix (.NET Standard)”模板(注意:该模板可能需要安装“.NET Compiler Platform SDK”扩展)。

项目创建后,你会看到三个关键文件:

  • XXXAnalyzer.cs:定义分析逻辑
  • XXXCodeFixProvider.cs:提供自动修复方案
  • XXXDiagnosticAnalyzer.cs:注册诊断规则

第二步:编写自定义规则

我们来实现一个简单但实用的规则:禁止在类中使用 public 字段,必须使用属性封装

打开 XXXAnalyzer.cs 文件,替换为以下代码:

using Microsoft.CodeAnalysis;using Microsoft.CodeAnalysis.CSharp;using Microsoft.CodeAnalysis.CSharp.Syntax;using Microsoft.CodeAnalysis.Diagnostics;using System.Collections.Immutable;namespace MyCustomAnalyzer{    [DiagnosticAnalyzer(LanguageNames.CSharp)]    public class NoPublicFieldsAnalyzer : DiagnosticAnalyzer    {        public const string DiagnosticId = "NoPublicFields";        private static readonly LocalizableString Title = "Avoid public fields";        private static readonly LocalizableString MessageFormat =             "Public field '{0}' should be converted to a property.";        private static readonly LocalizableString Description =             "Public fields break encapsulation. Use properties instead.";        private const string Category = "Design";        private static readonly DiagnosticDescriptor Rule = new(            DiagnosticId,            Title,            MessageFormat,            Category,            DiagnosticSeverity.Warning,            isEnabledByDefault: true,            description: Description);        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>             ImmutableArray.Create(Rule);        public override void Initialize(AnalysisContext context)        {            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);            context.EnableConcurrentExecution();            context.RegisterSyntaxNodeAction(AnalyzeFieldDeclaration, SyntaxKind.FieldDeclaration);        }        private static void AnalyzeFieldDeclaration(SyntaxNodeAnalysisContext context)        {            var fieldDeclaration = (FieldDeclarationSyntax)context.Node;            // 检查字段是否包含 public 修饰符            if (fieldDeclaration.Modifiers.Any(SyntaxKind.PublicKeyword))            {                foreach (var variable in fieldDeclaration.Declaration.Variables)                {                    var diagnostic = Diagnostic.Create(                        Rule,                         variable.GetLocation(),                         variable.Identifier.Text);                    context.ReportDiagnostic(diagnostic);                }            }        }    }}

第三步:添加代码修复(Code Fix)

为了让开发者能一键修复问题,我们还需实现 CodeFixProvider。以下是简化版的修复逻辑(仅处理单个字段):

using Microsoft.CodeAnalysis;using Microsoft.CodeAnalysis.CodeActions;using Microsoft.CodeAnalysis.CodeFixes;using Microsoft.CodeAnalysis.CSharp;using Microsoft.CodeAnalysis.CSharp.Syntax;using System.Collections.Immutable;using System.Composition;using System.Linq;using System.Threading;using System.Threading.Tasks;namespace MyCustomAnalyzer{    [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(NoPublicFieldsCodeFixProvider))]    [Shared]    public class NoPublicFieldsCodeFixProvider : CodeFixProvider    {        public sealed override ImmutableArray<string> FixableDiagnosticIds =>            ImmutableArray.Create(NoPublicFieldsAnalyzer.DiagnosticId);        public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;        public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)        {            var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);            var diagnostic = context.Diagnostics.First();            var diagnosticSpan = diagnostic.Location.SourceSpan;            var fieldIdentifier = root.FindToken(diagnosticSpan.Start).Parent                .AncestorsAndSelf().OfType<VariableDeclaratorSyntax>().First();            context.RegisterCodeFix(                CodeAction.Create(                    title: "Convert to auto-property",                    createChangedDocument: c => ConvertToProperty(context.Document, fieldIdentifier, c),                    equivalenceKey: "ConvertToProperty"),                diagnostic);        }        private async Task<Document> ConvertToProperty(            Document document,             VariableDeclaratorSyntax field,             CancellationToken cancellationToken)        {            var root = await document.GetSyntaxRootAsync(cancellationToken);            var fieldDecl = field.AncestorsOfType<FieldDeclarationSyntax>().First();            // 创建属性语法            var property = SyntaxFactory.PropertyDeclaration(                fieldDecl.Declaration.Type,                field.Identifier)                .WithModifiers(fieldDecl.Modifiers)                .WithAccessorList(SyntaxFactory.AccessorList(                    SyntaxFactory.List(new[] {                        SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)                            .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)),                        SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)                            .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))                    })));            var newRoot = root.ReplaceNode(fieldDecl, property);            return document.WithSyntaxRoot(newRoot);        }    }}

第四步:测试你的分析器

按 F5 运行项目,Visual Studio 会启动一个新的实验实例。在新窗口中创建一个控制台项目,输入以下代码:

public class Person{    public string Name; // ← 这里会出现警告!}

你会看到 Name 字段下方出现波浪线,并提示“Public field 'Name' should be converted to a property.”。点击灯泡图标,即可应用自动修复,将其转换为属性。

部署与共享

完成开发后,你可以将分析器打包为 NuGet 包,供团队其他成员使用。只需在项目文件中添加:

<PackageReference Include="MyCustomAnalyzer" Version="1.0.0" PrivateAssets="all" />

这样,所有引用该项目的代码都会自动应用你的C#代码分析规则。

结语

通过 Roslyn 分析器,你可以将团队的最佳实践固化为代码规则,大幅提升代码质量和维护效率。无论是实施安全策略、性能优化还是风格统一,.NET静态分析都是不可或缺的利器。现在就动手创建属于你自己的分析器吧!

关键词回顾:Roslyn分析器、C#代码分析、自定义代码规则、.NET静态分析