CodeGym /행동 /C# SELF /C#에서 메서드 오버라이딩(Method Overriding)

C#에서 메서드 오버라이딩(Method Overriding)

C# SELF
레벨 21 , 레슨 2
사용 가능

1. 소개

혹시 오버라이딩이라는 단어 듣고 살짝 당황했다면 걱정하지 마! 이거 무서운 거 아니고, 오히려 엄청 편리한 기능이야! 메서드 오버라이딩은 기본 클래스의 메서드 동작을 파생 클래스에서 내 마음대로 바꿀 수 있게 해주는 거야. 덕분에 코드가 유연해지고, 확장성도 좋아지고, 진짜 현실 세계처럼 동작할 수 있어. 예를 들어, 동물마다 "그냥 소리"만 내고 싶진 않잖아.

메서드를 오버라이딩할 수 있게 하려면, 기본 클래스에서 해당 메서드에 virtual 키워드를 붙여줘야 해. 파생 클래스에서 구현을 바꾸고 싶으면 override 키워드를 써주면 돼.

예시


public class Animal
{
    public string Name { get; set; }
    public virtual void MakeSound()
    {
        Console.WriteLine("어떤 일반적인 동물 소리...");
    }
}
        

이제 Dog 클래스에서는 이렇게 해보자:


public class Dog : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("멍멍!");
    }
}
        
  • 기본 클래스에는 virtual이 붙어있어.
  • 파생 클래스에는 override가 붙어있어.
  • 메서드 시그니처(이름, 반환 타입, 파라미터)는 완전히 같아야 해.

비주얼 다이어그램

Dog는 Animal을 상속하고 MakeSound()를 오버라이딩할 수 있어

2. 동작 시연

오버라이딩이 실제로 어떻게 동작하는지 보자. 동물들을 만들어서 소리를 확인해보자:

Animal pet1 = new Animal { Name = "이름 없는 동물" };
Dog pet2 = new Dog { Name = "바르보스" };

pet1.MakeSound(); // 출력: 어떤 일반적인 동물 소리...
pet2.MakeSound(); // 출력: 멍멍!

이제 좀 더 복잡하게 해보자:
만약 Dog를 Animal 타입 변수에 저장하면 어떻게 될까?

Animal pet3 = new Dog { Name = "샤릭" };
pet3.MakeSound(); // ???

어떻게 될 것 같아?
정답: "멍멍!"이 출력돼
왜냐하면 변수 타입은 Animal이지만, 실제로는 Dog를 가리키고 있어서 오버라이딩된 메서드가 호출되는 거야!
이게 바로 동적(혹은 늦은) 바인딩의 마법이지.

3. 오버라이딩할 때 base 키워드 사용하기

가끔은 메서드 구현을 완전히 바꾸는 게 아니라 확장하고 싶을 때가 있어 — 예를 들어, 내 기능을 추가하고, 그 다음에 원래 동작도 실행하고 싶을 때. 이럴 때 base 키워드를 써. 이걸로 기본 클래스의 메서드 버전을 호출할 수 있어.


public class Cat : Animal
{
    public override void MakeSound()
    {
        base.MakeSound(); // 기본 구현 호출
        Console.WriteLine("야옹!");
    }
}
        

이 메서드를 호출하면 먼저 "어떤 일반적인 동물 소리..."가 나오고, 그 다음에 "야옹!"이 출력돼

4. 오버라이딩 시 메서드 선택 원리

"속을 들여다보는" 느낌으로, 이런 표(가상 디스패치 테이블)를 상상해봐:

변수 타입 객체 타입 어떤 메서드가 호출됨
Animal Animal Animal.MakeSound
Animal Dog Dog.MakeSound
Animal Labrador Labrador.MakeSound
Dog Labrador Labrador.MakeSound
Dog Dog Dog.MakeSound

핵심 규칙:
변수 타입은 컴파일러한테만 중요하고, 실행할 때는 실제 객체 타입(우리가 new로 만든 것)이 기준이야.
이 메커니즘을 동적(혹은 늦은) 바인딩이라고 해 — 이게 바로 다형성의 핵심(이건 다음 강의에서 더 자세히 다룰 거야!).

왜 메서드를 오버라이딩할까?

  • GUI 프레임워크에서: 기본 윈도우 클래스가 있고, 각 요소를 그리는 메서드를 오버라이딩해서 커스텀하게 그릴 수 있어.
  • 게임 엔진에서: 기본 Enemy 클래스가 있고, 상속받은 클래스들이 각자 다른 행동을 구현해.
  • 유닛 테스트에서: 메서드에 대한 "스텁"(stubs, mocks) 만들 때도 써.

최신 .NET 프레임워크들은 이벤트, 템플릿 코드, 설정 상속, 심지어 객체 직렬화(예: 가상 프로퍼티로)에도 이 메커니즘을 엄청 많이 써.

5. new 키워드로 메서드 숨기기

