C# partialとは?partialクラス・メソッドの使い方と分割すべき場面をわかりやすく解説

はじめに

C#のpartialは、1つのクラスやメソッドの定義を複数のファイルに分けて書ける仕組みです。

たとえば、Userクラスのプロパティ定義はUser.csに書き、バリデーション処理はUser.Validation.csに書く、といった分割ができます。分割していても、コンパイル時には1つのクラスとして扱われます。

特に、WinForms、WPF、Entity Framework、Source Generator、自動生成コードを使う場面では、partial classを見かけることが多いです。一方で、何でもpartialで分ければよいわけではありません。設計が悪い巨大クラスを隠すために使うと、かえって可読性が下がります。

この記事では、C#のpartialについて、partialクラスとpartialメソッドを中心に、基本構文、使いどころ、注意点、具体的なコード例までわかりやすく解説します。

1. C#のpartialとは?クラス定義を複数ファイルに分割できる仕組み

1-1. partialキーワードの意味

C#のpartialキーワードは、クラス、構造体、インターフェイス、メソッドなどの定義を複数の場所に分けて記述するためのキーワードです。

通常、1つのクラスは1つのファイルにまとめて書きます。

C#
public class User
{
public string Name { get; set; }

public void PrintName()
{
Console.WriteLine(Name);
}
}

これをpartialを使うと、次のように複数ファイルへ分割できます。

C#
// User.cs
public partial class User
{
public string Name { get; set; }
}
C#
// User.Methods.cs
public partial class User
{
public void PrintName()
{
Console.WriteLine(Name);
}
}

ファイルは分かれていますが、どちらも同じUserクラスの一部です。MicrosoftのC#ドキュメントでも、partialは型やメンバーの定義を2つ以上のソースファイルに分割し、コンパイル時に結合する仕組みとして説明されています。

1-2. partialで分割できるもの:クラス・構造体・インターフェイス・メソッド

C#で代表的にpartialを使えるものは、次のような要素です。

C#
public partial class User
{
}
C#
public partial struct Point
{
}
C#
public partial interface IRepository
{
}
C#
partial void OnCreated();

特によく使われるのはpartial classpartial methodです。

partial classは、1つのクラス定義を複数ファイルに分けるために使います。partial methodは、メソッドの宣言と実装を分けるために使います。

また、近年のC#ではpartialメンバーの範囲も広がっています。C# 13ではpartialプロパティやpartialインデクサーが追加され、C# 14ではpartialコンストラクターも扱えるようになっています。

ただし、この記事では実務でよく登場するpartial classpartial methodを中心に解説します。

1-3. コンパイル時には1つの型として扱われる

partialで分割されたクラスは、コンパイル時に1つの型として扱われます。

たとえば、次の2つのファイルがあるとします。

C#
// Customer.cs
public partial class Customer
{
public string Name { get; set; }
}
C#
// Customer.Contact.cs
public partial class Customer
{
public string Email { get; set; }
}

この場合、コンパイル後のCustomerクラスは、実質的に次のようなクラスとして扱われます。

C#
public class Customer
{
public string Name { get; set; }

public string Email { get; set; }
}

つまり、partialは実行時に何か特別な処理をする仕組みではありません。あくまでソースコードを分割して書くための機能です。

そのため、partialを使ったからといって、基本的に実行時のパフォーマンスが速くなったり遅くなったりするわけではありません。コンパイル後には1つの型として扱われるため、主な目的はコード整理や自動生成コードとの分離です。

1-4. partialがよく使われる場面の全体像

partialがよく使われる場面は、主に次のようなケースです。

自動生成コードと手書きコードを分けたい場合、partial classは非常に便利です。たとえば、Visual Studioが生成する画面定義コードや、ORMが生成するエンティティコードに対して、開発者が安全に処理を追加できます。

また、Source Generatorのように、コンパイル時にコードを生成する仕組みでもpartialがよく使われます。生成側がpartial classの一部を作り、開発者が別ファイルで残りを実装することで、生成コードと人間が書くコードを分離できます。Microsoftのドキュメントでも、Visual Studioの自動生成コードやSource Generatorでの利用が代表例として挙げられています。

さらに、クラスが大きくなりやすい場合に、機能ごとにファイルを分ける目的でも使われます。

たとえば、次のような分け方です。

User.cs
User.Validation.cs
User.Authentication.cs
User.Profile.cs

ただし、これはあくまで「同じクラスである必要がある場合」の整理方法です。本来は別クラスに分けるべき処理までpartialで無理にまとめると、設計がわかりにくくなります。

2. partialクラスの基本的な使い方

2-1. partial classの基本構文

partial classの基本構文は次のとおりです。

C#
public partial class ClassName
{
// メンバーを定義する
}

