본문 바로가기
2학년 2학기/윈도우즈 프로그래밍

인터페이스(1) - 파생 클래스, 메소드 오버라이딩, 추상 클래스

by kkkkk1023 2024. 9. 30.

1. 파생 클래스 (Derived Class)

  • 상속(Inheritance): 상속은 베이스 클래스의 모든 멤버파생 클래스로 전달하여, 코드의 재사용성을 높이는 기능입니다. C#에서는 단일 상속만 지원하며, 하나의 베이스 클래스로부터만 상속할 수 있습니다.

파생 클래스의 정의 예시:

class BaseClass {
    int a;
    void MethodA() {
        // ...
    }
}

class DerivedClass : BaseClass {
    int b;
    void MethodB() {
        // ...
    }
}

base 지정어: 베이스 클래스의 생성자나 메소드를 호출할 때 base 키워드를 사용합니다.

만약 상속받은 클래스의 값을 사용하고 싶다면 base.a 이런식으로 사용하면된다.

 

예시1 - base 키워드를 사용한 생성자 호출 예시

using System;

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

    // 부모 클래스의 생성자
    public Animal(string name) {
        Name = name;
        Console.WriteLine($"Animal 생성자 호출: {Name}");
    }
}

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

    // 파생 클래스의 생성자에서 base를 사용해 부모 생성자 호출
    public Dog(string name, string breed) : base(name) {
        Breed = breed;
        Console.WriteLine($"Dog 생성자 호출: {Breed}");
    }
}

class Program {
    static void Main() {
        Dog myDog = new Dog("Buddy", "Golden Retriever");
    }
}

 

예시2 - base 키워드를 사용한 메서드 호출 예시

using System;

// 부모 클래스
class Animal {
    public virtual void Speak() {
        Console.WriteLine("Animal is making a sound.");
    }
}

// 자식 클래스
class Dog : Animal {
    // 부모 클래스의 메서드를 재정의
    public override void Speak() {
        base.Speak();  // 부모 클래스의 Speak 메서드 호출
        Console.WriteLine("Dog is barking!");
    }
}

class Program {
    static void Main() {
        Dog myDog = new Dog();
        myDog.Speak();
    }
}

 

 

2. 메소드 재정의 (Method Overriding)

  • 메소드 재정의는 베이스 클래스에서 정의된 메소드를 파생 클래스에서 재정의하는 것입니다. 메소드 시그니처가 동일해야 재정의가 가능합니다.
  • 메소드를 재정의하려면 override 키워드를 사용합니다.

예시:

class BaseClass {
    public virtual void Print() {
        Console.WriteLine("Base Print Method");
    }
}

class DerivedClass : BaseClass {
    public override void Print() {
        Console.WriteLine("Derived Print Method");
    }
}

3. 가상 메소드와 봉인 메소드

  • 가상 메소드(virtual method): virtual로 선언된 메소드는 파생 클래스에서 재정의할 수 있습니다.
    • 기본 클래스에서 정의된 메소드프로퍼티파생 클래스에서 재정의할 수 있도록 허용
    • 기본 클래스에서 virtual 키워드로 선언된 메소드는 파생 클래스에서 override 키워드를 사용하여 재정의할 수 있습니다.
    • override를 작성하지 않으면 재정의가 되지 않는다.
using System;

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

    // virtual 키워드를 사용하여 재정의 가능하도록 설정
    public virtual void Speak() {
        Console.WriteLine("Animal is making a sound.");
    }
}

// 자식 클래스
class Dog : Animal {
    // override 키워드를 사용하여 부모의 Speak 메서드를 재정의
    public override void Speak() {
        Console.WriteLine($"{Name} is barking!");
    }
}



