C#のpartialとは?partial class/partial methodの使い方と実践例をわかりやすく解説
はじめに
C#には、1つのクラスやメソッドの定義を複数の場所に分けて書けるpartialというキーワードがあります。
partialは、特に自動生成コードと手書きコードを分けたいときや、Windows Forms、WPF、Entity Frameworkなどのフレームワークを使うときによく登場します。
たとえば、1つのクラスを次のように複数ファイルに分割できます。
C#public partial class User
{
public string Name { get; set; }
}
C#public partial class User
{
public void SayHello()
{
Console.WriteLine($"こんにちは、{Name}さん");
}
}
この2つのUserクラスは、コンパイル時には1つのクラスとして扱われます。
この記事では、C#のpartialについて、partial classとpartial methodの基本から実践的な使いどころ、メリット・注意点まで、初心者にもわかりやすく解説します。
1. C#のpartialとは何か
C#のpartialとは、クラスやメソッドなどの定義を複数の宣言に分けて書くためのキーワードです。
日本語では「部分的な」「一部の」という意味があり、C#では「1つの型やメソッドを複数の部分に分けて定義する」という役割を持ちます。
1-1. partialの意味と役割
partialを使うと、1つのクラスを複数のファイルに分割できます。
通常、C#では1つのクラスを1つのファイルに書くことが多いです。
C#public class User
{
public string Name { get; set; }
public void SayHello()
{
Console.WriteLine($"こんにちは、{Name}さん");
}
}
しかし、クラスが大きくなると、プロパティ、メソッド、イベント、バリデーション処理などが1つのファイルに集まり、見通しが悪くなることがあります。
そこでpartialを使うと、次のように分けられます。
C#public partial class User
{
public string Name { get; set; }
}
C#public partial class User
{
public void SayHello()
{
Console.WriteLine($"こんにちは、{Name}さん");
}
}
このように分けても、C#コンパイラは最終的に1つのUserクラスとして扱います。
1-2. partialでできること
C#のpartialでは、主に次のようなことができます。
partial classを使うと、1つのクラスを複数のファイルに分割できます。
partial methodを使うと、メソッドの宣言部分と実装部分を分けられます。
また、クラスだけでなく、構造体、インターフェイス、レコードなどにもpartialを使える場合があります。
C#public partial class SampleClass
{
}
C#public partial struct SampleStruct
{
}
C#public partial interface ISample
{
}
C#public partial record UserRecord
{
}
ただし、実務で特によく使われるのはpartial classとpartial methodです。
1-3. partialが使われる主な場面
partialがよく使われる場面は、自動生成コードが関係する場面です。
たとえば、Windows Formsでは、フォームの画面部品に関するコードが自動生成されます。一方で、ボタンクリック時の処理などは開発者が手で書きます。
このとき、自動生成されたコードと手書きコードを同じファイルに混ぜてしまうと、再生成時に手書き部分が消える危険があります。
そこでpartial classを使い、次のように分けます。
C#// Form1.cs
public partial class Form1
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("クリックされました");
}
}
C#// Form1.Designer.cs
public partial class Form1
{
private void InitializeComponent()
{
// 画面部品の初期化コード
}
}
このように分けることで、自動生成コードと手書きコードを安全に共存させられます。
1-4. partialはクラスを分割してもコンパイル時に1つになる
partialで分割されたクラスは、実行時に別々のクラスになるわけではありません。
コンパイル時にすべての部分が統合され、1つのクラスとして扱われます。
たとえば、次の2つのファイルがあるとします。
C#// User.Properties.cs
public partial class User
{
public string Name { get; set; }
}
C#// User.Methods.cs
public partial class User
{
public void ShowName()
{
Console.WriteLine(Name);
}
}
これは、コンパイル後には次のような1つのクラスと同じ意味になります。
C#public class User
{
public string Name { get; set; }
public void ShowName()
{
Console.WriteLine(Name);
}
}
つまり、partialは「クラスを物理的なファイル上で分けて書くための仕組み」であり、オブジェクト指向の継承や委譲とは異なります。
2. partial classの基本
partial classは、C#のpartialの中でも最もよく使われる機能です。
特に、クラスの定義を複数ファイルに分けたい場合や、コード生成ツールと組み合わせる場合に便利です。
2-1. partial classとは
partial classとは、1つのクラス定義を複数の部分に分けて記述できる仕組みです。
通常のクラスは次のように書きます。
C#public class Product
{
public string Name { get; set; }
public int Price { get; set; }
public int GetTaxIncludedPrice()
{
return (int)(Price * 1.1);
}
}
これをpartial classで分割すると、次のようになります。
C#public partial class Product
{
public string Name { get; set; }
public int Price { get; set; }
}
C#public partial class Product
{
public int GetTaxIncludedPrice()
{
return (int)(Price * 1.1);
}
}
どちらも同じProductクラスの一部です。
2-2. partial classの基本構文
partial classの基本構文は次のとおりです。
C#アクセス修飾子 partial class クラス名
{
// メンバー
}
具体例は次のようになります。
C#public partial class Customer
{
public string Name { get; set; }
}
別のファイルに同じクラス名で、同じようにpartialを付けます。
C#public partial class Customer
{
public void Display()
{
Console.WriteLine(Name);
}
}
ポイントは、分割するすべてのクラス宣言にpartialを付けることです。
片方だけにpartialを付けても正しく分割できません。
2-3. 複数ファイルにクラスを分ける書き方
実務では、ファイル名で役割を分けることがよくあります。
たとえば、Orderクラスを次のように分割します。
C#// Order.cs
public partial class Order
{
public int Id { get; set; }
public DateTime OrderDate { get; set; }
}
C#// Order.Calculation.cs
public partial class Order
{
public int TotalPrice { get; set; }
public int CalculateTax()
{
return (int)(TotalPrice * 0.1);
}
}
C#// Order.Validation.cs
public partial class Order
{
public bool IsValid()
{
return TotalPrice > 0;
}
}
このように、プロパティ、計算処理、検証処理などをファイル単位で分けることができます。
ただし、分けすぎると逆に探しにくくなるため、一定のルールを決めて使うことが大切です。
2-4. partial classでフィールド・プロパティ・メソッドを分ける例
partial classでは、フィールド、プロパティ、メソッドなどを別々のファイルに分けられます。
C#// Employee.Fields.cs
public partial class Employee
{
private int _salary;
}
C#// Employee.Properties.cs
public partial class Employee
{
public string Name { get; set; }
public int Salary
{
get { return _salary; }
set { _salary = value; }
}
}
C#// Employee.Methods.cs
public partial class Employee
{
public void PrintInfo()
{
Console.WriteLine($"{Name}: {Salary}円");
}
}
ここで重要なのは、別ファイルにある_salaryやNameにも、同じクラス内のメンバーとしてアクセスできる点です。
partialで分けても、最終的には1つのクラスになるため、通常のクラス内メンバーと同じように扱えます。
2-5. partial classを使うときのコンパイル上のルール
partial classを使うときには、いくつかのルールがあります。
まず、分割されたすべての定義にpartialキーワードが必要です。
C#public partial class Sample
{
}
C#public partial class Sample
{
}
次に、同じ名前空間にある必要があります。
C#namespace MyApp.Models
{
public partial class User
{
}
}
C#namespace MyApp.Models
{
public partial class User
{
}
}
片方が別の名前空間にあると、別のクラスとして扱われます。
C#namespace MyApp.Models
{
public partial class User
{
}
}
namespace MyApp.Entities
{
public partial class User
{
}
}
この場合、MyApp.Models.UserとMyApp.Entities.Userは別の型です。
また、アクセス修飾子は矛盾しないようにする必要があります。
C#public partial class User
{
}
C#public partial class User
{
}
同じ型を分割しているため、片方だけpublic、片方だけprivateのように矛盾した指定はできません。
さらに、同じメンバーを重複して定義することはできません。
C#public partial class User
{
public string Name { get; set; }
}
C#public partial class User
{
// 同じプロパティ名を重複定義しているためエラー
public string Name { get; set; }
}
partial classは便利ですが、通常のクラスと同じコンパイルルールに従う点を理解しておきましょう。
3. partial classの実践的な使いどころ
partial classは、単にファイルを分けるためだけの機能ではありません。
実務では、自動生成コードとの分離、UIフレームワーク、ORM、チーム開発などでよく使われます。
3-1. 自動生成コードと手書きコードを分ける
partial classの代表的な使いどころは、自動生成コードと手書きコードの分離です。
コード生成ツールが作成するファイルは、再生成によって上書きされることがあります。
もし自動生成ファイルに直接コードを書き足してしまうと、再生成時に手書きコードが消えてしまう可能性があります。
そこで、生成されるコード側を次のようにします。
C#// User.Generated.cs
public partial class User
{
public string Name { get; set; }
public int Age { get; set; }
}
そして、開発者が書くコードは別ファイルにします。
C#// User.Custom.cs
public partial class User
{
public bool IsAdult()
{
return Age >= 18;
}
}
このようにすれば、自動生成ファイルが上書きされても、User.Custom.csに書いたコードは残ります。
3-2. Windows FormsやWPFで使われるpartial class
Windows Formsでは、フォームクラスがpartial classとして作られます。
C#// Form1.cs
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void buttonSave_Click(object sender, EventArgs e)
{
MessageBox.Show("保存しました");
}
}
C#// Form1.Designer.cs
partial class Form1
{
private Button buttonSave;
private void InitializeComponent()
{
buttonSave = new Button();
buttonSave.Text = "保存";
buttonSave.Click += buttonSave_Click;
}
}
Form1.Designer.csには、画面上のボタンやテキストボックスなどを作成するコードが入ります。
開発者は主にForm1.csにイベント処理を書きます。
WPFでも、XAMLから生成されるコードと、開発者が書くコードビハインドがpartial classでつながります。
C#public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
このように、UI開発ではpartial classが自然に使われています。
3-3. Entity Frameworkやコード生成ツールで使われるpartial class
Entity FrameworkなどのORMや、独自のコード生成ツールでもpartial classが使われることがあります。
たとえば、データベースのテーブルから次のようなクラスが自動生成されるとします。
C#// Product.Generated.cs
public partial class Product
{
public int Id { get; set; }
public string Name { get; set; }
public int Price { get; set; }
}
ここに手書きで便利メソッドを追加したい場合、自動生成ファイルを直接編集するのではなく、別ファイルを作成します。
C#// Product.Custom.cs
public partial class Product
{
public string GetDisplayName()
{
return $"{Name}({Price}円)";
}
}
こうすることで、データベース変更によりProduct.Generated.csが再生成されても、手書きのGetDisplayNameメソッドは消えません。
3-4. 大きなクラスを機能ごとに分割する
大きなクラスを機能ごとに分けたいときにもpartial classは使えます。
たとえば、ReportServiceというクラスに、データ取得、集計、出力、ログ処理がある場合です。
C#// ReportService.Fetch.cs
public partial class ReportService
{
public void FetchData()
{
Console.WriteLine("データを取得します");
}
}
C#// ReportService.Calculate.cs
public partial class ReportService
{
public void Calculate()
{
Console.WriteLine("集計します");
}
}
C#// ReportService.Export.cs
public partial class ReportService
{
public void Export()
{
Console.WriteLine("出力します");
}
}
ただし、この使い方には注意が必要です。
クラスが大きくなりすぎている場合、本来はクラスを分割して責務を分けるべきかもしれません。
partial classでファイルを分けるだけでは、設計上の問題は解決しません。
3-5. チーム開発で担当範囲を分けやすくする
チーム開発では、複数人が同じクラスを編集することがあります。
1つのファイルに全員が変更を加えると、コンフリクトが発生しやすくなります。
partial classを使えば、機能ごとにファイルを分けられるため、担当範囲を分けやすくなります。
C#// User.Authentication.cs
public partial class UserService
{
public bool Login(string userName, string password)
{
return true;
}
}
C#// User.Profile.cs
public partial class UserService
{
public void UpdateProfile(string userName)
{
Console.WriteLine($"{userName}のプロフィールを更新しました");
}
}
ただし、チーム開発でpartialを使う場合は、ファイル名や分割ルールを決めておくことが重要です。
ルールがないまま自由に分割すると、どこに何が書いてあるのかわかりにくくなります。
4. partial methodの基本
partial methodは、メソッドの宣言と実装を分けるための仕組みです。
特に、自動生成コードに対して、開発者が任意の処理を追加できるようにする目的で使われます。
4-1. partial methodとは
partial methodとは、partialで定義された型の中で、メソッドの宣言部分と実装部分を分けて書ける機能です。
たとえば、自動生成コード側に次のようなメソッド呼び出しを用意します。
C#public partial class User
{
public string Name { get; set; }
public void Save()
{
OnSaving();
Console.WriteLine("保存しました");
OnSaved();
}
partial void OnSaving();
partial void OnSaved();
}
そして、別ファイルで必要な処理だけ実装します。
C#public partial class User
{
partial void OnSaving()
{
Console.WriteLine("保存前の処理です");
}
partial void OnSaved()
{
Console.WriteLine("保存後の処理です");
}
}
このように、partial methodは「必要なら実装できる拡張ポイント」として使えます。
4-2. partial methodの基本構文
partial methodの基本構文は次のようになります。
C#partial void メソッド名();
宣言部分は次のように書きます。
C#public partial class Sample
{
partial void OnCompleted();
}
実装部分は、同じpartial型の別の場所に書きます。
C#public partial class Sample
{
partial void OnCompleted()
{
Console.WriteLine("完了しました");
}
}
昔からある典型的なpartial methodは、戻り値がvoidで、アクセス修飾子を付けずに宣言します。
この形式では、実装がなくてもコンパイルできます。
4-3. 宣言部分と実装部分の書き方
partial methodは、宣言部分と実装部分を分けて書きます。
C#// Order.Generated.cs
public partial class Order
{
public void Submit()
{
OnSubmitting();
Console.WriteLine("注文を送信しました");
OnSubmitted();
}
partial void OnSubmitting();
partial void OnSubmitted();
}
C#// Order.Custom.cs
public partial class Order
{
partial void OnSubmitting()
{
Console.WriteLine("注文送信前のチェックを行います");
}
partial void OnSubmitted()
{
Console.WriteLine("注文送信後の通知を行います");
}
}
Submitメソッド内では、OnSubmittingとOnSubmittedを呼び出しています。
実装があればその処理が実行されます。
実装がなければ、呼び出し自体がコンパイル時に取り除かれる形式もあります。
4-4. 実装しないpartial methodはどう扱われるか
戻り値がvoidで、アクセス修飾子を持たない従来形式のpartial methodは、実装しなくても問題ありません。
C#public partial class Sample
{
public void Execute()
{
OnExecuting();
Console.WriteLine("実行しました");
}
partial void OnExecuting();
}
このOnExecutingがどこにも実装されていない場合、コンパイラはOnExecutingの宣言と呼び出しを取り除きます。
つまり、次のようなコードと同じように扱われます。
C#public partial class Sample
{
public void Execute()
{
Console.WriteLine("実行しました");
}
}
これにより、「必要な場合だけ追加処理を書く」という柔軟な設計ができます。
4-5. partial methodが使える場所と条件
partial methodは、partialで宣言された型の中で使います。
C#public partial class Sample
{
partial void OnChanged();
}
主な条件は次のとおりです。
従来形式のpartial methodでは、戻り値はvoidです。
C#partial void OnChanged();
また、実装を省略できる形式では、アクセス修飾子を付けません。
C#partial void OnChanged();
一方で、最近のC#では、アクセス修飾子や戻り値を持つpartial methodも書けます。
C#public partial class Sample
{
public partial int Calculate();
}
このようにpublicや戻り値を持つpartial methodは、実装が必須です。
C#public partial class Sample
{
public partial int Calculate()
{
return 100;
}
}
つまり、partial methodには大きく分けて次の2種類があります。
実装が任意の従来形式。
C#partial void OnChanged();
実装が必須の拡張形式。
C#public partial int Calculate();
実務では、自動生成コードのフック処理には、戻り値なしのpartial voidがよく使われます。
5. partial methodの実践例
partial methodは、自動生成コードに処理を差し込む場面で特に便利です。
ここでは、実務でイメージしやすい例を紹介します。
5-1. 自動生成コードに処理を差し込む
自動生成されたクラスに、保存前後の処理を差し込む例です。
C#// Customer.Generated.cs
public partial class Customer
{
public string Name { get; set; }
public void Save()
{
OnBeforeSave();
Console.WriteLine($"{Name}を保存しました");
OnAfterSave();
}
partial void OnBeforeSave();
partial void OnAfterSave();
}
開発者は、必要なフックだけを別ファイルで実装します。
C#// Customer.Custom.cs
public partial class Customer
{
partial void OnBeforeSave()
{
if (string.IsNullOrWhiteSpace(Name))
{
throw new InvalidOperationException("名前は必須です");
}
}
partial void OnAfterSave()
{
Console.WriteLine("保存後のログを出力しました");
}
}
このようにすれば、自動生成コードを直接変更せずに処理を追加できます。
5-2. イベント前後のフック処理に使う
partial methodは、処理の前後にフックポイントを用意するのに向いています。
C#public partial class FileImporter
{
public void Import()
{
OnImportStarting();
Console.WriteLine("ファイルを取り込みます");
OnImportCompleted();
}
partial void OnImportStarting();
partial void OnImportCompleted();
}
必要に応じて、別ファイルで処理を追加します。
C#public partial class FileImporter
{
partial void OnImportStarting()
{
Console.WriteLine("取り込み開始ログを出力します");
}
partial void OnImportCompleted()
{
Console.WriteLine("取り込み完了メールを送信します");
}
}
この方法を使うと、メイン処理の流れを保ちながら、追加処理を分離できます。
5-3. プロパティ変更時の処理を追加する
プロパティ変更時に任意の処理を追加する例です。
C#public partial class User
{
private string _name;
public string Name
{
get { return _name; }
set
{
OnNameChanging(value);
_name = value;
OnNameChanged();
}
}
partial void OnNameChanging(string newValue);
partial void OnNameChanged();
}
別ファイルで、変更前後の処理を実装します。
C#public partial class User
{
partial void OnNameChanging(string newValue)
{
if (string.IsNullOrWhiteSpace(newValue))
{
throw new ArgumentException("名前は空にできません");
}
}
partial void OnNameChanged()
{
Console.WriteLine($"名前が{Name}に変更されました");
}
}
このように、プロパティの変更処理にバリデーションやログ出力を追加できます。
5-4. 実装が任意の拡張ポイントを作る
ライブラリやコード生成ツールを作る場合、partial methodを使うと任意の拡張ポイントを作れます。
C#public partial class ReportGenerator
{
public void Generate()
{
OnBeforeGenerate();
Console.WriteLine("レポートを生成しました");
OnAfterGenerate();
}
partial void OnBeforeGenerate();
partial void OnAfterGenerate();
}
利用側は必要な場合だけ実装します。
C#public partial class ReportGenerator
{
partial void OnBeforeGenerate()
{
Console.WriteLine("生成前に設定を確認します");
}
}
OnAfterGenerateは実装していませんが、従来形式のpartial voidであれば問題ありません。
実装されていないメソッドはコンパイル時に取り除かれます。
5-5. partial methodを使わないほうがよいケース
partial methodは便利ですが、すべての拡張に使うべきではありません。
たとえば、明確なインターフェイスとして公開したい処理には向いていません。
C#public interface IValidator
{
bool Validate();
}
このように、外部から利用される契約を表したい場合は、インターフェイスのほうが適しています。
また、複数の実装を切り替えたい場合は、継承、委譲、DIなどを検討したほうがよいでしょう。
partial methodは、主に「同じ型の中に任意の処理を差し込む」ための仕組みです。
設計全体を拡張するための万能な仕組みではありません。
6. partialのメリット
C#のpartialには、コード整理、自動生成コードの保護、保守性向上などのメリットがあります。
ここでは、代表的なメリットを見ていきましょう。
6-1. ファイルを分けてコードを整理できる
partial classを使うと、1つのクラスを複数ファイルに分けられます。
たとえば、プロパティ、バリデーション、計算処理を分けることができます。
C#// Invoice.Properties.cs
public partial class Invoice
{
public int Amount { get; set; }
}
C#// Invoice.Validation.cs
public partial class Invoice
{
public bool IsValid()
{
return Amount > 0;
}
}
C#// Invoice.Calculation.cs
public partial class Invoice
{
public int AddTax()
{
return (int)(Amount * 1.1);
}
}
ファイルを適切に分けることで、目的のコードを探しやすくなります。
6-2. 自動生成コードを安全に拡張できる
partialの大きなメリットは、自動生成コードを直接編集せずに拡張できることです。
C#// 自動生成コード
public partial class Item
{
public string Name { get; set; }
}
C#// 手書きコード
public partial class Item
{
public string Display()
{
return $"商品名: {Name}";
}
}
自動生成ファイルが再生成されても、手書きコードは別ファイルにあるため安全です。
これは、Windows Forms、WPF、Entity Framework、OpenAPIクライアント生成、独自コード生成などで役立ちます。
6-3. 変更箇所を分離して保守しやすくなる
partialを使うと、変更されやすい部分と変更されにくい部分を分離できます。
たとえば、生成コードとカスタム処理を分ければ、どこを変更してよいかが明確になります。
C#// User.Generated.cs
public partial class User
{
public string Name { get; set; }
}
C#// User.BusinessLogic.cs
public partial class User
{
public bool HasValidName()
{
return !string.IsNullOrWhiteSpace(Name);
}
}
手書きコードだけを確認すればよい場面が増えるため、保守しやすくなります。
6-4. 複数人での開発がしやすくなる
複数人で同じクラスを触る場合、1つのファイルを全員が編集すると競合が起きやすくなります。
partial classで機能ごとにファイルを分けておけば、担当者ごとに編集範囲を分けられます。
C#// Account.Login.cs
public partial class AccountService
{
public void Login()
{
Console.WriteLine("ログイン処理");
}
}
C#// Account.Password.cs
public partial class AccountService
{
public void ChangePassword()
{
Console.WriteLine("パスワード変更処理");
}
}
ただし、チーム開発では「どの機能をどのファイルに書くか」というルールを決めることが重要です。
6-5. 関心ごとごとにコードを管理できる
partialを使うと、関心ごとごとにコードを整理できます。
たとえば、1つのクラスに次のような処理がある場合です。
データ保持
バリデーション
表示用フォーマット
ログ出力
自動生成部分
これらを適切に分けることで、コードの見通しが良くなります。
C#// Customer.Validation.cs
public partial class Customer
{
public bool IsValid()
{
return !string.IsNullOrWhiteSpace(Name);
}
}
C#// Customer.Display.cs
public partial class Customer
{
public string ToDisplayText()
{
return $"顧客名: {Name}";
}
}
ただし、関心ごとが大きく違う場合は、partialで分けるよりも別クラスに分けたほうがよいこともあります。
7. partialのデメリットと注意点
partialは便利な機能ですが、使い方を間違えるとコードがわかりにくくなります。
ここでは、partialを使うときの注意点を解説します。
7-1. クラスの全体像が見えにくくなる
partial classでファイルを分けると、1つのクラスの内容が複数ファイルに散らばります。
そのため、クラス全体を把握するには複数のファイルを確認する必要があります。
C#User.cs
User.Validation.cs
User.Calculation.cs
User.Display.cs
このようにファイルが増えすぎると、どこに何が書かれているのか探しにくくなります。
特に、初めてプロジェクトに参加した人にとっては、クラスの全体像が見えにくくなる可能性があります。
7-2. 責務の大きいクラスを隠してしまう
partialを使うと、大きなクラスを複数ファイルに分けられます。
しかし、それは「クラスの責務が小さくなった」という意味ではありません。
たとえば、1つのUserServiceにログイン、決済、メール送信、レポート作成がすべて入っている場合、ファイルを分けても責務が大きいことに変わりはありません。
C#UserService.Login.cs
UserService.Payment.cs
UserService.Mail.cs
UserService.Report.cs
このような場合は、partial classで分けるよりも、別々のクラスに分割するほうが適切です。
C#LoginService
PaymentService
MailService
ReportService
partialはファイルを分ける仕組みであり、設計上の責務を分割する仕組みではありません。
7-3. ファイル分割のルールがないと保守しにくい
partial classを使う場合、ファイル分割のルールが重要です。
ルールがないと、似たような処理が複数のファイルに散らばってしまいます。
たとえば、次のような状態は避けたいところです。
C#User1.cs
User2.cs
UserOther.cs
UserNew.cs
ファイル名から中身が想像できないと、保守性が下がります。
おすすめは、役割がわかる名前を付けることです。
C#User.Properties.cs
User.Validation.cs
User.Calculation.cs
User.Display.cs
また、自動生成コードの場合は次のように分けるとわかりやすいです。
C#User.Generated.cs
User.Custom.cs
ファイル名のルールを決めておくことで、partialのメリットを活かしやすくなります。
7-4. partialを使う前に設計を見直すべきケース
partialを使う前に、本当にクラスを分割するだけでよいのか考える必要があります。
次のような場合は、設計を見直したほうがよい可能性があります。
クラスの行数が非常に多い。
複数の責務が1つのクラスに混ざっている。
テストしにくい。
メソッド同士の依存関係が複雑になっている。
ファイルを分けても理解しにくい。
たとえば、次のようなクラスがあるとします。
C#public partial class ApplicationService
{
public void Login() { }
public void CreateOrder() { }
public void SendMail() { }
public void ExportReport() { }
}
この場合、partialでファイルを分けるよりも、責務ごとにクラスを分けるほうが自然です。
C#public class LoginService
{
}
public class OrderService
{
}
public class MailService
{
}
public class ReportService
{
}
partialは整理の手段ですが、設計改善の代わりにはなりません。
7-5. partial classと継承・インターフェイスの違い
partial classは、継承やインターフェイスとは目的が違います。
partial classは、同じクラスを複数の場所に分けて書く仕組みです。
C#public partial class User
{
}
C#public partial class User
{
}
一方、継承は既存のクラスを元に新しいクラスを作る仕組みです。
C#public class Animal
{
public void Eat()
{
Console.WriteLine("食べます");
}
}
public class Dog : Animal
{
}
インターフェイスは、クラスが実装すべき機能の契約を定義する仕組みです。
C#public interface ILogger
{
void Log(string message);
}
public class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine(message);
}
}
partial classは設計の拡張ではなく、同じ型の定義を分割するための構文です。
8. partial classとpartial methodの違い
partial classとpartial methodは、どちらもpartialキーワードを使いますが、目的が違います。
ここでは、それぞれの違いを整理します。
8-1. partial classは型を分割する仕組み
partial classは、クラスという型そのものを分割する仕組みです。
C#public partial class User
{
public string Name { get; set; }
}
C#public partial class User
{
public void Show()
{
Console.WriteLine(Name);
}
}
これは、1つのUserクラスを複数ファイルに分けているだけです。
コンパイル時には1つのクラスになります。
8-2. partial methodはメソッドの宣言と実装を分ける仕組み
partial methodは、メソッドの宣言と実装を分ける仕組みです。
C#public partial class User
{
public void Save()
{
OnSaved();
}
partial void OnSaved();
}
C#public partial class User
{
partial void OnSaved()
{
Console.WriteLine("保存されました");
}
}
partial methodは、主に「ここに必要なら処理を追加できます」という拡張ポイントとして使われます。
8-3. それぞれの用途の違い
partial classは、クラスのコードを分割したいときに使います。
主な用途は次のとおりです。
自動生成コードと手書きコードを分ける。
UIの自動生成部分とイベント処理を分ける。
クラスの機能をファイル単位で整理する。
一方、partial methodは、処理の差し込み口を作りたいときに使います。
主な用途は次のとおりです。
保存前後の処理を追加する。
プロパティ変更時の処理を追加する。
自動生成コードに任意のカスタム処理を加える。
つまり、partial classは型の分割、partial methodは処理の分割・差し込みに使います。
8-4. 使い分けの判断基準
partial classを使うべきなのは、同じクラスのコードを複数ファイルに分けたいときです。
C#public partial class Order
{
public int Id { get; set; }
}
C#public partial class Order
{
public void Submit()
{
Console.WriteLine("注文しました");
}
}
partial methodを使うべきなのは、特定のタイミングで任意の処理を差し込みたいときです。
C#public partial class Order
{
public void Submit()
{
OnSubmitting();
Console.WriteLine("注文しました");
}
partial void OnSubmitting();
}
C#public partial class Order
{
partial void OnSubmitting()
{
Console.WriteLine("注文前チェック");
}
}
判断基準は、分けたい対象が「クラス全体」なのか「メソッドの実装」なのかです。
クラスを分けたいならpartial class。
処理の差し込み口を作りたいならpartial method。
このように考えると、使い分けしやすくなります。
9. partialのサンプルコードで理解する
ここでは、partial classとpartial methodを実際のコードで確認します。
サンプルを通して、C#のpartialがどのように動くのか理解しましょう。
9-1. partial classでクラスを2ファイルに分ける例
まずは、partial classでクラスを2つのファイルに分ける例です。
C#// Person.Properties.cs
public partial class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
C#// Person.Methods.cs
public partial class Person
{
public void Introduce()
{
Console.WriteLine($"私は{Name}です。{Age}歳です。");
}
}
このPersonクラスは、次のように使えます。
C#Person person = new Person();
person.Name = "田中";
person.Age = 30;
person.Introduce();
実行結果は次のようになります。
C#私は田中です。30歳です。
NameやAgeは別ファイルに定義されていますが、同じクラスのメンバーとして扱えます。
9-2. partial methodで任意処理を追加する例
次に、partial methodで任意処理を追加する例です。
C#// TaskRunner.Generated.cs
public partial class TaskRunner
{
public void Run()
{
OnBeforeRun();
Console.WriteLine("タスクを実行します");
OnAfterRun();
}
partial void OnBeforeRun();
partial void OnAfterRun();
}
C#// TaskRunner.Custom.cs
public partial class TaskRunner
{
partial void OnBeforeRun()
{
Console.WriteLine("実行前の準備をします");
}
partial void OnAfterRun()
{
Console.WriteLine("実行後の片付けをします");
}
}
使い方は次のとおりです。
C#TaskRunner runner = new TaskRunner();
runner.Run();
実行結果は次のようになります。
C#実行前の準備をします
タスクを実行します
実行後の片付けをします
partial methodを使うことで、メイン処理の前後にカスタム処理を差し込めます。
9-3. 自動生成コード風の実践例
実務では、自動生成コードに手書き処理を追加する形でpartialを使うことが多いです。
たとえば、商品クラスが自動生成されているとします。
C#// Product.Generated.cs
public partial class Product
{
public string Name { get; set; }
public int Price { get; set; }
public void Save()
{
OnSaving();
Console.WriteLine($"{Name}を保存しました");
OnSaved();
}
partial void OnSaving();
partial void OnSaved();
}
開発者は、別ファイルにカスタム処理を書きます。
C#// Product.Custom.cs
public partial class Product
{
public int TaxIncludedPrice
{
get
{
return (int)(Price * 1.1);
}
}
partial void OnSaving()
{
if (string.IsNullOrWhiteSpace(Name))
{
throw new InvalidOperationException("商品名は必須です");
}
if (Price <= 0)
{
throw new InvalidOperationException("価格は1円以上にしてください");
}
}
partial void OnSaved()
{
Console.WriteLine($"税込価格は{TaxIncludedPrice}円です");
}
}
使い方は次のようになります。
C#Product product = new Product();
product.Name = "ノート";
product.Price = 100;
product.Save();
実行結果は次のようになります。
C#ノートを保存しました
税込価格は110円です
このように、partial classでクラスを拡張し、partial methodで処理を差し込めます。
9-4. partialを使わない場合との比較
partialを使わない場合、自動生成コードと手書きコードが同じファイルに混ざることがあります。
C#public class Product
{
public string Name { get; set; }
public int Price { get; set; }
public void Save()
{
if (string.IsNullOrWhiteSpace(Name))
{
throw new InvalidOperationException("商品名は必須です");
}
Console.WriteLine($"{Name}を保存しました");
}
public int TaxIncludedPrice
{
get
{
return (int)(Price * 1.1);
}
}
}
小さなクラスならこれでも問題ありません。
しかし、自動生成コードが含まれる場合や、ファイルが大きくなる場合は管理しにくくなります。
partialを使うと、次のように分けられます。
C#// Product.Generated.cs
public partial class Product
{
public string Name { get; set; }
public int Price { get; set; }
}
C#// Product.Custom.cs
public partial class Product
{
public int TaxIncludedPrice => (int)(Price * 1.1);
}
このように、partialを使うとコードの役割を分離しやすくなります。
ただし、自動生成コードがない場合や、クラスが小さい場合は、無理にpartialを使う必要はありません。
10. partialに関するよくある疑問
最後に、C#のpartialについてよくある疑問を解説します。
10-1. partialは1つのファイル内でも使えるのか
partialは、1つのファイル内でも使えます。
C#public partial class Sample
{
public string Name { get; set; }
}
public partial class Sample
{
public void Show()
{
Console.WriteLine(Name);
}
}
このコードは、同じファイル内に2つのpartial classを書いています。
ただし、実務ではあまり意味がありません。
partialの主な目的は、複数ファイルに分けて管理することだからです。
学習用や特殊なケースを除けば、1つのファイル内でpartialを使うことは少ないでしょう。
10-2. partial classに同じメソッド名を書けるのか
partial classでは、同じシグネチャのメソッドを重複して定義することはできません。
C#public partial class Sample
{
public void Execute()
{
Console.WriteLine("A");
}
}
C#public partial class Sample
{
public void Execute()
{
Console.WriteLine("B");
}
}
この場合、同じExecuteメソッドが重複しているためエラーになります。
ただし、オーバーロードとして引数が異なる場合は定義できます。
C#public partial class Sample
{
public void Execute()
{
Console.WriteLine("引数なし");
}
}
C#public partial class Sample
{
public void Execute(string message)
{
Console.WriteLine(message);
}
}
これは通常のクラスと同じルールです。
10-3. partial classでコンストラクタは分けられるのか
partial classでは、コンストラクタを別ファイルに書くこともできます。
C#// User.cs
public partial class User
{
public string Name { get; set; }
}
C#// User.Constructor.cs
public partial class User
{
public User(string name)
{
Name = name;
}
}
このように、コンストラクタも同じクラスのメンバーとして扱われます。
ただし、同じ引数のコンストラクタを複数定義することはできません。
C#public partial class User
{
public User()
{
}
}
C#public partial class User
{
// 同じ引数なしコンストラクタなのでエラー
public User()
{
}
}
コンストラクタも通常のメンバーと同じように、重複定義には注意が必要です。
10-4. partialはpublicやprivateと一緒に使えるのか
partialは、publicやprivateなどのアクセス修飾子と一緒に使えます。
C#public partial class User
{
}
C#internal partial class UserRepository
{
}
ネストされたクラスであれば、private partial classのような指定もできます。
C#public class Outer
{
private partial class Inner
{
}
}
C#public class Outer
{
private partial class Inner
{
}
}
ただし、分割された同じ型のアクセス修飾子は矛盾しないようにする必要があります。
C#public partial class User
{
}
C#// publicとinternalが矛盾するためエラー
internal partial class User
{
}
また、partial methodについては注意が必要です。
実装を省略できる従来形式のpartial methodでは、アクセス修飾子を付けません。
C#partial void OnChanged();
一方、public partial void OnChanged();のようにアクセス修飾子を付ける場合は、実装が必要です。
10-5. partialは使いすぎてもよいのか
partialは便利ですが、使いすぎはおすすめできません。
ファイルを分けすぎると、クラス全体の構造が見えにくくなるからです。
たとえば、1つのクラスが次のように細かく分かれていると、確認するだけで手間がかかります。
C#User.A.cs
User.B.cs
User.C.cs
User.D.cs
User.E.cs
このような状態になると、partialによる整理よりも、別クラスへの分割を検討したほうがよい場合があります。
partialを使うべき代表的なケースは、自動生成コードと手書きコードを分けたい場合です。
それ以外では、次の点を確認してから使うとよいでしょう。
クラスの責務は大きすぎないか。
ファイル名のルールは明確か。
分割することで本当に読みやすくなるか。
別クラスに分けたほうが自然ではないか。
partialは便利な道具ですが、設計をよくする魔法ではありません。
適切な場面で使うことが大切です。
まとめ
C#のpartialは、クラスやメソッドの定義を複数の部分に分けて書ける便利なキーワードです。
partial classを使うと、1つのクラスを複数ファイルに分割できます。
C#public partial class User
{
public string Name { get; set; }
}
C#public partial class User
{
public void Show()
{
Console.WriteLine(Name);
}
}
この2つは、コンパイル時に1つのUserクラスとして扱われます。
また、partial methodを使うと、メソッドの宣言と実装を分けられます。
C#public partial class User
{
public void Save()
{
OnSaved();
}
partial void OnSaved();
}
C#public partial class User
{
partial void OnSaved()
{
Console.WriteLine("保存されました");
}
}
partialは、特に自動生成コードと手書きコードを分けたいときに役立ちます。
Windows Forms、WPF、Entity Framework、コード生成ツールなどでは、partial classがよく使われます。
一方で、partialを使いすぎると、クラス全体が見えにくくなったり、責務の大きいクラスを隠してしまったりすることがあります。
partial classは型を分割する仕組みです。
partial methodは処理の宣言と実装を分ける仕組みです。
どちらも便利ですが、目的を理解して使うことが重要です。
C#でpartialを使うときは、「自動生成コードを安全に拡張したいのか」「ファイルを分けることで本当に読みやすくなるのか」「別クラスに分けるべきではないのか」を考えながら使うと、保守しやすいコードを書けるようになります。

