部分クラスと部分メソッド (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 で修飾する必要があります。 たとえば、次のクラス宣言はエラーになります。
    public partial class A { }
    //public class A { }  // Error, must also be marked partial
    
  • partial 修飾子は、classstruct、または interface キーワードの直前にのみ配置できます。
  • 入れ子にされた部分型は、次の例に示すように、部分型定義で宣言できます。
    partial class ClassWithNestedClass
    {
        partial class NestedClass { }
    }
    
    partial class ClassWithNestedClass
    {
        partial class NestedClass { }
    }
    
  • 同じ型の部分である部分型定義は、すべて同じアセンブリおよび同じモジュール (.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 修飾子を持つパラメーターはありません。
  • メソッドの宣言に次の修飾子を含めることはできません。

このメソッドと、このメソッドのすべての呼び出しは、実装がない場合、コンパイル時に削除されます。

これらのすべての制限に準拠していないメソッド (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# の構文と使用法に関する信頼性のある情報源です。 部分メソッドの追加機能は、機能仕様で定義されています。

関連項目