Communityコーディング&開発github.com

現代的なC#コーディング規約

レコード、パターンマッチング、値オブジェクト、非同期処理、Span<T>/Memory<T>、そしてベストプラクティスなAPI設計パターンを用いて、モダンで高性能なC#コードを記述します。C# 12以降の機能を活用した関数型プログラミングスタイルを重視します。

現代的なC#コーディング規約 とは?

このスキルは、C#開発者向けに設計された包括的なコーディング規約です。特にC# 9〜12の最新機能を活用し、不変性(レコード型)、型安全性(null許容参照型)、パターンマッチング、非同期処理(async/await)、ゼロアロケーション戦略(Span<T>/Memory<T>)を重視します。主要なターゲットは、新しいC#コードの作成、既存コードのリファクタリング、パフォーマンスが重要なコードパスの最適化、およびドメインモデルの実装です。このスキルは、Claude Code、Cursor、GitHub CopilotなどのAIエージェントで使用するために設計されており、`.clinerules`や`.cursorrules`などのルールファイルとして適用できます。また、特定のプラットフォームに依存せず、.NET 8+ プロジェクトに幅広く適用可能で、JSONやYAMLなどの設定ファイルを介してプロジェクトに統合できます。Specification-Driven Development(仕様駆動開発)の文脈では、推論ルールや制約として機能し、AIエージェントが一貫性のある高品質なC#コードを生成するのを支援します。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Aaronontheweb/dotnet-skills/tree/main/skills/csharp-coding-standards

Installed? Explore more コーディング&開発 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

現代的なC#コーディング規約 は何をしますか?

このスキルを使用するタイミング

以下の場合にこのスキルを使用します。

  • 新しいC#コードを作成する、または既存のコードをリファクタリングする場合
  • ライブラリやサービスのパブリックAPIを設計する場合
  • パフォーマンスが重要なコードパスを最適化する場合
  • 強い型付けを持つドメインモデルを実装する場合
  • 非同期/待機(async/await)を多用するアプリケーションを構築する場合
  • バイナリデータ、バッファ、または高スループットのシナリオを扱う場合

参照ファイル

中核原則

  1. デフォルトで不変 - record型とinit専用プロパティを使用
  2. 型安全性 - null許容参照型と値オブジェクトを活用
  3. モダンなパターンマッチング - switch式とパターンを広範囲に使用
  4. あらゆる場面で非同期 - 適切なキャンセルサポート付きの非同期APIを優先
  5. ゼロアロケーションパターン - パフォーマンスが重要なコードではSpan<T>とMemory<T>を使用
  6. API設計 - 抽象化を受け入れ、適切に特化した型を返す
  7. 継承より合成 - 抽象基底クラスを避け、合成を優先
  8. 値オブジェクトとしての構造体 - 値オブジェクトにはreadonly record structを使用

言語パターン

