Override キーワードと New キーワードを使用する場合について (C# プログラミング ガイド)

C# では、派生クラスのメソッドを基底クラスのメソッドと同じ名前にすることができます。 new および override キーワードを使って、メソッドでの処理を指定できます。 override 修飾子は基底クラスの virtualメソッドを "拡張" し、new 修飾子はアクセス可能な基底クラスのメソッドを "非表示" にします。 このトピックの例ではその違いを示します。

コンソール アプリケーションで、次の 2 つのクラス BaseClassDerivedClass を宣言します。 DerivedClassBaseClassを継承します。

class BaseClass  
{  
    public void Method1()  
    {  
        Console.WriteLine("Base - Method1");  
    }  
}  
  
class DerivedClass : BaseClass  
{  
    public void Method2()  
    {  
        Console.WriteLine("Derived - Method2");  
    }  
}  

Main メソッドで、変数 bcdcbcdc を宣言します。

  • bcBaseClass 型であり、その値は BaseClass です。

  • dcDerivedClass 型であり、その値は DerivedClass です。

  • bcdcBaseClass 型であり、その値は DerivedClass です。 この変数には注意する必要があります。

bcbcdcBaseClass 型なので、キャストを使わない限り、直接アクセスできるのは Method1 だけです。 変数 dc は、Method1Method2 の両方にアクセスできます。 これらの関係を次のコードに示します。

class Program  
{  
    static void Main(string[] args)  
    {  
        BaseClass bc = new BaseClass();  
        DerivedClass dc = new DerivedClass();  
        BaseClass bcdc = new DerivedClass();  
  
        bc.Method1();  
        dc.Method1();  
        dc.Method2();  
        bcdc.Method1();  
    }  
    // Output:  
    // Base - Method1  
    // Base - Method1  
    // Derived - Method2  
    // Base - Method1  
}  

次に、以下の Method2 メソッドを BaseClass に追加します。 このメソッドのシグネチャは、DerivedClassMethod2 メソッドのシグネチャと一致します。

public void Method2()  
{  
    Console.WriteLine("Base - Method2");  
}  

BaseClassMethod2 メソッドを追加したので、次のコードに示すように、BaseClass の変数 bc および bcdc に 2 つめの呼び出しステートメントを追加できます。

bc.Method1();  
bc.Method2();  
dc.Method1();  
dc.Method2();  
bcdc.Method1();  
bcdc.Method2();  

プロジェクトをビルドすると、BaseClassMethod2 メソッドを追加したことで警告が発生するようになります。 警告の内容は、DerivedClassMethod2 メソッドが BaseClassMethod2 メソッドを隠ぺいしているというものです。 それが意図する結果である場合は、Method2 の定義で new キーワードを使うことをお勧めします。 または、どちらかの Method2 メソッドの名前を変更して警告を解決することもできますが、実用的ではない場合があります。

new を追加する前に、プログラムを実行して、追加した呼び出しステートメントによって生成される出力を確認します。 次のような結果が表示されます。

// Output:  
// Base - Method1  
// Base - Method2  
// Base - Method1  
// Derived - Method2  
// Base - Method1  
// Base - Method2  

new キーワードは、その出力を生成する関係を維持しますが、警告を抑制します。 BaseClass 型の変数は引き続き BaseClass のメンバーにアクセスし、DerivedClass 型の変数は引き続き最初に DerivedClass のメンバーにアクセスした後、BaseClass から継承されたメンバーを考慮します。

警告を抑制するには、次のコードで示すように、DerivedClass での Method2 の定義に new 修飾子を追加します。 修飾子を追加する位置は、public の前でも後でもかまいません。

public new void Method2()  
{  
    Console.WriteLine("Derived - Method2");  
}  

もう一度プログラムを実行し、出力が変わらないことを確認します。 また、警告が表示されなくなったことを確認します。 new を使うことにより、それによって修飾されるメンバーが基底クラスから継承されたメンバーを隠ぺいすることを了解していることを明示します。 継承による名前の非表示について詳しくは、「new 修飾子」をご覧ください。

この動作を override を使ったときの効果と比較するため、次のメソッドを DerivedClass に追加します。 override 修飾子を追加する位置は、public の前でも後でもかまいません。

public override void Method1()  
{  
    Console.WriteLine("Derived - Method1");  
}  

BaseClass での Method1 の定義に virtual 修飾子を追加します。 virtual 修飾子を追加する位置は、public の前でも後でもかまいません。

public virtual void Method1()  
{  
    Console.WriteLine("Base - Method1");  
}  

プロジェクトを再度実行します。 次の出力の最後の 2 行に特に注意してください。

// Output:  
// Base - Method1  
// Base - Method2  
// Derived - Method1  
// Derived - Method2  
// Derived - Method1  
// Base - Method2  

override 修飾子を使うことで、bcdc は、DerivedClass で定義されている Method1 メソッドにアクセスできます。 通常、これは継承階層での必要な動作です。 派生クラスから作成される値を持つオブジェクトには、派生クラスで定義されているメソッドを使うことが必要とされます。 そのような動作は、override を使って基底クラスのメソッドを拡張することで実現できます。

ここまでの例をすべて含んだコードを次に示します。

using System;  
using System.Text;  
  
namespace OverrideAndNew  
{  
    class Program  
    {  
        static void Main(string[] args)  
        {  
            BaseClass bc = new BaseClass();  
            DerivedClass dc = new DerivedClass();  
            BaseClass bcdc = new DerivedClass();  
  
            // The following two calls do what you would expect. They call  
            // the methods that are defined in BaseClass.  
            bc.Method1();  
            bc.Method2();  
            // Output:  
            // Base - Method1  
            // Base - Method2  
  
            // The following two calls do what you would expect. They call  
            // the methods that are defined in DerivedClass.  
            dc.Method1();  
            dc.Method2();  
            // Output:  
            // Derived - Method1  
            // Derived - Method2  
  
            // The following two calls produce different results, depending
            // on whether override (Method1) or new (Method2) is used.  
            bcdc.Method1();  
            bcdc.Method2();  
            // Output:  
            // Derived - Method1  
            // Base - Method2  
        }  
    }  
  
    class BaseClass  
    {  
        public virtual void Method1()  
        {  
            Console.WriteLine("Base - Method1");  
        }  
  
        public virtual void Method2()  
        {  
            Console.WriteLine("Base - Method2");  
        }  
    }  
  
    class DerivedClass : BaseClass  
    {  
        public override void Method1()  
        {  
            Console.WriteLine("Derived - Method1");  
        }  
  
        public new void Method2()  
        {  
            Console.WriteLine("Derived - Method2");  
        }  
    }  
}  

次の例では、異なるコンテキストでの同様の動作を示します。 この例では、3 つのクラスが定義されています。基底クラス Car と、それから派生される 2 つのクラス ConvertibleCarMinivan です。 基底クラスには、DescribeCar メソッドが含まれます。 このメソッドは、車の基本的な説明を表示した後、ShowDetails を呼び出して追加情報を提供します。 これら 3 つのクラスのそれぞれで、ShowDetails メソッドが定義されています。 new 修飾子は、ConvertibleCar クラスで ShowDetails を定義するために使われています。 override 修飾子は、Minivan クラスで ShowDetails を定義するために使われています。

// Define the base class, Car. The class defines two methods,  
// DescribeCar and ShowDetails. DescribeCar calls ShowDetails, and each derived  
// class also defines a ShowDetails method. The example tests which version of  
// ShowDetails is selected, the base class method or the derived class method.  
class Car  
{  
    public void DescribeCar()  
    {  
        System.Console.WriteLine("Four wheels and an engine.");  
        ShowDetails();  
    }  
  
    public virtual void ShowDetails()  
    {  
        System.Console.WriteLine("Standard transportation.");  
    }  
}  
  
// Define the derived classes.  
  
// Class ConvertibleCar uses the new modifier to acknowledge that ShowDetails  
// hides the base class method.  
class ConvertibleCar : Car  
{  
    public new void ShowDetails()  
    {  
        System.Console.WriteLine("A roof that opens up.");  
    }  
}  
  
// Class Minivan uses the override modifier to specify that ShowDetails  
// extends the base class method.  
class Minivan : Car  
{  
    public override void ShowDetails()  
    {  
        System.Console.WriteLine("Carries seven people.");  
    }  
}  

この例では、どちらのバージョンの ShowDetails が呼び出されるかをテストします。 次の TestCars1 メソッドでは、各クラスのインスタンスを宣言した後、各インスタンスの DescribeCar を呼び出します。

public static void TestCars1()  
{  
    System.Console.WriteLine("\nTestCars1");  
    System.Console.WriteLine("----------");  
  
    Car car1 = new Car();  
    car1.DescribeCar();  
    System.Console.WriteLine("----------");  
  
    // Notice the output from this test case. The new modifier is  
    // used in the definition of ShowDetails in the ConvertibleCar  
    // class.
  
    ConvertibleCar car2 = new ConvertibleCar();  
    car2.DescribeCar();  
    System.Console.WriteLine("----------");  
  
    Minivan car3 = new Minivan();  
    car3.DescribeCar();  
    System.Console.WriteLine("----------");  
}  

TestCars1 で生成される出力は次のとおりです。 car2 の結果に特に注目してください。予測していた内容と違っている可能性があります。 オブジェクトの型は ConvertibleCar ですが、DescribeCarConvertibleCar クラスで定義されているバージョンの ShowDetails にアクセスしていません。これは、このメソッドが、override 修飾子ではなく new 修飾子を使って宣言されているためです。 結果として、ConvertibleCar オブジェクトでは Car オブジェクトと同じ説明が表示されます。 Minivan オブジェクトである car3 の結果と比べてみてください。 この場合は、Minivan で宣言されている ShowDetails メソッドは、Car クラスで宣言されている ShowDetails メソッドをオーバーライドし、表示されるのはミニバンの説明です。

// TestCars1  
// ----------  
// Four wheels and an engine.  
// Standard transportation.  
// ----------  
// Four wheels and an engine.  
// Standard transportation.  
// ----------  
// Four wheels and an engine.  
// Carries seven people.  
// ----------  

TestCars2 は、Car 型のオブジェクトのリストを作成します。 オブジェクトの値は、CarConvertibleCarMinivan の各クラスからインスタンス化されます。 DescribeCar は、リストの各要素に対して呼び出されます。 次のコードは、TestCars2 の定義です。

public static void TestCars2()  
{  
    System.Console.WriteLine("\nTestCars2");  
    System.Console.WriteLine("----------");  
  
    var cars = new List<Car> { new Car(), new ConvertibleCar(),
        new Minivan() };  
  
    foreach (var car in cars)  
    {  
        car.DescribeCar();  
        System.Console.WriteLine("----------");  
    }  
}  

次のような出力が表示されます。 TestCars1 によって表示される出力と同じであることに注意してください。 オブジェクトの型が ConvertibleCar であるか (TestCars1 の場合)、Car であるか (TestCars2 の場合) にかかわらず、ConvertibleCar クラスの ShowDetails メソッドは呼び出されません。 逆に、car3 は、型が Minivan でも Car でも、Minivan クラスの ShowDetails を呼び出します。

// TestCars2  
// ----------  
// Four wheels and an engine.  
// Standard transportation.  
// ----------  
// Four wheels and an engine.  
// Standard transportation.  
// ----------  
// Four wheels and an engine.  
// Carries seven people.  
// ----------  

メソッド TestCars3TestCars4 でこの例は終わりです。 これらのメソッドは、最初は型 ConvertibleCar および Minivan として宣言されているオブジェクトから (TestCars3)、次に型 Car として宣言されているオブジェクトから (TestCars4)、ShowDetails を直接呼び出します。 次のコードは、これら 2 つのメソッドの定義です。

public static void TestCars3()  
{  
    System.Console.WriteLine("\nTestCars3");  
    System.Console.WriteLine("----------");  
    ConvertibleCar car2 = new ConvertibleCar();  
    Minivan car3 = new Minivan();  
    car2.ShowDetails();  
    car3.ShowDetails();  
}  
  
public static void TestCars4()  
{  
    System.Console.WriteLine("\nTestCars4");  
    System.Console.WriteLine("----------");  
    Car car2 = new ConvertibleCar();  
    Car car3 = new Minivan();  
    car2.ShowDetails();  
    car3.ShowDetails();  
}  

これらのメソッドからは次のような出力が生成され、これはこのトピックの最初の例からの結果に対応します。

// TestCars3  
// ----------  
// A roof that opens up.  
// Carries seven people.  
  
// TestCars4  
// ----------  
// Standard transportation.  
// Carries seven people.  

完全なプロジェクトとその出力を次に示します。

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
  
namespace OverrideAndNew2  
{  
    class Program  
    {  
        static void Main(string[] args)  
        {  
            // Declare objects of the derived classes and test which version  
            // of ShowDetails is run, base or derived.  
            TestCars1();  
  
            // Declare objects of the base class, instantiated with the  
            // derived classes, and repeat the tests.  
            TestCars2();  
  
            // Declare objects of the derived classes and call ShowDetails  
            // directly.  
            TestCars3();  
  
            // Declare objects of the base class, instantiated with the  
            // derived classes, and repeat the tests.  
            TestCars4();  
        }  
  
        public static void TestCars1()  
        {  
            System.Console.WriteLine("\nTestCars1");  
            System.Console.WriteLine("----------");  
  
            Car car1 = new Car();  
            car1.DescribeCar();  
            System.Console.WriteLine("----------");  
  
            // Notice the output from this test case. The new modifier is  
            // used in the definition of ShowDetails in the ConvertibleCar  
            // class.
            ConvertibleCar car2 = new ConvertibleCar();  
            car2.DescribeCar();  
            System.Console.WriteLine("----------");  
  
            Minivan car3 = new Minivan();  
            car3.DescribeCar();  
            System.Console.WriteLine("----------");  
        }  
        // Output:  
        // TestCars1  
        // ----------  
        // Four wheels and an engine.  
        // Standard transportation.  
        // ----------  
        // Four wheels and an engine.  
        // Standard transportation.  
        // ----------  
        // Four wheels and an engine.  
        // Carries seven people.  
        // ----------  
  
        public static void TestCars2()  
        {  
            System.Console.WriteLine("\nTestCars2");  
            System.Console.WriteLine("----------");  
  
            var cars = new List<Car> { new Car(), new ConvertibleCar(),
                new Minivan() };  
  
            foreach (var car in cars)  
            {  
                car.DescribeCar();  
                System.Console.WriteLine("----------");  
            }  
        }  
        // Output:  
        // TestCars2  
        // ----------  
        // Four wheels and an engine.  
        // Standard transportation.  
        // ----------  
        // Four wheels and an engine.  
        // Standard transportation.  
        // ----------  
        // Four wheels and an engine.  
        // Carries seven people.  
        // ----------  
  
        public static void TestCars3()  
        {  
            System.Console.WriteLine("\nTestCars3");  
            System.Console.WriteLine("----------");  
            ConvertibleCar car2 = new ConvertibleCar();  
            Minivan car3 = new Minivan();  
            car2.ShowDetails();  
            car3.ShowDetails();  
        }  
        // Output:  
        // TestCars3  
        // ----------  
        // A roof that opens up.  
        // Carries seven people.  
  
        public static void TestCars4()  
        {  
            System.Console.WriteLine("\nTestCars4");  
            System.Console.WriteLine("----------");  
            Car car2 = new ConvertibleCar();  
            Car car3 = new Minivan();  
            car2.ShowDetails();  
            car3.ShowDetails();  
        }  
        // Output:  
        // TestCars4  
        // ----------  
        // Standard transportation.  
        // Carries seven people.  
    }  
  
    // Define the base class, Car. The class defines two virtual methods,  
    // DescribeCar and ShowDetails. DescribeCar calls ShowDetails, and each derived  
    // class also defines a ShowDetails method. The example tests which version of  
    // ShowDetails is used, the base class method or the derived class method.  
    class Car  
    {  
        public virtual void DescribeCar()  
        {  
            System.Console.WriteLine("Four wheels and an engine.");  
            ShowDetails();  
        }  
  
        public virtual void ShowDetails()  
        {  
            System.Console.WriteLine("Standard transportation.");  
        }  
    }  
  
    // Define the derived classes.  
  
    // Class ConvertibleCar uses the new modifier to acknowledge that ShowDetails  
    // hides the base class method.  
    class ConvertibleCar : Car  
    {  
        public new void ShowDetails()  
        {  
            System.Console.WriteLine("A roof that opens up.");  
        }  
    }  
  
    // Class Minivan uses the override modifier to specify that ShowDetails  
    // extends the base class method.  
    class Minivan : Car  
    {  
        public override void ShowDetails()  
        {  
            System.Console.WriteLine("Carries seven people.");  
        }  
    }  
  
}  

関連項目