【C#】コーディング規約がない会社で最初に作るべきコーディングルールと実践サンプル

システム開発

「社内でコードの書き方がバラバラ…」「レビューのたびに指摘が飛び交う…」「後から読むと何をしているかわからない…」そんな悩みを抱えていませんか? コーディング規約がない環境では、コードの可読性や保守性が低下し、チームの生産性が落ちてしまいます。

本記事では、コーディング規約が存在しない会社で、最初に作るべき基本ルールを解説します。C#の標準的なベストプラクティスを参考にしながら、実践しやすいシンプルなルールを紹介し、サンプルコードも交えて解説します。チーム全体で統一したルールを導入し、メンテナブルなコードを目指しましょう!


なぜコーディング規約が必要なのか?

コーディング規約がないとどうなる?

コーディング規約が存在しない場合、開発現場では以下のような問題が発生します。

  • コードの可読性が低下する
    • 開発者ごとに異なる書き方になり、統一感がなくなる
    • 変数名やメソッド名のルールがバラバラで、直感的に理解できない
  • バグの発生率が高まる
    • コードの不統一により、意図しないバグが生まれやすくなる
    • 命名の曖昧さや書き方の違いで誤解が生じ、修正が増える
  • コードレビューの負担が増える
    • レビューのたびに「この書き方はこう統一しましょう」と指摘が必要になる
    • スタイルの違いを指摘することで、本来のロジックの確認が後回しになる
  • 後からの改修が困難になる
    • 書き手のクセに依存したコードが増え、別の人が理解しにくくなる
    • 一貫性のないコードは修正の影響範囲が読みにくく、デバッグに時間がかかる

このように、規約がない状態ではチーム開発の効率が下がり、最悪の場合、技術的負債を積み上げてしまうことになります。

コーディング規約を導入するメリット

一方で、コーディング規約を導入することで、次のようなメリットが得られます。

可読性の向上

  • 統一されたスタイルで書かれたコードは、誰が読んでも理解しやすくなる
  • 修正や機能追加の際に、意図が明確で素早く対応できる

バグの削減

  • 命名ルールや書き方の統一により、意図しないバグを防げる
  • 可読性が高くなることで、ミスの発見が容易になる

レビューの効率化

  • スタイルの違いを指摘する必要がなくなり、ロジックの改善に集中できる
  • 規約を守っていれば、誰が書いたコードでも一貫した品質を維持できる

保守性の向上

  • 他の開発者がスムーズにコードを理解し、変更を加えられる
  • 一貫したルールがあるため、長期的にプロジェクトを維持しやすくなる

最初に導入すべき基本ルールとは?

完璧なコーディング規約を作る必要はありません。

まずは、最低限守るべき5つの基本ルール から導入しましょう。

  1. 命名規則(クラス・メソッド・変数の統一)
  2. インデント・スペースの統一(フォーマットを統一)
  3. コメントのルール(必要な情報のみ記述)
  4. エラーハンドリングの統一(例外処理の方針を決める)
  5. ディレクトリ・ファイル構成の統一(フォルダ構成のルールを定める)

これらのルールを定めることで、開発の品質を向上させ、チーム全体の生産性を向上させることができます。


ルール①:命名規則を統一する

命名規則を統一する重要性

命名規則が統一されていないと、コードの可読性が著しく低下し、開発者ごとに異なるルールで命名されるため、理解に時間がかかります。

また、命名のルールがバラバラだと、メンテナンス時に予期しないバグを引き起こす可能性もあります。

そのため、C#の標準的な命名規則に従い、誰が見ても理解しやすい名前をつけることが重要です。

C#の基本的な命名規則

C#では、以下の命名ルールが推奨されています。

  • クラス・インターフェース名PascalCase
  • メソッド名・プロパティ名PascalCase
  • 変数名(ローカル変数・パラメータ)camelCase
  • プライベートフィールド(クラス内部)_camelCase(アンダースコア付き)
  • 定数UPPER_SNAKE_CASE

