次の方法で共有


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

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

部分クラス

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

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

クラス定義を分割するには、 partial キーワード修飾子を使用します。 実際には、各部分クラスは通常、個別のファイルで定義されるため、時間の経過に伴うクラスの管理と拡張が容易になります。

次の Employee 例は、クラスを 2 つのファイル (Employee_Part1.cs と Employee_Part2.cs) に分割する方法を示しています。

// This is in Employee_Part1.cs
public partial class Employee
{
    public void DoWork()
    {
        Console.WriteLine("Employee is working.");
    }
}

// This is in Employee_Part2.cs
public partial class Employee
{
    public void GoToLunch()
    {
        Console.WriteLine("Employee is at lunch.");
    }
}

//Main program demonstrating the Employee class usage
public class Program
{
    public static void Main()
    {
        Employee emp = new Employee();
        emp.DoWork();
        emp.GoToLunch();
    }
}

// Expected Output:
// Employee is working.
// Employee is at lunch.

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 メンバーの両方の宣言にコメントが含まれている場合は、実装メンバーからのコメントのみが含まれます。
  • interfaces
  • ジェネリック型パラメーター属性
  • クラス属性
  • メンバー

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

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 つの部分クラス定義 (Coords_Part1.cs) で宣言され、 PrintCoords メソッドは別の部分クラス定義 (Coords_Part2.cs) で宣言されています。 この分離は、保守容易にするために部分クラスを複数のファイルに分割する方法を示しています。

 // This is in Coords_Part1.cs
 public partial class Coords
 {
     private int x;
     private int y;

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

 // This is in Coords_Part2.cs
 public partial class Coords
 {
     public void PrintCoords()
     {
         Console.WriteLine("Coords: {0},{1}", x, y);
     }
 }

// Main program demonstrating the Coords class usage
 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() { }
}

partial メンバー

部分クラスまたは構造体には partial メンバーを含めることができます。 クラスのある部分に、メンバーの署名が含まれます。 実装は、同じ部分でも別の部分でも定義できます。

シグネチャが次の規則に従っている場合、部分メソッドの実装は必要ありません。

  • 宣言にアクセス修飾子が含まれません。 このメソッドには、既定で private アクセス権があります。
  • 戻り値の型は void です。
  • out 修飾子を持つパラメーターはありません。
  • メソッドの宣言に次の修飾子を含めることはできません。

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

これらのすべての制限に準拠していないメソッド (プロパティやインデクサーなど) は、実装を提供する必要があります。 その実装は、"ソース ジェネレーター" によって提供される場合があります。 部分プロパティ 自動的に実装されるプロパティを使用して実装することはできません。 コンパイラは、自動的に実装されるプロパティと部分プロパティの宣言を区別できません。

C# 13 以降では、部分プロパティの実装宣言では、 フィールドでサポートされるプロパティを使用して 実装宣言を定義できます。 フィールド に基づくプロパティは、 field キーワードがプロパティのコンパイラ合成バッキング フィールドにアクセスする簡潔な構文を提供します。 たとえば、次のように記述できます。

// in file1.cs
public partial class PropertyBag
{
    // Defining declaration
    public partial int MyProperty { get; set; }
}

// In file2.cs
public partial class PropertyBag
{
    // Defining declaration
    public partial int MyProperty { get => field; set; }
}

fieldは、getアクセサーまたは set アクセサー、またはその両方で使用できます。

重要

field キーワードは、C# 13 のプレビュー機能です。 fieldコンテキスト キーワードを使用するには、.NET 9 を使用し、<LangVersion>要素をプロジェクト ファイルにpreviewするように設定する必要があります。

field という名前のフィールドがあるクラスでは、field キーワード機能を使用する場合は注意が必要です。 新しい field キーワードは、プロパティ アクセサーのスコープ内の field という名前のフィールドをシャドウします。 field変数の名前を変更するか、@ トークンを使用してfield識別子を@fieldとして参照できます。 詳細については、field キーワード機能の仕様を参照してください。

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

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

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

C# 言語仕様

詳しくは、C# 言語仕様部分型および部分メソッドに関する説明をご覧ください。 言語仕様は、C# の構文と使用法に関する信頼性のある情報源です。 部分メソッドの新機能は、 機能の仕様で定義されています。

関連項目