同じクラスを別ファイルにも定義する場合、そちらにも必ずpartialを付けます。

C#
public partial class ClassName
{
// 別のメンバーを定義する
}

重要なのは、分割されたすべての定義にpartialを付けることです。

次のように片方だけpartialを付ける書き方はできません。

C#
// OK
public partial class Product
{
}
C#
// NG
public class Product
{
}

同じ型を複数の場所で定義する場合、各宣言にpartialが必要です。partialが不足していると、コンパイラエラーになります。

2-2. 複数ファイルに分けて同じクラスを定義する例

実際に、Userクラスを2つのファイルに分けてみます。

C#
// User.cs
namespace SampleApp.Models;

public partial class User
{
public int Id { get; set; }

public string Name { get; set; } = string.Empty;
}
C#
// User.Methods.cs
namespace SampleApp.Models;

public partial class User
{
public void PrintProfile()
{
Console.WriteLine($"ID: {Id}, Name: {Name}");
}
}

呼び出し側では、通常のクラスと同じように使えます。

C#
using SampleApp.Models;

var user = new User
{
Id = 1,
Name = "Taro"
};

user.PrintProfile();

IdNameUser.csにありますが、PrintProfileUser.Methods.csにあります。それでも、呼び出し側から見ると1つのUserクラスです。

2-3. namespace・クラス名・型パラメータを一致させる必要がある

partial classとして同じクラスにまとめるには、クラス名だけでなく、名前空間も一致している必要があります。

たとえば、次の2つは同じUserクラスとして扱われます。

C#
namespace SampleApp.Models;

public partial class User
{
}
C#
namespace SampleApp.Models;

public partial class User
{
}

しかし、名前空間が異なると別のクラスになります。

C#
namespace SampleApp.Models;

public partial class User
{
}
C#
namespace SampleApp.Entities;

public partial class User
{
}

この場合、上はSampleApp.Models.User、下はSampleApp.Entities.Userです。クラス名は同じでも、完全修飾名が違うため別の型です。

ジェネリッククラスの場合は、型パラメータの数や順序にも注意が必要です。

C#
public partial class Repository<T>
{
}
C#
public partial class Repository<T>
{
}

これは問題ありません。

しかし、次のように型パラメータの構成が合わない場合は同じ型として扱えません。

C#
public partial class Repository<T>
{
}
C#
public partial class Repository<TKey, TValue>
{
}

また、制約を付ける場合も整合性が必要です。

C#
public partial class Repository<T> where T : class
{
}
C#
public partial class Repository<T> where T : class
{
}

partialは「同じ型の別パーツ」を書く機能なので、別の型として解釈されるような宣言になってはいけません。

2-4. フィールド・プロパティ・メソッドを別ファイルに分ける例

partial classでは、フィールド、プロパティ、メソッドなどをファイルごとに分けて定義できます。

C#
// Order.cs
namespace SampleApp.Models;

public partial class Order
{
private readonly List<OrderItem> _items = new();

public int Id { get; set; }

public DateTime OrderedAt { get; set; }
}
C#
// Order.Items.cs
namespace SampleApp.Models;

public partial class Order
{
public IReadOnlyList<OrderItem> Items => _items;

public void AddItem(OrderItem item)
{
_items.Add(item);
}
}
C#
// Order.Calculation.cs
namespace SampleApp.Models;

public partial class Order
{
public decimal GetTotalPrice()
{
return _items.Sum(item => item.Price * item.Quantity);
}
}

このように、注文情報、明細操作、金額計算を別ファイルに分けられます。

ただし、分けすぎると「Orderクラスの全体像」が見えにくくなります。partialは便利ですが、ファイルを増やせば必ず読みやすくなるわけではありません。

3. partialクラスを使うメリット

3-1. 自動生成コードと手書きコードを分離できる

partial class最大のメリットは、自動生成コードと手書きコードを分離できることです。

たとえば、ツールが次のようなコードを自動生成するとします。

C#
// Customer.Generated.cs
public partial class Customer
{
public int Id { get; set; }

public string Name { get; set; } = string.Empty;
}

開発者は、別ファイルに手書きコードを追加できます。

C#
// Customer.cs
public partial class Customer
{
public string GetDisplayName()
{
return $"顧客名: {Name}";
}
}

自動生成ファイルを直接編集すると、再生成時に変更が消えてしまうことがあります。しかし、partialで別ファイルに拡張コードを書けば、自動生成コードを壊さずに機能追加できます。

WinFormsやWPFなどでも、この考え方がよく使われます。画面部品の生成コードはツール側が管理し、イベント処理やビジネスロジックは開発者が別ファイルに書く、という分担ができます。

3-2. 大きなクラスを機能単位で整理できる

クラスがどうしても大きくなる場合、partialを使うことで機能単位に整理できます。

