C# regionとは?使い方・折りたたみ方法・メリットと注意点を初心者向けに解説
はじめに
C#のソースコードを読んでいると、次のような記述を見かけることがあります。
C##region プロパティ
public string Name { get; set; }
public int Age { get; set; }
#endregion
#regionと#endregionは、一定範囲のコードをグループ化し、Visual Studioなどのコードエディター上で折りたためるようにするための記述です。クラスが長くなった場合でも、関連するフィールド、プロパティ、メソッドなどをまとめて非表示にできるため、編集したい部分へ集中しやすくなります。
ただし、regionを多用すると、肥大化したクラスや複雑な設計を見た目だけで隠してしまう可能性があります。便利な機能だからこそ、使い方だけでなく、メリットや注意点も理解しておくことが重要です。
本記事では、C#のregionの基本構文、コードを折りたたむ方法、効果的な使い方、避けたほうがよいケースについて、初心者にも分かりやすく解説します。
1. C#のregionとは
1-1. regionはコードを折りたたむためのプリプロセッサディレクティブ
C#のregionとは、ソースコード内に折りたたみ可能な範囲を作るためのプリプロセッサディレクティブです。
プリプロセッサディレクティブとは、先頭に#が付いた特別な命令を指します。C#には、regionのほかにも次のようなディレクティブがあります。
C##define
#if
#elif
#else
#endif
#pragma
regionを定義すると、対応しているコードエディターのアウトライン機能を使って、その範囲を展開したり折りたたんだりできます。MicrosoftのC#リファレンスでも、#regionと#endregionは、アウトライン上で折りたためるコード領域を定義するディレクティブとして説明されています。Microsoft Learn
regionは、コードそのものを削除する機能ではありません。折りたたんだ部分はエディター上で一時的に非表示になりますが、ソースファイルには残っています。
1-2. #regionと#endregionの役割
regionを作るときは、開始位置に#region、終了位置に#endregionを記述します。
C##region ユーザー情報
public string UserName { get; set; }
public void DisplayUserName()
{
Console.WriteLine(UserName);
}
#endregion
それぞれの役割は次のとおりです。
#region:折りたたみ範囲の開始を示す#endregion:折りたたみ範囲の終了を示す
#regionの後ろには、領域の内容を表す名前を記述できます。この例では「ユーザー情報」がregion名です。
region名は必須ではありませんが、何が含まれているか分かる名前を付けるのが一般的です。
C##region
public void Execute()
{
}
#endregion
名前を省略しても構文としては成立します。しかし、折りたたんだときに内容を判断しにくいため、通常は具体的な名前を付けましょう。
1-3. regionがコンパイル結果やプログラムの動作に与える影響
#regionと#endregionは、コードエディター上の表示を整理するためのディレクティブです。正しく記述されている限り、プログラムの処理内容や実行速度には影響しません。
次の2つのコードは、実行時の動作が同じです。
C#public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
}
C#public class Calculator
{
#region 計算処理
public int Add(int a, int b)
{
return a + b;
}
#endregion
}
regionを追加しても、Addメソッドの処理は変化しません。コンパイル後のプログラムに、region名や折りたたみ状態が実行処理として組み込まれるわけでもありません。
ただし、#regionに対応する#endregionを書き忘れるなど、ディレクティブの構造に誤りがある場合はコンパイルエラーになります。
1-4. regionを利用できる主な開発環境
C#のregionは、主に次のような開発環境で利用できます。
Visual Studio
Visual Studio Code
JetBrains Rider
C#のregionに対応したその他のコードエディター
Visual StudioとVisual Studio Codeでは、コード左側の折りたたみアイコンやショートカットキーを使ってregionを操作できます。Visual Studio Codeも、C#の#regionと#endregionをマーカーによる折りたたみ範囲としてサポートしています。Visual Studio Code
JetBrains Riderでも、regionの追加、削除、折りたたみなどが可能です。JetBrains+1
ただし、折りたたみアイコンの形やショートカットキーは開発環境によって異なります。
2. C#でregionを使う基本的な方法
2-1. #regionと#endregionの基本構文
regionの基本構文は次のとおりです。
C##region region名
// 折りたたみたいコード
#endregion
実際のクラスに記述すると、次のようになります。
C#public class Product
{
#region フィールド
private string _name = string.Empty;
private decimal _price;
#endregion
}
#regionと#endregionは、それぞれ独立した行に記述するのが基本です。
また、プリプロセッサディレクティブの前には空白を入れられますが、通常のC#コードと同じ行には記述しません。
C#// 分かりやすい書き方
#region メソッド
public void Save()
{
}
#endregion
regionは、クラスの内側だけでなく、名前空間やクラス全体を含む範囲にも設定できます。ただし、むやみに広い範囲を囲むと構造を把握しにくくなるため、目的に合った単位で使うことが大切です。
2-2. region名を付けてコードを整理する書き方
region名には、内部に含まれるコードを端的に表す名前を付けます。
C##region 公開プロパティ
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
#endregion
よく使われるregion名には、次のようなものがあります。
C##region フィールド
#endregion
#region コンストラクター
#endregion
#region プロパティ
#endregion
#region 公開メソッド
#endregion
#region 非公開メソッド
#endregion
#region イベント
#endregion
機能単位で分類する場合は、次のような名前も使用できます。
C##region 認証処理
#endregion
#region 入力チェック
#endregion
#region データベース処理
#endregion
#region ログ出力
#endregion
「処理」「その他」のように範囲が曖昧な名前よりも、「注文金額の計算」「ユーザー入力の検証」など、内容を予測できる名前が適しています。
2-3. メソッドやプロパティをregionでグループ化する例
次の例では、商品クラスの構成要素をregionで分類しています。
C#public class Product
{
#region フィールド
private decimal _price;
#endregion
#region コンストラクター
public Product(string name, decimal price)
{
Name = name;
Price = price;
}
#endregion
#region プロパティ
public string Name { get; set; }
public decimal Price
{
get => _price;
set
{
if (value < 0)
{
throw new ArgumentOutOfRangeException(
nameof(value),
"価格には0以上の値を指定してください。");
}
_price = value;
}
}
#endregion
#region 公開メソッド
public decimal CalculateTaxIncludedPrice(decimal taxRate)
{
return Price * (1 + taxRate);
}
#endregion
}
折りたたんだ状態では、クラスの大まかな構成だけを確認できます。
フィールド
コンストラクター
プロパティ
公開メソッド
目的のregionだけを展開すればよいため、長いファイルを上下に何度も移動する手間を減らせます。
2-4. regionを入れ子にする方法
regionの中に、別のregionを記述することもできます。
C##region ユーザー管理
#region プロパティ
public int UserId { get; set; }
public string UserName { get; set; } = string.Empty;
#endregion
#region メソッド
public void Register()
{
}
public void Delete()
{
}
#endregion
#endregion
外側の「ユーザー管理」を折りたためば、その中にあるプロパティとメソッドをまとめて非表示にできます。
ただし、入れ子が深くなるほど、どの#endregionがどの#regionに対応しているのか分かりにくくなります。実務では、1階層程度に抑えるのが分かりやすいでしょう。
終了側にもコメントを付ける方法があります。
C##region ユーザー管理
#region プロパティ
public int UserId { get; set; }
#endregion // プロパティ
#endregion // ユーザー管理
ただし、コメントの付け方はチーム内で統一することが大切です。
2-5. #ifなど他のプリプロセッサディレクティブと併用する際のルール
regionは、#ifや#endifなどの条件付きコンパイルディレクティブと併用できます。
次のように、regionの中に#ifを入れることができます。
C##region デバッグ処理
#if DEBUG
public void WriteDebugLog(string message)
{
Console.WriteLine(message);
}
#endif
#endregion
反対に、#ifの中にregionを入れることも可能です。
C##if DEBUG
#region デバッグ用メソッド
public void DisplayDebugInformation()
{
Console.WriteLine("Debug mode");
}
#endregion
#endif
ただし、regionと#ifの範囲を交差させることはできません。
次のような書き方は避ける必要があります。
C##region デバッグ処理
#if DEBUG
public void DebugMethod()
{
}
#endregion
#endif
#regionブロックと#ifブロックは、それぞれ正しく完結した入れ子構造にしなければなりません。Microsoftの公式リファレンスでも、regionと#ifは相互に入れ子にできる一方、範囲を重ねて交差させることはできないと説明されています。Microsoft Learn
3. regionでコードを折りたたむ・展開する方法
3-1. Visual Studioのボタンでregionを折りたたむ方法
Visual Studioでregionを記述すると、コードエディター左側のアウトライン領域に、マイナス記号または下向きのアイコンが表示されます。
折りたたむ手順は次のとおりです。
#regionが記述された行の左側を確認するマイナス記号または折りたたみアイコンをクリックする
region内のコードが非表示になる
再び表示する場合は、プラス記号または展開アイコンをクリックする
折りたたむと、一般的にはregion名だけが表示されます。
C##region プロパティ ...
Visual Studioでは、アウトライン領域にあるアイコンをクリックするほか、対象範囲をダブルクリックして折りたたむ操作も用意されています。Microsoft Learn
3-2. Visual Studioのショートカットキーで折りたたみ・展開する方法
Visual Studioでは、カーソルがあるregionをショートカットキーで折りたたんだり展開したりできます。
代表的なショートカットは次のとおりです。
| 操作 | ショートカット |
|---|---|
| 現在位置の折りたたみ状態を切り替える | Ctrl→M、続けてCtrl→M |
| 現在のregionを折りたたむ | Ctrl→M、続けてCtrl→S |
| 現在のregionを展開する | Ctrl→M、続けてCtrl→E |
| すべてのアウトラインを折りたたむ | Ctrl→M、続けてCtrl→A |
| すべてのアウトラインを展開する | Ctrl→M、続けてCtrl→X |
例えばCtrl+M, Ctrl+Mは、4つのキーを同時に押す操作ではありません。最初にCtrl+Mを押し、その後、もう一度Ctrl+Mを押します。
これらはVisual Studioの標準キーバインドに基づく操作です。設定しているキーボードマッピングによっては、別のキーが割り当てられている場合があります。Microsoft Learn
3-3. ファイル内のコードを一括で折りたたむ・展開する方法
現在開いているファイル内のコードをまとめて操作したい場合は、Visual Studioの「編集」メニューから「アウトライン」を選択します。
主な操作は次のとおりです。
すべてのアウトラインを折りたたむ
すべてのアウトラインを展開する
すべてのアウトラインの状態を切り替える
定義部分まで折りたたむ
アウトライン機能を停止する
ショートカットでは、次の操作が利用できます。
すべて折りたたむ:Ctrl+M, Ctrl+A
すべて展開する:Ctrl+M, Ctrl+X
すべての状態を切り替える:Ctrl+M, Ctrl+L
定義まで折りたたむ:Ctrl+M, Ctrl+O
Ctrl+M, Ctrl+Oの「定義まで折りたたむ」は、regionだけでなく、クラスやメソッドなどの本体もまとめて折りたたみたい場合に便利です。Microsoft Learn+1
3-4. Visual Studio Codeでregionを折りたたむ方法
Visual Studio Codeでも、C#の#regionと#endregionを使ってコードを折りたためます。
C##region 注文処理
public void CreateOrder()
{
}
public void CancelOrder()
{
}
#endregion
行番号の右側に表示される折りたたみアイコンをクリックすると、regionを折りたためます。
WindowsおよびLinuxで使える代表的なショートカットは次のとおりです。
| 操作 | ショートカット |
|---|---|
| カーソル位置を折りたたむ | Ctrl+Shift+[ |
| カーソル位置を展開する | Ctrl+Shift+] |
| 折りたたみ状態を切り替える | Ctrl+K、続けてCtrl+L |
| すべて折りたたむ | Ctrl+K、続けてCtrl+0 |
| すべて展開する | Ctrl+K、続けてCtrl+J |
| regionマーカーだけを折りたたむ | Ctrl+K、続けてCtrl+8 |
| regionマーカーだけを展開する | Ctrl+K、続けてCtrl+9 |
macOSでは、Ctrlの代わりにCommandキーやOptionキーを使う組み合わせがあります。使用しているOSやキーバインド設定に合わせて確認してください。Visual Studio Code
3-5. regionが折りたためない場合に確認すること
regionを記述しても折りたためない場合は、次の点を確認しましょう。
まず、#regionと#endregionが正しく対応しているか確認します。
C##region メソッド
public void Execute()
{
}
// #endregionがない
#endregionが不足していると、regionが正しく認識されず、コンパイルエラーの原因にもなります。
次に、スペルを確認します。
C#// 誤り
#regoin メソッド
// 正しい
#region メソッド
ほかにも、次のような原因が考えられます。
#ifなどのディレクティブと範囲が交差しているコードエディターのアウトライン機能が無効になっている
ファイルがC#として認識されていない
Visual Studio Codeの言語モードがPlain Textになっている
拡張機能の読み込みに問題がある
キーボードショートカットが変更されている
Visual Studioでアウトライン機能を停止している場合は、「編集」から「アウトライン」を開き、自動アウトラインを再開します。
4. C#でregionを使うメリット
4-1. 長いソースコードの見通しをよくできる
regionの代表的なメリットは、長いソースコードの表示を整理できることです。
例えば、数百行あるクラスでも、次のように分類して折りたためば、全体の構成を把握しやすくなります。
コンストラクター
プロパティ
入力チェック
データ取得
データ更新
ログ出力
コードの行数自体は減りませんが、画面上に表示される情報量を抑えられます。現在確認する必要がない部分を閉じることで、目的のコードへ移動しやすくなります。
4-2. 関連するフィールド・プロパティ・メソッドを分類できる
regionを使うと、関連するメンバーをひとまとまりにできます。
C##region 検索条件
public string Keyword { get; set; } = string.Empty;
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
private bool IsValidSearchCondition()
{
return StartDate <= EndDate;
}
#endregion
単に「プロパティ」「メソッド」と種類で分けるだけでなく、「検索」「認証」「注文」など、機能単位で分類することも可能です。
ただし、1つのクラスに独立した機能が多数存在する場合は、regionで分類する前にクラス分割を検討する必要があります。
4-3. 編集中の処理に集中しやすくなる
現在編集していない部分を折りたたむと、対象となる処理だけを画面に残せます。
例えば、入力チェックを修正している場合は、データベース処理やログ処理のregionを閉じておくことで、関係のないコードが視界に入りにくくなります。
プロパティ
入力チェック:展開
データベース処理:折りたたみ
ログ処理:折りたたみ
特に、同じファイル内を何度も行き来する作業では、スクロール量を減らす効果が期待できます。
4-4. 自動生成コードや定型コードを非表示にできる
自動生成されたコードや、普段あまり編集しない定型的なコードをregionで囲む使い方もあります。
C##region 自動生成コード
public override bool Equals(object? obj)
{
return obj is Product product &&
Id == product.Id;
}
public override int GetHashCode()
{
return HashCode.Combine(Id);
}
#endregion
イベントハンドラー、インターフェースの実装、デザイナー関連のコードなど、必要ではあるものの頻繁には編集しない部分を折りたたむと、主要なロジックを確認しやすくなります。
ただし、自動生成コードを手動で編集する必要がない場合は、別ファイルやpartialクラスに分離する方法も検討しましょう。
4-5. チーム内でファイル構成を統一しやすくなる
チームでregionの使用ルールを決めると、クラス内の並び順や分類を統一できます。
例えば、次の順番にすると決めておけば、初めて開いたファイルでも目的のメンバーを探しやすくなります。
1. 定数
2. フィールド
3. コンストラクター
4. プロパティ
5. 公開メソッド
6. 非公開メソッド
一方で、開発者ごとにregion名や分類方法が異なると、かえって読みづらくなります。使用する場合は、コーディング規約として基準を共有することが重要です。
5. C#でregionを使う際のデメリットと注意点
5-1. regionの多用はコードの複雑さを隠してしまう
regionを折りたたむと、数百行のコードでも短く整理されたように見えます。
しかし、実際のコード量や複雑さが減ったわけではありません。
注文処理
顧客処理
在庫処理
決済処理
メール処理
ログ処理
このようなregionが1つのクラスに並んでいる場合、そのクラスが多くの責務を持ちすぎている可能性があります。
regionによって見た目を整える前に、クラスを分割できないか検討しましょう。
5-2. 折りたたまれたコードの見落としが起こりやすい
regionが閉じられていると、その中に重要な処理があっても目に入りません。
例えば、次のregion内に副作用のある処理が隠れているとします。
C##region 初期化
public void Initialize()
{
DeleteTemporaryFiles();
ResetDatabaseConnection();
SendStartupNotification();
}
#endregion
region名だけを見て単純な初期化処理だと判断すると、ファイル削除や通知送信が行われることを見落とす可能性があります。
コードレビューでは、regionを展開して内部を確認する習慣が必要です。
5-3. region名と実際の処理内容がずれることがある
コードの変更を重ねると、region名と内部の処理が一致しなくなる場合があります。
C##region 読み取り処理
public Product GetProduct(int id)
{
// 取得処理
}
public void DeleteProduct(int id)
{
// 削除処理
}
#endregion
この例では、「読み取り処理」という名前の中に削除処理が含まれています。
region内のコードを追加、移動、削除した場合は、region名も見直しましょう。名前と内容が一致していないregionは、コード理解を妨げます。
5-4. 深い入れ子は可読性と保守性を低下させる
regionは入れ子にできますが、階層が深くなると対応関係を追いにくくなります。
C##region 注文
#region 作成
#region 検証
// 処理
#endregion
#endregion
#endregion
このような構成では、目的のコードを確認するために複数のregionを開かなければなりません。
深い階層が必要になる場合は、メソッドやクラスの分割を検討したほうがよいでしょう。
5-5. #endregionの書き忘れでコンパイルエラーが発生する
#regionを記述した場合は、必ず対応する#endregionが必要です。
C##region プロパティ
public string Name { get; set; }
// #endregionがない
この状態では、コンパイラから#endregion directive expectedに相当するエラーが報告されます。
regionを追加した後は、開始と終了が正しく対応しているか確認してください。特にregionを入れ子にした場合や、大きなコードを移動した場合は注意が必要です。
5-6. regionはコード設計上の問題を解決する機能ではない
regionで解決できるのは、主にコードエディター上の表示です。
次のような問題を直接解決することはできません。
クラスの責務が多すぎる
メソッドが長すぎる
依存関係が複雑である
重複コードが多い
名前が分かりにくい
テストしにくい構造になっている
regionを追加して読みやすくなったように感じても、設計上の問題が残っている可能性があります。
「折りたためば読みやすい」ではなく、「展開した状態でも理解しやすい」コードを目指しましょう。
6. regionを効果的に使うためのルール
6-1. regionを使う目的を明確にする
regionを追加する前に、何のために使うのかを明確にします。
適切な目的には、次のようなものがあります。
関連するメンバーを分類する
普段編集しない定型コードを折りたたむ
インターフェースごとの実装をまとめる
長いファイル内の移動をしやすくする
単に「コードが長く見えるから」という理由だけで追加すると、設計上の問題を隠してしまうことがあります。
6-2. 内容が分かる具体的なregion名を付ける
region名は、中身を開かなくても内容を推測できるようにします。
分かりにくい例は次のとおりです。
C##region 処理
#endregion
#region その他
#endregion
#region 共通
#endregion
より具体的な名前にすると、目的が伝わりやすくなります。
C##region 注文データの入力検証
#endregion
#region 決済結果の変換処理
#endregion
#region エラーログの出力
#endregion
ただし、region名が長すぎると一覧性が低下します。内部の役割を簡潔に表現しましょう。
6-3. アクセス修飾子や機能単位で分類する
regionの分類方法には、大きく分けてメンバーの種類による分類と、機能による分類があります。
メンバーの種類で分ける例は次のとおりです。
C##region Public Methods
#endregion
#region Private Methods
#endregion
機能単位で分ける例は次のとおりです。
C##region User Authentication
#endregion
#region Password Reset
#endregion
どちらが適切かは、クラスの役割やチームの方針によって異なります。
小さなクラスではメンバーの種類による分類、複数の関連処理があるクラスでは機能単位の分類が分かりやすい場合があります。ただし、機能単位のregionが増えすぎる場合は、クラス分割のサインと考えましょう。
6-4. 1つのregionに大量のコードを詰め込まない
1つのregionに数百行のコードを入れると、regionを開いた後の見通しが悪くなります。
C##region 全処理
// 数百行のコード
#endregion
この使い方では、コードを一時的に隠しているだけで、整理にはなっていません。
region内のコードが大きくなった場合は、次の対応を検討します。
長いメソッドを分割する
関連する処理を別クラスへ移動する
partialクラスでファイルを分ける
不要な処理を削除する
regionは、意味のあるまとまりを示すために使いましょう。
6-5. 入れ子を最小限に抑える
regionの入れ子は、原則として使わないか、1階層程度に抑えると分かりやすくなります。
C##region 注文処理
#region 公開メソッド
#endregion
#region 非公開メソッド
#endregion
#endregion
これ以上の階層が必要になる場合は、コード構造そのものが複雑になっていないか確認しましょう。
入れ子を減らすと、#regionと#endregionの対応も追いやすくなり、書き忘れや配置ミスを防げます。
6-6. チームのコーディング規約に使用方針を定める
regionの評価は、開発チームやプロジェクトによって異なります。
積極的に使用するチームもあれば、原則禁止しているチームもあります。そのため、次のような内容をコーディング規約に定めておくとよいでしょう。
regionを使用してよい場所
region名の命名ルール
分類方法
入れ子を許可するか
自動生成コードへの使用方針
コードレビュー時の確認方法
個人の好みだけで追加したり削除したりすると、不要な差分や議論が増えます。既存プロジェクトでは、現在の規約と周囲のコードに合わせることが基本です。
7. regionを使わないほうがよいケース
7-1. クラスやファイルが肥大化している場合
1つのクラスが数千行あり、多数のregionで分割されている場合は、クラスを分割すべき可能性があります。
顧客管理
商品管理
注文管理
在庫管理
決済管理
通知管理
これらが同じクラスに存在するなら、それぞれを独立したクラスへ分けることを検討します。
C#CustomerService
ProductService
OrderService
InventoryService
PaymentService
NotificationService
regionを削除しても理解しやすいサイズにすることが理想です。
7-2. メソッドの処理が長すぎる場合
長いメソッドの一部分をregionで囲んでも、メソッド自体の複雑さは解消されません。
C#public void ProcessOrder()
{
#region 入力チェック
// 長い処理
#endregion
#region 在庫確認
// 長い処理
#endregion
#region 決済
// 長い処理
#endregion
}
この場合は、それぞれをメソッドとして抽出するほうが分かりやすくなります。
C#public void ProcessOrder()
{
ValidateOrder();
CheckInventory();
ProcessPayment();
}
処理の流れがメソッド名で表現されるため、regionを開閉しなくても全体像を理解できます。
7-3. 複数の責務を1つのクラスが持っている場合
認証、データ保存、メール送信、帳票作成など、性質の異なる処理が1つのクラスに集まっている場合、regionで分類するだけでは不十分です。
C##region 認証
#endregion
#region データベース
#endregion
#region メール送信
#endregion
#region PDF出力
#endregion
このような構成では、責務ごとにクラスを分け、必要な機能を組み合わせる設計を検討します。
C#AuthenticationService
UserRepository
EmailService
ReportGenerator
クラスを分割すると、変更の影響範囲が限定され、単体テストも作成しやすくなります。
7-4. regionを開かなければ処理の流れを把握できない場合
重要な処理が複数のregionに隠され、すべて開かなければ流れが分からない状態は避けるべきです。
処理の中心となるメソッドは、次のように概要を読める形が理想です。
C#public async Task CompleteOrderAsync(Order order)
{
Validate(order);
await ReserveInventoryAsync(order);
await ChargeAsync(order);
await SendConfirmationAsync(order);
}
このコードなら、各メソッドの内部を開かなくても、注文完了処理の流れを理解できます。
regionによる折りたたみではなく、適切なメソッド名と処理分割によって可読性を高めましょう。
7-5. 見た目を整えることだけが目的になっている場合
短いクラスに細かなregionを多数追加すると、ディレクティブの行数が増え、かえって読みづらくなります。
C#public class User
{
#region プロパティ
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
#endregion
#region メソッド
public void Rename(string name)
{
Name = name;
}
#endregion
}
この程度の大きさであれば、regionを使わなくても十分に把握できます。
C#public class User
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public void Rename(string name)
{
Name = name;
}
}
regionを追加することで本当に読みやすくなるかを判断しましょう。
8. regionの代わりに検討したいコード整理方法
8-1. 長いメソッドを小さなメソッドへ分割する
長い処理は、役割ごとに小さなメソッドへ分割します。
分割前の例は次のとおりです。
C#public void RegisterUser(User user)
{
// 名前の検証
// メールアドレスの検証
// パスワードの検証
// データベースへの保存
// 確認メールの送信
}
分割すると、処理の流れが明確になります。
C#public void RegisterUser(User user)
{
ValidateUser(user);
SaveUser(user);
SendConfirmationEmail(user);
}
private void ValidateUser(User user)
{
ValidateName(user.Name);
ValidateEmail(user.Email);
ValidatePassword(user.Password);
}
メソッド名が説明の役割を持つため、regionを使わなくてもコードの意図を読み取れます。
8-2. 責務ごとにクラスを分割する
1つのクラスが複数の責務を持っている場合は、責務ごとにクラスを分けます。
例えば、次のクラスがユーザー保存とメール送信を担当しているとします。
C#public class UserService
{
public void SaveUser(User user)
{
}
public void SendWelcomeEmail(User user)
{
}
}
メール送信を別クラスへ移動すると、役割が明確になります。
C#public class UserService
{
private readonly UserRepository _repository;
private readonly EmailService _emailService;
public UserService(
UserRepository repository,
EmailService emailService)
{
_repository = repository;
_emailService = emailService;
}
public void Register(User user)
{
_repository.Save(user);
_emailService.SendWelcomeEmail(user);
}
}
クラス分割は、コード量を物理的に分けるだけでなく、変更理由を分離する効果があります。
8-3. partialクラスでファイルを分割する
partialクラスを使うと、1つのクラス定義を複数のファイルへ分割できます。
C#// Customer.cs
public partial class Customer
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
}
C#// Customer.Validation.cs
public partial class Customer
{
public bool IsValid()
{
return Id > 0 && !string.IsNullOrWhiteSpace(Name);
}
}
コンパイル時には1つのクラスとして扱われます。
partialクラスは、次のようなケースで特に有効です。
自動生成コードと手書きコードを分けたい
デザイナー生成コードを別ファイルに置きたい
大きなクラスを機能別ファイルに分けたい
ただし、partialを使ってもクラスの責務が減るわけではありません。責務が異なる場合は、別クラスへの分割を優先しましょう。
8-4. インターフェースや継承を活用して設計を整理する
複数の処理をregionで分類している場合、インターフェースを使って役割を明確にできることがあります。
C#public interface IOrderValidator
{
void Validate(Order order);
}
public interface IPaymentProcessor
{
void ProcessPayment(Order order);
}
public interface IOrderNotifier
{
void SendNotification(Order order);
}
実装を別クラスへ分ければ、注文処理のクラスは各機能を組み合わせる役割に集中できます。
C#public class OrderService
{
private readonly IOrderValidator _validator;
private readonly IPaymentProcessor _paymentProcessor;
private readonly IOrderNotifier _notifier;
public OrderService(
IOrderValidator validator,
IPaymentProcessor paymentProcessor,
IOrderNotifier notifier)
{
_validator = validator;
_paymentProcessor = paymentProcessor;
_notifier = notifier;
}
public void Complete(Order order)
{
_validator.Validate(order);
_paymentProcessor.ProcessPayment(order);
_notifier.SendNotification(order);
}
}
継承については、共通の性質を持つ型の整理には有効ですが、コードを分割するためだけに使うべきではありません。継承関係が本当に成立するかを確認する必要があります。
8-5. IDEの標準アウトライン機能を活用する
Visual StudioやVisual Studio Codeは、regionを記述しなくても、クラス、メソッド、プロパティ、条件分岐などを自動的に折りたためます。
例えば、次のメソッドにはregionがありませんが、メソッド本体を折りたためます。
C#public void Execute()
{
Console.WriteLine("Start");
Console.WriteLine("End");
}
Visual Studioでは「定義まで折りたたむ」、Visual Studio Codeでは「すべて折りたたむ」などの機能を利用できます。
regionを追加する前に、IDEが標準で提供しているアウトライン機能だけで十分ではないか確認しましょう。
9. C#のregionに関するよくある質問
9-1. regionを使うと処理速度に影響する?
正しく記述されたregionは、プログラムの処理速度に影響しません。
#regionと#endregionはコードエディター上で折りたたみ範囲を示すためのもので、実行時にregionの開始や終了を判定する処理が行われるわけではありません。
そのため、regionの有無によってメソッドの実行時間やメモリ使用量が増減することは基本的にありません。
9-2. region内にクラスやメソッドを記述できる?
region内には、クラス、メソッド、プロパティ、フィールド、名前空間など、さまざまなコードを記述できます。
C##region クラス定義
public class Customer
{
public int Id { get; set; }
public void Save()
{
}
}
#endregion
regionはC#のスコープを作る機能ではありません。そのため、regionで囲んだからといって、変数の有効範囲やアクセス権が変化することはありません。
通常のC#構文として正しい場所にコードを記述する必要があります。
9-3. regionは何階層まで入れ子にできる?
regionは入れ子にできますが、実用上は深い階層を作らないほうがよいでしょう。
C##region レベル1
#region レベル2
#region レベル3
// コード
#endregion
#endregion
#endregion
構文上入れ子にできても、階層が深いほど対応関係を追いにくくなります。
2階層以上の入れ子が必要になった場合は、クラスやファイルを分割できないか検討するのがおすすめです。
9-4. regionの範囲をまたいで#ifを記述できる?
regionと#ifの範囲を交差させることはできません。
次の構造は不適切です。
C##region デバッグ処理
#if DEBUG
public void DebugMethod()
{
}
#endregion
#endif
regionと#ifは、どちらかをもう一方の中へ完全に含める必要があります。
C##region デバッグ処理
#if DEBUG
public void DebugMethod()
{
}
#endif
#endregion
または、次のように記述します。
C##if DEBUG
#region デバッグ処理
public void DebugMethod()
{
}
#endregion
#endif
開始と終了の順序を守り、正しい入れ子構造にしてください。Microsoft Learn
9-5. regionとpartialクラスはどのように使い分ける?
regionは、1つのファイル内でコードを折りたたむために使います。ソースファイル自体は分割されません。
一方、partialクラスは、1つのクラスを複数のソースファイルへ物理的に分割します。
region
・同じファイル内で分類する
・エディター上で折りたためる
・ファイルの行数は減らない
partialクラス
・複数のファイルに分割する
・自動生成部分と手書き部分を分離できる
・ファイル単位で管理できる
数十行程度の関連コードを整理するならregion、明確に分離できる大きなコード群を別ファイルへ移すならpartialクラスが候補になります。
ただし、異なる責務を分離したい場合は、partialではなく別クラスへの分割が適切です。
9-6. regionは実務のC#開発で使わないほうがよい?
regionを一律に使わないほうがよいとは限りません。
自動生成コード、インターフェースの実装、頻繁に編集しない定型コードなどを整理する目的では有効です。一方で、肥大化したクラスや長すぎるメソッドを隠すために使うのはおすすめできません。
実務では、次の順番で判断するとよいでしょう。
メソッドやクラスを適切に分割できないか考える
IDEの標準アウトライン機能で十分か確認する
それでもファイル内の分類が必要ならregionを使う
チームのコーディング規約に従う
regionは、適切な設計を補助するための表示機能として使うことが重要です。
まとめ
C#のregionは、#regionと#endregionで囲んだコードを、Visual StudioやVisual Studio Codeなどのコードエディター上で折りたためるようにするプリプロセッサディレクティブです。
基本的な書き方は次のとおりです。
C##region プロパティ
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
#endregion
regionを使うと、長いソースコードの見通しをよくし、関連するフィールド、プロパティ、メソッドを分類できます。編集中の処理へ集中しやすくなる点もメリットです。
一方で、regionを多用すると、肥大化したクラスや複雑な処理を隠してしまいます。深い入れ子、曖昧なregion名、内容と名前の不一致にも注意が必要です。
コードが長い場合は、regionを追加する前に、次の方法を検討しましょう。
長いメソッドを分割する
責務ごとにクラスを分ける
partialクラスでファイルを分割する
インターフェースを使って役割を整理する
IDEの標準アウトライン機能を利用する
regionはコード設計上の問題を解決する機能ではありません。展開した状態でも理解しやすいコードを前提として、必要な場所に限定して活用することが大切です。