命名規則の実践サンプル

以下のサンプルコードは、C#の標準的な命名規則に従っています。

public class UserManager  // クラス名はPascalCase
{
    private string _userName;  // 非公開フィールドは _camelCase

    public string UserName { get; set; }  // プロパティはPascalCase

    public void UpdateProfile()  // メソッドもPascalCase
    {
        string tempData = "Updating";  // ローカル変数はcamelCase
        Console.WriteLine(tempData);
    }
}

NG例(統一されていない命名)

public class user_manager  // 小文字+アンダースコアは禁止
{
    private string username;  // 非公開フィールドにはアンダースコアをつける
    public string userName { get; set; }  // PascalCaseにする
    public void update_Profile()  // メソッドはPascalCase
    {
        String TempDATA = "Updating";  // ローカル変数もcamelCaseにする
    }
}

チームで徹底すべきポイント

✅ クラス・インターフェース

  • クラス名は PascalCase にする(例:UserManager)。
  • インターフェースは I を先頭につける(例:IUserService)。

✅ 変数・メソッド

  • メソッド、プロパティは PascalCase(例:GetUserData())。
  • ローカル変数・パラメータは camelCase(例:userId)。
  • 非公開フィールドは _camelCase(例:_userName)。

✅ 定数

  • UPPER_SNAKE_CASE で記述(例:MAX_USER_COUNT)。

ルール②:インデントとスペースを統一する【サンプル付き】

インデントやスペースの統一が重要な理由

コードの可読性を向上させるためには、一貫したフォーマットで記述することが重要です。

インデントやスペースの使い方が統一されていないと、以下のような問題が発生します。

  • コードの可読性が低下
    • インデントがバラバラだと、コードの構造が直感的に理解しにくくなる
    • どこがブロックの開始・終了なのか判断しにくくなる
  • コードレビューの負担が増大
    • 余計なフォーマットの修正が必要になり、本来のロジックの確認が後回しになる
    • 修正のたびに異なるスタイルで書かれると、レビューの効率が悪化する
  • バグの発生リスクが上昇
    • 似たようなコードの見た目が統一されていないと、ミスに気付きにくくなる
    • 条件分岐の範囲が曖昧になり、意図しない動作を引き起こす可能性がある

これらの問題を回避するために、インデントやスペースのルールを統一することが不可欠です。

C#の推奨されるフォーマット

以下のルールに従うことで、C#の一般的なスタイルに統一できます。

インデントはスペース4つ(タブ禁止)

波かっこ {} は改行して配置

1行の長さは80~120文字以内にする

制御構文(if, for, while など)は必ず {} をつける

演算子の前後にはスペースを入れる

インデント・スペースの統一【サンプル付き】

✅ 正しい書き方(統一されたフォーマット)

public class Product
{
    public string Name { get; set; }

    public void Display()
    {
        Console.WriteLine($"Product Name: {Name}");
    }

    public int CalculatePrice(int basePrice, int tax)
    {
        return basePrice + tax;
    }
}

❌ NG例(バラバラなインデント・スペース)

public class Product {
  public string Name{get;set;}

  public void Display(){
Console.WriteLine("Product Name: "+Name); }

  public int CalculatePrice(int basePrice,int tax){
return basePrice+tax;
  }
}

❌ NGコードの問題点

  • {} の位置が統一されていない
  • インデントが2スペースやタブでバラバラ
  • プロパティの get; set; にスペースがない
  • + 演算子の前後にスペースがなく、可読性が低い

チームで徹底すべきポイント

✅ インデントルール

  • スペース4つを使用(タブは禁止)
  • すべてのコードブロックを統一されたインデントで記述する

{} の配置

  • メソッドやクラスの {} は改行して配置
  • if文・ループも {} を省略しない(1行でも必ず {} をつける)
