名称: dotnet-modernize 描述: “分析.NET代码以进行现代化。过时的TFMs、已弃用的包、被替代的模式。”
dotnet-modernize
分析现有.NET代码以识别现代化机会。检测过时的目标框架、已弃用的包、被替代的API模式以及缺失的现代最佳实践。为每个发现提供可行建议。
范围
- 过时目标框架检测
- 已弃用包识别
- 被替代API模式标记
- 可操作的现代化建议
超出范围
- 实际迁移路径和polyfill策略——参见[skill:dotnet-version-upgrade]
- 多目标指南——参见[skill:dotnet-multi-targeting]
先决条件: 首先运行[skill:dotnet-version-detection]以确定当前SDK、TFM和语言版本。运行[skill:dotnet-project-analysis]以理解解决方案结构和依赖关系。
交叉引用:[skill:dotnet-project-structure]用于现代布局约定,[skill:dotnet-add-analyzers]用于基于分析器的已弃用模式检测,[skill:dotnet-scaffold-project]用于完全现代化项目的目标状态。
现代化检查清单
针对现有代码库运行此检查清单。每个部分标识要查找的内容以及现代替代方案。
1. 目标框架
检查.csproj文件(或Directory.Build.props)中的<TargetFramework>:
| 当前TFM | 状态 | 建议 |
|---|---|---|
net8.0 |
LTS——支持至2026年11月 | 计划升级到net10.0(LTS) |
net9.0 |
STS——支持结束于2026年5月 | 及时升级到net10.0 |
net7.0 |
生命周期结束 | 立即升级 |
net6.0 |
生命周期结束 | 立即升级 |
net5.0或更低 |
生命周期结束 | 立即升级 |
netstandard2.0/2.1 |
支持(库兼容) | 如果多目标以广泛覆盖则保留 |
netcoreapp3.1 |
生命周期结束 | 立即升级 |
.NET Framework 4.x |
传统 | 评估迁移可行性 |
扫描所有项目:
# 查找解决方案中的所有TFMs
find . -name "*.csproj" -exec grep -h "TargetFramework" {} \; | sort -u
# 检查Directory.Build.props
grep "TargetFramework" Directory.Build.props 2>/dev/null
2. 已弃用和被替代的包
扫描Directory.Packages.props(或单个.csproj文件)以查找已被替代的包:
| 已弃用包 | 替代方案 | 自 |
|---|---|---|
Microsoft.Extensions.Http.Polly |
Microsoft.Extensions.Http.Resilience |
.NET 8 |
Newtonsoft.Json(新项目) |
System.Text.Json |
.NET Core 3.0+ |
Microsoft.AspNetCore.Mvc.NewtonsoftJson |
内置STJ | .NET Core 3.0+ |
Swashbuckle.AspNetCore |
内置OpenAPI(Microsoft.AspNetCore.OpenApi)用于文档生成;如果使用Swagger UI、过滤器或代码生成,则保留Swashbuckle |
.NET 9 |
NSwag.AspNetCore |
内置OpenAPI用于文档生成;如果使用客户端生成或Swagger UI功能,则保留NSwag | .NET 9 |
Microsoft.Extensions.Logging.Log4Net.AspNetCore |
内置日志 + Serilog或OpenTelemetry |
.NET Core 2.0+ |
Microsoft.AspNetCore.Authentication.JwtBearer(显式NuGet包) |
移除显式PackageReference——包含在Microsoft.AspNetCore.App共享框架中 |
.NET Core 3.0+ |
System.Data.SqlClient |
Microsoft.Data.SqlClient |
.NET Core 3.0+ |
Microsoft.Azure.Storage.* |
Azure.Storage.* |
2020+ |
WindowsAzure.Storage |
Azure.Storage.Blobs / Azure.Storage.Queues |
2020+ |
Microsoft.Azure.ServiceBus |
Azure.Messaging.ServiceBus |
2020+ |
Microsoft.Azure.EventHubs |
Azure.Messaging.EventHubs |
2020+ |
EntityFramework(EF6) |
Microsoft.EntityFrameworkCore |
.NET Core 1.0+ |
RestSharp(旧版本) |
HttpClient + System.Text.Json |
.NET Core+ |
AutoMapper |
手动映射或源生成映射器 | 偏好 |
扫描已弃用包:
# 列出所有包引用
grep -rh "PackageVersion\|PackageReference" \
Directory.Packages.props $(find . -name "*.csproj") 2>/dev/null | \
grep -i "Include=" | sort -u
关于Newtonsoft.Json的注意: 具有深度Newtonsoft.Json用法(自定义转换器、JObject操作)的现有项目可能不会从立即迁移中受益。标记它但评估迁移成本。
3. 被替代的API模式
查找具有现代替代方案的代码模式:
Startup.cs / Program.cs 模式
旧(.NET 6之前):
public class Startup
{
public void ConfigureServices(IServiceCollection services) { }
public void Configure(IApplicationBuilder app) { }
}
现代(最小托管):
var builder = WebApplication.CreateBuilder(args);
// ConfigureServices等效
var app = builder.Build();
// Configure等效
app.Run();
HttpClient 注册
旧:
services.AddHttpClient<MyService>(client =>
{
client.BaseAddress = new Uri("https://api.example.com");
})
.AddTransientHttpErrorPolicy(p => p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(300)));
现代(使用Microsoft.Extensions.Resilience):
services.AddHttpClient<MyService>(client =>
{
client.BaseAddress = new Uri("https://api.example.com");
})
.AddStandardResilienceHandler();
同步I/O
标记: File.ReadAllText、Stream.Read、不带Async后缀的HttpClient。
现代: 使用async变体——File.ReadAllTextAsync、Stream.ReadAsync、await httpClient.GetAsync()。
热路径中的字符串连接
标记: 字符串连接(+)或String.Format在日志记录、循环中。
现代: 使用字符串插值配合LoggerMessage源生成器,或StringBuilder。
传统集合模式
标记: Hashtable、ArrayList、非泛型集合。
现代: Dictionary<TKey, TValue>、List<T>、泛型集合。
ILogger 模式
旧:
_logger.LogInformation("Processing order {OrderId}", orderId);
现代(高性能):
[LoggerMessage(Level = LogLevel.Information, Message = "Processing order {OrderId}")]
static partial void LogProcessingOrder(ILogger logger, string orderId);
4. 缺失的现代构建配置
检查缺少推荐构建基础设施的情况:
| 缺失 | 检查 | 建议 |
|---|---|---|
| 中央包管理 | 无Directory.Packages.props |
参见[skill:dotnet-project-structure] |
| Directory.Build.props | 属性分散在.csproj文件中 |
集中共享属性 |
| .editorconfig | 仓库根目录无.editorconfig |
参见[skill:dotnet-project-structure] |
| global.json | 无SDK固定 | 添加以实现可重现构建 |
| NuGet 审计 | 无NuGetAudit属性 |
在Directory.Build.props中启用 |
| 锁文件 | 无RestorePackagesWithLockFile |
启用以实现确定性还原 |
| 包源映射 | 在nuget.config中无packageSourceMapping |
添加以增强供应链安全 |
| 分析器 | 无AnalysisLevel或EnforceCodeStyleInBuild |
参见[skill:dotnet-add-analyzers] |
| SourceLink | 无SourceLink包引用 | 添加以实现调试器源导航 |
| 可为空引用类型 | 未启用<Nullable> |
全局启用 |
| .slnx | 仍使用.sln配合.NET 9+ SDK |
使用dotnet sln migrate迁移 |
5. 已弃用的C#语言模式
| 旧模式 | 现代替代方案 | 语言版本 |
|---|---|---|
带case的switch语句 |
switch表达式 |
C# 8 |
null != x / x != null检查 |
x is not null |
C# 9 |
带有明显类型的new ClassName() |
目标类型化new() |
C# 9 |
| 块作用域命名空间 | 文件作用域命名空间 | C# 10 |
record class显式构造函数 |
带位置参数的record |
C# 10 |
| 用于多行的手动字符串连接 | 原始字符串字面量("""...""") |
C# 11 |
为INumber<T>的显式接口调度 |
泛型数学接口 | C# 11 |
[Flags]枚举手动检查 |
改进的枚举模式匹配 | C# 11+ |
| 无自然类型的Lambda | 自然函数类型 | C# 10+ |
ValueTask手动包装 |
Task/ValueTask配合ConfigureAwait模式 |
C# 所有 |
| 主要构造函数类(手动) | class/struct上的主要构造函数 |
C# 12 |
多个if/else if类型检查 |
类型switch配合列表模式 |
C# 11+ |
params T[] |
params ReadOnlySpan<T>、params集合 |
C# 13 |
使用object的锁 |
System.Threading.Lock |
C# 13 |
6. 安全和合规
| 问题 | 检测 | 修复 |
|---|---|---|
| 已知漏洞 | dotnet list package --vulnerable |
更新受影响包 |
| 已弃用包 | dotnet list package --deprecated |
替换为后继版本 |
| 过时包 | dotnet list package --outdated |
评估更新 |
| 缺失HTTPS重定向 | 无app.UseHttpsRedirection() |
添加到管道 |
| 缺失HSTS | 无app.UseHsts() |
为生产环境添加 |
| 硬编码秘密 | appsettings.json中的连接字符串 |
使用用户密钥或密钥保管库 |
# 运行所有NuGet审计
dotnet list package --vulnerable --include-transitive
dotnet list package --deprecated
dotnet list package --outdated
运行现代化扫描
将检查组合成系统扫描:
# 1. 检查TFMs
echo "=== 目标框架 ==="
find . -name "*.csproj" -exec grep -Hl "TargetFramework" {} \; | while read f; do
echo "$f: $(grep -o '<TargetFramework[s]*>[^<]*' "$f" | head -1)"
done
# 2. 检查已弃用包
echo "=== 包审计 ==="
dotnet list package --deprecated 2>/dev/null
dotnet list package --vulnerable --include-transitive 2>/dev/null
# 3. 检查构建基础设施
echo "=== 构建基础设施 ==="
test -f Directory.Build.props && echo "OK: Directory.Build.props" || echo "MISSING: Directory.Build.props"
test -f Directory.Packages.props && echo "OK: Directory.Packages.props (CPM)" || echo "MISSING: Directory.Packages.props"
test -f .editorconfig && echo "OK: .editorconfig" || echo "MISSING: .editorconfig"
test -f global.json && echo "OK: global.json" || echo "MISSING: global.json"
test -f nuget.config && echo "OK: nuget.config" || echo "MISSING: nuget.config"
# 4. 检查代码中的旧模式
echo "=== 代码模式 ==="
grep -rl "class Startup" --include="*.cs" . 2>/dev/null && echo "FOUND: 旧Startup.cs模式"
grep -rl "Microsoft.Extensions.Http.Polly" --include="*.csproj" --include="*.props" . 2>/dev/null && echo "FOUND: 已弃用Polly包"
grep -rl "Swashbuckle" --include="*.csproj" --include="*.props" . 2>/dev/null && echo "FOUND: Swashbuckle(考虑.NET 9+的内置OpenAPI)"
grep -rl "System.Data.SqlClient" --include="*.csproj" --include="*.props" . 2>/dev/null && echo "FOUND: System.Data.SqlClient(使用Microsoft.Data.SqlClient)"
优先化现代化
并非所有现代化都同样紧迫。按影响优先排序:
- 安全——有漏洞的包、生命周期结束的TFMs(无安全补丁)
- 可支持性——已弃用且无上游维护的包
- 性能——具有显著性能影响的模式(同步over-async、热路径中的传统集合)
- 开发人员体验——构建基础设施(CPM、分析器、editorconfig)改善日常工作流程
- 代码风格——语言模式更新是优先级最低的,但随时间减少认知负担
下一步
此技能标记现代化机会。对于执行升级:
- TFM版本升级和迁移路径——[skill:dotnet-version-upgrade]
- 多目标策略——[skill:dotnet-multi-targeting]
- 跨版本支持的polyfill包——[skill:dotnet-multi-targeting]
- 添加缺失的构建基础设施——[skill:dotnet-project-structure]、[skill:dotnet-scaffold-project]
- 配置分析器——[skill:dotnet-add-analyzers]
- 添加CI/CD——[skill:dotnet-add-ci]