본문 바로가기
C#

[C#] 메소드 재정의

by ifhead 2022. 8. 30.
반응형

 

다형성은 메서드 재정의 및 메서드 오버로딩을 포함하는 OOPS 원칙 중 하나입니다. Virtual 및 Override 키워드는 메서드 재정의에 사용되고 new 키워드는 메서드 숨김에 사용됩니다. 이 포스팅에서는 C# 코드를 사용하여 각 키워드에 대해 자세히 설명합니다.

단순한 클래스 상속

클래스 A, B, C가 있는 상속 관계를 생각해 봅시다. A는 상위/기본 클래스이고 B는 클래스 A에서 파생되며, C는 클래스 B에서 파생됩니다.

Class A > Class B > Class C

Test() 메서드가 기본 클래스 A에 선언되어 있습니다. 클래스 B와 C는 메서드가 없습니다.

using System;
namespace Polymorphism {
    class A {
      public void Test() { Console.WriteLine("A::Test()"); }
    }
    
    class B : A {}
    
    class C : B {}
    
    class Program {
      static void Main(string[] args) {
        A a = new A();
        a.Test();  // output --> "A::Test()"
    
        B b = new B();
        b.Test();  // output --> "A::Test()"
    
        C c = new C();
        c.Test();  // output --> "A::Test()"
    
        Console.ReadKey();
      }
    }
}

 

이번에는 한번 아래와 같이 모든 클래스 A, B, C에 Test() 메서드가 있다고 가정합니다.

using System;
namespace Polymorphism
{
 class A
 {
 public void Test() { Console.WriteLine("A::Test()"); }
 }

 class B : A
 {
 public void Test() { Console.WriteLine("B::Test()"); }
 }

 class C : B
 {
 public void Test() { Console.WriteLine("C::Test()"); }
 }

 class Program
 {
 static void Main(string[] args)
 {
 
 A a = new A();
 B b = new B();
 C c = new C();

 a.Test(); // output --> "A::Test()"
 b.Test(); // output --> "B::Test()"
 c.Test(); // output --> "C::Test()"

 a = new B();
 a.Test(); // output --> "A::Test()"

 b = new C();
 b.Test(); // output --> "B::Test()"

 Console.ReadKey();
 }
 }
}

 

위의 프로그램을 실행하면 성공적으로 실행됩니다. 그러나 이 프로그램은 아래와 같이 두 가지 경고를 표시합니다.

'Polymorphism.B.Test()' hides inherited member 'Polymorphism.A.Test()'. Use the new keyword if hiding was intended.
'Polymorphism.C.Test()' hides inherited member 'Polymorphism.B.Test()'. Use the new keyword if hiding was intended.

 

메소드 은닉 : new 키워드에 관하여

위의 예에서 에러가 왜 생겼다고 생각하시나요? C#은 '메서드 숨김'을 지원하기 때문에 경고를 생성합니다. 여러분은 위에서 묵시적으로 메서드 숨김을 했지만, 명시적으로 하라는 에러가 뜬 것입니다.

파생 클래스(Derived Class, Sub Class, Child class)에서 기본 클래스(Base Class, Super Class, Parent Class) 메서드를 숨기려면 new 키워드로 파생 클래스 메서드를 선언하기만 하면 됩니다. 따라서 위의 코드는 다음과 같이 다시 작성할 수 있습니다. 즉, 당신이 메소드 숨김을 의도했다면, 메소드를 명시적으로 숨겨야 합니다.

using System;
namespace Polymorphism {
class A {
  public void Test() { Console.WriteLine("A::Test()"); }
}

class B : A {
  public new void Test() { Console.WriteLine("B::Test()"); }
}

class C : B {
  public new void Test() { Console.WriteLine("C::Test()"); }
}

class Program {
  static void Main(string[] args) {
    A a = new A();
    B b = new B();
    C c = new C();

    a.Test();  // output --> "A::Test()"
    b.Test();  // output --> "B::Test()"
    c.Test();  // output --> "C::Test()"

    a = new B();
    a.Test();  // output --> "A::Test()"

    b = new C();
    b.Test();  // output --> "B::Test()"

    Console.ReadKey();
  }
}
}

 

메서드 오버라이딩 : virtual과 override 키워드

C#은 당신의 의도를 중요하게 생각하고 있습니다. 파생 클래스가 메소드를 바꿔 쓰지 않을 것이라면 애초에 일반 메소드로 선언을 하면 되지만 위처럼 A, B, C가 모두 다른 Test()를 가지고 있다고 한다면, 처음부터 그 의도를 담아 재정의를 염두에 두고 virtual로 선언해주면 좋습니다. 버추얼로 선언하면 상속을 받을 때 재정의해도 되고, 정의하지 않아도 부모 메소드를 그대로 호출합니다.

using System;
namespace Polymorphism {
class A {
  public virtual void Test() { Console.WriteLine("A::Test()"); }
}

class B : A {
  public override void Test() { Console.WriteLine("B::Test()"); }
}

class C : B {
  public override void Test() { Console.WriteLine("C::Test()"); }
}

class Program {
  static void Main(string[] args) {
    A a = new A();
    B b = new B();
    C c = new C();
    a.Test();  // output --> "A::Test()"
    b.Test();  // output --> "B::Test()"
    c.Test();  // output --> "C::Test()"

    a = new B();
    a.Test();  // output --> "B::Test()"

    b = new C();
    b.Test();  // output --> "C::Test()"

    Console.ReadKey();
  }
}
}

 

메소드 오버라이딩과 메소드 하이딩

파생 클래스의 메소드는 동시에 virtual과 new가 될 가능성이 있습니다. virtual 키워드와 new 키워드를 사용해, 메소드의 숨기기와 메소드의 오버라이드(override)를 혼재시킬 수도 있습니다. 이것은, 아래와 같이 클래스 C 에서 클래스 B, Test() 메소드를 오버라이드(override) 하고 있기 때문에, 파생 클래스 메소드를 다음의 레벨에 한층 더 오버라이드(override) 하는 경우에 필요합니다.

using System;
namespace Polymorphism {
class A {
  public void Test() { Console.WriteLine("A::Test()"); }
}

class B : A {
  public new virtual void Test() { Console.WriteLine("B::Test()"); }
}

class C : B {
  public override void Test() { Console.WriteLine("C::Test()"); }
}

class Program {
  static void Main(string[] args) {
    A a = new A();
    B b = new B();
    C c = new C();

    a.Test();  // output --> "A::Test()"
    b.Test();  // output --> "B::Test()"
    c.Test();  // output --> "C::Test()"

    a = new B();
    a.Test();  // output --> "A::Test()"

    b = new C();
    b.Test();  // output --> "C::Test()"

    Console.ReadKey();
  }
}
}

 

요약

1. 부모 타입으로 선언된 자식 객체는 new를 쓸 경우(자식은 Hide됨) 부모 클래스의 메소드를 호출하고, override일 경우 자식 메소드를 호출합니다.

2. virtual 키워드는 기본 클래스의 기능을 파생 클래스에서 수정하고 재정의할 수 있도록 하는 데 사용됩니다.
3. override 키워드는 기본 클래스의 기능을 파생 클래스로 확장하거나 변경하는 데 사용됩니다.
4. new 키워드는 기본 클래스의 메서드, 속성, 인덱서 또는 이벤트를 파생 클래스에 숨기는 데 사용됩니다.

 

반응형

댓글