关于 .NET Conventions
面向维护 .NET 代码的开发者,汇总格式、命名、异步处理、日志、空值和验证约定。还提供资源释放与依赖注入的代码模式。
主要功能
- 01代码格式与命名规则
- 02异步方法与取消令牌
- 03结构化日志与作用域
- 04空值处理与资源释放
- 05依赖注入和输入验证
适用场景
- 01统一团队 .NET 代码风格
- 02规范异步调用链
- 03为日志补充上下文
- 04检查空值与资源管理
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ exceptionless/ exceptionless --skill .net-conventions安装后在 Claude Code 中输入/.net-conventions确认已加载。
详细介绍
.NET 约定
风格与格式
- 严格遵循
.editorconfig规则 - 提交前运行
dotnet format - 尽量减少差异:只更改必要内容,保留现有格式和结构
- 与周围代码的风格完全一致
命名约定
| 元素 | 约定 | 示例 |
|---|---|---|
| 私有字段 | _camelCase | _organizationRepository |
| 公有成员 | PascalCase | GetByIdAsync |
| 局部变量 | camelCase | organizationId |
| 常量 | PascalCase | MaxRetryCount |
| 类型参数 | T 前缀 | TModel |
格式规则
- 缩进:4 个空格,不使用制表符
- 命名空间:文件范围(
namespace Foo;) - Usings:置于命名空间外
- 大括号:始终使用,即使代码块只有一行
- 不使用
#region:切勿使用#region/#endregion指令——它们会隐藏代码并妨碍重构
异步模式
- 后缀:异步方法始终使用
Async后缀 - CancellationToken:可用时沿调用链传递
- ValueTask:对于经常同步完成的热路径,优先使用
ValueTask<T> - ConfigureAwait:ASP.NET Core 中不需要
// From src/Exceptionless.Core/Services/UsageService.cs
public async Task SavePendingUsageAsync()
{
var utcNow = _timeProvider.GetUtcNow().UtcDateTime;
await SavePendingOrganizationUsageAsync(utcNow);
await SavePendingProjectUsageAsync(utcNow);
}
结构化日志记录
使用带有命名占位符的消息模板——值放在参数中,而不是使用字符串插值:
// ✅ Correct: Named placeholders for structured data
_logger.LogInformation("Saving org ({OrganizationId}-{OrganizationName}) event usage",
organizationId, organization.Name);
_logger.LogError(ex, "Error retrieving event post payload: {Path}", path);
_logger.LogWarning("Unable to parse user agent {UserAgent}. Exception: {Message}",
userAgent, ex.Message);
// ❌ Wrong: String interpolation loses structure
_logger.LogInformation($"Saving org {organizationId}");
使用 ExceptionlessState 的日志范围
使用范围为代码块中的所有日志条目添加上下文:
// From src/Exceptionless.Core/Jobs/EventPostsJob.cs
using var _ = _logger.BeginScope(new ExceptionlessState()
.Organization(ep.OrganizationId)
.Project(ep.ProjectId));
// All log entries in this scope automatically include org/project context
_logger.LogInformation("Processing event post");
添加标签和属性以提供更丰富的上下文:
using (_logger.BeginScope(new ExceptionlessState()
.Organization(organization.Id)
.Tag("Delete")
.Tag("Bot")))
{
_logger.LogInformation("Removing bot events");
}
可空引用类型
- 始终遵循可空注解
- 将可空警告视为错误
- 对可空类型使用
?后缀
public async Task<User?> FindUserAsync(string? email)
{
if (string.IsNullOrWhiteSpace(email))
return null;
return await _repository.FindByEmailAsync(email);
}
资源释放
// Prefer using declarations
using var stream = File.OpenRead(path);
// Async disposal
await using var connection = await CreateConnectionAsync();
构造函数注入
优先使用构造函数注入和只读字段:
// From src/Exceptionless.Core/Services/UsageService.cs
public class UsageService
{
private readonly IOrganizationRepository _organizationRepository;
private readonly ICacheClient _cache;
private readonly TimeProvider _timeProvider;
private readonly ILogger _logger;
public UsageService(
IOrganizationRepository organizationRepository,
ICacheClient cache,
TimeProvider timeProvider,
ILoggerFactory loggerFactory)
{
_organizationRepository = organizationRepository;
_cache = cache;
_timeProvider = timeProvider;
_logger = loggerFactory.CreateLogger<UsageService>();
}
}
验证模式
输入验证
尽早验证,快速失败:
public async Task<ActionResult> ProcessAsync(string id)
{
if (string.IsNullOrEmpty(id))
return BadRequest("Id is required");
var entity = await _repository.GetByIdAsync(id);
if (entity is null)
return NotFound();
// Continue processing
}
领域验证
有关验证模式(领域模型使用 FluentValidation,API 请求使用 MiniValidator),请参阅 backend-architecture。
评论
支持 Markdown