본문 바로가기
카테고리 없음

[C#] 상속과 다형성

by 오 복 이 2024. 11. 14.

상속을 통해 코드를 재사용하고, 다형성을 통해 유연하고 확장 가능한 구조를 만들 수 있습니다. 이 글에서는 상속의 개념과 구현 방법, 메서드 오버라이딩과 new 키워드, 추상 클래스와 인터페이스, 다형성의 활용을 자세히 설명합니다.

 

 

상속의 개념과 구현 방법

**상속(Inheritance)**은 기존 클래스의 속성과 메서드를 새 클래스에서 재사용할 수 있는 기능입니다. 상속을 통해 부모 클래스의 기능을 확장하거나 새로운 기능을 추가할 수 있습니다. 부모 클래스는 기본 클래스(Base Class), 자식 클래스는 **파생 클래스(Derived Class)**라고도 합니다.

상속 구현 예제

using System;

class Animal // 부모 클래스
{
    public string Name { get; set; }

    public void Eat()
    {
        Console.WriteLine($"{Name}가 먹고 있습니다.");
    }
}

class Dog : Animal // 자식 클래스 Dog가 Animal을 상속받음
{
    public void Bark()
    {
        Console.WriteLine($"{Name}가 짖고 있습니다.");
    }
}

class Program
{
    static void Main()
    {
        Dog dog = new Dog();
        dog.Name = "바둑이";
        dog.Eat(); // 부모 클래스의 메서드 사용
        dog.Bark(); // 자식 클래스의 메서드 사용
    }
}

 

💡 Dog 클래스는 Animal 클래스를 상속받아 Name 속성과 Eat() 메서드를 사용할 수 있습니다. 또한, Dog 클래스만의 Bark() 메서드를 추가하여 기능을 확장할 수 있습니다.


 

메서드 오버라이딩과 new 키워드

상속을 통해 부모 클래스의 메서드를 **오버라이딩(Overriding)**하여 자식 클래스에서 재정의할 수 있습니다. 메서드 오버라이딩은 부모 클래스의 동작을 자식 클래스에 맞게 변경할 때 유용합니다. 오버라이딩하려는 메서드에는 virtual 키워드를, 자식 클래스에서 재정의할 메서드에는 override 키워드를 사용합니다.

메서드 오버라이딩 예제

using System;

class Animal
{
    public string Name { get; set; }

    public virtual void MakeSound() // virtual 키워드로 오버라이딩 허용
    {
        Console.WriteLine($"{Name}이(가) 소리를 냅니다.");
    }
}

class Dog : Animal
{
    public override void MakeSound() // override 키워드로 재정의
    {
        Console.WriteLine($"{Name}이(가) 멍멍 짖습니다.");
    }
}

class Program
{
    static void Main()
    {
        Dog dog = new Dog();
        dog.Name = "바둑이";
        dog.MakeSound(); // 출력: 바둑이가 멍멍 짖습니다.
    }
}

💡 Dog 클래스에서 MakeSound() 메서드를 재정의하여 Dog에 맞는 소리를 출력하도록 했습니다. 이렇게 하면 상속받은 메서드를 자식 클래스에서 더 구체적으로 구현할 수 있습니다.

new 키워드

new 키워드를 사용해 부모 클래스의 메서드를 숨기고 자식 클래스의 메서드를 호출할 수 있습니다. new 키워드를 사용하면 부모 클래스의 메서드는 여전히 남아 있지만, 자식 클래스에서 호출 시 숨겨집니다.

 
class Animal
{
    public void MakeSound()
    {
        Console.WriteLine("동물이 소리를 냅니다.");
    }
}

class Dog : Animal
{
    public new void MakeSound() // new 키워드를 사용해 메서드 숨김
    {
        Console.WriteLine("강아지가 멍멍 짖습니다.");
    }
}

class Program
{
    static void Main()
    {
        Dog dog = new Dog();
        dog.MakeSound(); // 출력: 강아지가 멍멍 짖습니다.
    }
}

💡 new 키워드는 자식 클래스에서 부모의 메서드를 숨기고자 할 때 사용합니다. 단, 이 방식은 오버라이딩이 아니며 부모 메서드를 재정의하지 않고 숨기는 것임을 주의해야 합니다.


 

추상 클래스와 인터페이스

 

**추상 클래스(Abstract Class)**와 **인터페이스(Interface)**는 객체 지향 프로그래밍에서 클래스 간의 관계를 정의하고, 특정 기능을 강제할 때 사용됩니다.

추상 클래스

추상 클래스는 인스턴스를 생성할 수 없으며, 다른 클래스가 상속을 통해 구현해야 하는 메서드를 포함할 수 있습니다. 추상 클래스는 abstract 키워드로 선언되며, 추상 메서드도 abstract 키워드를 사용하여 선언합니다.

using System;

abstract class Animal
{
    public string Name { get; set; }
    public abstract void MakeSound();
}

class Dog : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine($"{Name}이(가) 멍멍 짖습니다.");
    }
}

