티스토리 뷰

C#

4. 클래스(3) - 상속, is와 as

루체도 2020. 1. 28. 23:30

클래스는 다른 클래스로부터 필드나 메소드, 프로퍼티 같은 멤버들을 물려 받을 수 있습니다.

객체 지향 프로그래밍에서는 물려받는 클래스(파생or자식 클래스)가 유산을 물려줄 클래스(기반or부모 클래스)를 지정

다음 코드가 상속의 예이다.

class Base()
{
  public void BaseMethod()
  {
    Console.WriteLine("BaseMethod");
  }
}

class Derived : Base
{
}

파생 클래스는 자신만의 고유한 멤버 외에도 기반 클래스로부터 물려받은 멤버를 가지고 있다.

이것은 바로 파생 클래스가 기반 클래스 위에 새로운 멤버를 얹어서 만든 것이기 때문이다.

파생 클래스는 객체를 생성할 때 내부적으로 기반 클래스의 생성자를 호출한 후에 자신의 생성자를 호출하고

객체가 소멸될 때는 반대로 파생클래스의 소멸자부터 기반 클래스의 소멸자를 호출한다.

class Base
{
  public Base()
  {
    Console.WriteLine("Base()");    
  }
  
  ~Base()
  {
    Console.WriteLine("~Base()");
  }
}

class Derived : Base
{
  public Derived()
  {
    Console.WriteLine("Derived()");
  }
  
  ~Derived()
  {
    Console.WriteLine("~Derived()");
  }
}

class MainApp{
  static void Main(string[] args)
  {
    Derived derived = new Derived();
  }
}

///result
/*
Base()
Derived()
~Derived()
~Base()
*/

위 코드의 결과처럼 기반 클래스의 생성자-> 파생 클래스의 생성자 -> 파생 클래스의 소멸자 -> 기반 클래스의 소멸자

순으로 호출이 된다.

그러면 기반 클래스의 생성자에 매개변수를 넣어줘야 하는 상황이면 파생 클래스는 어떻게 기반 클래스의 생성자에

접근을 할 수 있을까? 아래와 같이 접근을 하면 된다.

class Base()
{
 protected string Name;
 public Base(string Name)
 {
  this.Name = Name;
 }
}
//첫번째 방법
class Derived : Base
{
  public Derived(string Name) : base(Name)
  {

  }
}

파생 클래스에서 기반 클래스의 메소드를 호출하는 방법은 아래와 같다.

class Derived : Base
{
  public void DerivedMethod()
  {
    base.BaseMethod();
  }
}
seald 한정자
의도하지 않은 상속이나 파생 클래스의 구현을 막기 위해 상속이 불가능하도록 클래스를 선언하는 것이다.
seald class Base
{

}
이렇게 작성하면 된다. 이런 클래스를 봉인 클래스라고도 부른다.

 

형변환

파생 클래스의 인스턴스는 기반 클래스의 인스턴스로도 사용할 수 있다.

코드로 다음과 같이 작성할 수 있다.

class Mammal
{
  public void Nurse() { }
}

class Dog : Mammal
{
  public void Bark() { }
}

class Cat : Mammal
{
  public void Meow() { }
}

//Main에서 작성
Mammal mammal = new Mammal();
mammal = new Dog();
mammal.Nurse();

Dog dog = (Dog)mammal;
dog.Nurse();
dog.Bark();

mammal = new Cat();
mammal.Nurse();

Cat cat = (Cat)mammal;
cat.Nurse();
cat.Meow();

기반클래스와 파생 클래스의 형변환을 통해 코드의 생산성을 높일 수 있다.

예를 들어 Mammal 클래스에서 300가지의 클래스가 파생되었다고 가정하고

사육사 클래스를 만들어 이 동물들을 씻기는 Wash() 메소드를 구현했다고 가정한다.

class ZooKeeper
{
 public void Wash(Dog dog) { }
 public void Wash(Cat cat) { }
 public void Wash(Lion lion) { }
 .
 .
 .
}

이렇게 300종류의 동물들을 씻기는 Wash메소드를 오버로딩하는 것 보다

Mammal 클래스 하나만 씻기는 Wash메소드를 만들면 모든 동물 클래스에서 사용이 가능해진다.

class Zookeeper
{
  public void Wash(Mammal mammal) {  }
}

C#에서는 이 형변환을 위해 연산자 두 개를 제공한다.

연산자 설명
is 객체가 해당 형식에 해당하는지 검사해 결과를 bool 값으로 반환한다
as 형변환 연산자와 같은 역할. 다만 변환에 실패한 경우 객체 참조를 null로 만든다
//is 연산자 사용
Mammal mammal = new Dog();
Dog dog;

if(mammal is Dog)
{
 dog = (Dog)mammal;
 dog.Bark();
}

//as 연산자 사용
Mammal mammal2 = new Cat();

Cat cat = mammal2 as Cat;
if(cat != null) //형변환에 실패하는 경우에 cat은 null이 된다.
{
  cat.Meow();
}

일반적으로 형식 변환 연산자 대신에 as 연산자를 사용하는 쪽이 권장된다.

왜냐면 형 변환에 실패하더라도 예외가 일어나 갑자기 코드의 실행이 점프하는 일이 없으므로 관리하기가

더 수월하기 때문이다. 단 as 연산자는 참조 형식에 대해서만 사용가능

'C#' 카테고리의 다른 글

5. 구조체  (0) 2020.01.29
4. 클래스(4) - 오버라이딩  (0) 2020.01.29
4. 클래스(2) - 정적필드, this 키워드, 접근 한정자  (0) 2020.01.28
4. 클래스(1) - 클래스란? , 생성자와 소멸자  (0) 2020.01.28
3. 메소드란?  (0) 2020.01.28
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
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 28
글 보관함