Override 및 New 키워드를 사용해야 하는 경우(C# 프로그래밍 가이드)

C#에서는 파생 클래스의 메서드가 기본 클래스의 메서드와 동일한 이름을 사용할 수 있습니다. newoverride 키워드를 사용하여 메서드가 상호 작용하는 방식을 지정할 수 있습니다. override 한정자는 기본 클래스 virtual 메서드를 확장하고, new 한정자는 기본 클래스 메서드를 숨깁니다. 이 항목의 예제에서는 차이점을 보여 줍니다.

콘솔 애플리케이션에서 BaseClassDerivedClass라는 두 클래스를 선언합니다. DerivedClassBaseClass에서 상속됩니다.

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

Main 메서드에서 bc, dcbcdc 변수를 선언합니다.

  • 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에 추가합니다. 이 메서드의 시그니처는 DerivedClass에 있는 Method2 메서드의 시그니처와 일치합니다.

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

이제 BaseClassMethod2 메서드가 있으므로 다음 코드와 같이 BaseClass 변수 bcbcdc에 대해 두 번째 호출 문을 추가할 수 있습니다.

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에 추가합니다. public 앞이나 뒤에 override 한정자를 추가할 수 있습니다.

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

BaseClass에 있는 Method1의 정의에 virtual 한정자를 추가합니다. public 앞이나 뒤에 virtual 한정자를 추가할 수 있습니다.

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

프로젝트를 다시 실행합니다. 특히 다음 출력의 마지막 두 줄을 확인합니다.

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

override 한정자를 사용하면 bcdcDerivedClass에 정의된 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");  
        }  
    }  
}  

다음 예제에서는 다른 컨텍스트의 비슷한 동작을 보여 줍니다. 이 예제에서는 Car라는 기본 클래스 하나와 이 클래스에서 파생된 두 클래스 ConvertibleCarMinivan을 정의합니다. 기본 클래스에는 DescribeCar 메서드가 포함되어 있습니다. 이 메서드는 자동차에 대한 기본 설명을 표시한 다음 ShowDetails를 호출하여 추가 정보를 제공합니다. 세 클래스는 각각 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.  
// ----------  

TestCars2Car 형식인 개체 목록을 만듭니다. 개체의 값은 Car, ConvertibleCarMinivan 클래스에서 인스턴스화됩니다. 목록의 각 요소에 대해 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 메서드는 호출되지 않습니다. 한편, car3Minivan 형식 또는 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 메서드가 예제를 완성합니다. 이러한 메서드는 ConvertibleCarMinivan(TestCars3) 형식으로 선언된 개체와 Car(TestCars4) 형식으로 선언된 개체에서 차례로 ShowDetails를 직접 호출합니다. 다음 코드에서는 이러한 두 메서드를 정의합니다.

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.");  
        }  
    }  
  
}  

참고 항목