部分クラスと部分メソッド (C# プログラミング ガイド)

クラス構造体インターフェイスやメソッドの定義を複数のソース ファイルに分割できます。 各ソース ファイルには型やメソッドの定義のセクションが含まれ、分割されたすべての部分はアプリケーションのコンパイル時に結合されます。

部分クラス

クラス定義を分割するのが望ましいのは、次のような場合です。

  • 大型プロジェクトを開発する際にクラスを別個のファイルに分割すると、複数のプログラマーが同時にそのクラスの作業を行うことができます。
  • 自動的に生成されたソースを使用する場合、ソース ファイルを再作成することなく、コードをクラスに追加できます。 Visual Studio では、Windows フォームや Web サービス ラッパー コードなどを作成するときにこのアプローチを使用します。 Visual Studio によって作成されたファイルを変更せずに、これらのクラスを使用するコードを作成できます。
  • ソース ジェネレーターを使用してクラスに追加の機能を生成するとき。

クラス定義を分割するには、次のように partial キーワード修飾子を使用します。

public partial class Employee
{
    public void DoWork()
    {
    }
}

public partial class Employee
{
    public void GoToLunch()
    {
    }
}

partial キーワードは、クラス、構造体、またはインターフェイスの他の部分を名前空間内で定義できることを示します。 partial キーワードは、すべての部分で使用する必要があります。 最終的な型を形成するためには、コンパイル時にすべての部分が利用可能である必要があります。 また、すべての部分で同じアクセシビリティ (publicprivate など) を使用する必要があります。

abstract と宣言された部分がある場合、型全体が抽象と見なされます。 sealed と宣言された部分がある場合、型全体が sealed と見なされます。 また、基本データ型を宣言する部分がある場合は、型全体が該当するクラスを継承します。

基底クラスを指定する部分はすべて一致する必要がありますが、基底クラスを省略する部分も基本データ型を継承します。 部分は別の基本インターフェイスを指定でき、すべての部分宣言で示されたすべてのインターフェイスが最終的な型によって実装されます。 部分定義で宣言されたクラス、構造体、インターフェイスの各メンバーは、他のすべての部分で利用できます。 最終的な型は、コンパイル時にすべての部分を結合して形成されます。

注意

partial 識別子は、デリゲートや列挙宣言では使用できません。

次の例は、入れ子にされた型は、それを包含する型自体が partial でない場合でも、partial にできることを示しています。

class Container
{
    partial class Nested
    {
        void Test() { }
    }

    partial class Nested
    {
        void Test2() { }
    }
}

部分型定義の属性は、コンパイル時に結合されます。 たとえば、次のような宣言があるとします。

[SerializableAttribute]
partial class Moon { }

[ObsoleteAttribute]
partial class Moon { }

これらは、次の宣言と等価です。

[SerializableAttribute]
[ObsoleteAttribute]
class Moon { }

各部分型定義に含まれる次の要素は、すべて結合されます。

  • XML コメント
  • インターフェイス
  • ジェネリック型パラメーター属性
  • クラス属性
  • メンバー

たとえば、次のような宣言があるとします。

partial class Earth : Planet, IRotate { }
partial class Earth : IRevolve { }

これらは、次の宣言と等価です。

class Earth : Planet, IRotate, IRevolve { }

制約

部分クラス定義を使用する場合は、いくつかの規則に従う必要があります。

  • 同じ型の部分である部分型定義はすべて partial で修飾する必要があります。 たとえば、次のクラス宣言はエラーになります。 [!code-csharpAllDefinitionsMustBePartials#7]
  • partial 修飾子は、classstruct、または interface キーワードの直前にのみ配置できます。
  • 入れ子にされた部分型は、次の例に示すように、部分型定義で宣言できます。 [!code-csharpNestedPartialTypes#8]
  • 同じ型の部分である部分型定義は、すべて同じアセンブリおよび同じモジュール (.exe ファイルまたは .dll ファイル) 内で定義する必要があります。 部分定義は、複数のモジュールにまたがることができません。
  • クラス名とジェネリック型パラメーターはすべての部分型定義で一致する必要があります。 ジェネリック型は partial にできます。 それぞれの部分宣言では、同じパラメーター名を同じ順序で使用する必要があります。
  • 以下のキーワードは、部分型定義では省略できますが、ある 1 つの部分型定義に存在する場合は、同じ型の別の部分定義で指定されているキーワードと競合できません。

詳細については、「型パラメーターの制約」を参照してください。

次の例では、クラス Coords のフィールドとコンストラクターを 1 つの部分クラス定義で宣言し、メンバー PrintCoords を別の部分クラス定義で宣言しています。

public partial class Coords
{
    private int x;
    private int y;

    public Coords(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}

public partial class Coords
{
    public void PrintCoords()
    {
        Console.WriteLine("Coords: {0},{1}", x, y);
    }
}

class TestCoords
{
    static void Main()
    {
        Coords myCoords = new Coords(10, 15);
        myCoords.PrintCoords();

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
// Output: Coords: 10,15

次の例は、部分構造体と部分インターフェイスも開発できることを示しています。

partial interface ITest
{
    void Interface_Test();
}

partial interface ITest
{
    void Interface_Test2();
}

partial struct S1
{
    void Struct_Test() { }
}

partial struct S1
{
    void Struct_Test2() { }
}

部分メソッド

部分クラスまたは構造体には部分メソッドを含めることができます。 クラスのある部分に、メソッドのシグネチャが含まれます。 実装は、同じ部分でも別の部分でも定義できます。 実装が指定されていない場合、メソッドとメソッドに対するすべての呼び出しは、コンパイル時に削除されます。 メソッド シグネチャによっては、実装が必要になる場合があります。 次の場合には、部分メソッドを実装する必要はありません。

  • アクセシビリティ修飾子 (既定の private を含む) はありません。
  • void を返します。
  • out パラメーターはありません。
  • virtualoverridesealednewextern のいずれの修飾子もありません。

これらのすべての制限に準拠していないメソッド (public virtual partial void メソッドなど) は、実装を提供する必要があります。 その実装は、"ソース ジェネレーター" で提供できます。

部分メソッドを使用すると、クラスのある部分の実装者がメソッドを宣言できます。 クラスの別の部分の実装者は、そのメソッドを定義できます。 これは、定型コードを生成するテンプレートとソース ジェネレーターの 2 つのシナリオで便利です。

  • テンプレート コード: テンプレートでは、生成されたコードでメソッドを呼び出すことができるように、メソッド名とシグネチャが予約されます。 これらのメソッドは、開発者がメソッドを実装するかどうかを決定できるようにする制限に従います。 メソッドが実装されない場合、コンパイラは、メソッド シグネチャとメソッドに対するすべての呼び出しを削除します。 このメソッドの呼び出しは、呼び出しの引数の評価から発生するすべての結果を含め、実行時に影響を及ぼしません。 そのため、実装が指定されていない場合でも、部分クラス内のすべてのコードで部分メソッドを自由に使用できます。 実装されていないメソッドが呼び出された場合、コンパイル時エラーまたは実行時エラーにはなりません。
  • ソース ジェネレーター: ソース ジェネレーターでは、メソッドの実装が提供されます。 人間の開発者は、(多くの場合、ソース ジェネレーターによって読み取られる属性を使用して) メソッドの宣言を追加できます。 開発者は、これらのメソッドを呼び出すコードを記述できます。 コンパイルの間に実行されるソース ジェネレーターによって、実装が提供されます。 このシナリオでは、多くの場合、実装されない可能性がある部分メソッドの制限には従いません。
// Definition in file1.cs
partial void OnNameChanged();

// Implementation in file2.cs
partial void OnNameChanged()
{
  // method body
}
  • 部分メソッドの宣言は、コンテキスト キーワード partial で始まる必要があります。
  • 部分型の両方の部分で部分メソッド シグネチャが一致する必要があります。
  • 部分メソッドには static 修飾子と unsafe 修飾子を使用できます。
  • 部分メソッドはジェネリックにできます。 制約は部分メソッドの定義宣言に置き、必要に応じて実装宣言で繰り返すことができます。 パラメーター名と型パラメーター名は、定義宣言と実装宣言で同じである必要はありません。
  • delegate は、定義および実装されている部分メソッドには使用できますが、定義されているのみの部分メソッドには使用できません。

C# 言語仕様

詳細については、「C# 言語仕様」の部分型に関するセクションを参照してください。 言語仕様は、C# の構文と使用法に関する信頼性のある情報源です。

関連項目