たとえば、UserServiceにユーザー登録、ログイン、パスワード変更、プロフィール更新などの処理がある場合、次のように分けられます。

UserService.cs
UserService.Registration.cs
UserService.Authentication.cs
UserService.Password.cs
UserService.Profile.cs

このように分けると、目的の処理を探しやすくなることがあります。

ただし、サービスクラスが大きくなりすぎている場合は、partialで分ける前に、責務ごとに別クラスへ分割できないか検討するべきです。

たとえば、次のように分けられるかもしれません。

UserRegistrationService.cs
UserAuthenticationService.cs
PasswordChangeService.cs
UserProfileService.cs

partialは「巨大なクラスを見かけ上小さくするための道具」ではなく、「同じクラスである必要があるものを整理するための道具」と考えるとよいでしょう。

3-3. 複数人で同じクラスを編集しやすくなる

partial classを使うと、複数人で同じクラスを編集しやすくなる場合があります。

たとえば、1つの大きなファイルにすべての処理が書かれていると、複数人が同じファイルを編集し、Gitなどでコンフリクトが起きやすくなります。

一方、処理ごとにファイルが分かれていれば、担当者ごとに別ファイルを編集できます。

ProductService.Search.cs
ProductService.Import.cs
ProductService.Export.cs

このような構成にすると、検索機能を担当する人はProductService.Search.csを編集し、インポート機能を担当する人はProductService.Import.csを編集できます。

ただし、チーム開発でpartialを使う場合は、ファイルの命名規則や分割ルールを決めておくことが重要です。ルールなしに分割すると、どの処理がどのファイルにあるのか分かりにくくなります。

3-4. WinForms・WPF・Entity Framework・Source Generatorで使われる理由

partial classは、次のような技術でよく使われます。

WinFormsでは、フォームのデザイナーが生成するコードと、開発者が書くイベント処理を分けるために使われます。

MainForm.cs
MainForm.Designer.cs

WPFでは、XAMLから生成されるコードと、開発者が書くコードビハインドを組み合わせるために使われます。

Entity FrameworkなどのORMでは、データベース構造に基づいて生成されるエンティティクラスを、手書きコードで拡張するために使われることがあります。

Source Generatorでは、生成されたコードを既存の型に追加するためにpartialがよく使われます。Microsoftのドキュメントでも、Visual Studioによる自動生成コードやSource Generatorでpartialが使われることが説明されています。

これらに共通しているのは、「ツールが生成するコード」と「人間が書くコード」を安全に分けたい、という目的です。

4. partialクラスを使うべき場面・使わないほうがよい場面

4-1. 使うべき場面:自動生成コードを壊さず拡張したいとき

partial classを使うべき代表的な場面は、自動生成コードを壊さずに拡張したいときです。

たとえば、次のような自動生成ファイルがあるとします。

C#
// Product.Generated.cs
public partial class Product
{
public int Id { get; set; }

public string Name { get; set; } = string.Empty;

public decimal Price { get; set; }
}

このファイルを直接編集すると、ツールによる再生成時に変更が消える可能性があります。

そこで、手書きコードは別ファイルに書きます。

C#
// Product.cs
public partial class Product
{
public bool IsExpensive()
{
return Price >= 10000;
}
}

このようにすれば、自動生成コードを触らずに、同じProductクラスへ処理を追加できます。

自動生成コードと手書きコードの分離は、partialが最も自然に活きる場面です。

4-2. 使うべき場面:仕様上どうしても巨大になりやすいクラスを整理したいとき

仕様上、どうしても1つのクラスが大きくなりやすい場合にも、partialが役立つことがあります。

たとえば、画面クラス、フォームクラス、設定クラス、生成されたAPIクライアントなどは、メンバー数が多くなりやすいです。

Settings.cs
Settings.Database.cs
Settings.Logging.cs
Settings.Security.cs

このように分けると、関連する設定をファイル単位で探しやすくなります。

ただし、巨大なクラスをpartialで分ける前に、「本当に1つのクラスである必要があるのか」を確認することが大切です。もし責務が分けられるなら、partialよりもクラス分割を優先したほうがよい場合があります。

4-3. 使わないほうがよい場面:設計が悪い神クラスを隠すための分割

partialを使わないほうがよい代表例は、設計が悪い神クラスを隠すための分割です。

神クラスとは、1つのクラスが多すぎる責務を持っている状態です。

たとえば、次のようなクラスです。

C#
public class ApplicationManager
{
// ユーザー管理
// 商品管理
// 注文管理
// メール送信
// ログ出力
// ファイル操作
// 外部API通信
}

このクラスをpartialで分けたとしても、根本的な設計問題は解決しません。

