Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
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 29 30
Archives
Today
Total
관리 메뉴

nomad-programmer

[Programming/C#] 부모 클래스와 파생 클래스 사이의 형식 변환 (is, as) 본문

Programming/C#

[Programming/C#] 부모 클래스와 파생 클래스 사이의 형식 변환 (is, as)

scii 2020. 9. 8. 18:50

개와 고양이는 종은 다르지만 젖을 먹인다는 공통점이 있다. 그러기에 포유류로 분류된다. 이를 플래스로 다음과 같이 표현할 수 있다.

class Mammal
{
    public void Nurse() {}
}

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

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

// /////////////////////////

Mammal mammal = new Dog();
mammal.Nurse();

Dog dog = (Dog)mammal;

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

 위의 예처럼 부모 클래스와 파생 클래스 사이를 왔다 갔다 형식 변환이 가능하다. 파생 클래스의 인스턴스는 부모 클래스의 인스턴스로도 사용할 수 있다. 이렇게되면 코드의 생산성이 높아진다. 
예를 들어 Mammal 클래스에서 300가지의 클래스가 파생되었다고 해보자. 그리고 동물원의 사육사 클래스를 만들고 이 동물들을 씻기는 Wash() 메소드를 구현한다고 생각해보자. 다음과 같이 300개의 Wash() 메소드를 오버로딩해야 한다.

class Zookeeper
{
    public void Wash( Dog dog ) {}
    public void Wash( Cat cat ) {}
    public void Wash( Lion lion ) {}
    public void Wash( Tiger tiger ) {}
    .
    .
    .
    // 등... 300개 버전의 Wash() 메소드 선언
}

하지만 300개의 동물 클래스가 모두 Mammal 클래스로부터 상속받았기 때문에 이들은 모두 Mammal로 간주할 수 있다.
따라서 다음과 같이 단 하나의 Wash() 메소드만 준비하면 300개의 동물 클래스에게 사용이 가능하다.

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

한편, C#은 형 변환을 위해 연산자 두 개를 제공한다. is와 as가 바로 그것이다.

연산자 설명
is 객체가 해당 형식에 해당하는지를 검사하여 그 결과를 bool 값으로 반환한다.
as 형식 변환 연산자와 같은 역할을 한다. 다만 형 변환 연산자가 변환에 실패하는 경우 예외를 던지는 반면에 as 연산자는 객체 참조를 null로 만든다는 것이 다르다.

is, as 사용 예

// is 연산자
Mammal mammal = new Dog();
Dog dog;
if (mammal is Dog)
{
    // mammal 객체가 Dog형식임을 확인했으니 안전하게 형식 변환이 이뤄짐.
    dog = (Dog)mammal;
    dog.Bark();
}

// ///////////////////////////////////////

// as 연산자
Mammal mammal2 = new Cat();
Cat cat = mammal2 as Cat;
if (cat != null)
{
    // mammal2가 Cat형식 변환에 실패하면 cat은 null이 된다.
    // mammal2는 Cat형식이므로 안전하게 형식 변환이 이뤄짐.
    cat.Meow();
}
일반적으로 형식 변환 연산자 대신 as 연산자를 사용하는 쪽이 권장된다 (빌 완그너가 지은 "Effective C#" 중).

형식 변환에 실패하더라도 예외가 일어나 갑자기 코드의 실행이 점프하는 일이 없으므로 코드 관리가 더 수월하다. 단, as 연산자는 참조 형식에 대해서만 사용이 가능하므로 값 형식의 객체는 기존의 형식 변환 연산자를 사용해야 한다.
using System;
using System.Globalization;

namespace CSharpExample
{
    class Mammal
    {
        public void Nurse()
        {
            Console.WriteLine("Nurse()");
        }
    }

    class Dog : Mammal
    {
        public void Bark()
        {
            Console.WriteLine("Bark()");
        }
    }

    class Cat : Mammal
    {
        public void Meow()
        {
            Console.WriteLine("Meow()");
        }
    }

    class MainApp
    {
        static int Main(string[] args)
        {
            Mammal mammal = new Dog();

            Dog dog = null;

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

            Mammal mammal2 = new Cat();

            // Cat으로 형 변환이 실패하면 cat 참조 형식 변수에는 null이 들어감.
            Cat cat = mammal2 as Cat;

            if (cat != null)
                cat.Meow();

            Cat cat2 = mammal as Cat;

            if (cat2 != null)
                cat2.Meow();
            else
                Console.WriteLine("cat2 is not a Cat");

            return 0;
        }
    }
}

/ * 결과

Bark()
Meow()
cat2 is not a Cat

*/
Comments