class Cat : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine($"{Name}이(가) 야옹 소리를 냅니다.");
    }
}

class Program
{
    static void Main()
    {
        Animal myDog = new Dog { Name = "바둑이" };
        Animal myCat = new Cat { Name = "나비" };

        myDog.MakeSound(); // 출력: 바둑이가 멍멍 짖습니다.
        myCat.MakeSound(); // 출력: 나비가 야옹 소리를 냅니다.

        // 다양한 Animal 객체들을 한 번에 관리할 수 있음
        Animal[] animals = { myDog, myCat };
        foreach (Animal animal in animals)
        {
            animal.MakeSound(); // 각각의 객체에 맞는 소리를 냄
        }
    }
}
 
 

💡 추상 클래스는 상속을 통해서만 사용 가능하며, 추상 메서드는 반드시 자식 클래스에서 구현해야 합니다.

인터페이스

인터페이스는 클래스가 구현해야 할 메서드나 속성의 계약을 정의하며, 클래스는 여러 인터페이스를 동시에 구현할 수 있습니다. 인터페이스는 interface 키워드로 정의합니다.

 

using System;

abstract class Animal
{
    public string Name { get; set; }
    public abstract void MakeSound();
}

class Dog : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine($"{Name}이(가) 멍멍 짖습니다.");
    }
}

class Cat : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine($"{Name}이(가) 야옹 소리를 냅니다.");
    }
}

class Program
{
    static void Main()
    {
        Animal myDog = new Dog { Name = "바둑이" };
        Animal myCat = new Cat { Name = "나비" };

        myDog.MakeSound(); // 출력: 바둑이가 멍멍 짖습니다.
        myCat.MakeSound(); // 출력: 나비가 야옹 소리를 냅니다.

        // 다양한 Animal 객체들을 한 번에 관리할 수 있음
        Animal[] animals = { myDog, myCat };
        foreach (Animal animal in animals)
        {
            animal.MakeSound(); // 각각의 객체에 맞는 소리를 냄
        }
    }
}
 

💡 인터페이스는 클래스가 여러 인터페이스를 구현할 수 있어 다중 상속의 효과를 얻을 수 있습니다. 인터페이스는 객체의 행동을 정의하는 데 유용하며, 다양한 클래스에서 동일한 기능을 구현하도록 강제할 수 있습니다.


다형성의 활용

**다형성(Polymorphism)**은 하나의 인터페이스로 다양한 데이터 타입을 처리할 수 있게 하는 특성입니다. 상속과 인터페이스를 사용하여 다형성을 구현할 수 있습니다. 다형성을 통해 코드의 재사용성과 유연성이 향상됩니다.

다형성 예제

다형성은 부모 클래스 타입의 변수가 자식 클래스 인스턴스를 참조할 수 있게 하여, 동일한 메서드를 호출하더라도 각 자식 클래스에서 재정의된 메서드가 실행되도록 합니다.

using System;

abstract class Animal
{
    public string Name { get; set; }
    public abstract void MakeSound();
}

class Dog : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine($"{Name}이(가) 멍멍 짖습니다.");
    }
}

class Cat : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine($"{Name}이(가) 야옹 소리를 냅니다.");
    }
}

class Program
{
    static void Main()
    {
        Animal myDog = new Dog { Name = "바둑이" };
        Animal myCat = new Cat { Name = "나비" };

        myDog.MakeSound(); // 출력: 바둑이가 멍멍 짖습니다.
        myCat.MakeSound(); // 출력: 나비가 야옹 소리를 냅니다.

        // 다양한 Animal 객체들을 한 번에 관리할 수 있음
        Animal[] animals = { myDog, myCat };
        foreach (Animal animal in animals)
        {
            animal.MakeSound(); // 각각의 객체에 맞는 소리를 냄
        }
    }
}
 

💡 다형성을 활용하면 Animal 배열에 여러 종류의 동물을 추가하고, 동일한 MakeSound() 메서드를 호출하여 각 객체에 맞는 동작을 수행할 수 있습니다. 이를 통해 코드의 유연성과 확장성을 높일 수 있습니다.

728x90
반응형