Post

[C#] 상속과 다형성

[C#] 상속과 다형성

상속과 다형성


상속

  1. 상속의 개념:
    • 상속은 기존의 클래스(부모 클래스 또는 상위 클래스)를 확장하거나 재사용하여 새로운 클래스(자식 클래스 또는 하위 클래스)를 생성하는 것.
    • 자식 클래스는 부모 클래스의 멤버(필드, 메서드, 프로퍼티 등)를 상속받아 사용할 수 있다.
    • 상속을 통해 부모 클래스의 기능을 확장하거나 수정하여 새로운 클래스를 정의할 수 있다.
  2. 상속의 장점:
    • 코드의 재사용성: 상속을 통해 기존 클래스의 코드를 재사용할 수 있으므로, 반복적인 코드 작성을 줄일 수 있다.
    • 계층 구조의 표현: 클래스 간의 계층 구조를 표현하여 코드의 구조를 명확하게 표현할 수 있다.
    • 유지보수성의 향상: 상속을 통해 기존 클래스의 수정이 필요한 경우, 해당 클래스만 수정하면 된다. 이로써 코드의 유지보수성이 향상된다.
  3. 상속의 종류:
    • 단일 상속: 하나의 자식 클래스가 하나의 부모 클래스만 상속받는 것을 말한다. C#에서는 단일 상속만을 지원한다.
    • 다중 상속: 하나의 자식 클래스가 여러 개의 부모 클래스를 동시에 상속받는 것을 말한다. C#은 다중 상속을 지원하지 않습니다.
    • 인터페이스 상속: 클래스가 인터페이스를 상속받는 것을 말한다. 인터페이스는 다중 상속을 지원하며, 클래스는 하나의 클래스와 여러 개의 인터페이스를 동시에 상속받을 수 있다.
  4. 상속의 특징:
    • 부모 클래스의 멤버에 접근:
      • 자식 클래스는 상속받은 부모 클래스의 멤버에 접근할 수 있으며, 이를 통해 부모 클래스의 기능을 재사용할 수 있다.
    • 메서드 재정의:
      • 자식 클래스는 부모 클래스의 메서드를 재정의하여 자신에게 맞게 수정할 수 있다. 이를 통해 다형성(Polymorphism)을 구현할 수 있다.
    • 상속의 깊이 :
      • 클래스는 다수의 계층적인 상속 구조를 가질 수 있다. 부모 클래스가 또 다른 클래스의 자식 클래스가 될 수 있으며, 이를 통해 상속의 계층 구조를 형성할 수 있다.
      • 상속의 깊이가 깊어질수록 클래스 간의 관계가 복잡해질 수 있으므로, 적절한 상속의 깊이를 유지하고 상속을 적절하게 사용하는 것이 중요한다.
  5. 접근 제한자와 상속:
    • 상속 관계에서 멤버의 접근 제한자는 중요한 역할을 한다. 부모 클래스의 멤버의 접근 제한자에 따라 자식 클래스에서 해당 멤버에 접근할 수 있는 범위가 결정된다.
    • 접근 제한자를 통해 상속된 멤버의 가시성을 조절하여 캡슐화와 정보 은닉을 구현할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 부모 클래스
public class Animal
{
    public string Name { get; set; }

    public void Eat()
    {
        Console.WriteLine("Animal is eating.");
    }

    public void Sleep()
    {
        Console.WriteLine("Animal is sleeping.");
    }
}

// 자식 클래스
public class Dog : Animal
{
    public int Age { get; set; }
}

// 자식 클래스
public class Cat : Animal
{
    public int Age { get; set; }
}

다중 상속을 사용하지 않는 이유

  1. 다이아몬드 문제(Diamond Problem): 다중 상속을 허용하면 한 클래스가 두 개 이상의 부모 클래스로부터 동일한 멤버를 상속받을 수 있다. 이 경우, 같은 이름의 멤버를 가지고 있을 때 어떤 부모 클래스의 멤버를 사용해야 하는지 모호해진다.

  2. 설계의 복잡성 증가: 클래스가 다중 상속을 받을 경우, 어떤 클래스로부터 어떤 멤버를 상속받을지 결정해야 한다. 이로 인해 클래스 간의 상속 관계를 파악하기 어려워지고 코드의 유지 보수성이 저하될 수 있다.

  3. 이름 충돌과 충돌 해결의 어려움: 다중 상속을 허용하면 여러 부모 클래스로부터 상속받은 멤버들이 이름이 충돌할 수 있다. 이러한 충돌을 해결하기 위해 충돌하는 멤버를 재정의해야 하거나 명시적으로 부모 클래스를 지정해야 할 수 있다. 이는 코드의 복잡성을 증가시키고 오류 발생 가능성을 높입니다.

  4. 설계의 일관성과 단순성 유지: C#은 단일 상속을 통해 설계의 일관성과 단순성을 유지하고자 한다. 단일 상속을 통해 클래스 간의 관계를 명확하게 만들고 코드의 가독성과 이해도를 높일 수 있다. 또한 인터페이스를 사용하여 다중 상속이 필요한 경우에도 유사한 기능을 구현할 수 있다.

다향성

  1. 가상(Virtual) 메서드
    • 가상 메서드는 기본적으로 부모 클래스에서 정의되고 자식 클래스에서 재정의할 수 있는 메서드입니다.
    • 가상 메서드는 virtual 키워드를 사용하여 선언되며, 자식 클래스에서 필요에 따라 재정의될 수 있다.
    • 이를 통해 자식 클래스에서 부모 클래스의 메서드를 변경하거나 확장할 수 있다.
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      
        public class Unit
        {
            public virtual void Move()
            {
                Console.WriteLine("두발로 걷기");
            }
      
            public void Attack()
            {
                Console.WriteLine("Unit 공격");
            }
        }
      
        public class Marine : Unit
        {
      
        }
      
        public class Zergling : Unit
        {
            public override void Move()
            {
                Console.WriteLine("네발로 걷기");
            }
        }
      
  2. 추상(Abstract) 클래스와 메서드
    • 추상 클래스는 직접적으로 인스턴스를 생성할 수 없는 클래스.
    • 주로 상속을 위한 베이스 클래스로 사용.
    • 추상 클래스는 abstract 키워드를 사용하여 선언되며, 추상 메서드를 포함할 수 있다.
    • 추상 메서드는 구현부가 없는 메서드로, 자식 클래스에서 반드시 구현되어야 한다.
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      
        abstract class Shape
        {
            public abstract void Draw();
        }
      
        class Circle : Shape
        {
            public override void Draw()
            {
                Console.WriteLine("Drawing a circle");
            }
        }
      
        class Square : Shape
        {
            public override void Draw()
            {
                Console.WriteLine("Drawing a square");
            }
        }
      
  3. 오버라이딩 과 오버로딩

    오버라이딩(Overriding)과 오버로딩(Overloading)은 객체 지향 프로그래밍에서 다른 개념을 나타낸다.

    • 오버라이딩(Overriding)
      • 부모 클래스에서 이미 정의된 메서드를 자식 클래스에서 재정의하는 것을 의미한다.
      • 이는 상속 관계에 있는 클래스 간에 발생하며, 메서드의 이름, 매개변수 및 반환타입이 동일해야 한다.
      • 오버라이딩을 통해 자식 클래스는 부모 클래스의 메서드를 재정의하여 자신에게 맞는 동작을 구현할 수 있다.
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        
          public class Shape
          {
              public virtual void Draw()
              {
                  Console.WriteLine("Drawing a shape.");
              }
          }
        
          public class Circle : Shape
          {
              public override void Draw()
              {
                  Console.WriteLine("Drawing a circle.");
              }
          }
        
          public class Rectangle : Shape
          {
              public override void Draw()
              {
                  Console.WriteLine("Drawing a rectangle.");
              }
          }
        
    • 오버로딩(Overloading)
      • 동일한 메서드 이름을 가지고 있지만, 매개변수의 개수, 타입 또는 순서가 다른 여러 개의 메서드를 정의하는 것을 의미한다.
      • 오버로딩을 통해 동일한 이름을 가진 메서드를 다양한 매개변수 조합으로 호출할 수 있다.
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        
          public class Calculator
          {
              public int Add(int a, int b)
              {
                  return a + b;
              }
        
              public int Add(int a, int b, int c)
              {
                  return a + b + c;
              }
          }
        
This post is licensed under CC BY 4.0 by the author.