次の方法で共有


オーバーライドと新しいキーワードを使用するタイミングを把握する (C# プログラミング ガイド)

C# では、派生クラスのメソッドは基底クラスのメソッドと同じ名前を持つことができます。 新しいキーワードとオーバーライド キーワードを使用して、メソッドの対話方法を指定できます。 override修飾子は基底クラスメソッドをvirtualし、new修飾子はアクセス可能な基底クラス メソッドを非表示にします。 このトピックの例では、違いを示します。

コンソール アプリケーションで、 BaseClassDerivedClassの 2 つのクラスを宣言します。 DerivedClass は、BaseClass から継承されます。

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

Main メソッドで、変数bcdc、およびbcdcを宣言します。

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

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

  • bcdcBaseClass型であり、その値は DerivedClass型です。 これは注意を払う変数です。

bcbcdcには型BaseClassがあるため、キャストを使用しない限り、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に追加します。 このメソッドのシグネチャは、Method2DerivedClass メソッドのシグネチャと一致します。

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

BaseClassには Method2 メソッドがあるため、次のコードに示すように、BaseClass変数のbcbcdcに対して 2 つ目の呼び出しステートメントを追加できます。

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

プロジェクトをビルドすると、Method2BaseClass メソッドを追加すると警告が発生することがわかります。 警告は、Method2DerivedClassメソッドがMethod2BaseClassメソッドを非表示にすることを示しています。 その結果を発生させる場合は、new定義で Method2 キーワードを使用することをお勧めします。 または、警告を解決するために Method2 メソッドの 1 つの名前を変更することもできますが、必ずしも実用的であるとは限りません。

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

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

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

警告を抑制するには、次のコードに示すように、newMethod2の定義にDerivedClass修飾子を追加します。 修飾子は、 publicの前または後に追加できます。

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

プログラムをもう一度実行して、出力が変更されていないことを確認します。 また、警告が表示されなくなったことを確認します。 newを使用すると、変更されたメンバーが基底クラスから継承されたメンバーを非表示にすることを認識していることをアサートします。 継承による名前の非表示の詳細については、「 新しい修飾子」を参照してください。

この動作と overrideの使用による影響を比較するには、次のメソッドを DerivedClassに追加します。 override修飾子は、publicの前または後に追加できます。

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

virtualMethod1の定義にBaseClass修飾子を追加します。 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は、Method1で定義されているDerivedClass メソッドにアクセスできます。 通常、これは継承階層で必要な動作です。 派生クラスから作成された値を持つオブジェクトで、派生クラスで定義されているメソッドを使用する必要があります。 この動作を実現するには、 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");  
        }  
    }  
}  

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

// 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ですが、DescribeCarShowDetails クラスで定義されているConvertibleCarのバージョンにはアクセスできません。これは、そのメソッドがnew修飾子ではなく、override修飾子で宣言されているためです。 その結果、 ConvertibleCar オブジェクトには、 Car オブジェクトと同じ説明が表示されます。 car3 オブジェクトであるMinivanの結果を比較します。 この場合、ShowDetails クラスで宣言されているMinivan メソッドは、ShowDetails クラスで宣言されているCar メソッドをオーバーライドし、表示される説明では、ミニバンについて説明します。

// 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オブジェクトの一覧を作成します。 オブジェクトの値は、 CarConvertibleCar、および Minivan クラスからインスタンス化されます。 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によって表示される出力と同じであることに注意してください。 ShowDetails クラスのConvertibleCar メソッドは、ConvertibleCarのように、オブジェクトの型がTestCars1CarのどちらでTestCars2されているかに関係なく呼び出されません。 逆に、car3はどちらの場合も、ShowDetails クラスからMinivan メソッドを呼び出します(型Minivanか型Carか)。

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

メソッド TestCars3 および TestCars4 は例を完成します。 これらのメソッドは ShowDetails を直接呼び出し、最初に型 ConvertibleCarMinivan (TestCars3) を持つ宣言されたオブジェクトから、次に型 Car (TestCars4) を持つ宣言されたオブジェクトから呼び出します。 次のコードでは、これら 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.");  
        }  
    }  
  
}  

こちらも参照ください