[C#] 상속 (Inheritance)

AsiaticRicecake·2025년 3월 26일

객체지향 프로그래밍 특징 중 가장 중요하다고 언급한 상속 개념을 알아보겠습니다.

1. 📖 상속

상속은 클래스를 만들었을 때 그 클래스의 기능을 다른 클래스에도 사용할 수 있도록 만드는 개념입니다.

상속을 쓰면 코드의 중복을 줄이고, 간결하게 작성할 수 있게 할 수 있어 유지보수하기에 유리합니다.

class 부모 클래스
{

}

class 자식 클래스 : 부모 클래스
{

}

🖥️ 예시

이렇게 Slime과 Dragon 클래스에 아무것도 없지만 부모클래스인 Monster에서 기능을 상속을 받아서 사용합니다.

class Monster // 부모클래스
  {
      public int hp;
      public string name;

      public void Move()
      {
          Console.WriteLine("{0} 움직인다.");
      }

  }

  class Slime : Monster // Monster라는 부모클래스에서 상속
  {

  }

  class Dragon : Monster // Monster라는 부모클래스에서 상속
  {

  }

  static void Main(string[] args)
  {
      Slime slime = new Slime(); 
      Dragon dragon = new Dragon();

      slime.Move();
      dragon.Move();
  }

1-1 🔖 Protected : 상속된 클래스만 사용하도록 제한

접근제한자 중에 public과 private에 대해서는 알아보았지만
protected라는 접근제한자는 처음 들어보았을 것입니다.

이것은 상속 개념에서 볼 수 있는데
부모 클래스에서 상속받은 자식 클래스만 사용가능하도록 제한하는 기능입니다.

class Monster
{
    protected int hp; // 상속받은 클래스만 사용하도록 제한
    public string name; // 제한 없음
	private int level // 외부 사용 완전 제한

}

1-2 🔖 this와 base의 역할

this는 클래스 내부의 변수에 접근하는 경우 사용하고
base는 부모클래스의 변수에 접근하는 경우 사용합니다.

class Dragon : Monster
{
	public float speed;

	public Dragon() 
    {
      name = "드래곤";
      hp = 10000;
      base.speed = 10f; // 부모클래스인 Monster 클래스의 speed에 접근
      this.speed = 5.5f // Dragon 클래스의 speed에 접근
    }

1-3 🔖 업캐스팅, 다운캐스팅, is, as

1-3-1 ✔️ 업캐스팅

업캐스팅은 자식클래스가 부모클래스로 형변환하는 것을 의미하며,
흔히 상속 개념에서 자식 클래스가 부모 클래스의 기능을 사용할 수 있는 것을 생각하시면 됩니다
결론적으로 상속은 기본적으로 업캐스팅을 지원한다고 보시면 됩니다.

1-3-2 ✔️ 다운캐스팅

상위 클래스에서 하위 클래스로 형 변환을 하는것을 의미합니다.
상속은 업캐스팅을 지원하지만 다운캐스팅을 지원하지 않습니다.
간단히 말하자면 자식클래스에 있는 함수를 부모클래스에서 사용할 수 없는 것입니다.

사용을 하기 위해서는 As와 Is 개념이 필요합니다!

1-3-2-1 ✔️ As

다운캐스팅이 성공한다면 객체를 반환하고 실패하면 null값을 반환하는 구문입니다.

1-3-2-2 ✔️ Is

다운캐스팅이 가능하다면 true, 불가능할 경우 false를 반환하는 구문입니다.

1-3-2-3 🖥️ As Is 간단 실습 (C# 교과서 p331 참고)

static void Main(string[] args)
{
    object s = "반갑습니다.";
    string r1 = s as string;
    Console.WriteLine($"[1] {r1}");

    object i = 1234;
    string r2 = i as string;
    if  (r2 == null)
    {
        Console.WriteLine("[2] null 입니다.");
    }

    object i2 = 3456;
    if (i2 is string)
    {
        string r3 = i2 as string;
        Console.WriteLine($"[3] {r3}");
    }
    else
    {
        Console.WriteLine("[3] 변환 불가");
    }
}
[1] 반갑습니다.
[2] null 입니다.
[3] 변환 불가

1-3-2-4 🖥️ 다운캐스팅 할 때 As Is 활용법

 internal class Program
 {

     class Movie
     {
         public virtual void Position()
         {
             Console.WriteLine("CGV에 가자");
         }
     }
     class Coco : Movie
     {
         public override void Position()
         {
             Console.WriteLine("Coco 보러 CGV에 가자");
         }

         public void CocoScore()
         {
             Console.WriteLine(95);
         }
     }
     class Soul : Movie
     {
         public override void Position()
         {
             Console.WriteLine("Soul 보러 CGV에 가자");
         }
         public void SoulScore()
         {
             Console.WriteLine(90);
         }
     }

     static void Main(string[] args)
     {
         Movie[] movies =
         {
             new Coco(),
             new Soul()
         };

         for (int i = 0; i < movies.Length; i++)
         {
             movies[i].Position();
			 
             // IS : 다운캐스팅이 가능하다면 true, 불가능할 경우 false를 반환
             if (movies[i] is Coco) 
             {
             	// As : 다운캐스팅이 성공한다면 객체를 반환하고 실패하면 null값을 반환
                 (movies[i] as Coco).CocoScore(); 
             }
             else if (movies[i] is Soul)
             {
                 (movies[i] as Soul).SoulScore();
             }
          
         }
     }
 }
Coco 보러 CGV에 가자
95
Soul 보러 CGV에 가자
90

1-3-2-5 🖥️ ?

앞에 코드에서 if문 안쓰고 ?를 이용하여 간결하게 나타낼 수도 있다.
?는 null인지 아닌지 판단하는 구문으로 조건문의 성격을 가진 연산자입니다!

 (movies[i] as Coco)?.CocoScore();
 (movies[i] as Soul)?.SoulScore();

1-4 🔖 sealed

해당 클래스를 상속시키지 않도록 만들어주는 구문입니다.
보통 프로젝트를 할 때 해당 클래스를 상속하지 않고 단일로 사용하고자 할 때 사용하는 문법입니다.

sealed class cat
{

{

1-5 🔖 오버로딩, 오버라이딩

1-5-1 🖥️ 오버로딩 (Overloading)

같은 이름의 메서드를 여러 개 정의하는 기능입니다.
보통 함수의 경우 만들어둔 변수와 같은 변수로 사용할 경우 오류가 발생하는데
클래서 내부에서는 매개변수가 다른 경우 서로 다른 메서드로 인식하여 같은 이름으로 만들 수 있습니다!

  class Cat
  {
      public string name = null;
      public int Weight = 0;

      public Cat(string catName)
      {
          name = catName;
          Console.WriteLine("고양이의 이름은 " + name + "입니다. ");
      }
      public Cat(string catName, int catWeight) // 메서드명은 같지만 매개변수가 달라 작동하고 있음
      {
          name = catName;
          Weight = catWeight;
          Console.WriteLine("고양이의 이름은 " + name + "이며, 몸무게는 " + Weight + "Kg입니다.");
      }
  }
  static void Main(string[] args)
  {
      Cat jemmy = new Cat("지미");
      Cat coco = new Cat("코코", 3);
  }
고양이의 이름은 지미입니다.
고양이의 이름은 코코이며, 몸무게는 3Kg입니다.

1-5-2 🖥️ 오버라이딩 (Overriding)

부모클래스에 있는 메서드를 자식클래스에서 재정의하는 기능입니다

만약 NPC를 만들 때 청소부, 구급대원을 만들 경우 하는 일이 달라 움직이는 매커니즘을 다르게 해야하는데 이 경우 각각 메서드명을 달리하면 유지보수하기 상당히 힘들 것이기 때문에
부모 클래스에 있는 메서드의 이름을 그대로 가져와서 재정의를 할 수 있도록 할 수 있도록 만들 수 있는 편리한 기능입니다.

부모클래스에서 virtual을 붙여 가상 메서드로 만들고 자식클래스에서 override를 붙여 오버라이딩을 한다
꼭 재정의할 때에 메서드 이름과 매개변수 개수, 반환형은 같아야 한다!

 class Npc
 {
     public virtual void Move()   // 부모클래스에서 virtual을 붙여 가상 메서드로 만든다.
     {
         Console.WriteLine("이동합니다.");
     }
 }

 class CleanMan : Npc
 {
     public override void Move() // // 자식클래스에서 override를 붙여 오버라이딩을 한다
     {
         Console.WriteLine("쓰레기차로 이동합니다.");
     }
 }


 class AmbulanceWorker : Npc
 {
     public override void Move()
     {
         Console.WriteLine("구급차로 이동합니다.");
     }

 }
 static void Main(string[] args)
 {
     CleanMan cleanMan = new CleanMan();
     AmbulanceWorker worker = new AmbulanceWorker();
     cleanMan.Move();
     worker.Move();
 }
쓰레기차로 이동합니다.
구급차로 이동합니다.

1-5-2-1 🖥️ 오버라이딩 실습

오버라이딩을 안할 경우 상속은 받아 사용할 수는 있지만
부모클래스에 있는 내용이 출력되는 것을 알 수 있습니다.

internal class Program
{

    class Movie
    {
        public void Position()
        {
            Console.WriteLine("CGV에 가자");
        }
    }


    class Coco : Movie 
    {
        public void Position()
        {
            Console.WriteLine("Coco 보러 CGV에 가자");
        }

        public void CocoScore()
        {
            Console.WriteLine(95);
        }
    }


    class Soul : Movie
    {
        public void Position()
        {
            Console.WriteLine("Soul 보러 CGV에 가자");
        }
        public void SoulScore()
        {
            Console.WriteLine(90);
        }
    }

    static void Main(string[] args)
    {
       Movie coco1 = new Coco();
        Coco coco2 = new Coco();

        coco1.Position();
        coco2.Position();

        Movie soul1 = new Soul(); 
        Soul soul2 = new Soul();

        soul1.Position();
        soul2.Position();
    }
}
CGV에 가자
Coco 보러 CGV에 가자
CGV에 가자
Soul 보러 CGV에 가자

오버라이딩을 한 경우에 정상적으로 자식클래스의 구문이 활성화 되는 것을 볼 수 있습니다!

 internal class Program
 {

     class Movie
     {
         public virtual void Position()
         {
             Console.WriteLine("CGV에 가자");
         }
     }


     class Coco : Movie 
     {
         public override void Position()
         {
             Console.WriteLine("Coco 보러 CGV에 가자");
         }

         public void CocoScore()
         {
             Console.WriteLine(95);
         }
     }


     class Soul : Movie
     {
         public override void Position()
         {
             Console.WriteLine("Soul 보러 CGV에 가자");
         }
         public void SoulScore()
         {
             Console.WriteLine(90);
         }
     }

     static void Main(string[] args)
     {
        Movie coco1 = new Coco();
         Coco coco2 = new Coco();

         coco1.Position();
         coco2.Position();

         Movie soul1 = new Soul(); 
         Soul soul2 = new Soul();

         soul1.Position();
         soul2.Position();
     }
 }
Coco 보러 CGV에 가자
Coco 보러 CGV에 가자
Soul 보러 CGV에 가자
Soul 보러 CGV에 가자

0개의 댓글