ApplicationManager.User.cs
ApplicationManager.Product.cs
ApplicationManager.Order.cs
ApplicationManager.Mail.cs
ApplicationManager.File.cs

ファイルは分かれていても、クラスとしては1つのままです。責務が多すぎることに変わりはありません。

このような場合は、partialではなく、責務ごとに別クラスへ分割するべきです。

UserManager.cs
ProductManager.cs
OrderManager.cs
MailSender.cs
FileStorage.cs

partialは設計改善の代わりにはなりません。

4-4. 使わないほうがよい場面:単にファイルを細かく分けたいだけの場合

「1ファイルが長いから、とりあえず分けたい」という理由だけでpartialを使うのは注意が必要です。

たとえば、次のように細かく分けすぎると、逆に読みにくくなります。

User.Name.cs
User.Email.cs
User.Age.cs
User.Address.cs
User.Phone.cs

このような分け方をすると、Userクラスの全体像を把握するために多くのファイルを開く必要があります。

小さなクラスであれば、1ファイルにまとまっていたほうが読みやすいことも多いです。

partialを使うかどうかは、ファイルの行数だけで判断するのではなく、分割することで理解しやすくなるかどうかで判断しましょう。

4-5. partialよりクラス分割・継承・委譲を検討すべきケース

partialを使う前に、クラス分割、継承、委譲で解決できないかを検討することも大切です。

たとえば、注文処理の中に支払い処理が混ざっている場合、partialで分けるよりも、支払い専用のクラスを作るほうが自然です。

C#
public class OrderService
{
private readonly PaymentService _paymentService;

public OrderService(PaymentService paymentService)
{
_paymentService = paymentService;
}

public void PlaceOrder(Order order)
{
_paymentService.Pay(order.TotalPrice);
}
}

このように、別クラスに処理を委譲すれば、責務が明確になります。

partialは同じクラスを複数ファイルに分ける機能です。別の責務を別のクラスに分ける機能ではありません。

次のような場合は、partialより設計の見直しを優先しましょう。

・1つのクラスが複数の業務領域を担当している
・メソッド同士の関連が薄い
・テストしにくい
・変更のたびに関係ない処理へ影響が出る
・クラス名が曖昧になっている

このような状態でpartialを使うと、問題が見えにくくなるだけです。

5. partialメソッドとは?宣言と実装を分けられるメソッド

5-1. partial methodの基本構文

partial methodは、メソッドの宣言と実装を分けられる機能です。

基本的な構文は次のとおりです。

C#
partial void OnCreated();

そして、別の場所で実装できます。

C#
partial void OnCreated()
{
Console.WriteLine("作成されました");
}

partial methodは、partialな型の中でのみ宣言できます。C#の言語仕様でも、partialメソッドはpartial型のメンバーとしてのみ宣言できるとされています。

たとえば、次のように書きます。

C#
public partial class User
{
partial void OnCreated();

public User()
{
OnCreated();
}
}
C#
public partial class User
{
partial void OnCreated()
{
Console.WriteLine("User created.");
}
}

5-2. partialメソッドの宣言側と実装側の書き方

partial methodには、宣言側と実装側があります。

宣言側は、メソッド本体を書かずにセミコロンで終わります。

C#
partial void OnNameChanged(string name);

実装側は、同じシグネチャでメソッド本体を書きます。

C#
partial void OnNameChanged(string name)
{
Console.WriteLine($"Name changed: {name}");
}

具体例を見てみましょう。

C#
// User.Generated.cs
public partial class User
{
private string _name = string.Empty;

public string Name
{
get => _name;
set
{
_name = value;
OnNameChanged(value);
}
}

partial void OnNameChanged(string name);
}
C#
// User.cs
public partial class User
{
partial void OnNameChanged(string name)
{
Console.WriteLine($"名前が変更されました: {name}");
}
}

この例では、自動生成コード側がOnNameChangedという拡張ポイントを用意し、手書きコード側で必要に応じて実装しています。

5-3. 実装しないpartialメソッドはどう扱われるのか

従来のpartial voidメソッドは、実装しなくてもエラーにならないケースがあります。

たとえば、次のようなコードです。

C#
public partial class User
{
public void Create()
{
OnCreated();
}

partial void OnCreated();
}

OnCreatedの実装を書かなかった場合、古典的なpartial voidメソッドでは、その呼び出し自体がコンパイル時に取り除かれます。

そのため、自動生成コード側は「必要なら実装してね」という拡張ポイントを用意できます。開発者が実装しなければ、何も起きません。

ただし、新しいC#ではpartial methodのルールが拡張されており、すべてのpartialメソッドが省略可能というわけではありません。アクセス修飾子を持つpartialメソッドや、戻り値を持つpartialメソッドなどは実装が必要になります。C#仕様では、明示的なアクセス修飾子を持つpartialメソッドはrequired partial methodとして扱われ、定義と実装の両方が必要とされています。

