.NET编码规范
第 5 篇:Roslyn 分析器与 StyleCop
------ 代码质量与风格双保险
前言
如果 .editorconfig 是交通规则标识,那么 Roslyn 分析器就是 24 小时执勤的交警 ------ 它在代码编写的瞬间就开始工作,实时检查你的代码是否合规。
本文重点讲解 .NET 内置的 Roslyn 分析器体系(CAxxxx / IDExxxx)、第三方 StyleCop.Analyzers 的配置、以及两者的协同使用。
一、Roslyn 分析器是什么?
Roslyn 是 .NET 的编译器平台,它不只是把 C# 编译成 IL,还对外开放了语法分析 API。分析器(Analyzer) 就是基于 Roslyn API 的插件,在编译时对代码进行静态分析。
你的代码 → Roslyn 解析 → 语法树 → 分析器检查 → 诊断报告
↓
实时反馈到 IDE
二、内置分析器:CAxxxx 与 IDExxxx
2.1 两大类规则
| 前缀 | 类别 | 关注点 | 示例 |
|---|---|---|---|
| CAxxxx | 代码质量分析 | 潜在 Bug、性能、安全性 | CA2007(缺少 ConfigureAwait) |
| IDExxxx | 代码风格分析 | 命名、格式、可读性 | IDE0004(可以移除不必要的类型转换) |
在visual studio中机场能看到CA和IDE的消息提醒
2.2 常用 CA 规则
csharp
// CA1001: 拥有可释放字段的类型应实现 IDisposable
public class ResourceHolder // ⚠️ 未实现 IDisposable
{
private FileStream _fileStream; // IDisposable 字段
}
// CA1062: 验证公共方法的参数
public void ValidateUser(User user) // ⚠️ 未验证 null
{
var name = user.Name.ToUpper();
}
// 正确:
public void ValidateUser(User user)
{
ArgumentNullException.ThrowIfNull(user);
var name = user.Name.ToUpper();
}
// CA1303: 不要将文本作为参数传递
Console.WriteLine("Welcome back, " + user.Name); // ⚠️ 应本地化
// 或使用资源文件
// CA1822: 不访问实例数据的成员可以标记为 static
public string FormatName(string name) // ⚠️ 可以设为 static
{
return name.Trim();
}
// CA2007: 等待的任务不需要 ConfigureAwait
var result = await _httpClient.GetAsync(url)
.ConfigureAwait(false); // ⚠️ .NET Core 不需要此调用
2.3 常用 IDE 规则
csharp
// IDE0004: 移除不必要的类型转换
int x = (int)5; // ⚠️ 不必要的强制转换
// IDE0017: 使用对象初始化器
var user = new User(); // ⚠️ 可以用对象初始化器
user.Name = "John";
user.Age = 30;
// 应该改为:
var user = new User { Name = "John", Age = 30 };
// IDE0031: 使用 null 传播
if (user != null && user.Address != null) // ⚠️ 可以用 ?.
{
Console.WriteLine(user.Address.City);
}
// 应该改为:
Console.WriteLine(user?.Address?.City);
// IDE0063: 简化 using 语句
using (var file = new FileStream(...)) // ⚠️ 可以简化
{
// ...
}
// 应该改为 (C# 8+):
using var file = new FileStream(...);
// IDE0090: 简化 new 表达式
Customer customer = new Customer(); // ⚠️ 可以用 new()
// 应该改为:
Customer customer = new();
三、配置分析器严重性
在 .editorconfig 中配置每条规则的严重级别:
ini
[*.cs]
# 将重要规则设为 error(编译失败)
dotnet_diagnostic.CA1001.severity = error # 必须实现 IDisposable
dotnet_diagnostic.CA1062.severity = error # 必须验证参数
dotnet_diagnostic.CA2007.severity = warning # ConfigureAwait 检查
# 将风格规则设为 suggestion/warning
dotnet_diagnostic.IDE0017.severity = warning # 对象初始化器
dotnet_diagnostic.IDE0031.severity = suggestion # null 传播
全局禁用特定规则
ini
# 禁用某条不合适的规则
dotnet_diagnostic.CA1707.severity = none # 禁用"标识符不应包含下划线"
四、StyleCop.Analyzers:更严格的风格审查
4.1 安装
xml
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
注意:当前稳定版是 1.2.0-beta.556,已经相当成熟。生产项目可以考虑使用此版本。
4.2 StyleCop 规则分类及核心规则
| 规则前缀 | 类别 | 典型规则 |
|---|---|---|
| SA1xxx | 布局 | SA1503: 大括号不能省略 |
| SA1xxx | 间距 | SA1000: 关键字后必须有空格 |
| SA1xxx | 可读性 | SA1116: 用括号分隔多条件 |
| SA12xx | 排序 | SA1200: using 必须放在命名空间外 |
| SA14xx | 可维护性 | SA1401: 字段必须是私有 |
4.3 核心规则示例
csharp
// SA1503: 大括号不能省略
if (isValid)
DoSomething(); // ⚠️ StyleCop 强制要求大括号
// ✅ 正确
if (isValid)
{
DoSomething();
}
// SA1200: using 指令必须放在命名空间外部
namespace MyApp
{
using System; // ⚠️ StyleCop 要求放在外面
}
// ✅ 正确
using System;
namespace MyApp { }
// SA1611: 方法参数必须有文档注释
public void UpdateUser(string userId) // ⚠️ userId 缺少 <param>
{
}
// ✅ 正确
/// <summary>更新用户信息。</summary>
/// <param name="userId">用户ID。</param>
public void UpdateUser(string userId)
{
}
4.4 使用 stylecop.json 微调
在项目根目录创建 stylecop.json:
json
{
"$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
"settings": {
"orderingRules": {
"usingDirectivesPlacement": "outsideNamespace",
"systemUsingDirectivesFirst": true
},
"documentationRules": {
"companyName": "YourCompany",
"copyrightText": "Copyright (c) {companyName}. All rights reserved.",
"xmlHeader": false,
"fileNamingConvention": "stylecop"
},
"namingRules": {
"allowCommonHungarianPrefixes": false,
"allowedHungarianPrefixes": []
},
"layoutRules": {
"newlineAtEndOfFile": "require"
}
}
}
在 .csproj 中引用:
xml
<ItemGroup>
<AdditionalFiles Include="stylecop.json" />
</ItemGroup>
五、内置分析器 vs StyleCop ------ 如何选择?
| 维度 | 内置 Roslyn 分析器 | StyleCop.Analyzers |
|---|---|---|
| 安装 | .NET SDK 自带,无需安装 | 需 NuGet 引用 |
| 代码质量 | 强(Bug、性能、安全) | 弱(主要聚焦风格) |
| 代码风格 | 中等 | 强(极其严格,强制文档注释) |
| 命名规则 | 通过 .editorconfig | 自带 + stylecop.json |
| XAML 分析 | 支持 | 不支持 |
| 配置复杂度 | 中 | 较高(需要 stylecop.json) |
| 推荐场景 | 所有项目的基础分析 | 对文档和格式有极高要求的项目 |
推荐组合
方案 A(推荐):内置分析器 + .editorconfig 命名规则
方案 B(严格要求):内置分析器 + StyleCop.Analyzers + stylecop.json
方案 C(极客路线):内置分析器 + StyleCop + 自定义分析器
对于大多数团队,方案 A 足以覆盖 90% 的需求。
六、自定义分析器简介
如果内置规则不够满足特殊需求,可以编写自定义分析器:
csharp
// 示例:检测 Controller 方法是否缺少 [Authorize]
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ControllerAuthorizationAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "CUSTOM001";
private static readonly DiagnosticDescriptor Rule = new(
DiagnosticId,
"Controller actions must be authorized",
"The action method '{0}' is not protected by authorization",
"Security",
DiagnosticSeverity.Error,
isEnabledByDefault: true);
// ... 分析方法语法树
}
自定义分析器开发已超出本文范围,建议从 Microsoft 官方教程 入门。
七、实战:分析器落地三步走
第一步:在 Directory.Build.props 中全局启用
xml
<Project>
<PropertyGroup>
<AnalysisLevel>latest-recommended</AnalysisLevel>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
</Project>
第二步:在 .editorconfig 中细化规则
ini
[*.cs]
# 核心质量规则 → error
dotnet_diagnostic.CA1001.severity = error
dotnet_diagnostic.CA1062.severity = error
# 风格建议 → suggestion(先提示,后收紧)
dotnet_diagnostic.IDE0017.severity = suggestion
dotnet_diagnostic.IDE0031.severity = suggestion
第三步:CI/CD 中强制检查
yaml
# GitHub Actions 示例
- name: Build with analysis
run: dotnet build --configuration Release /p:TreatWarningsAsErrors=true
