각 자료형은 서로 변환하는 것이 가능하다. 형변환의 방법으로 암시적 형변환과 명시적 형변환이 있다.
암시적 형변환은 int -> double처럼 크기가 작은 자료형에서 큰 자료형으로 변환되는 것과 같이 컴파일러가 암시적으로 간주할 수 있는 형변환 방식이다. 반면, 이와 반대로 크기가 큰 자료형에서 작은 자료형으로 변환될 때 데이터 손실을 방지하기 위해 '개발자가 의도한 형변환'이라고 컴파일러에게 알리는 방식이 명시적 형변환 방식이다.
형변환 방식에 대한 예제는 아래와 같다.
int n = 3;
double d = 3.4;
d = n; // 암시적 형변환, int => double 손실 X, Ok!
n = d; // double => int 손실 O, Error!
n = (int)d; // 명시적 형변환, Ok!
참조타입에서 형변환이 필요한 경우가 있다. 아래 예제가 참조타입에서 형변환이 필요한 경우이다.
Animal을 상속받는 Dog 클래스에 Cry라는 함수가 있고 Animal 클래스를 매개변수로 받는 foo 함수에서 Cry를 호출하는 상황이다.
class Animal { }
class Dog : Animal
{
public void Cry() { Console.WriteLine("Dog Cry"); }
}
class Program
{
public static void foo(Animal a)
{
a.Cry(); // Error!
}
public static void Main(string[] args)
{
foo(new Dog());
}
}
매개변수로 넘어가는 객체의 실제 타입은 Dog이지만, 함수 호출은 참조하고 있는 것을 호출하기 때문에 호출되는 Cry는 Animal.Cry이다. 하지만, Animal에는 Cry가 존재하지 않아 Error가 발생한다.
위의 상황에서 사용하려면 아래와 같이 Animal변수를 Dog로 형변환 하도록 코드를 변경해야한다.
public static void foo(Animal a)
{
Dog d = (Dog)a;
d.Cry();
}
실제 값이 Animal로 넘어오는 경우, Dog로의 Casting이 불가하고 예외가 발생한다.
public static void foo(Animal a)
{
Dog d = (Dog)a; // Error!
d.Cry();
}
public static void Main(string[] args)
{
foo(new Animal());
}
is와 as를 사용하면 이런 문제를 해결할 수 있다. is는 특정 변수의 참조를 확인하는 것이 아닌 할당된 값을 확인할 때 사용한다. is를 사용하도록 코드를 수정한다면 아래 예제와 같이 수정할 수 있다.
public static void foo(Animal a)
{
if(a is Dog)
{
// 참조 변수가 가리키는 실제 타입을 확인
Dog d = (Dog)a;
d.Cry();
}
}
as는 특정 변수의 형변환을 시도하되 실패시 예외를 발생시키지 않고 null을 발생시키는 연산자이다. as를 사용하도록 코드를 수정한다면 아래와 같이 형변환한 결과가 null인지 확인함으로 안전한 형변환을 구현할 수 있다.
public static void foo(Animal a)
{
Dog d1 = (Dog)a; // 실패시 예외 발생
Dog d2 = a as Dog; // a를 Dog로 Casting하되 실패시 null을 반환한다.
if (d2 != null)
{
}
}
사용자가 정의하는 Class의 형변환은 직접 형변환 방식을 구현해주어야 한다. 아래와 같은 Point클래스가 있을 때 int와의 형변환을 예로들어 보자.
class Point
{
private int x;
private int y;
public Point(int xPos, int yPos)
{
x = xPos;
y = yPos;
}
}
class Program
{
public static void Main(string[] args)
{
double d = 3.4;
int n1 = (int)d;
Point pt = new Point(1, 2);
}
}
Point의 형변환은 int->Point와 Point->int로 두가지의 방식이 있는데, 둘다 구형주어야 한다.
대입 연산자를 기준으로 형변환하고자 하는 변수의 타입을 매개변수로 받고 어떤 방식으로 변환을 진행할지를 아래코드와 같이 구현해주면 된다. 아래 코드에서 public static explicit operator int(Point pt)가 Point->int를 구현한 것이고 public static explicit operator Point(int n)가 int->Point를 구현한 것이다.
class Point
{
private int x;
private int y;
public Point(int xPos, int yPos)
{
x = xPos;
y = yPos;
}
public static explicit operator int(Point pt)
{
return pt.x;
}
public static explicit operator Point(int n)
{
return new Point(n, n);
}
}
위 Point를 사용하는 예제는 아래와 같다. explicit으로 구현되어 있기 때문에 명시적 형변환을 사용해야 한다. 하지만, as를 사용하는 형변환 방식을 사용하면 구현한 변환연산자가 호출되지 않기 때문에 에러가 발생한다.
class Program
{
public static void Main(string[] args)
{
double d = 3.4;
int n1 = (int)d;
Point pt = new Point(1, 2);
int n2 = pt; // Error
int n3 = (int)pt; // Ok!
// explicit으로 인해 명시적으로 사용해야 함
Point pt2 = (Point)n3;
// as 연산자를 사용하면 변환연산자가 호출되지 않는다.
Point pt3 = n2 as Point; // Error!
}
}