as, is operator

캐스팅 연산자를 이용한 강제적인 형변환은 예기치못한 결과를 가져올 수 있다. 특히, 이미 정의된 C#의 몇몇 메서드는 System.Object를 다루기 때문에 레퍼런스 타입 변수는 더욱 조심히 다루어야 한다.

레퍼런스 타입의 변수에 대해서 캐스팅 연산자 (type_name)보다는, as 연산자를 쓰는 것이 좋다.

as연산자는 “피연산자인 객체가 해당 타입으로 형변환이 가능하다면 형변환”한다.
캐스팅 연산자 ()와의 차이는 다음과 같다.

comp as operator () operator
캐스팅 가능 캐스팅됨
캐스팅 불가능 null 반환 예외 발생

as연산자가 캐스팅에 실패할 경우 null을 반환하기 때문에 nullable이 아닌 int, double등은 as로 캐스팅할 수 없다. (as 연산자로 캐스팅이 가능한 변수는 nullable이거나, 레퍼런스 타입이어야 한다.)
그러나 nullable이 아닌 타입도 nullable로 바꿀 수 있다. 다음 두가지 방법을 이용한다.

1. 제네릭 클래스 System.Nullable를 이용
2. nullable이 아닌 built-in type의 뒤에 ?를 붙임

따라서 다음 코드는 작동을 잘 한다.

[code language=”csharp”]
static void Main(string[] args){
object obj = 3;
int? num = obj as int?;

Console.WriteLine(num);
}
[/code]

물론 int의 경우 상속이 불가능한 구조체이므로 쓸 용도가 딱히 object에서의 캐스팅밖에 없을 듯 하다 (…)

nullable이 아닌 타입 그대로 캐스팅을 하려면 기존에 쓰던 ()연산자를 이용해야 한다. 그러나 문제는 object같이 폭넓은 애를 해당 타입으로 캐스팅할 수 있는가에대한 문제이다. 위의 표에 나와있듯이 ()연산자는 캐스팅에 실패하면 바로 예외(InvalidCastException)를 던진다.
이러한 캐스팅 가능 여부는 is로 알아볼 수 있다. 만약 object 타입의 obj가 int로 캐스팅이 가능하다면, obj is int는 true를 반환한다. 그렇지 않으면 false를 반환한다.

[code language=”csharp”]
static void Main(string[] args){
object obj1 = new object();
object obj2 = 3;

if(obj1 is int) // false
Console.WriteLine((int)obj1);
if(obj2 is int) // true
Console.WriteLine((int)obj2);
}
[/code]

그래서 위의 코드는 obj2의 값인 3만 출력한다.

explicit, implicit

캐스팅 연산자인 ()와 as 연산자는 오버로딩이 불가능하다. 그러나 ()의 경우에는 특정 타입만 정해서 캐스팅가능하게 만들 수 있다. 그렇게 만들어주는 데에 필요한 키워드가 바로 explicit과 implicit이다.

explicit은 명시적인 형변환이 가능하도록 만든다. 반대로 implicit은 암시적 형변환이 가능하도록 만든다. 겉보기로 말하자면 ()연산자의 오버로딩은 explicit을 사용하고, =연산자의 오버로딩은 implicit을 사용한다고 생각하면 될 듯 싶다.
그리고 implicit을 구현하면 explicit은 따라오고, 다른 암시적 캐스팅도 따라오는 것 같다. 가령 int에 대한 implicit operator를 지정해놓으면, 다른 정수형(byte, short, long) 변수에 대입해도 아무런 문제가 없다. (물론 int를 지정해놓으면 byte나 short으로의 캐스팅은 각각의 MAX_VALUE와 MIN_VALUE사이에 있는 값이어야 한다.)

[code language=”csharp” highlight=”26,27,28,29,31,32,33,34″]
using System;

namespace CSharpConsole
{
class Program
{
static void Main(string[] args)
{
MyDouble myDouble = Math.PI; // 31~34
double pi = myDouble; // 26~29

Console.WriteLine(pi);
}
}

class MyDouble
{
private double num;
public double Num
{
get { return num; }
set { num = value; }
}

public static implicit operator double(MyDouble m)
{
return m.num;
}

public static implicit operator MyDouble(double d)
{
return new MyDouble(d);
}

public MyDouble(double n = 0)
{
num = n;
}
}
}
[/code]

위 코드는 아무런 문제없이 원주율을 출력한다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다