if (condition)  // ✅ 良い例
{
    DoSomething();
}
if (condition) DoSomething();  // ❌ NG(省略しない)

✅ 1行の長さ

  • 80~120文字以内に収める
  • 長くなる場合は適切に改行する
Console.WriteLine(
    "This is a very long text that should be split into multiple lines for better readability."
);

ルール③:コメントの書き方を決める

コメントの統一が重要な理由

コードの可読性を高めるためには、適切なコメントを記述することが重要です。

しかし、コメントの書き方が統一されていないと、以下のような問題が発生します。

  • 過剰なコメントによるノイズ
    • 不要なコメントが多すぎると、かえってコードが読みにくくなる
    • 「変数を宣言する」など、明らかに不要なコメントは記述しない
  • コメントが少なすぎて理解が難しい
    • 重要な処理の意図が分からず、後から読んだときに理解しづらい
    • APIの仕様やメソッドの用途が明確でないと、再利用が難しくなる
  • 書き方が統一されていない
    • 人によってコメントのスタイルがバラバラだと、統一感がなくなる
    • 変更履歴をコメントに書くと、ソース管理システムと重複して冗長になる

こうした問題を回避するために、コメントのルールを統一し、必要な情報を適切に記述することが重要です。

C#のコメントの基本ルール

XMLドキュメントコメントを活用する(メソッド・クラス・プロパティ)

冗長なコメントは書かない(コードから明らかに分かる内容は省略)

必要な箇所にだけコメントを記述する(意図や処理の目的を明確に)

TODOコメントを活用する(未実装の処理がある場合)

コメントの実践サンプル

✅ XMLドキュメントコメント(推奨)

メソッドやクラスの説明には、XMLコメントを使用するのがベストプラクティスです。

/// <summary>/// ユーザー情報を管理するクラス
/// </summary>public class User
{
    /// <summary>/// ユーザー名を取得または設定します。
    /// </summary>public string UserName { get; set; }

    /// <summary>/// プロフィールを更新します。
    /// </summary>public void UpdateProfile()
    {
        // TODO: API連携の処理を追加する
        Console.WriteLine("プロフィールを更新しました。");
    }
}

ポイント

  • /// <summary> を使い、クラス・メソッドの目的を明確に記述
  • TODO: を活用し、未実装の部分を示す

✅ 処理の意図を明確にするコメント

処理の意図が分かりにくい場合にのみ、簡潔なコメントを追加します。

public class OrderService
{
    public void ProcessOrder(int orderId)
    {
        // 在庫を確認してから注文処理を実行
        if (CheckStock(orderId))
        {
            CompleteOrder(orderId);
        }
        else
        {
            Console.WriteLine("在庫が不足しています。");
        }
    }
}

ポイント

  • 処理の流れが直感的に分かりにくい箇所にのみコメントを追加
  • 「何をしているのか」ではなく、「なぜその処理を行うのか」を説明する

❌ NG例(避けるべきコメント)

❌ 不要なコメント

public class User
{
    // ユーザー名を格納する変数
    public string UserName { get; set; }

    // プロフィールを更新するメソッド
    public void UpdateProfile()
    {
        Console.WriteLine("プロフィールを更新しました。");
    }
}

なぜNGか?

  • UserNameUpdateProfile() の役割はコードを見れば明らか
  • 無意味なコメントは可読性を下げるだけなので不要

❌ 変更履歴をコメントに書く

// 2024-01-10 修正: バグ修正
// 2024-02-05 修正: ログ出力追加
public void ProcessPayment()
{
    Console.WriteLine("支払い処理を実行");
}

なぜNGか?

  • 変更履歴はGitなどのバージョン管理で管理すべき
  • コメントで履歴を管理すると、コードが汚くなり、メンテナンスが面倒になる

チームで徹底すべきポイント

✅ クラス・メソッドの説明には /// を使う

✅ 処理の意図が不明瞭な箇所だけコメントを追加

