override

override (C# 참조)

상속받은 메서드, 속성, 인덱서, 이벤트의 추상(abstract) 구현이나 가상(virtual) 구현을 확장하거나 바꾸고 싶을 때 override 수정자를 써요. 상속받은 그대로 쓰는 게 아니라, 파생 클래스 입장에서 "나만의 구현으로 덮어쓴다"는 걸 C# 컴파일러에 알려주는 역할이죠.

출처: override - C# Reference

본문

아래 예시에서 Square 클래스가 GetArea를 반드시 override로 구현해야 하는 걸 볼게요. GetArea는 추상 클래스 Shape에서 물려받은 메서드인데, 추상 메서드는 구현이 없어서 파생 클래스가 채워줘야 하거든요.

abstract class Shape
{
    public abstract int GetArea();
}

class Square : Shape
{
    private int _side;

    public Square(int n) => _side = n;

    // GetArea method is required to avoid a compile-time error.
    public override int GetArea() => _side * _side;

    static void Main()
    {
        var sq = new Square(12);
        Console.WriteLine($"Area of the square = {sq.GetArea()}");
    }
}
// Output: Area of the square = 144

override 메서드는 기본 클래스에서 상속받은 메서드에 새로운 구현을 제공해요. 그 덕분에 파생 클래스는 기본 메서드보다 더 구체적인(그 클래스에 맞는) 구현을 갖게 되죠. 이때 지켜야 할 규칙이 몇 가지 있는데, 하나씩 볼게요.

  • override 메서드는 재정의 대상인 기본 메서드와 시그니처가 같아야 해요. 이름, 매개변수 목록이 똑같아야 한다는 뜻이에요.
  • override 메서드는 공변(covariant) 반환 타입을 지원해요. 쉽게 말하면, override 메서드의 반환 타입이 기본 메서드의 반환 타입에서 파생된 타입일 수 있어요. 말하자면 기본 메서드가 Animal을 반환한다면 override 쪽은 Dog을 반환해도 된다는 거죠(DogAnimal을 상속받은 경우).

그리고 되지 않는 것들도 분명히 짚고 넘어갈게요.

  • non-virtual 메서드나 static 메서드는 override할 수 없어요. 오버라이드의 대상이 되려면 기본 메서드가 virtual, abstract, override 중 하나여야 해요.
  • override 선언은 virtual 메서드의 접근 수준을 바꿀 수 없어요. override 메서드와 virtual 메서드는 같은 접근 수준 수정자를 가져야 해요.
  • override 메서드에는 new, static, virtual 수정자를 붙일 수 없어요.

속성을 override할 때도 규칙이 있어요. overriding 속성 선언은 상속받은 속성과 정확히 같은 접근 수정자, 타입, 이름을 지정해야 해요. 읽기 전용 overriding 속성은 공변 반환 타입을 지원하고, 재정의 대상 속성은 virtual, abstract, override 중 하나여야 해요.

override 키워드를 언제 어떻게 쓰는지 더 자세히 보려면 Versioning with the Override and New KeywordsKnowing when to use Override and New Keywords를 참고해요. 상속 자체가 궁금하다면 Inheritance를 보면 돼요.

예제

기본 클래스 Employee와 그를 상속받는 파생 클래스 SalesEmployee를 정의하는 예시예요. SalesEmployee에는 salesbonus라는 필드가 하나 더 있고, CalculatePay 메서드를 override해서 보너스를 급여 계산에 반영해요.

class TestOverride
{
    public class Employee
    {
        public string Name { get; }

        // Basepay is defined as protected, so that it may be
        // accessed only by this class and derived classes.
        protected decimal _basepay;

        // Constructor to set the name and basepay values.
        public Employee(string name, decimal basepay)
        {
            Name = name;
            _basepay = basepay;
        }

        // Declared virtual so it can be overridden.
        public virtual decimal CalculatePay()
        {
            return _basepay;
        }
    }

    // Derive a new class from Employee.
    public class SalesEmployee : Employee
    {
        // New field that will affect the base pay.
        private decimal _salesbonus;

        // The constructor calls the base-class version, and
        // initializes the salesbonus field.
        public SalesEmployee(string name, decimal basepay, decimal salesbonus)
            : base(name, basepay)
        {
            _salesbonus = salesbonus;
        }

        // Override the CalculatePay method
        // to take bonus into account.
        public override decimal CalculatePay()
        {
            return _basepay + _salesbonus;
        }
    }

    static void Main()
    {
        // Create some new employees.
        var employee1 = new SalesEmployee("Alice", 1000, 500);
        var employee2 = new Employee("Bob", 1200);

        Console.WriteLine($"Employee1 {employee1.Name} earned: {employee1.CalculatePay()}");
        Console.WriteLine($"Employee2 {employee2.Name} earned: {employee2.CalculatePay()}");
    }
}
/*
    Output:
    Employee1 Alice earned: 1500
    Employee2 Bob earned: 1200
*/

SalesEmployee의 생성자에서 : base(name, basepay)로 기본 클래스 생성자를 호출해서 이름과 기본 급여를 먼저 세워두고, override한 CalculatePay에서 _basepay + _salesbonus로 보너스를 더해요. 그래서 영업사원인 Alice는 1000 + 500 = 1500을, 영업사원이 아닌 Bob은 기본 급여 1200을 그대로 받는 거죠.

C# 언어 사양

더 자세한 내용은 C# 언어 사양Override methods 섹션을 참고해요. 공변 반환 타입에 대해 더 알고 싶다면 같은 문서의 Covariant return types를 보면 돼요.

더 알아보기