class Program {
    static void Main() {
        // Dog 객체 생성
        Dog myDog = new Dog();
        myDog.Name = "Buddy";
        myDog.Speak();  // 출력: Buddy is barking!

        // 부모 클래스의 Speak 호출
        Animal myAnimal = new Animal();
        myAnimal.Name = "Some animal";
        myAnimal.Speak();  // 출력: Animal is making a sound.
    }
}

 

 

  • 봉인 메소드(sealed method): sealed로 선언된 메소드는 더 이상 파생 클래스에서 재정의할 수 없습니다.
    • 재정의를 허용하지 않는다.

4. 추상 클래스 (Abstract Class)

  • 추상 클래스는 실질적인 구현을 제공하지 않는 추상 메소드를 포함하는 클래스입니다. 이러한 추상 메소드는 파생 클래스에서 반드시 구현해야 합니다.
  • 추상 메소드abstract 키워드를 사용하여 선언하며, 추상 클래스는 직접 인스턴스를 만들 수 없습니다.

추상 클래스 예시:

abstract class AbstractClass {
    public abstract void AbstractMethod();
    public void NormalMethod() {
        Console.WriteLine("Normal method in abstract class");
    }
}

class ConcreteClass : AbstractClass {
    public override void AbstractMethod() {
        Console.WriteLine("Abstract method implemented");
    }
}

5. 클래스형 변환 (Casting)

  • 상향식 캐스트(Upcasting): 파생 클래스 객체를 베이스 클래스 형식으로 변환하는 것은 가능합니다.
  • 하향식 캐스트(Downcasting): 베이스 클래스 객체를 파생 클래스 형식으로 변환하는 것은 명시적인 캐스트가 필요하며, 경우에 따라 예외가 발생할 수 있습니다.

클래스형 변환 예시:

BaseClass obj = new DerivedClass();  // 상향식 캐스트 (자동)
DerivedClass derivedObj = (DerivedClass)obj;  // 하향식 캐스트 (명시적)

상속 관계에서의 클래스 형변환기본 클래스(부모 클래스, base)파생 클래스(자식 클래스) 간에 업캐스팅(Upcasting)다운캐스팅(Downcasting)을 통해 가능합니다. 각각의 변환은 특정 규칙에 따라 가능하거나 제한됩니다.

 

 

1. 업캐스팅(Upcasting): 파생 클래스에서 기본 클래스로 변환

  • 업캐스팅(Upcasting)파생 클래스의 객체를 기본 클래스 타입으로 변환하는 것입니다.
  • 항상 안전하며, 명시적 캐스팅 없이도 가능합니다.
  • 상속 관계에서 파생 클래스는 기본 클래스의 모든 멤버를 상속받기 때문에, 파생 클래스 객체를 기본 클래스 타입으로 변환할 때 데이터 손실이 발생하지 않습니다.

업캐스팅 예시:

class BaseClass {
    public void Show() {
        Console.WriteLine("Base class method");
    }
}

class DerivedClass : BaseClass {
    public void Display() {
        Console.WriteLine("Derived class method");
    }
}

class Program {
    public static void Main() {
        DerivedClass derived = new DerivedClass();

        // 업캐스팅: 파생 클래스의 객체를 기본 클래스 타입으로 변환
        BaseClass baseObj = derived;

        baseObj.Show();  // Base class method 출력
        // baseObj.Display();  // 오류: BaseClass에는 Display() 메소드가 없음
    }
}

설명:

  • 업캐스팅을 통해 DerivedClass 객체BaseClass 타입으로 변환합니다.
  • 업캐스팅 후에는 기본 클래스의 멤버들만 사용할 수 있습니다. 파생 클래스에서 추가된 메소드나 속성(Display() 같은)은 기본 클래스 타입으로 변환된 후에는 접근할 수 없습니다.

 

 

2. 다운캐스팅(Downcasting): 기본 클래스에서 파생 클래스로 변환 -> 🚨"예외 발생"🚨

  • 다운캐스팅(Downcasting)기본 클래스의 객체를 파생 클래스 타입으로 변환하는 것입니다.
  • 다운캐스팅은 안전하지 않을 수 있으며, 명시적 캐스팅이 필요합니다.
  • 기본 클래스의 객체가 실제로 파생 클래스의 객체인 경우에만 다운캐스팅이 성공합니다. 그렇지 않으면 InvalidCastException 예외가 발생합니다.

