1. 상속으로 코드 재활용하기
상속은 객체 지향 프로그래밍의 가장 중요한 특성 중 하나입니다. C#에서 상속을 통해 기존 클래스의 기능을 확장하거나 수정할 수 있습니다.
1.1 상속의 기본 개념
파생 클래스는 기반 클래스의 모든 멤버를 상속받아 사용할 수 있습니다.
public class Animal
{
public void Eat() { Console.WriteLine("Eating..."); }
}
public class Dog : Animal
{
public void Bark() { Console.WriteLine("Barking..."); }
}
C#
복사
이 예제에서 Dog 클래스는 Animal 클래스를 상속받아 Eat() 메소드를 사용할 수 있습니다.
1.2 생성자와 소멸자의 호출 순서
파생 클래스의 객체가 생성될 때, 기반 클래스의 생성자가 먼저 호출된 후 파생 클래스의 생성자가 호출됩니다. 소멸 시에는 이 순서가 반대로 됩니다.
public class Base
{
public Base() { Console.WriteLine("Base Constructor"); }
~Base() { Console.WriteLine("Base Destructor"); }
}
public class Derived : Base
{
public Derived() { Console.WriteLine("Derived Constructor"); }
~Derived() { Console.WriteLine("Derived Destructor"); }
}
C#
복사
이 코드를 실행하면 다음과 같은 순서로 출력됩니다:
1.
기반 클래스의 생성자 → 파생 클래스의 생성자
2.
파생 클래스의 소멸자 → 기반 클래스의 소멸자
1.3 base 키워드 활용
base 키워드를 사용하면 파생 클래스에서 기반 클래스의 멤버에 접근할 수 있습니다. 특히 생성자에서 유용하게 사용됩니다.
public class Animal
{
protected string name;
public Animal(string name) { this.name = name; }
}
public class Dog : Animal
{
public Dog(string name) : base(name) { }
public void Introduce() { Console.WriteLine($"I'm {name}"); }
}
C#
복사
여기서 Dog 클래스의 생성자는 base(name)을 통해 Animal 클래스의 생성자를 호출합니다.
1.4 상속 봉인하기
sealed 키워드를 사용하면 클래스가 더 이상 상속되지 않도록 할 수 있습니다.
public sealed class FinalClass
{
// 이 클래스는 상속될 수 없습니다.
}
C#
복사
2. 기반 클래스와 파생 클래스 사이의 형식 변환
C#에서는 리스코프 치환 원칙에 따라 파생 클래스의 인스턴스를 기반 클래스의 인스턴스로 취급할 수 있습니다.
2.1 형식 변환 연산자
C#은 is와 as 두 가지 형식 변환 연산자를 제공합니다.
1.
is 연산자: 객체가 특정 형식에 해당하는지 검사
2.
as 연산자: 형식 변환을 시도하며, 실패 시 null 반환
Animal myPet = new Dog();
if (myPet is Dog)
{
Dog myDog = (Dog)myPet;
myDog.Bark();
}
Dog anotherDog = myPet as Dog;
if (anotherDog != null)
{
anotherDog.Bark();
}
C#
복사
as 연산자는 예외를 발생시키지 않아 안전한 형식 변환에 적합하지만, 값 형식에는 사용할 수 없다는 점을 유의해야 합니다.
3. 오버라이딩과 다형성
메소드 오버라이딩은 다형성을 구현하는 핵심 메커니즘입니다. C#에서 메소드를 오버라이딩하려면 다음 조건을 만족해야 합니다:
1.
기반 클래스의 메소드가 virtual 키워드로 선언되어 있어야 함
2.
파생 클래스에서 override 키워드를 사용하여 메소드를 재정의
3.1 virtual과 override 키워드
기반 클래스에서 virtual 키워드로 선언된 메소드는 파생 클래스에서 override 키워드를 사용해 재정의할 수 있습니다.
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");
}
}
C#
복사
이렇게 하면 Circle 객체의 Draw 메소드를 호출할 때 "Drawing a circle"이 출력됩니다.
4. 메소드 숨기기
메소드 숨기기는 오버라이딩과 유사하지만, 다형성을 구현하지 않습니다. 대신, 파생 클래스에서 기반 클래스의 메소드를 '가리는' 효과를 냅니다.
public class Base
{
public void Method() { Console.WriteLine("Base Method"); }
}
public class Derived : Base
{
public new void Method() { Console.WriteLine("Derived Method"); }
}
C#
복사
이 경우, Derived 객체의 Method를 호출하면 "Derived Method"가 출력되지만, 이 객체를 Base 타입으로 캐스팅하면 "Base Method"가 출력됩니다.
5. 오버라이딩 봉인하기
sealed 키워드를 사용하여 파생 클래스에서 더 이상 오버라이딩할 수 없도록 메소드를 봉인할 수 있습니다.
public class Base
{
public virtual void Method() { }
}
public class Derived : Base
{
public sealed override void Method() { }
}
C#
복사
6. 읽기 전용 필드
readonly 키워드를 사용하여 읽기 전용 필드를 선언할 수 있습니다. 이는 상수와 변수의 중간 형태로, 생성자에서만 값을 할당할 수 있습니다.
public class MyClass
{
public readonly int MyReadOnlyField;
public MyClass(int value)
{
MyReadOnlyField = value; // 생성자에서만 값 할당 가능
}
}
C#
복사
참고 자료
•
박상현, "이것이 C#이다", 한빛미디어, 2023