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]
위 코드는 아무런 문제없이 원주율을 출력한다.
답글 남기기