✅ コードを見れば分かるコメントは省略

TODO: を活用して、未実装部分を明確にする

✅ 変更履歴はコメントではなく、Gitなどで管理する


ルール④:エラーハンドリングの統一【サンプル付き】

エラーハンドリングを統一する重要性

エラーハンドリングが統一されていないと、以下のような問題が発生します。

  • 例外が適切に処理されず、予期しないクラッシュが発生
    • 例外をキャッチしても何もしない場合、バグの原因が分かりにくくなる
    • 例外を適切に処理しないと、システムが不安定になる
  • エラーログがバラバラで、原因調査が困難
    • 例外のログ出力が統一されていないと、問題発生時の調査が難しくなる
    • ログなしで例外を握りつぶすと、どこでエラーが起きたのか分からなくなる
  • 例外の再スローや処理方法が統一されていない
    • throw を適切に使わないと、スタックトレースが失われてデバッグが困難になる
    • 業務ロジックとエラーハンドリングの境界が曖昧になり、コードの可読性が下がる

これらの問題を防ぐために、エラーハンドリングのルールを統一することが重要です。

C#のエラーハンドリングの基本ルール

try-catch を使用して例外を適切に処理する

catch 内で例外を握りつぶさず、ログを記録する

✅ 例外メッセージを適切に記述し、問題発生時に調査しやすくする

✅ カスタム例外を活用し、エラーの種類を明確に区別する

throw; を使用して例外を適切に再スローする


エラーハンドリングの実践サンプル

✅ 正しいエラーハンドリングの例

public class FileManager
{
    public void ReadFile(string filePath)
    {
        try
        {
            string content = File.ReadAllText(filePath);
            Console.WriteLine(content);
        }
        catch (FileNotFoundException ex)
        {
            Console.WriteLine($"ファイルが見つかりません: {ex.Message}");
            // ログを記録する(実際の環境ではログ出力を適用)
            LogError(ex);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"予期しないエラー: {ex.Message}");
            // 例外を再スローすることで、呼び出し元に通知
            throw;
        }
    }

    private void LogError(Exception ex)
    {
        // ログの記録処理(実際のシステムではLoggerを使用)
        Console.WriteLine($"[ERROR] {ex.GetType()}: {ex.Message}");
    }
}

ポイント

  • try-catch を適用し、特定の例外(FileNotFoundException)をハンドリング
  • 予期しない例外(Exception)は再スローし、問題を隠さない
  • LogError メソッドでエラー情報をログ出力し、デバッグしやすくする

❌ NG例(避けるべきエラーハンドリング)

❌ 例外を握りつぶす

public void ReadFile(string filePath)
{
    try
    {
        string content = File.ReadAllText(filePath);
        Console.WriteLine(content);
    }
    catch (Exception)
    {
        // 何もせずに例外を無視
    }
}

なぜNGか?

  • エラー発生時の情報が失われ、デバッグが困難になる
  • 問題が起きても気づけないため、重大なバグに繋がる可能性がある

throw ex; を使用してスタックトレースを失う

public void ReadFile(string filePath)
{
    try
    {
        string content = File.ReadAllText(filePath);
        Console.WriteLine(content);
    }
    catch (Exception ex)
    {
        Console.WriteLine($"エラー発生: {ex.Message}");
        throw ex; // ❌ スタックトレースが失われる
    }
}

なぜNGか?

  • throw ex; を使うと、元の例外のスタックトレースが失われ、どこで発生したエラーなのか分かりにくくなる
  • 正しくは throw; を使い、元の例外をそのまま再スローする

カスタム例外を活用する

業務ロジックに特化したエラーを扱う場合は、カスタム例外を作成するのが適切です。

✅ カスタム例外のサンプル

// ユーザー認証に関する例外
public class AuthenticationException : Exception
{
    public AuthenticationException(string message) : base(message)
    {
    }
}