不変データのためのレコード(C# 9以降)

DTO、メッセージ、イベント、ドメインエンティティにはrecord型を使用します。

// 単純な不変DTO
public record CustomerDto(string Id, string Name, string Email);

// コンストラクタで検証を行うレコード
public record EmailAddress
{
    public string Value { get; init; }

    public EmailAddress(string value)
    {
        if (string.IsNullOrWhiteSpace(value) || !value.Contains('@'))
            throw new ArgumentException("無効なメールアドレスです", nameof(value));

        Value = value;
    }
}

// コレクションを持つレコード - IReadOnlyListを使用
public record ShoppingCart(
    string CartId,
    string CustomerId,
    IReadOnlyList<CartItem> Items
)
{
    public decimal Total => Items.Sum(item => item.Price * item.Quantity);
}

record classとrecord structの使い分け:

  • record class(デフォルト):参照型。エンティティ、集約、複数のプロパティを持つDTOに使用
  • record struct:値型。値オブジェクトに使用(次のセクション参照)

読み取り専用レコード構造体としての値オブジェクト

値オブジェクトは、パフォーマンスと値のセマンティクスのために**常にreadonly record struct**にします。明示的な変換を使用し、暗黙の演算子は使用しないでください。

public readonly record struct OrderId(string Value)
{
    public OrderId(string value) : this(
        !string.IsNullOrWhiteSpace(value)
            ? value
            : throw new ArgumentException("OrderIdは空にできません", nameof(value)))
    { }
    public override string ToString() => Value;
}

public readonly record struct Money(decimal Amount, string Currency);
public readonly record struct CustomerId(Guid Value)
{
    public static CustomerId New() => new(Guid.NewGuid());
}

完全な例(複数値オブジェクト、ファクトリパターン、暗黙変換禁止ルールを含む)については、value-objects-and-patterns.mdを参照してください。

パターンマッチング(C# 8-12)

switch式、プロパティパターン、関係パターン、リストパターンを使用して、よりクリーンなコードを実現します。

public decimal CalculateDiscount(Order order) => order switch
{
    { Total: > 1000m } => order.Total * 0.15m,
    { Total: > 500m } => order.Total * 0.10m,
    { Total: > 100m } => order.Total * 0.05m,
    _ => 0m
};

パターンマッチングの完全な例については、value-objects-and-patterns.mdを参照してください。


null許容参照型(C# 8以降)

プロジェクトでnull許容参照型を有効にし、nullを明示的に処理します。

// .csproj内
<PropertyGroup>
    <Nullable>enable</Nullable>
</PropertyGroup>

// 明示的なnull許容性
public string? FindUserName(string userId)
{
    var user = _repository.Find(userId);
    return user?.Name;
}

// nullチェックとのパターンマッチング
public decimal GetDiscount(Customer? customer) => customer switch
{
    null => 0m,
    { IsVip: true } => 0.20m,
    { OrderCount: > 10 } => 0.10m,
    _ => 0.05m
};

// ArgumentNullException.ThrowIfNullを使用したガード節(C# 11以降)
public void ProcessOrder(Order? order)
{
    ArgumentNullException.ThrowIfNull(order);
    // このスコープではorderは非nullになります
    Console.WriteLine(order.Id);
}

継承より合成

抽象基底クラスを避けます。 インターフェース+合成を使用します。共有ロジックには静的ヘルパーを使用します。バリアントにはファクトリメソッドを持つレコードを使用します。

完全な例については、composition-and-error-handling.mdを参照してください。


パフォーマンスパターン

非同期/待機(Async/Await)のベストプラクティス

// 非同期を徹底する - 常にCancellationTokenを受け入れる
public async Task<Order> GetOrderAsync(string orderId, CancellationToken cancellationToken)
{
    var order = await _repository.GetAsync(orderId, cancellationToken);
    return order;
}

// 頻繁に呼び出され、しばしば同期的なメソッドにはValueTask
public ValueTask<Order?> GetCachedOrderAsync(string orderId, CancellationToken cancellationToken)
{
    if (_cache.TryGetValue(orderId, out var order))
        return ValueTask.FromResult<Order?>(order);
    return GetFromDatabaseAsync(orderId, cancellationToken);
}

// ストリーミングにはIAsyncEnumerable
public async IAsyncEnumerable<Order> StreamOrdersAsync(
    string customerId,
    [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
    await foreach (var order in _repository.StreamAllAsync(cancellationToken))
    {
        if (order.CustomerId == customerId)
            yield return order;
    }
}

重要なルール:

  • 常に= default付きでCancellationTokenを受け入れる
  • ライブラリコードではConfigureAwait(false)を使用する
  • 非同期コードをブロックしない(.Resultや.Wait()は使わない)
  • タイムアウトにはリンクされたCancellationTokenSourceを使用する

SpanとMemory

同期的なゼロアロケーション操作にはSpan<T>を、非同期操作にはMemory<T>を、大きな一時バッファにはArrayPool<T>を使用します。

完全なSpan/Memoryの例とAPI設計セクションについては、performance-and-api-design.mdを参照してください。


エラーハンドリング:Result型

予期されるエラーには、例外の代わりにResult<T, TError>を使用します。例外は予期しない/システムエラーの場合にのみ使用します。

Result型の実装と使用例の完全な説明については、composition-and-error-handling.mdを参照してください。


リフレクションベースのメタプログラミングを避ける

禁止: AutoMapper、Mapster、ExpressMapper。代わりに明示的なマッピング拡張メソッドを使用します。プライベートメンバーへのアクセスが本当に必要な場合は、UnsafeAccessorAttribute(.NET 8以降)を使用します。

完全なガイダンスについては、anti-patterns-and-reflection.mdを参照してください。


コード構成

// ファイル: Domain/Orders/Order.cs

namespace MyApp.Domain.Orders;

// 1. プライマリドメイン型
public record Order(
    OrderId Id,
    CustomerId CustomerId,
    Money Total,
    OrderStatus Status,
    IReadOnlyList<OrderItem> Items
)
{
    public bool IsCompleted => Status is OrderStatus.Completed;

    public Result<Order, OrderError> AddItem(OrderItem item)
    {
        if (Status is not OrderStatus.Draft)
            return Result<Order, OrderError>.Failure(
                new OrderError("ORDER_NOT_DRAFT", "下書き注文にのみアイテムを追加できます"));

        var newItems = Items.Append(item).ToList();
        var newTotal = new Money(
            Items.Sum(i => i.Total.Amount) + item.Total.Amount,
            Total.Currency);

        return Result<Order, OrderError>.Success(
            this with { Items = newItems, Total = newTotal });
    }
}

// 2. 状態を表す列挙型
public enum OrderStatus { Draft, Submitted, Processing, Completed, Cancelled }

// 3. 関連する型
public record OrderItem(ProductId ProductId, Quantity Quantity, Money UnitPrice)
{
    public Money Total => new(UnitPrice.Amount * Quantity.Value, UnitPrice.Currency);
}

// 4. 値オブジェクト
public readonly record struct OrderId(Guid Value)
{
    public static OrderId New() => new(Guid.NewGuid());
}

// 5. エラー
public readonly record struct OrderError(string Code, string Message);

ベストプラクティスのまとめ

推奨事項

  • DTO、メッセージ、ドメインエンティティにはrecordを使用する
  • 値オブジェクトにはreadonly record structを使用する
  • switch式でパターンマッチングを活用する
  • null許容参照型を有効にし、尊重する
  • すべてのI/O操作に非同期/待機を使用する
  • すべての非同期メソッドでCancellationTokenを受け入れる
  • 高パフォーマンスシナリオにはSpan<T>とMemory<T>を使用する
  • 抽象化(IEnumerable<T>、IReadOnlyList<T>)を受け入れる
  • 予期されるエラーにはResult<T, TError>を使用する
  • 大きなアロケーションにはArrayPool<T>でバッファをプールする
  • 継承より合成を優先する

禁止事項

  • レコードで十分な場合に変更可能なクラスを使用しない
  • 値オブジェクトにクラスを使用しない(readonly record structを使用する)
  • 深い継承階層を作成しない
  • null許容参照型の警告を無視しない
  • 非同期コードをブロックしない(.Result、.Wait())
  • Span<byte>で十分な場合にbyte[]を使用しない
  • CancellationTokenパラメータを忘れない
  • APIから変更可能なコレクションを返さない
  • 予期されるビジネスエラーに対して例外をスローしない
  • 大きな配列を繰り返し割り当てない(ArrayPoolを使用する)

詳細なアンチパターンの例については、anti-patterns-and-reflection.mdを参照してください。


追加リソース

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

akka-hosting-actor-patterns

Patterns for building entity actors with Akka.Hosting - GenericChildPerEntityParent, message extractors, cluster sharding abstraction, akka-reminders, and ITimeProvider. Supports both local testing and clustered production modes.

akka-net-aspire-configuration

Configure Akka.NET with .NET Aspire for local development and production deployments. Covers actor system setup, clustering, persistence, Akka.Management integration, and Aspire orchestration patterns.

akka-net-best-practices

Critical Akka.NET best practices including EventStream vs DistributedPubSub, supervision strategies, error handling, Props vs DependencyResolver, work distribution patterns, and cluster/local mode abstractions for testability.

akka-net-management

Akka.Management for cluster bootstrapping, service discovery (Kubernetes, Azure, Config), health checks, and dynamic cluster formation without static seed nodes.

akka-net-testing-patterns

Write unit and integration tests for Akka.NET actors using modern Akka.Hosting.TestKit patterns. Covers dependency injection, TestProbes, persistence testing, and actor interaction verification. Includes guidance on when to use traditional TestKit.

api-design

Design stable, compatible public APIs using extend-only design principles. Manage API compatibility, wire compatibility, and versioning for NuGet packages and distributed systems.

aspire-configuration

Configure Aspire AppHost to emit explicit app config via environment variables; keep app code free of Aspire clients and service discovery.

aspire-integration-testing

Write integration tests using .NET Aspire

aspire-service-defaults

Create a shared ServiceDefaults project for Aspire applications. Centralizes OpenTelemetry, health checks, resilience, and service discovery configuration across all services.

crap-analysis

Analyze code coverage and CRAP (Change Risk Anti-Patterns) scores to identify high-risk code. Use OpenCover format with ReportGenerator for Risk Hotspots showing cyclomatic complexity and untested code paths.

csharp-concurrency-patterns

Choosing the right concurrency abstraction in .NET - from async/await for I/O to Channels for producer/consumer to Akka.NET for stateful entity management. Avoid locks and manual synchronization unless absolutely necessary.

database-performance

Database access patterns for performance. Separate read/write models, avoid N+1 queries, use AsNoTracking, apply row limits, and never do application-side joins. Works with EF Core and Dapper.

dependency-injection-patterns

Organize DI registrations using IServiceCollection extension methods. Group related services into composable Add* methods for clean Program.cs and reusable configuration in tests.

dotnet-devcert-trust

Diagnose and fix .NET HTTPS dev certificate trust issues on Linux. Covers the full certificate lifecycle from generation to system CA bundle inclusion, with distro-specific guidance for Ubuntu, Fedora, Arch, and WSL2.

dotnet-local-tools

Managing local .NET tools with dotnet-tools.json for consistent tooling across development environments and CI/CD pipelines.

dotnet-project-structure

Modern .NET project structure including .slnx solution format, Directory.Build.props, central package management, SourceLink, version management with RELEASE_NOTES.md, and SDK pinning with global.json.

dotnet-slopwatch

Use Slopwatch to detect LLM reward hacking in .NET code changes. Run after every code modification to catch disabled tests, suppressed warnings, empty catch blocks, and other shortcuts that mask real problems.

efcore-patterns

Entity Framework Core best practices including NoTracking by default, query splitting for navigation collections, migration management, dedicated migration services, and common pitfalls to avoid.

ilspy-decompile

Understand implementation details of .NET code by decompiling assemblies. Use when you want to see how a .NET API works internally, inspect NuGet package source, view framework implementation, or understand compiled .NET binaries.

mailpit-integration

Test email sending locally using Mailpit with .NET Aspire. Captures all outgoing emails without sending them. View rendered HTML, inspect headers, and verify delivery in integration tests.

関連スキル