5-4. partialメソッドが自動生成コードの拡張ポイントになる理由

partial methodは、自動生成コードに拡張ポイントを用意するのに向いています。

たとえば、自動生成コードが次のように書かれているとします。

C#
public partial class Order
{
public void Save()
{
OnBeforeSave();

Console.WriteLine("注文を保存しました");

OnAfterSave();
}

partial void OnBeforeSave();

partial void OnAfterSave();
}

開発者は必要な処理だけを別ファイルに実装できます。

C#
public partial class Order
{
partial void OnBeforeSave()
{
Console.WriteLine("保存前のチェックを実行します");
}

partial void OnAfterSave()
{
Console.WriteLine("保存後の通知を実行します");
}
}

この仕組みにより、自動生成コード側は、手書きコードを直接知らなくても処理を差し込めます。

イベントや継承でも似たようなことはできますが、partial methodは同じクラス内で宣言と実装を分けられるため、自動生成コードとの相性がよいです。

5-5. 新しいC#におけるpartialメソッドの制限と変更点

古いC#のpartial methodには、主に次のような制限がありました。

・戻り値はvoid
・暗黙的にprivate
・アクセス修飾子を付けられない
・実装しなくてもよい

しかし、C# 9以降ではpartial methodの制限が緩和され、アクセス修飾子や戻り値を持つpartialメソッドも扱えるようになりました。

たとえば、次のような書き方ができます。

C#
public partial class Calculator
{
public partial int Add(int x, int y);
}
C#
public partial class Calculator
{
public partial int Add(int x, int y)
{
return x + y;
}
}

ただし、このような戻り値を持つpartialメソッドや、アクセス修飾子を持つpartialメソッドは、実装側が必要です。実装しない場合、呼び出しを消すことができないためです。

Microsoftのコンパイラエラー一覧でも、戻り値を持つpartialメソッドにはアクセシビリティが必要であること、アクセシビリティを持つpartialメンバーには実装が必要であることなどが説明されています。

つまり、現在のC#ではpartial methodには大きく分けて次の2種類があります。

・実装を省略できる古典的なpartial voidメソッド
・実装が必須の拡張されたpartialメソッド

自動生成コードの拡張ポイントとして使う場合は、どちらの形式を使っているのかを意識しましょう。

6. partialの具体的なコード例

6-1. 1つのクラスを2つのファイルに分割するサンプル

ここでは、Employeeクラスを2つのファイルに分ける例を見てみます。

C#
// Employee.cs
namespace SampleApp.Models;

public partial class Employee
{
public int Id { get; set; }

public string FirstName { get; set; } = string.Empty;

public string LastName { get; set; } = string.Empty;
}
C#
// Employee.Display.cs
namespace SampleApp.Models;

public partial class Employee
{
public string GetFullName()
{
return $"{LastName} {FirstName}";
}

public void Print()
{
Console.WriteLine(GetFullName());
}
}

呼び出し側は、ファイル分割を意識する必要がありません。

C#
using SampleApp.Models;

var employee = new Employee
{
Id = 1,
FirstName = "Taro",
LastName = "Yamada"
};

employee.Print();

実行結果は次のようになります。

Yamada Taro

Employee.csにはデータ、Employee.Display.csには表示関連の処理を置いています。

このように、役割ごとにファイルを分けると、クラスの中身を整理しやすくなります。

6-2. 自動生成コードと手書きコードを分けるサンプル

次に、自動生成コードと手書きコードを分ける例です。

C#
// Invoice.Generated.cs
namespace SampleApp.Models;

public partial class Invoice
{
public int Id { get; set; }

public decimal Amount { get; set; }

public DateTime IssuedAt { get; set; }
}

このファイルはツールによって生成される想定です。そのため、直接編集しません。

開発者が処理を追加したい場合は、別ファイルを作ります。

C#
// Invoice.cs
namespace SampleApp.Models;

public partial class Invoice
{
public bool IsHighValue()
{
return Amount >= 100000;
}

public string GetSummary()
{
return $"請求ID: {Id}, 金額: {Amount:N0}円, 発行日: {IssuedAt:yyyy/MM/dd}";
}
}

このように分けると、Invoice.Generated.csが再生成されても、Invoice.csに書いた手動の処理は残ります。

利用側は通常のクラスと同じです。

C#
var invoice = new Invoice
{
Id = 10,
Amount = 150000,
IssuedAt = DateTime.Today
};

Console.WriteLine(invoice.GetSummary());
Console.WriteLine(invoice.IsHighValue());

6-3. partialメソッドで処理を差し込むサンプル

次に、partial methodで処理を差し込む例です。

C#
// Account.Generated.cs
namespace SampleApp.Models;

