.NET Conventions について
.NET コードを保守する開発者向けの規約ガイドです。書式、命名、非同期処理、ログ、null 許容、検証を扱い、リソース破棄と依存性注入のパターンも示します。
主な機能
- 01書式と命名の規則
- 02非同期メソッドとキャンセルトークン
- 03構造化ログとスコープ
- 04null 許容とリソース破棄
- 05依存性注入と検証
活用シーン
- 01チーム内の .NET スタイル統一
- 02非同期呼び出しチェーンの統一
- 03ログへのコンテキスト追加
- 04null 処理と破棄の確認
インストール
コマンドをコピーしてターミナルで実行します。
$
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:名前空間の外側に記述します
- 波括弧:1 行のブロックでも必ず使用します
#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");
}
Null 許容参照型
- 全体を通して null 許容注釈を遵守します
- null 許容に関する警告はエラーとして扱います
- null 許容型には
?サフィックスを使用します
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
}
ドメイン検証
検証パターンについては、backend-architecture を参照してください(ドメインモデルには FluentValidation、API リクエストには MiniValidator を使用します)。
コメント
Markdown 対応