이번에는 C# 프로그래밍 언어의 상속과 다형성 개념과 객체지향 프로그래밍의 주요 개념을 쉽게 이해할 수 있도록 정리하고, 코드 예시와 실행 결과를 통해 상속과 다형성을 쉽고 자세하게 설명해 보도록 하겠습니다.
C# 프로그래밍 언어의 상속과 다형성
C#에서 상속과 다형성은 객체지향 프로그래밍(OOP: Object-Oriented Programming)의 핵심 개념입니다. 이 두 개념은 코드의 재사용성을 높이고, 유연성과 확장성을 부여하며, 유지 보수를 쉽게 할 수 있는 강력한 도구입니다.
상속(Inheritance)의 개념
상속은 하나의 클래스가 다른 클래스의 속성과 메서드를 물려받는 기능을 말합니다. 상속을 통해 코드의 중복을 피할 수 있으며, 기존 클래스를 확장하여 새로운 기능을 추가할 수 있습니다.
// 기본 클래스 (부모 클래스 또는 상위 클래스)
public class Animal
{
public string Name { get; set; }
public void Eat()
{
Console.WriteLine($"{Name}이(가) 먹고 있습니다.");
}
}
// 파생 클래스 (자식 클래스 또는 하위 클래스)
public class Dog : Animal
{
public void Bark()
{
Console.WriteLine($"{Name}이(가) 짖고 있습니다.");
}
}
위 코드에서 `Dog` 클래스는 `Animal` 클래스를 상속받습니다. 즉, `Dog` 클래스는 `Animal` 클래스의 속성(`Name`)과 메서드(`Eat`)를 물려받으며, `Dog` 클래스 자체의 메서드(`Bark`)를 추가로 정의할 수 있습니다.
public class Program
{
public static void Main(string[] args)
{
Dog myDog = new Dog();
myDog.Name = "바둑이";
myDog.Eat(); // Animal 클래스에서 상속받은 메서드 호출
myDog.Bark(); // Dog 클래스에서 정의한 메서드 호출
}
}
실행 결과:
바둑이이(가) 먹고 있습니다.
바둑이이(가) 짖고 있습니다.
이처럼, `Dog` 클래스는 `Animal` 클래스를 상속받아 `Name` 속성과 `Eat` 메서드를 그대로 사용할 수 있습니다. 이를 통해 코드를 재사용하고, 필요한 기능만 추가하는 방식으로 쉽게 확장할 수 있습니다.
다형성(Polymorphism)의 개념
다형성은 하나의 객체가 여러 형태를 가질 수 있는 성질을 의미합니다. 이는 주로 **메서드 오버라이딩(Method Overriding)**을 통해 구현됩니다. 다형성을 사용하면 동일한 메서드를 호출하더라도, 객체에 따라 다르게 동작하도록 만들 수 있습니다.
예시:
// 기본 클래스 (부모 클래스)
public class Animal
{
public virtual void Speak() // virtual 키워드로 오버라이딩 가능성을 명시
{
Console.WriteLine("동물이 소리를 냅니다.");
}
}
// 파생 클래스 (자식 클래스)
public class Dog : Animal
{
public override void Speak() // override 키워드로 메서드 오버라이딩
{
Console.WriteLine("개가 멍멍 짖습니다.");
}
}
public class Cat : Animal
{
public override void Speak() // override 키워드로 메서드 오버라이딩
{
Console.WriteLine("고양이가 야옹 울습니다.");
}
}
위 코드에서 `Dog` 클래스와 `Cat` 클래스는 각각 `Animal` 클래스의 `Speak` 메서드를 오버라이딩하여 자신만의 방식으로 구현했습니다. 부모 클래스(`Animal`)의 `Speak` 메서드는 "동물이 소리를 냅니다."라고 출력하지만, 자식 클래스에서 이를 오버라이딩하면 `Dog` 객체는 "개가 멍멍 짖습니다."를, `Cat` 객체는 "고양이가 야옹 울습니다."를 출력하게 됩니다.
실행 예시:
public class Program
{
public static void Main(string[] args)
{
Animal myAnimal = new Animal();
Animal myDog = new Dog();
Animal myCat = new Cat();
myAnimal.Speak(); // 부모 클래스의 메서드 호출
myDog.Speak(); // Dog 클래스에서 오버라이딩한 메서드 호출
myCat.Speak(); // Cat 클래스에서 오버라이딩한 메서드 호출
}
}
실행 결과:
동물이 소리를 냅니다.
개가 멍멍 짖습니다.
고양이가 야옹 울습니다.
여기서 중요한 점은 `myDog`와 `myCat` 객체의 데이터 타입은 `Animal`이지만, 실제로는 각각 `Dog`와 `Cat` 객체로 생성되었기 때문에 해당 객체에 맞는 오버라이딩된 메서드가 호출된다는 것입니다. 이를 **런타임 다형성**이라고 부릅니다.
상속과 다형성의 결합
상속과 다형성을 결합하면 매우 유연한 프로그램을 작성할 수 있습니다. 상속을 통해 클래스 간의 관계를 형성하고, 다형성을 통해 객체가 서로 다른 형태로 동작할 수 있게 하는 것입니다.
예시:
public class Program
{
public static void MakeAnimalSpeak(Animal animal)
{
animal.Speak(); // 다형성을 활용한 메서드 호출
}
public static void Main(string[] args)
{
Animal myDog = new Dog();
Animal myCat = new Cat();
MakeAnimalSpeak(myDog); // 개가 멍멍 짖습니다.
MakeAnimalSpeak(myCat); // 고양이가 야옹 울습니다.
}
}
`MakeAnimalSpeak` 메서드는 `Animal` 타입의 객체를 매개변수로 받아들입니다. 그러나 실제로 전달된 객체가 `Dog`인지 `Cat`인지에 따라 다르게 동작합니다. 이는 다형성 덕분에 가능합니다. 이러한 설계는 유연성을 높이고, 코드 재사용성을 극대화합니다.
추상 클래스와 인터페이스를 통한 상속과 다형성
C#에서는 추상 클래스(Abstract Class)와 인터페이스(Interface)를 통해 좀 더 명확하게 상속과 다형성을 사용할 수 있습니다. 추상 클래스는 일부 메서드가 구현되지 않은 채로 남아 있으며, 자식 클래스가 반드시 해당 메서드를 구현해야 합니다.
추상 클래스 예시:
public abstract class Animal
{
public abstract void Speak(); // 추상 메서드: 반드시 자식 클래스에서 구현해야 함
}
public class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("개가 멍멍 짖습니다.");
}
}
public class Cat : Animal
{
public override void Speak()
{
Console.WriteLine("고양이가 야옹 울습니다.");
}
}
추상 클래스는 구체적인 구현이 없는 메서드를 통해 자식 클래스에게 특정 행동을 강제할 수 있습니다. `Animal` 클래스에서는 `Speak` 메서드가 추상 메서드로 정의되어 있어, 자식 클래스가 이를 반드시 구현해야 합니다.
실행 예시:
public class Program
{
public static void Main(string[] args)
{
Animal myDog = new Dog();
Animal myCat = new Cat();
myDog.Speak(); // 개가 멍멍 짖습니다.
myCat.Speak(); // 고양이가 야옹 울습니다.
}
}
상속은 코드의 재사용성을 높이고, 다형성은 객체의 행동을 유연하게 조정할 수 있도록 합니다. 이 두 개념을 적절히 활용하면 확장 가능하고 유지보수가 용이한 코드를 작성할 수 있습니다.
'프로그래밍 언어 > C#' 카테고리의 다른 글
C# 배열과 리스트 선언 및 초기화 방법 자료구조 메모리 관리 (0) | 2024.10.14 |
---|---|
C# 프로그래밍 언어에서 인터페이스와 추상 클래스 차이점 및 사용법 (0) | 2024.10.07 |
C# 생성자와 소멸자, 객체 생성과 메모리 관리의 핵심 원리 - C#10 (0) | 2024.10.02 |
C# 클래스와 객체 차이점, 필드와 메서드, 생성자 및 상속 예제로 이해하기 - C#9 (0) | 2024.09.30 |
C# 재귀 호출 마스터하기, 팩토리얼과 피보나치 수열 예제로 배우는 프로그래밍 - C#8 (0) | 2024.09.25 |