public partial class Account
{
public string Email { get; private set; } = string.Empty;

public void ChangeEmail(string email)
{
OnBeforeEmailChanged(email);

Email = email;

OnAfterEmailChanged(email);
}

partial void OnBeforeEmailChanged(string email);

partial void OnAfterEmailChanged(string email);
}

この時点では、OnBeforeEmailChangedOnAfterEmailChangedの実装はありません。

必要であれば、別ファイルで実装します。

C#
// Account.cs
namespace SampleApp.Models;

public partial class Account
{
partial void OnBeforeEmailChanged(string email)
{
if (string.IsNullOrWhiteSpace(email))
{
throw new ArgumentException("メールアドレスは必須です。", nameof(email));
}
}

partial void OnAfterEmailChanged(string email)
{
Console.WriteLine($"メールアドレスが変更されました: {email}");
}
}

これにより、自動生成コード側の処理の流れを変えずに、手書きコードでバリデーションやログ出力を追加できます。

実装しない場合は何も起きず、実装した場合だけ処理が差し込まれます。

6-4. Visual Studioでのファイル構成例

Visual Studioでpartial classを使う場合、次のようなファイル構成にするとわかりやすくなります。

Models
User.cs
User.Validation.cs
User.Display.cs

Forms
MainForm.cs
MainForm.Designer.cs

Generated
Product.Generated.cs
Order.Generated.cs

クラス名を先頭にそろえ、ドットの後ろに役割を書く命名にすると、関連ファイルを見つけやすくなります。

User.cs
User.Validation.cs
User.Authentication.cs
User.Notification.cs

自動生成ファイルには.Generated.cs.Designer.csを付けると、手書きコードと区別しやすくなります。

Customer.Generated.cs
Customer.cs

このように命名しておけば、「どれが自動生成で、どれが手書きか」がすぐに分かります。

7. partialを使うときの注意点

7-1. 同じクラスの処理が分散して可読性が下がる

partialを使うと、1つのクラスの処理が複数ファイルに分散します。

これは便利な反面、可読性を下げる原因にもなります。

たとえば、Userクラスの処理が次のように分かれているとします。

User.cs
User.Basic.cs
User.Login.cs
User.Password.cs
User.Profile.cs
User.Notification.cs
User.Validation.cs

この場合、Userクラスにどのようなメンバーがあるのかを把握するには、複数のファイルを確認しなければなりません。

特に、同じフィールドを複数ファイルから参照している場合、変更の影響範囲が見えにくくなります。

partialを使う場合は、分割によって読みやすくなるか、逆に追いにくくなるかを考える必要があります。

7-2. どのファイルに何があるか分かりにくくなる

partialを無計画に使うと、どのファイルに何があるのか分かりにくくなります。

たとえば、次のようなファイル名では中身が想像しづらいです。

User.Part1.cs
User.Part2.cs
User.Other.cs
User.Sub.cs

これでは、どのファイルを開けば目的の処理が見つかるのか分かりません。

分割する場合は、役割が分かる名前にしましょう。

User.Validation.cs
User.Authentication.cs
User.Profile.cs
User.Notification.cs

また、チームで開発している場合は、「バリデーションは.Validation.csに書く」「自動生成コードは.Generated.csにする」といったルールを決めておくとよいです。

7-3. アクセス修飾子・属性・継承の扱いに注意する

partial classでは、アクセス修飾子や継承、インターフェイス実装の扱いに注意が必要です。

たとえば、同じpartialクラスの一部でpublic、別の一部でinternalのように異なるアクセス修飾子を付けることはできません。

C#
// NG
public partial class User
{
}
C#
// NG
internal partial class User
{
}

partialクラスやpartialメンバーの複数宣言では、アクセシビリティを一致させる必要があります。Microsoftのドキュメントでも、異なるアクセシビリティを宣言するとコンパイラエラーになると説明されています。

継承やインターフェイス実装についても、分割された宣言全体が1つの型として結合されます。

C#
public partial class User : IEntity
{
}
C#
public partial class User
{
}

このように、片方にインターフェイス実装を書いても、最終的なUserクラス全体がIEntityを実装することになります。

属性についても、分割された宣言に付けた属性が結合される場合があります。ただし、属性の種類によって複数指定できるかどうかは異なるため、属性の仕様にも注意が必要です。

7-4. チーム開発で命名規則や配置ルールを決める

チーム開発でpartialを使う場合は、命名規則や配置ルールを決めておくことが重要です。

たとえば、次のようなルールです。

・自動生成コードは .Generated.cs にする
・Visual Studioデザイナー生成コードは .Designer.cs にする
・バリデーションは .Validation.cs にする
・表示用メソッドは .Display.cs にする
・1つのpartialクラスを過度に分割しない