오버라이딩에는 virtual/override 콤보가 필요하다는 걸 이미 배웠지. 그런데 C#에는 상속 계층에서 메서드와 관련된 또 다른 수정자 new도 있어.

new는 언제 쓸까?

new는 파생 클래스에서 기본 클래스와 같은 시그니처의 메서드를 선언하지만, 오버라이딩이 아니라 숨기기(마스킹)만 하고 싶을 때 써.

  • 이건 오버라이딩이 아니라 숨김이야.
  • 이런 메서드는 변수 타입에 따라 호출돼. (동적 다형성 없음!)
  • 만약 new 없이 실수로 메서드를 숨기면, 컴파일러가 경고해줄 거야.

예시: override vs new 차이


public class Animal
{
    public virtual  void MakeSound()
    {
        Console.WriteLine("동물이 어떤 소리를 낸다...");
    }
}

public class Dog : Animal
{
    // 기본 클래스 메서드를 숨김(오버라이딩 아님)
    public new void MakeSound()
    {
        Console.WriteLine("이건 override 아냐! 그냥 Dog 메서드야.");
    }
}
        

이제 동작을 확인해보자:

Animal a = new Dog();
Dog d = new Dog();

a.MakeSound(); // "동물이 어떤 소리를 낸다..."
d.MakeSound(); // "이건 override 아냐! 그냥 Dog 메서드야."
  • 변수 타입이 Dog면 Dog의 메서드가 호출돼.
  • 변수 타입이 Animal이면, 안에 Dog가 들어있어도 Animal의 메서드가 호출돼!

6. 피드백과 구현 팁

프로그래밍 처음 할 때 자주 헷갈리는 게, "오버라이딩"했다고 생각했는데 왜 예전 방식대로 동작하지? 이런 경우 대부분 이유는 간단해: 기본 클래스에 virtual이 없거나, 파생 클래스에서 new로 선언했지 override로 안 했을 때. 두 번째 경우가 특히 헷갈려 — 기본 타입 변수로 메서드를 호출하면 기본 버전이 실행되고, 오버라이딩된 게 실행되지 않아. 그러니까 항상 키워드 제대로 썼는지 꼭 확인하자.

문법 실수 말고도, 초보 개발자들이 오버라이딩할 때 반환 타입을 바꾸려고 하는 경우가 있어. 예를 들어, 기본 함수는 object 반환, 파생 클래스는 string 반환. 이렇게 하면 안 돼: 메서드 시그니처는 완전히 같아야 해.

비교표: override vs new

특징 override new
메커니즘 가상 메서드 오버라이딩 기본 클래스 메서드 숨김
늦은 바인딩 O — 동적 다형성 동작 X — 변수 타입 기준 동작
base에 필요한 것... virtual, abstract 또는 이미 override 필요 없음
사용 권장 거의 항상 O 정말 특별한 경우만

7. 오버라이딩 메서드와 상속 계층

오버라이딩이 진짜 재미있어지는 건, 상속 체인이 길어질 때야:


public class Animal
{
    public virtual void MakeSound()
    {
        Console.WriteLine("동물이 뭔가 한다...");
    }
}

public class Dog : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("멍멍!");
    }
}

public class Labrador : Dog
{
    public override void MakeSound()
    {
        Console.WriteLine("난 래브라도야: 왈왈!");
    }
}
        

만약 이렇게 쓴다면:

Animal pet = new Labrador();
pet.MakeSound(); //   => "난 래브라도야: 왈왈!"

C#은 항상 실제 객체에 있는 가장 "깊은" 가상 메서드 구현을 선택해.

8. 오버라이딩 실수 모음

세상에 완벽한 사람은 없고, 학생(그리고 경력자도!)들도 실수하지. 자주 나오는 "지뢰"를 미리 피하는 법을 알아보자:

1. 기본 클래스에 virtual을 빼먹음


public class Animal
{
    public void MakeSound() { ... } // 'virtual' 없음
}

public class Dog : Animal
{
    // 컴파일 에러! 오버라이딩 불가.
    public override void MakeSound()
    {
        Console.WriteLine("멍!");
    }
}
        

C#은 바로 이렇게 말해줄 거야: 'Dog.MakeSound()': 오버라이드할 수 있는 적절한 메서드를 찾을 수 없음

2. 시그니처가 다름

메서드 이름, 반환 타입, 파라미터가 완전히 같은지 꼭 확인하자:


public class Animal
{
    public virtual void MakeSound() { ... }
}

public class Dog : Animal
{
    // 에러: 시그니처가 다름(예: 파라미터 추가됨)
    public override void MakeSound(string sound)
    {
        Console.WriteLine(sound);
    }
}
        

3. newoverride 대신 쓰지 말자

new 키워드는 기본 클래스 메서드를 숨길 때 쓰는 거지, 오버라이딩이 아니고 동적 다형성도 안 돼. 특별한 이유 없으면 이 메커니즘은 피하는 게 좋아.

코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION