[Effective C#] 캐스트보다는 is, as가 좋다

2024. 7. 29. 23:13·독서/Effective C#


01. Cast ?

캐스트(Cast)는 데이터 타입을 다른 데이터 타입으로 변경하는 과정을 의미한다. 형변환이라고도 한다.


02. 명시적 캐스팅(Explicit Casting)

일반적으로 프로그래밍을 처음 배울 때 사용되는 캐스팅은 다음과 같다.

int num = 54;
object numObject = (object)num;

 

 

위 코드는 int 타입의 변수를 object 타입으로 캐스팅한다. 위와 같이 캐스팅하는 방식을 명시적 캐스팅이라고 하며 컴파일에서 과정을 처리하기 때문에 올바르지 않은 캐스팅을 진행할 경우 에러 코드를 만날 수 있다.

 


03. is와 as 키워드를 통한 캐스팅, 안전한 캐스팅(Safe Casting)

C#에서 제공하는 is와 as 키워드를 통해서도 캐스팅을 진행할 수 있다. is 키워드는 객체의 타입을 확인하는 데에 사용되며, as 키워드는 특정 타입으로 변환을 시도하며 실패했을 경우 null을 반환한다. 사용 방법은 다음과 같다.

// 이 코드는 obj 변수가 string 타입이 맞는지 확인한다.
string obj = "Hello, World!";

if(obj is string)
    Console.WriteLine("문자열이 맞음");
else
    Console.WriteLine("문자열이 아님");
    
// 이 코드는 as 키워드를 통해서 캐스팅을 진행한다.
object obj = "Hello, World!";
string str = obj as string;

if(str == null)
    Console.WriteLine("캐스팅에 실패했습니다.");
else
    Console.WirteLine("캐스팅에 성공했습니다.");

 

is/as 키워드는 런타임에서 실행된다. 실패 시 예외를 발생시키지 않기 때문에 코드가 중단되지 않고 계속 실행된다. 변환 실패 여부는 null 확인을 하는 코드를 통해서 확인해야 한다.

 


04. 명시적 캐스팅과 안전한 캐스팅

public class SecondType
{
    private MyType _value;

    public static implicit operator MyType(SecondType t)
    {
        return t._value;
    }
}

// Factory 클래스의 GetObject 메서드는 SecondType 객체를 반환한다.
object o = Factory.GetObject();

// 안전한 캐스팅을 사용한 코드 -> 결과 : 실패
MyType t = o as MyType;
if(t != null) {}
else
    // 오류 발생

// 명시적 캐스팅을 사용한 코드 -> 결과 : 실패
MyType t1;
t1 = (MyType)o;

 

위 코드를 통해서 명시적 캐스팅과 안전한 캐스팅을 다뤘다. 결과는 두 코드는 실행되지 않는다. 명시적 캐스팅에서는 사용자 정의 형변환 연산자의 사용이 가능하다. 그렇기 때문에 실행이 되어야 할 것으로 보이지만, 컴파일러는 컴파일 타임에 선언된 타입을 추적한다. 런타임에 어떤  타입이 오는지 알지 않기 때문에 실패하게 된다.

 

컴파일러는 위 코드에서 object 타입의 변수 o가 런타임에 어떤 타입인지 알지 못한다. 단순히 object 타입으로 확인하고 object 타입에서 MyType 타입으로 형변환이 가능한지 확인한다. 하지만 변수 o는 SecondType이고 우리는 SecondType을 MyType으로 형변환이 가능하게 정의한 적이 없기 때문에 코드 실행이 실패한다.

 


05. 안전한 캐스팅의 테스트

책을 읽다가 내용이 조금 이해가지 않아서 한 가지 테스트를 진행해 봤다. 안전한 캐스팅이 런타임에서 진행된다면 안전한 캐스팅을 사용한 코드는 정상적으로 실행되어야 하는 것이 아닌가? 코드가 실행되는 시점에서 object 타입의 변수 o는 SecondType 타입이기 때문에 문제없이 MyType 타입의 변수 t는 SecondType으로 나타나야 한다고 생각했다.

 

public class MyType
{
    public int num = 0;

    public MyType(int num)
    {
        this.num = num;
    }
}

public class SecondType : MyType
{
    public SecondType(int num) : base(num)
    {
    }
}

public class Factory
{
    public SecondType secondType;
    public SecondType GetObject()
    {
        return secondType;
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        SecondType secondType = new SecondType(50);
        
        Factory factory = new Factory();
        factory.secondType = secondType;

        object o = factory.GetObject();
        MyType t = o as MyType;
        
        Console.WriteLine(t.GetType());
    }
}

 

위 코드가 저자의 환경과 동일하지 않겠지만 상속 관계에 있는 상태에서 as 키워드는 사용이 가능하다는 것을 확인했다. 단, SecondClass 타입을 작성할 때 MyType 타입과 SecondType 타입은 서로 상속 관계가 아님을 알았다.

 

public class MyType
{
    public int num = 0;

    public MyType(int num)
    {
        this.num = num;
    }
}

public class SecondType
{
    private MyType _value;

    public void SetType(MyType type)
    {
        _value = type;
    }
    
    public static implicit operator MyType(SecondType value)
    {
        return value._value;
    }
}

public class Factory
{
    public SecondType secondType;
    public SecondType GetObject()
    {
        return secondType;
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        SecondType secondType = new SecondType();
        secondType.SetType(new MyType(50));
        
        Factory factory = new Factory();
        factory.secondType = secondType;

        object o = factory.GetObject();
        o = (MyType)o;
        
        Console.WriteLine(o.GetType());
    }
}

 

위 코드는 명시적 캐스팅을 사용했고 MyType 타입과 SecondType 타입이 서로 상속 관계에 있지 않은 상태에서 작성이 되었다. 

 

 

다음과 같이 에러를 만날 수 있었다. 명시적 캐스팅이 컴파일에서 정해진다면 당연한 결과다. 안전한 캐스팅으로 확인을 하기 위해서는 object 타입 변수 o를 선언한 아래 부분만 변경하면 된다.

 

// 이전
o = (MyType)o;
// 변경
o = o as MyType;

 

이번에는 명시적 캐스팅으로 진행했을 때와 달리 캐스팅에 실패해서 null이 들어갔다는 것을 확인할 수 있다.

이제, 다음 코드와 같이 책에 나와있는 방법을 통해서 형변환을 진행한다면 성공적으로 MyType 타입으로 변경된 것을 볼 수 있다.

 

object o = factory.GetObject();
SecondType st = o as SecondType;
MyType t = (MyType)st;

// MyType이 출력된다!
Console.WriteLine(t.GetType());

 


06. 안전한 캐스팅을 사용할 수 없는 경우

as 키워드는 값 타입으로 변경할 때 사용할 수 없다. 값 타입은 null이 될 수 없기 때문이다. 이런 경우에는 null이 허락되는 nullable 타입을 사용해서 as 키워드를 사용하는 것이 좋다.

 

 

'독서 > Effective C#' 카테고리의 다른 글

[Effective C#] nameof() 연산자를 적극 활용하라  (0) 2024.08.01
[Effective C#] 문화권별로 다른 문자열을 생성하려면 FormattableString을 사용하라  (0) 2024.07.31
[Effective C#] string.Format()을 보간 문자열로 대체하라  (0) 2024.07.30
[Effective C#] const보다는 readonly가 좋다.  (1) 2024.07.24
[Effective C#] 지역변수를 선언할 때는 var를 사용하는 것이 낫다  (1) 2024.07.23
'독서/Effective C#' 카테고리의 다른 글
  • [Effective C#] 문화권별로 다른 문자열을 생성하려면 FormattableString을 사용하라
  • [Effective C#] string.Format()을 보간 문자열로 대체하라
  • [Effective C#] const보다는 readonly가 좋다.
  • [Effective C#] 지역변수를 선언할 때는 var를 사용하는 것이 낫다
태역
태역
  • 태역
    RYULAB
    태역
  • 전체
    오늘
    어제
    • 분류 전체보기
      • 언어
        • C
        • C++
        • C#
      • 엔진, 프레임워크
        • Unity
        • Unreal
        • Electron
      • 공부
        • 디자인 패턴
        • 수학
        • CS
        • Git
        • 알고리즘
        • 자료구조
      • 코테
        • 프로그래머스
        • 백준
      • 독서
        • Effective C#
        • CLR via C#
        • 뇌를 자극하는 윈도우즈 시스템 프로그래밍
        • 오브젝트
        • CSAPP
        • OSTEP
      • 프로젝트
        • Unity
      • 개발 일지
        • 퓨처리티
        • 골든타임
      • 활동
        • 게임잼 후기
        • 게임제작동아리 브릿지
        • 크래프톤 정글
        • 기타
      • 기타
  • 블로그 메뉴

    • 링크

    • 공지사항

      • 2024 04 17
    • 인기 글

    • 태그

      인프런 #인프런강의후기 #게임개발 #게임개발강의 #인강후기 #강의후기 #게임개발자 #인프런강의
      티스토리챌린지
      오블완
    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.3
    태역
    [Effective C#] 캐스트보다는 is, as가 좋다
    상단으로

    티스토리툴바