ルールがないと、人によって分け方がバラバラになります。

ある人はUser.Validation.csに書き、別の人はUser.Check.csに書き、さらに別の人はUser.Helpers.csに書く、といった状態になると、保守が難しくなります。

partialは便利な機能ですが、プロジェクト全体の一貫性がないと逆効果です。

7-5. partialに頼りすぎると設計改善が遅れる

partialに頼りすぎると、本来必要な設計改善が遅れることがあります。

たとえば、クラスが大きくなったときに、毎回partialでファイルを増やしていると、クラスの責務が膨らみ続けます。

OrderService.cs
OrderService.Payment.cs
OrderService.Shipping.cs
OrderService.Mail.cs
OrderService.Coupon.cs
OrderService.Report.cs

このような状態になったら、OrderServiceが多くの責務を持ちすぎている可能性があります。

本来は、次のように分けたほうがよいかもしれません。

OrderService.cs
PaymentService.cs
ShippingService.cs
MailService.cs
CouponService.cs
OrderReportService.cs

partialはファイルを分ける機能であり、責務を分ける設計手法ではありません。

コードが大きくなってきたら、partialで隠すのではなく、クラス設計を見直すことが大切です。

8. partialクラスと他の機能の違い

8-1. partialクラスと継承の違い

partialクラスと継承は、目的がまったく異なります。

partialは、同じクラスの定義を複数ファイルに分ける機能です。

C#
public partial class User
{
}
C#
public partial class User
{
}

一方、継承は、既存のクラスをもとに新しいクラスを作る機能です。

C#
public class User
{
}
C#
public class AdminUser : User
{
}

partialでは最終的に1つのUserクラスになります。継承では、UserクラスとAdminUserクラスという別々の型が存在します。

つまり、partialはコード整理のための機能であり、継承は型の関係を表現するための機能です。

8-2. partialクラスと拡張メソッドの違い

partialクラスと拡張メソッドも目的が異なります。

partialクラスは、同じクラスの内部にメンバーを追加します。

C#
public partial class User
{
public void Print()
{
Console.WriteLine("User");
}
}

一方、拡張メソッドは、既存の型に対して外部からメソッドを追加したように見せる機能です。

C#
public static class StringExtensions
{
public static bool IsEmpty(this string value)
{
return string.IsNullOrEmpty(value);
}
}

拡張メソッドは、対象クラスのprivateフィールドやprivateメソッドにはアクセスできません。

一方、partialクラスは同じクラスの一部なので、privateメンバーにもアクセスできます。

C#
public partial class User
{
private string _name = "Taro";
}
C#
public partial class User
{
public void PrintName()
{
Console.WriteLine(_name);
}
}

このように、partialはクラス内部の分割、拡張メソッドは外部からの機能追加です。

8-3. partialクラスとインターフェイスの違い

partialクラスとインターフェイスも別の概念です。

partialクラスは、クラスの定義を複数ファイルに分けるための機能です。

C#
public partial class User
{
}

インターフェイスは、クラスが実装すべき契約を定義する機能です。

C#
public interface IUserRepository
{
User FindById(int id);
}
C#
public class UserRepository : IUserRepository
{
public User FindById(int id)
{
return new User();
}
}

partialは「どこにコードを書くか」に関する仕組みです。

インターフェイスは「どのような機能を持つべきか」を表す仕組みです。

なお、インターフェイス自体もpartial interfaceとして分割できます。

C#
public partial interface IRepository
{
void Add();
}
C#
public partial interface IRepository
{
void Remove();
}

ただし、通常の業務コードでは、partial interfaceを頻繁に使う場面は多くありません。自動生成コードや大規模なAPI定義などで使われることがあります。

8-4. partialクラスとファイル分割・フォルダ分けの違い

partialクラスは、同じクラスの中身を複数ファイルに分ける機能です。

一方、通常のファイル分割やフォルダ分けは、別々のクラスを整理するためのものです。

たとえば、次のような構成は通常のクラス分割です。

Services
UserService.cs
ProductService.cs
OrderService.cs

これは、それぞれ別のクラスです。

一方、次のような構成はpartialによる分割です。

Models
User.cs
User.Validation.cs
User.Display.cs

これは、すべて同じUserクラスの一部です。

見た目はどちらもファイルが分かれていますが、意味は大きく異なります。

通常のクラス分割は責務を分けるためのものです。partialは同じ責務を持つ1つの型を、記述上分けるためのものです。

9. partialに関するよくある質問

9-1. partialは必ず使うべきですか?

いいえ、partialは必ず使うべきものではありません。

通常のクラスで十分に読みやすい場合は、partialを使う必要はありません。

partialが特に役立つのは、自動生成コードと手書きコードを分けたい場合や、同じクラスを機能単位で整理したい場合です。