public class AuthService
{
    public void Login(string username, string password)
    {
        if (username != "admin")
        {
            throw new AuthenticationException("無効なユーザー名です。");
        }
    }
}

ポイント

  • カスタム例外を定義することで、エラーの種類を明確にできる
  • 業務ロジックに応じた適切なエラーハンドリングが可能になる

チームで徹底すべきポイント

try-catch を適切に使用し、例外を握りつぶさない

catch でエラーログを記録し、デバッグしやすくする

throw; を使い、元のスタックトレースを保持する

✅ 必要に応じてカスタム例外を作成し、エラーを明確に分類する

✅ 例外の種類ごとに適切な処理を行い、一律で catch(Exception) しない


ルール⑤:ディレクトリ・ファイル構成を統一する

ディレクトリ・ファイル構成を統一する重要性

プロジェクトのディレクトリやファイルの構成が統一されていないと、以下のような問題が発生します。

  • ファイルの所在が分かりにくく、開発効率が低下
    • どのフォルダにどの種類のファイルを配置するのか決まっていないと、新しい開発者が迷う
    • 必要なファイルを探すのに時間がかかる
  • コードの責務が曖昧になり、保守が難しくなる
    • コントローラーやサービスのファイルが1つのフォルダに混在すると、依存関係が分かりにくくなる
    • 適切に分離されていないと、コードの変更時に不要な影響を与える可能性がある
  • プロジェクトの拡張が困難になる
    • 構造がバラバラのまま進めると、機能追加時に整理が必要になり、手間が増える
    • スケールするプロジェクトでは、一貫したルールがないと混乱が生じる

これらの問題を防ぐために、ディレクトリとファイル構成のルールを統一することが重要です。

C#プロジェクトの標準的なディレクトリ構成

C#の一般的なアプリケーション(ASP.NET Coreやクラスライブラリ)では、以下のようなディレクトリ構成が推奨されます。

/ProjectRoot
│── /Controllers      // APIコントローラー
│    ├── UserController.cs
│    ├── ProductController.cs
│
│── /Models           // データモデル(DTO含む)
│    ├── User.cs
│    ├── Product.cs
│
│── /Services         // ビジネスロジック
│    ├── IUserService.cs
│    ├── UserService.cs
│
│── /Repositories     // データアクセス層
│    ├── IUserRepository.cs
│    ├── UserRepository.cs
│
│── /Interfaces       // インターフェースを分離
│    ├── IEmailService.cs
│
│── /Middlewares      // カスタムミドルウェア
│    ├── ExceptionHandlingMiddleware.cs
│
│── /Configurations   // 設定ファイル関連
│    ├── AppSettings.cs
│
│── /Utils            // 共通処理(ヘルパークラス)
│    ├── StringHelper.cs
│
│── Program.cs        // アプリケーションのエントリポイント
│── appsettings.json  // 設定ファイル

ポイント

  • 責務ごとにフォルダを分離し、関連するファイルを整理
  • Interfaces フォルダを作成し、実装 (ServicesRepositories) から分離
  • 共通処理 (Utils フォルダ) や設定 (Configurations フォルダ) を適切に整理

ディレクトリ・ファイル構成のルール

フォルダごとに役割を分ける

  • Controllers → APIエンドポイントを定義
  • Models → データモデル(DTO含む)を定義
  • Services → ビジネスロジックを実装
  • Repositories → データアクセス層(DB操作)を実装
  • Interfaces → インターフェースを定義
  • Middlewares → カスタムミドルウェアを配置
  • Utils → ユーティリティ関数を管理

ファイル名とクラス名を一致させる

  • UserController.cs のクラス名は UserController にする
  • UserService.cs のクラス名は UserService にする
  • ファイル名とクラス名が一致していないと、検索が困難になる

インターフェース(Iプレフィックス)と実装を分離する

  • インターフェースは Interfaces フォルダに配置
  • 実装は ServicesRepositories に分離

