継承 (C# プログラミング ガイド)Inheritance (C# Programming Guide)

継承は、カプセル化およびポリモーフィズムと共に、オブジェクト指向プログラミングの主要な 3 つの特性の 1 つです。Inheritance, together with encapsulation and polymorphism, is one of the three primary characteristics of object-oriented programming. 継承を使用すると、他のクラスで定義されている動作を再利用、拡張、および変更する新しいクラスを作成できます。Inheritance enables you to create new classes that reuse, extend, and modify the behavior that is defined in other classes. メンバーが継承される側のクラスを "基底クラス" と呼び、メンバーを継承する側のクラスを "派生クラス" と呼びます。The class whose members are inherited is called the base class, and the class that inherits those members is called the derived class. 派生クラスは、直接の基底クラスを 1 つだけ持つことができます。A derived class can have only one direct base class. ただし、継承は推移的です。However, inheritance is transitive. ClassC が ClassB から派生し、ClassB が ClassA から派生している場合、ClassC は ClassB と ClassA で宣言されたメンバーを継承します。If ClassC is derived from ClassB, and ClassB is derived from ClassA, ClassC inherits the members declared in ClassB and ClassA.

注意

構造体では、継承がサポートされていませんが、インターフェイスを実装することはできます。Structs do not support inheritance, but they can implement interfaces. 詳細については、「インターフェイス」を参照してください。For more information, see Interfaces.

概念的には、派生クラスは基底クラスから特化したクラスです。Conceptually, a derived class is a specialization of the base class. たとえば、基底クラス Animal がある場合、Mammal という名前の派生クラスと、Reptile という名前の別の派生クラスを持つことができます。For example, if you have a base class Animal, you might have one derived class that is named Mammal and another derived class that is named Reptile. MammalAnimal であり、ReptileAnimal ですが、各派生クラスは、基底クラスから別々の特殊化を表します。A Mammal is an Animal, and a Reptile is an Animal, but each derived class represents different specializations of the base class.

クラスを別のクラスから派生するように定義すると、派生クラスには、コンストラクターとファイナライザーを除く、基底クラスのすべてのメンバーが暗黙的に引き継がれます。When you define a class to derive from another class, the derived class implicitly gains all the members of the base class, except for its constructors and finalizers. そのため、派生クラスでは、基底クラスのコードを再実装しなくても再利用できます。The derived class can thereby reuse the code in the base class without having to re-implement it. 派生クラスには、メンバーを追加できます。In the derived class, you can add more members. このような方法で、派生クラスは基底クラスの機能を拡張します。In this manner, the derived class extends the functionality of the base class.

次の図は、あるビジネス プロセスの作業項目を表す WorkItem クラスを示しています。The following illustration shows a class WorkItem that represents an item of work in some business process. 他のすべてのクラスと同様に、System.Object から派生し、そのすべてのメソッドを継承します。Like all classes, it derives from System.Object and inherits all its methods. WorkItem には、独自のメンバーが 5 つ追加されています。WorkItem adds five members of its own. これにはコンストラクターが含まれています。コンストラクターは継承されないためです。These include a constructor, because constructors are not inherited. WorkItem から継承される ChangeRequest クラスは、特定の種類の作業項目を表します。Class ChangeRequest inherits from WorkItem and represents a particular kind of work item. ChangeRequest には、WorkItemObject から継承したメンバーに 2 つのメンバーが追加されます。ChangeRequest adds two more members to the members that it inherits from WorkItem and from Object. 独自のコンストラクターを追加する必要があるほか、さらに originalItemID も追加されます。It must add its own constructor, and it also adds originalItemID. originalItemID プロパティを使用すると、ChangeRequest インスタンスは、変更要求が適用される元の WorkItem と関連付けることができます。Property originalItemID enables the ChangeRequest instance to be associated with the original WorkItem to which the change request applies.

クラスの継承を示す図

次の例は、前の図に示したクラスの関係が C# でどのように表現されるかを示しています。The following example shows how the class relationships demonstrated in the previous illustration are expressed in C#. また、WorkItem が仮想メソッド Object.ToString をオーバーライドする方法と、ChangeRequest クラスが WorkItem によるメソッドの実装を継承する方法も示しています。The example also shows how WorkItem overrides the virtual method Object.ToString, and how the ChangeRequest class inherits the WorkItem implementation of the method.

// WorkItem implicitly inherits from the Object class.
public class WorkItem
{
    // Static field currentID stores the job ID of the last WorkItem that
    // has been created.
    private static int currentID;

    //Properties.
    protected int ID { get; set; }
    protected string Title { get; set; }
    protected string Description { get; set; }
    protected TimeSpan jobLength { get; set; }

    // Default constructor. If a derived class does not invoke a base-
    // class constructor explicitly, the default constructor is called
    // implicitly. 
    public WorkItem()
    {
        ID = 0;
        Title = "Default title";
        Description = "Default description.";
        jobLength = new TimeSpan();
    }

    // Instance constructor that has three parameters.
    public WorkItem(string title, string desc, TimeSpan joblen)
    {
        this.ID = GetNextID();
        this.Title = title;
        this.Description = desc;
        this.jobLength = joblen;
    }

    // Static constructor to initialize the static member, currentID. This
    // constructor is called one time, automatically, before any instance
    // of WorkItem or ChangeRequest is created, or currentID is referenced.
    static WorkItem()
    {
        currentID = 0;
    }


    protected int GetNextID()
    {
        // currentID is a static field. It is incremented each time a new
        // instance of WorkItem is created.
        return ++currentID;
    }

    // Method Update enables you to update the title and job length of an
    // existing WorkItem object.
    public void Update(string title, TimeSpan joblen)
    {
        this.Title = title;
        this.jobLength = joblen;
    }

    // Virtual method override of the ToString method that is inherited
    // from System.Object.
    public override string ToString()
    {
        return $"{this.ID} - {this.Title}";
    }
}

// ChangeRequest derives from WorkItem and adds a property (originalItemID) 
// and two constructors.
public class ChangeRequest : WorkItem
{
    protected int originalItemID { get; set; }

    // Constructors. Because neither constructor calls a base-class 
    // constructor explicitly, the default constructor in the base class
    // is called implicitly. The base class must contain a default 
    // constructor.

    // Default constructor for the derived class.
    public ChangeRequest() { }

    // Instance constructor that has four parameters.
    public ChangeRequest(string title, string desc, TimeSpan jobLen,
                         int originalID)
    {
        // The following properties and the GetNexID method are inherited 
        // from WorkItem.
        this.ID = GetNextID();
        this.Title = title;
        this.Description = desc;
        this.jobLength = jobLen;

        // Property originalItemId is a member of ChangeRequest, but not 
        // of WorkItem.
        this.originalItemID = originalID;
    }
}

class Program
{
    static void Main()
    {
        // Create an instance of WorkItem by using the constructor in the 
        // base class that takes three arguments.
        WorkItem item = new WorkItem("Fix Bugs",
                                     "Fix all bugs in my code branch",
                                     new TimeSpan(3, 4, 0, 0));

        // Create an instance of ChangeRequest by using the constructor in
        // the derived class that takes four arguments.
        ChangeRequest change = new ChangeRequest("Change Base Class Design",
                                                 "Add members to the class",
                                                 new TimeSpan(4, 0, 0),
                                                 1);

        // Use the ToString method defined in WorkItem.
        Console.WriteLine(item.ToString());

        // Use the inherited Update method to change the title of the 
        // ChangeRequest object.
        change.Update("Change the Design of the Base Class",
            new TimeSpan(4, 0, 0));

        // ChangeRequest inherits WorkItem's override of ToString.
        Console.WriteLine(change.ToString());

        // Keep the console open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Output:
    1 - Fix Bugs
    2 - Change the Design of the Base Class
*/

抽象メソッドと仮想メソッドAbstract and Virtual Methods

基底クラスでメソッドを virtual として宣言する場合、派生クラスはそのメソッドを独自の実装でオーバーライドすることができます。When a base class declares a method as virtual, a derived class can override the method with its own implementation. 基底クラスでメンバーを abstract として宣言する場合、そのクラスから直接継承される非抽象クラスで、そのメソッドをオーバーライドする必要があります。If a base class declares a member as abstract, that method must be overridden in any non-abstract class that directly inherits from that class. 派生クラス自体が抽象クラスである場合は、抽象メンバーを実装することなく継承します。If a derived class is itself abstract, it inherits abstract members without implementing them. 抽象メンバーと仮想メンバーは、オブジェクト指向プログラミングの重要な特性の 2 つ目であるポリモーフィズムの基礎です。Abstract and virtual members are the basis for polymorphism, which is the second primary characteristic of object-oriented programming. 詳細については、「ポリモーフィズム」を参照してください。For more information, see Polymorphism.

抽象基本クラスAbstract Base Classes

new キーワードを使用して直接インスタンス化されないようにする場合は、クラスを abstract として宣言できます。You can declare a class as abstract if you want to prevent direct instantiation by using the new keyword. このようにすると、そのクラスは、新しいクラスを派生させないと使用できません。If you do this, the class can be used only if a new class is derived from it. 抽象クラスには、それ自体が abstract として宣言された 1 つ以上のメソッド シグネチャを含めることができます。An abstract class can contain one or more method signatures that themselves are declared as abstract. これらのシグネチャは、パラメーターと戻り値を指定しますが、実装 (メソッドの本体) は持ちません。These signatures specify the parameters and return value but have no implementation (method body). 抽象クラスには、抽象メンバーを含める必要はありません。ただし、クラスに抽象メンバーが含まれている場合は、クラス自体を抽象クラスとして宣言する必要があります。An abstract class does not have to contain abstract members; however, if a class does contain an abstract member, the class itself must be declared as abstract. 抽象クラスでない派生クラスは、抽象基底クラスから継承した抽象メソッドすべてを実装する必要があります。Derived classes that are not abstract themselves must provide the implementation for any abstract methods from an abstract base class. 詳細については、「抽象クラスとシール クラス、およびクラス メンバー」を参照してください。For more information, see Abstract and Sealed Classes and Class Members.

インターフェイスInterfaces

"インターフェイス" は、抽象メンバーのみで構成される抽象基底クラスに類似した参照型です。An interface is a reference type that is somewhat similar to an abstract base class that consists of only abstract members. クラスでインターフェイスを実装するときは、インターフェイスのすべてのメンバーを実装する必要があります。When a class implements an interface, it must provide an implementation for all the members of the interface. クラスは、直接的には 1 つの基底クラスからしか派生できませんが、複数のインターフェイスを実装できます。A class can implement multiple interfaces even though it can derive from only a single direct base class.

インターフェイスは、必ずしも "is a" 関係を持たないクラスの特定の機能を定義するために使用されます。Interfaces are used to define specific capabilities for classes that do not necessarily have an "is a" relationship. たとえば、ある型の 2 つのオブジェクトが等しいかどうかをクライアント コードで判別できるようにする (ただし、等価性の定義は型によって行う) クラスまたは構造体では、System.IEquatable<T> インターフェイスを実装できます。For example, the System.IEquatable<T> interface can be implemented by any class or struct that has to enable client code to determine whether two objects of the type are equivalent (however the type defines equivalence). IEquatable<T> は、基底クラスと派生クラス間に存在する "is a" 関係 ("MammalAnimal である" など) と同様の意味を示すものではありません。IEquatable<T> does not imply the same kind of "is a" relationship that exists between a base class and a derived class (for example, a Mammal is an Animal). 詳細については、「インターフェイス」を参照してください。For more information, see Interfaces.

それ以上の派生の禁止Preventing Further Derivation

クラス自体またはメンバーを sealedと宣言することにより、他のクラスがそのクラスまたはそのクラスのメンバーを継承できないようにすることができます。A class can prevent other classes from inheriting from it, or from any of its members, by declaring itself or the member as sealed. 詳細については、「抽象クラスとシール クラス、およびクラス メンバー」を参照してください。For more information, see Abstract and Sealed Classes and Class Members.

派生クラスによる基底クラスのメンバーの隠ぺいDerived Class Hiding of Base Class Members

派生クラスは、同じ名前とシグネチャでメンバーを宣言することで、基底クラスのメンバーを隠ぺいすることができます。A derived class can hide base class members by declaring members with the same name and signature. new 修飾子を使用すると、そのメンバーが基底クラスのメンバーのオーバーライドとして用意されているのではないことを明示的に指定できます。The new modifier can be used to explicitly indicate that the member is not intended to be an override of the base member. new の使用は必須ではありませんが、new が使用されていない場合はコンパイラの警告が生成されます。The use of new is not required, but a compiler warning will be generated if new is not used. 詳細については、「Override キーワードと New キーワードによるバージョン管理」および「Override キーワードと New キーワードを使用する場合について」を参照してください。For more information, see Versioning with the Override and New Keywords and Knowing When to Use Override and New Keywords.

関連項目See also