逆に、単にファイルを細かく分けたいだけなら、使わないほうが読みやすいこともあります。

9-2. partialクラスは別のプロジェクトに分けられますか?

基本的に、同じpartialクラスの各部分は、同じコンパイル単位に含まれている必要があります。

つまり、別のプロジェクトに分けて、それぞれを同じ1つのクラスとして結合することはできません。

たとえば、ProjectAに次のクラスがあるとします。

C#
namespace SampleApp.Models;

public partial class User
{
}

別のProjectBに同じ名前空間、同じクラス名で次のように書いても、同じUserクラスとして結合されるわけではありません。

C#
namespace SampleApp.Models;

public partial class User
{
}

それぞれのプロジェクトは別々にコンパイルされるためです。

別プロジェクトから機能を追加したい場合は、継承、拡張メソッド、ラッパークラス、委譲などを検討しましょう。

9-3. partialクラスのファイル名は同じである必要がありますか?

いいえ、ファイル名は同じである必要はありません。

たとえば、次のようなファイル名でも問題ありません。

User.cs
User.Validation.cs
User.Display.cs

C#コンパイラが見るのは、ファイル名ではなく、名前空間、クラス名、型パラメータなどの宣言です。

ただし、実務上は関連するファイルだと分かる名前にすることが大切です。

User.cs
User.Authentication.cs
User.Profile.cs

このようにクラス名を先頭にそろえると、IDE上でも見つけやすくなります。

9-4. partialメソッドは普通のメソッドと何が違いますか?

普通のメソッドは、基本的に宣言と実装を同じ場所に書きます。

C#
public void Print()
{
Console.WriteLine("Hello");
}

一方、partial methodは、宣言と実装を別々のpartial定義に分けられます。

C#
partial void OnCreated();
C#
partial void OnCreated()
{
Console.WriteLine("Created");
}

特に、自動生成コード側が宣言だけを用意し、手書きコード側で必要に応じて実装する、という使い方ができます。

古典的なpartial voidメソッドでは、実装しない場合に呼び出しが取り除かれるため、軽量な拡張ポイントとして使えます。

ただし、アクセス修飾子や戻り値を持つ新しい形式のpartialメソッドでは、実装が必須になる点に注意が必要です。

9-5. partialを使うとパフォーマンスに影響はありますか?

通常、partialを使ったこと自体による実行時パフォーマンスへの影響はありません。

partialは、ソースコードを複数ファイルに分けて書くための仕組みです。コンパイル時には1つの型として結合されます。

そのため、次の2つは設計上ほぼ同じ意味になります。

C#
public class User
{
public string Name { get; set; }

public void Print()
{
Console.WriteLine(Name);
}
}
C#
public partial class User
{
public string Name { get; set; }
}
C#
public partial class User
{
public void Print()
{
Console.WriteLine(Name);
}
}

違いは、ソースコードを1ファイルに書いたか、複数ファイルに分けて書いたかです。

ただし、partialによってコードが分散し、保守性が下がると、間接的にバグや設計劣化の原因になることはあります。パフォーマンスよりも、可読性と保守性への影響を意識しましょう。

まとめ

C#のpartialは、クラスやメソッドなどの定義を複数ファイルに分けて書ける便利な機能です。

特にpartial classは、自動生成コードと手書きコードを分離する場面でよく使われます。WinForms、WPF、Entity Framework、Source Generatorなどで見かけることが多いのも、ツールが生成するコードと開発者が書くコードを安全に分けられるからです。

partial classを使うと、1つのクラスを機能単位で整理できます。

User.cs
User.Validation.cs
User.Authentication.cs

一方で、partialは設計の悪さを解決する機能ではありません。責務が多すぎるクラスをpartialで分割しても、クラス自体が抱えている問題は残ります。

partialを使うべきなのは、主に次のような場面です。

・自動生成コードと手書きコードを分けたい
・同じクラスを機能単位で整理したい
・Source Generatorなどで生成コードを既存型に追加したい
・チーム開発で編集箇所を分けたい

反対に、次のような場面では注意が必要です。

・神クラスを隠すために分割する
・単にファイルを細かくしたいだけ
・本来は別クラスにすべき責務を同じクラスに押し込んでいる
・分割ルールがなく、どこに何があるか分からない

また、partial methodを使うと、自動生成コードに拡張ポイントを用意できます。

C#
partial void OnBeforeSave();

必要な場合だけ別ファイルで実装できるため、生成コードを変更せずに処理を差し込めます。

C#のpartialは、正しく使えばコード管理をしやすくする強力な機能です。しかし、使いすぎると処理が分散し、かえって読みにくくなります。

「同じクラスとして分けるべきか」「別クラスに分割すべきか」を判断しながら、必要な場面でpartialを活用しましょう。