設定ファイル・環境変数の管理を明確にする

  • appsettings.json に設定値を記述
  • Configurations フォルダで設定クラスを管理

サンプルコード:適切なフォルダ分離を適用した場合

Services における適切な分離

// IUserService.cs(Interfacesフォルダに配置)
public interface IUserService
{
    User GetUserById(int id);
}

// UserService.cs(Servicesフォルダに配置)
public class UserService : IUserService
{
    private readonly IUserRepository _userRepository;

    public UserService(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public User GetUserById(int id)
    {
        return _userRepository.FindById(id);
    }
}

ポイント

  • インターフェース (IUserService) を Interfaces フォルダに分離
  • 実装 (UserService) は Services フォルダに配置し、責務を明確化
  • 依存関係をコンストラクタで注入し、疎結合を実現

❌ NG例(フォルダ分離が不適切な例)

❌ すべてのコードが Controllers に詰め込まれている

public class UserController : ControllerBase
{
    private readonly UserRepository _userRepository = new UserRepository();

    [HttpGet("{id}")]
    public IActionResult GetUser(int id)
    {
        var user = _userRepository.FindById(id);
        if (user == null) return NotFound();
        return Ok(user);
    }
}

なぜNGか?

  • Controllers にリポジトリのロジックが直書きされ、責務が分離されていない
  • UserService を介さず、UserRepository を直接使用しているため、変更に弱い

チームで徹底すべきポイント

フォルダ構成をプロジェクトの責務ごとに統一する

Interfaces を作成し、インターフェースと実装を分離する

Controllers にビジネスロジックを書かず、Services を活用する

✅ ファイル名とクラス名を一致させ、検索しやすくする

ConfigurationsUtils を用いて共通処理や設定を整理する


まとめ

本記事では、C#のコーディング規約が存在しない会社で最初に作るべき基本ルール を解説しました。

チーム全体で統一したコーディングルールを導入することで、可読性・保守性の向上 だけでなく、

バグの削減・レビューの効率化・開発速度の向上 につながります。

最初に導入すべき5つの基本ルール

  1. 命名規則の統一
    • PascalCase / camelCase を適切に使い分ける
    • クラス・メソッド・変数名を一貫したスタイルにする
  2. インデント・スペースの統一
    • スペース4つのインデントを使用(タブ禁止)
    • 波かっこ {} は改行して配置し、1行の長さは80~120文字以内
  3. コメントのルールを統一
    • ///(XMLコメント)を活用し、メソッドやクラスの意図を明確化
    • 不要なコメントは書かず、コードから意図が分かる場合は省略
  4. エラーハンドリングの統一
    • try-catch を適切に使用し、例外を握りつぶさない
    • エラーログを記録し、デバッグしやすくする
    • カスタム例外を活用し、エラーの種類を明確にする
  5. ディレクトリ・ファイル構成の統一
    • 責務ごとにフォルダを分け、適切に整理
    • Interfaces を作成し、インターフェースと実装を分離する
    • 設定ファイル (Configurations) や共通処理 (Utils) を適切に管理

コーディング規約を導入するメリット

✅ コードの可読性が向上し、開発者全員が理解しやすいコードになる

✅ バグを減らし、開発の品質を向上させる

✅ コードレビューの負担を減らし、より生産的なフィードバックが可能になる

✅ チームの開発スピードが向上し、プロジェクトがスムーズに進む

✅ 保守性が高まり、新しいメンバーがプロジェクトに参加しやすくなる

まずはシンプルなルールから始めよう!

完璧なコーディング規約を最初から作る必要はありません。

まずは、基本ルールをチーム全体で守ることからスタートしましょう。

ルールを少しずつ追加・改善しながら、より良いコーディング規約を確立することが大切です。

統一されたルールで、チーム全員がストレスなく開発できる環境を作りましょう! 🚀

コメント

タイトルとURLをコピーしました