ポリモーフィズム (C# プログラミング ガイド)Polymorphism (C# Programming Guide)

ポリモーフィズムは、カプセル化と継承に次ぐ、オブジェクト指向プログラミングの第 3 の柱と言われることがよくあります。Polymorphism is often referred to as the third pillar of object-oriented programming, after encapsulation and inheritance. ポリモーフィズムは、ギリシャ語で "多形" を意味し、次の 2 つの側面を持っています。Polymorphism is a Greek word that means "many-shaped" and it has two distinct aspects:

  • メソッド パラメーター、コレクション、配列などに渡された派生クラスのオブジェクトは、実行時に基底クラスのオブジェクトとして扱われることがあります。At run time, objects of a derived class may be treated as objects of a base class in places such as method parameters and collections or arrays. この場合、オブジェクトの宣言された型は、その実行時の型と同じではなくなります。When this occurs, the object's declared type is no longer identical to its run-time type.

  • 基底クラスでは、virtual メソッドを定義して実行できます。派生クラスでそれをオーバーライドすると、独自の定義と実装を提供できます。Base classes may define and implement virtual methods, and derived classes can override them, which means they provide their own definition and implementation. 実行時には、クライアント コードがメソッドを呼び出したとき、CLR によってオブジェクトの実行時の型が検索され、仮想メソッドのオーバーライドが呼び出されます。At run-time, when client code calls the method, the CLR looks up the run-time type of the object, and invokes that override of the virtual method. このように、ソース コード内で基底クラスのメソッドを呼び出して、派生クラスのメソッドが実行されるようにできます。Thus in your source code you can call a method on a base class, and cause a derived class's version of the method to be executed.

仮想メソッドを使用すると、関連するオブジェクトのグループを同一の方法で扱うことができます。Virtual methods enable you to work with groups of related objects in a uniform way. たとえば、描画サーフェイスにさまざまな種類の図形を作成できる描画アプリケーションがあるとします。For example, suppose you have a drawing application that enables a user to create various kinds of shapes on a drawing surface. コンパイル時には、ユーザーがどのような種類の図形を作成するかわかりません。You do not know at compile time which specific types of shapes the user will create. しかし、アプリケーションでは、作成されたさまざまな種類の図形を追跡し、ユーザーのマウス操作に応じて更新する必要があります。However, the application has to keep track of all the various types of shapes that are created, and it has to update them in response to user mouse actions. ポリモーフィズムを使用すると、2 つの基本的な手順でこの問題を解決できます。You can use polymorphism to solve this problem in two basic steps:

  1. 各図形クラスが共通の基底クラスから派生するようなクラス階層を作成します。Create a class hierarchy in which each specific shape class derives from a common base class.

  2. 仮想メソッドを使用して、基底クラスの 1 つのメソッドを呼び出すことで、派生クラスの適切なメソッドが呼び出されるようにします。Use a virtual method to invoke the appropriate method on any derived class through a single call to the base class method.

まず、Shape という基底クラスと、RectangleCircleTriangle などの派生クラスを作成します。First, create a base class called Shape, and derived classes such as Rectangle, Circle, and Triangle. Shape クラスで Draw という仮想メソッドを定義し、各派生クラスでそれをオーバーライドして、そのクラスが表す特定の図形を描画します。Give the Shape class a virtual method called Draw, and override it in each derived class to draw the particular shape that the class represents. List<Shape> オブジェクトを作成し、Circle、Triangle、および Rectangle を追加します。Create a List<Shape> object and add a Circle, Triangle and Rectangle to it. 描画サーフェイスを更新するには、foreach ループを使用してリストを反復処理し、リスト内の各 Shape オブジェクトの Draw メソッドを呼び出します。To update the drawing surface, use a foreach loop to iterate through the list and call the Draw method on each Shape object in the list. リスト内の各オブジェクトの宣言された型は Shape ですが、呼び出されるのは実行時の型 (それぞれの派生クラスでオーバーライドされたメソッド) になります。Even though each object in the list has a declared type of Shape, it is the run-time type (the overridden version of the method in each derived class) that will be invoked.

using System;
using System.Collections.Generic;

public class Shape
{
    // A few example members
    public int X { get; private set; }
    public int Y { get; private set; }
    public int Height { get; set; }
    public int Width { get; set; }
   
    // Virtual method
    public virtual void Draw()
    {
        Console.WriteLine("Performing base class drawing tasks");
    }
}

class Circle : Shape
{
    public override void Draw()
    {
        // Code to draw a circle...
        Console.WriteLine("Drawing a circle");
        base.Draw();
    }
}
class Rectangle : Shape
{
    public override void Draw()
    {
        // Code to draw a rectangle...
        Console.WriteLine("Drawing a rectangle");
        base.Draw();
    }
}
class Triangle : Shape
{
    public override void Draw()
    {
        // Code to draw a triangle...
        Console.WriteLine("Drawing a triangle");
        base.Draw();
    }
}

class Program
{
    static void Main(string[] args)
    {
        // Polymorphism at work #1: a Rectangle, Triangle and Circle
        // can all be used whereever a Shape is expected. No cast is
        // required because an implicit conversion exists from a derived 
        // class to its base class.
        var shapes = new List<Shape>
        {
            new Rectangle(),
            new Triangle(),
            new Circle()
        };

        // Polymorphism at work #2: the virtual method Draw is
        // invoked on each of the derived classes, not the base class.
        foreach (var shape in shapes)
        {
            shape.Draw();
        }

        // Keep the console open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }

}

/* Output:
    Drawing a rectangle
    Performing base class drawing tasks
    Drawing a triangle
    Performing base class drawing tasks
    Drawing a circle
    Performing base class drawing tasks
 */

C# では、すべての型がポリモーフィックです。これは、ユーザー定義型を含むすべての型が Object から派生するためです。In C#, every type is polymorphic because all types, including user-defined types, inherit from Object.

ポリモーフィズムの概要Polymorphism Overview

仮想メンバーVirtual Members

基底クラスから派生クラスを継承すると、派生クラスは、基底クラスのすべてのメソッド、フィールド、プロパティ、およびイベントを継承します。When a derived class inherits from a base class, it gains all the methods, fields, properties and events of the base class. 派生クラスの設計者は、次の点を選択できます。The designer of the derived class can choose whether to

  • 基底クラスの仮想メンバーをオーバーライドするかどうかoverride virtual members in the base class,

  • 最も近い基底クラスのメソッドを、オーバーライドせずに継承するかどうかinherit the closest base class method without overriding it

  • これらのメンバーの仮想でない実装を新しく定義して、基底クラスの実装を隠ぺいするかどうかdefine new non-virtual implementation of those members that hide the base class implementations

派生クラスが基底クラスのメンバーをオーバーライドできるのは、基底クラスのメンバーが virtual または abstract として宣言されている場合だけです。A derived class can override a base class member only if the base class member is declared as virtual or abstract. 派生メンバーでは、override キーワードを使用して、そのメソッドが仮想呼び出しに加わることを明示的に示す必要があります。The derived member must use the override keyword to explicitly indicate that the method is intended to participate in virtual invocation. 次にコード例を示します。The following code provides an example:

public class BaseClass
{
    public virtual void DoWork() { }
    public virtual int WorkProperty
    {
        get { return 0; }
    }
}
public class DerivedClass : BaseClass
{
    public override void DoWork() { }
    public override int WorkProperty
    {
        get { return 0; }
    }
}

フィールドは仮想メンバーにできません。仮想メンバーにできるのは、メソッド、プロパティ、イベント、およびインデクサーに限られます。Fields cannot be virtual; only methods, properties, events and indexers can be virtual. 派生クラスが仮想メンバーをオーバーライドすると、派生クラスのメンバーは、そのクラスのインスタンスが基底クラスのインスタンスとしてアクセスされるときでも呼び出されます。When a derived class overrides a virtual member, that member is called even when an instance of that class is being accessed as an instance of the base class. 次にコード例を示します。The following code provides an example:

DerivedClass B = new DerivedClass();
B.DoWork();  // Calls the new method.

BaseClass A = (BaseClass)B;
A.DoWork();  // Also calls the new method.

仮想メソッドとプロパティを使用すると、派生クラスは、基底クラスのメソッドの実装を使用せずに基底クラスを拡張できます。Virtual methods and properties enable derived classes to extend a base class without needing to use the base class implementation of a method. 詳細については、「Override キーワードと New キーワードによるバージョン管理」を参照してください。For more information, see Versioning with the Override and New Keywords. 1 つまたは一連のメソッドを定義し、その実装を派生クラスに任せるもう 1 つの方法として、インターフェイスがあります。An interface provides another way to define a method or set of methods whose implementation is left to derived classes. 詳細については、「インターフェイス」を参照してください。For more information, see Interfaces.

新しいメンバーによる基底クラスのメンバーの隠ぺいHiding Base Class Members with New Members

派生メンバーに基底クラスのメンバーと同じ名前を付けながら、そのメンバーが仮想呼び出しに加わらないようにするには、new キーワードを使用します。If you want your derived member to have the same name as a member in a base class, but you do not want it to participate in virtual invocation, you can use the new keyword. new キーワードは、置き換えられるクラス メンバーの戻り値の型の前に配置します。The new keyword is put before the return type of a class member that is being replaced. 次にコード例を示します。The following code provides an example:

public class BaseClass
{
    public void DoWork() { WorkField++; }
    public int WorkField;
    public int WorkProperty
    {
        get { return 0; }
    }
}

public class DerivedClass : BaseClass
{
    public new void DoWork() { WorkField++; }
    public new int WorkField;
    public new int WorkProperty
    {
        get { return 0; }
    }
}

基底クラスのメンバーが隠ぺいされても、派生クラスのインスタンスを基底クラスのインスタンスにキャストすることで、クライアント コードから基底クラスのメンバーにアクセスできます。Hidden base class members can still be accessed from client code by casting the instance of the derived class to an instance of the base class. 次に例を示します。For example:

DerivedClass B = new DerivedClass();
B.DoWork();  // Calls the new method.

BaseClass A = (BaseClass)B;
A.DoWork();  // Calls the old method.

派生クラスが仮想メンバーをオーバーライドしないようにするPreventing Derived Classes from Overriding Virtual Members

仮想メンバーは、それを最初に宣言したクラスとの間でどれほど多くのクラスが宣言されても、いつまでも仮想のままです。Virtual members remain virtual indefinitely, regardless of how many classes have been declared between the virtual member and the class that originally declared it. たとえば、クラス A が仮想メンバーを宣言し、クラス B がクラス A から派生し、クラス C がクラス B から派生した場合、クラス C は仮想メンバーを継承し、クラス B がその仮想メンバーのオーバーライドを宣言したかどうかに関係なく、そのメンバーをオーバーライドできます。If class A declares a virtual member, and class B derives from A, and class C derives from B, class C inherits the virtual member, and has the option to override it, regardless of whether class B declared an override for that member. 次にコード例を示します。The following code provides an example:

public class A
{
    public virtual void DoWork() { }
}
public class B : A
{
    public override void DoWork() { }
}

派生クラスでは、オーバーライドを sealed として宣言することで仮想継承を中止できます。A derived class can stop virtual inheritance by declaring an override as sealed. この場合、クラス メンバーの宣言で、sealed キーワードの前に override キーワードを指定する必要があります。This requires putting the sealed keyword before the override keyword in the class member declaration. 次にコード例を示します。The following code provides an example:

public class C : B
{
    public sealed override void DoWork() { }
}

上の例では、DoWork メソッドは C から派生したどのクラスに対しても仮想メソッドではありません。C のインスタンスに対しては、B 型や A 型にキャストされた場合でも、依然として仮想メソッドです。シール メソッドは、次のコード例に示すように、new キーワードを使用して派生クラスに置き換えることができます。In the previous example, the method DoWork is no longer virtual to any class derived from C. It is still virtual for instances of C, even if they are cast to type B or type A. Sealed methods can be replaced by derived classes by using the new keyword, as the following example shows:

public class D : C
{
    public new void DoWork() { }
}

このコード例では、DoWork が、D 型の変数を使用して D で呼び出されると、新しい DoWork が呼び出されます。In this case, if DoWork is called on D using a variable of type D, the new DoWork is called. また、C 型、B 型、または A 型の変数を使用して D のインスタンスにアクセスした場合、DoWork への呼び出しは、仮想継承の規則に従って、クラス C の DoWork の実装に転送されます。If a variable of type C, B, or A is used to access an instance of D, a call to DoWork will follow the rules of virtual inheritance, routing those calls to the implementation of DoWork on class C.

派生クラスからの基底クラスの仮想メンバーへのアクセスAccessing Base Class Virtual Members from Derived Classes

メソッドやプロパティを置き換えたり、オーバーライドしたりした派生クラスでは、base キーワードを使用して、基底クラスのメソッドやプロパティに引き続きアクセスできます。A derived class that has replaced or overridden a method or property can still access the method or property on the base class using the base keyword. 次にコード例を示します。The following code provides an example:

public class Base
{
    public virtual void DoWork() {/*...*/ }
}
public class Derived : Base
{
    public override void DoWork()
    {
        //Perform Derived's work here
        //...
        // Call DoWork on base class
        base.DoWork();
    }
}

詳細については、「base」を参照してください。For more information, see base.

注意

仮想メンバーの場合、その固有の実装で base を使用して、その仮想メンバーの基底クラス実装を呼び出すことをお勧めします。It is recommended that virtual members use base to call the base class implementation of that member in their own implementation. 基底クラスの動作を実行できるようにすることで、派生クラスは、派生クラスに固有の動作を実装することに集中できます。Letting the base class behavior occur enables the derived class to concentrate on implementing behavior specific to the derived class. 基底クラス実装を呼び出さない場合は、基底クラスの動作と互換性のある動作を派生クラスで実現する必要があります。If the base class implementation is not called, it is up to the derived class to make their behavior compatible with the behavior of the base class.

このセクションの内容In This Section

関連項目See also