개발/C#

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

huiyu 2021. 1. 16. 06:00

C#에서의 형 변환

- as

object o = Factory.GetObject();

// as 변환
MyType t = o as MyType;

if (t != null)
{
  //MyType 타입의 t 객체 사용
}

else
{
  //형변환 실패 시
}

- cast(강제 형 변환)

object o = Factory.GetObject();

// cast 변환
try
{
  MyType t;
  t = (MyType) o;
  //MyType 타입의 t 객체 사용
}
catch (InvalidCastException)
{
  //형변환 실패 오류
}

=> as를 사용할 경우 작성하기 더 쉽고 읽기도 편하다.  try/catch 문이 없기 때문에 성능도 더 좋다.

as와 cast의 동작 차이- '사용자 정의 형변환을 어떻게 다루는가.'

- as, is : 런타임에 객체 타입을 확인하기 필요한 경우 박싱을 수행. 객체를 다른 타입으로 형변환하려면 이 객체는 지정한 타입이거나 혹은 지정한 타입을 상속한 타입이어야 한다. 그 외의 경우는 모두 실패

- 캐스팅 : 타입 변환시 형 변환 연산자가 개입한다. 대표적인 형변환 연산자는 숫자 타입에 대한 형 변환 연산자 개입이다. long->short타입으로 캐스팅하면 일부정보를 잃을 수 있는데 이러한 변환이 강제 형변환 연산으로 발생할 수 있는 문제이다.

형변환 연산자는 아래 두 MS문서를 참고해보면 된다.

 * MS document - 사용자 정의 전환 연산자(User-defined conversion operators)

  MS document - Conversions

SecondType에 사용자 정의 형변환 연산자를 선언한다.

public class SecondType
{
  private MyType _value;
  // (중략)
  
  //형변환 연산자
  public static implicit operator MyType(SecondType t)
  {
    return t._value;
  }
}

위 정의한 형변환연산자 함수의 유무에 따라 아래 코드의 두번째 줄의 명시적 형변환이 빌드가 될지, 안될지를 결정된다.(컴파일 시점 결정)

SecondType secondType = GetSecondType();
Mytype t = (MyType)secondType; // 형변환 연산자에 따라 빌드

 is, as의 경우엔 형변환연산자 유무와는 상관없이 MyType이거나 MyType을 상속받은 타입인 경우에만 정상적으로 컴파일 되게 된다. 

SecondType secondType = GetSecondType();
Mytype t = secondType as SecondType; // 본인, 상속관계인 경우만 빌드

 

아래는 문제상황의 코드이다.

object o = Factory.GetObject();//*SecondType을 반환

//ver1 - as 사용
MyType t = o as MyType; //o가 MyType, MyType을 상속받은 경우가 아니면 실패

if (t != null)
{
  //MyType 타입 t객체 사용
}
else
{
  //실패
}


//ver 2 - cast 사용
try
{
  MyType t1;
  t1 = (MyType)o; //o가 MyType이 아니면 실패
  //MMyType 타입 t 객체 사용
}
catch (InvalidCastException)
{
  // 형변환 요류
}

위 코드는 두가지 버전 모두 형변환에 실패한다.

- as의 경우, 형변환 연산자와 상관없이 MyType or MyType을 상속받은 경우에만 형변환이 되기 때문에 이 경우는 실패한다.

- 두번째 cast의 경우엔 o의 값에 SecondType을 반환하고 있어 사용자 정의 연산자에 의해 MyType으로 변환하는 것이 가능해 보이나 컴파일 시점에 Object로 결정되어 Object가 MyType으로 변환이 가능한지만 판단하는 코드만 생성한다. 런타임 시점에는 o가 SecondType이므로 SecondType은 MyType이 아니기 때문에 실패한다.

-----------------

**이부분이 이해가 잘안되서 한참을 몇번씩 읽어보게 됐다. 쉬운챕터라고 생각했는데 말이 너무 이해가 안되는게 많은 챕터이다;

당연히 object라서 SecondType의 cast가 안될거라 생각하고 읽었는데,
만약 내가 구현시에 SecondType에 형변환 연산자를 재정의하고 object o의 값이 SecondType을 반환한다면
SecondType의 재정의된 연산자로 계산될거란 착각을 할 수도 있을거 같다. 

이러한 경우를 막을 수 있게 언제나 일관된 동작을 하는 is, as 사용을 하는게 좋다.

그러니 결론은 보기도 좋가 동작에 오해도 없는 is, as를 사용하자.인듯?

-----------

is/as와 cast 정리

1. 형변환 시 is, as를 사용하는 것이 더 안정적이고 런타임에 효율적이다.
2. is/as를 사용하여 try/catch문을 없앨 수 있어 코드가 간결하며 부하가 줄어든다.
3. value type 변환은 cast가 필요하다. is,as의 경우 실패하면 null을 반환하는데 value type은 null을 넣을 수 없다.
4. 사용자 정의변환(user-defined operator)은 cast만 가능하다.
  cast연산자는 컴파일 타임 코드를 생성하기 때문에 컴파일시점에 선언된 타입으로 결정한다. 런타임 시점의 타입은 모른다. 

 

말이 어려운데 결론은 value type/사용자 정의 변환을 제외한 경우에는 is/as변환을 사용하는게 좋다.

 

[참고]

Effective C# item3: 캐스트보다는 is,as가 좋다.

728x90
반응형