다운캐스팅 예시:

class BaseClass {
    public void Show() {
        Console.WriteLine("Base class method");
    }
}

class DerivedClass : BaseClass {
    public void Display() {
        Console.WriteLine("Derived class method");
    }
}

class Program {
    public static void Main() {
        BaseClass baseObj = new DerivedClass();  // 업캐스팅
        baseObj.Show();  // Base class method 출력

        // 다운캐스팅: BaseClass 객체를 DerivedClass 타입으로 변환
        DerivedClass derivedObj = (DerivedClass)baseObj;  // 명시적 캐스팅
        derivedObj.Display();  // Derived class method 출력
    }
}

설명:

  • BaseClass 객체가 실제로 DerivedClass 객체로 생성된 경우에만 다운캐스팅이 성공합니다.
  • BaseClass 타입에서 다시 DerivedClass 타입으로 변환한 후에는 파생 클래스의 멤버(Display())를 호출할 수 있습니다.

 

 

3. 다운캐스팅의 위험성과 as 키워드

다운캐스팅이 성공하려면, 기본 클래스 객체가 실제로 파생 클래스 객체여야 합니다. 그렇지 않으면 InvalidCastException이 발생할 수 있습니다. 이를 방지하기 위해 as 키워드 또는 is 키워드를 사용할 수 있습니다.

as 키워드를 사용한 안전한 다운캐스팅:

BaseClass baseObj = new BaseClass();
DerivedClass derivedObj = baseObj as DerivedClass;

if (derivedObj != null) {
    derivedObj.Display();
} else {
    Console.WriteLine("다운캐스팅에 실패했습니다.");
}

설명:

  • as 키워드를 사용하면 다운캐스팅이 실패할 경우 예외를 발생시키지 않고, 대신 null을 반환합니다. 이를 통해 다운캐스팅이 실패했을 때의 처리를 할 수 있습니다.

 

 

4. 업캐스팅과 다운캐스팅의 차이점 요약

캐스팅 종류 설명 명시적 캐스팅 필요 여부 안전성
업캐스팅(Upcasting) 파생 클래스 객체를 기본 클래스 타입으로 변환. 항상 안전하며 명시적 캐스팅이 필요 없음. 필요 없음 안전함
다운캐스팅(Downcasting) 기본 클래스 객체를 파생 클래스 타입으로 변환. 명시적 캐스팅 필요. 실제로 파생 클래스 객체일 경우에만 성공하며, 실패 시 예외 발생 가능. 필요 안전하지 않음. 실패 시 예외 발생 가능

 

 

5. 정리

  • 업캐스팅(파생 클래스 → 기본 클래스)은 항상 안전하며, 명시적인 캐스팅이 필요하지 않습니다. 기본 클래스는 파생 클래스에서 상속받은 모든 멤버를 포함하므로, 파생 클래스를 기본 클래스 타입으로 변환하는 것은 문제가 없습니다.
  • 다운캐스팅(기본 클래스 → 파생 클래스)은 안전하지 않을 수 있으며, 명시적 캐스팅이 필요합니다. 다운캐스팅하려는 객체가 실제로 파생 클래스의 객체일 때만 성공하며, 그렇지 않으면 예외가 발생할 수 있습니다.

 

 

 

6. 다형성 (Polymorphism)

  • 다형성은 객체의 실제 형식에 따라 메소드 호출이 달라지는 특성을 말합니다. C#에서는 virtualoverride 키워드를 통해 구현됩니다. 다형성을 통해 동일한 인터페이스로 다양한 객체를 처리할 수 있습니다.

다형성 예시:

BaseClass obj = new DerivedClass();
obj.Print();  // DerivedClass의 Print 메소드가 호출됨