CS 기초강좌 10. 캐스팅(casting), is, as, 변환 연산자
c# 캐스팅 규칙
int n = 10;
float f = 5.5f;
f = n; //가능
n = f; //불가, 데이터 손실이 일어나는 경우 불가
데이터의 손실이 날 수 있는 경우, 캐스팅(암시적 형 변환)이 되지 않는다.
int n = 10;
float f = 5.5f;
f = n; //가능
n = (int)f; //가능 명시적 형 변환을 가능
명시적으로 (int) 를 표기시켜 형 변환하는 것은 가능하다.(데이터 손실은 당연히 발생함
is, as
다음 코드를 보자.
using System;
class Car
{
}
class Tank : Car
{
public void Fire() { Console.WriteLine("Fire!!"); }
}
class Program
{
public static void f1(Tank t)
{
t.Fire();
}
static void Main(string[] args)
{
Tank t = new Tank();
f1(t);
}
}
Car을 상속받은 Tank를 만들고 f1 함수는 Tank를 받아서 fire을 호출한다.
코드 수정을 해보자.
public static void f1(Car c)
{
c.Fire(); //불가
}
f1의 인자를 Car 로 바꾸고 싶다. 하지만 c로는 fire를 호출할 수 없다. 자동차에는 해당 기능이 없다. 캐스팅을 해야 한다.
아래 코드를 보자.
public static void f1(Car c)
{
Tank t = (Tank)c;
t.Fire();
}
명시적인 (tank) 캐시팅을 통하여, c를 t로 변환한디 fire 메소드를 호출하였다. 해당 코드는 문제없이 돌아간다. 이제는 메인 메서드를 조금 고쳐보자.
class Program
{
public static void f1(Car c)
{
Tank t = (Tank)c;
t.Fire();
}
static void Main(string[] args)
{
//Tank t = new Tank();
Car c = new Car();
f1(c);
}
}
Main 메서드에서 Tank t 대신 Car c를 생성하고 인자로 넣었다. 컴파일시 아무 문제가 없지만 실행 시, 오류를 발생시킨다.
Tank t = (Tank)c; 라인에서 오류를 발생시키다. c는 원형이 Tank로 생성되지 않아 캐스팅을 할 수 없다. 사실 f1 메서드는 인자로 Car을 넣기 때문에 사용자 입장에선 Tank 만을 생성해서 넣는다는 보장은 할 수 없을 것이다. 우리야 테스트로 만드는 것이지만,
is
캐스팅을 할 수 있는지 확인하고 가능하면 캐스팅을 하는 코드를 작성하자. 다음을 보자.
public static void f1(Car c)
{
if (c is Tank)
{
Tank t = (Tank)c;
t.Fire();
}
}
c is Tank 는 c가 변환될 수 있는 타입을 조사할 때 사용한다. Tank로 변환될 수 있는가를 질의했을 때는 위의 내용에서 Car를 넣었기 때문에 변환될 수 없고, false 반환해서 이하 코드는 수행되지 않는다.
as
이번에는 캐스팅 방법을 수정해보자. 기존 is는 제거를 해보겠다.
public static void f1(Car c)
{
Tank t1 = (Tank)c; //a
t1.Fire();
Tank t2 = c as Tank; //b
t2.Fire();
}
a, b 는 둘 다 형 변환을 할 수 있다. 캐스팅을 할 수 있다.
a와 같은 경우 형변환을 할 수 없어 실패 시 실행시간 오류를 발생시킨다. 관련 수정을 하려면 try catch 등으로 구현해야 한다.
b는 형변활을 할 수 없어 실패 시 null을 반환한다. 실행 시 t2가 null 되고 t2.fire 에서 오류가 발생될 것이다. if(t2 != null) 등으로 형변환 이후 조건 검사하여 추가 사용할 수 있을 것이다.
위 기능은 용도에 따라 사용할수 있다.
값 형식의 캐스팅
C#은 모든 형식이 Object를 상속한다고 했다. 그ᄅ면 as를 사용해서 값 형식을 넣어볼 수 있을까?
다음 코드를 보자.
n = obj; //a 불가
n = (int)obj; //b 가능
n = obj as int; //c 불가
a는 캐스팅 처리가 없으니 불가이다.
b는 명시적으로 (int) 캐스팅을 해주어서 가능이다.
c는 왜 안되는 것일까?
as는 실패시 null 반환이 되어야 한다. 값 형식은 null 받을 수가 없다. nullable을 사용하면 된다.
n = obj as int; //c 불가
int? n1 = obj as int?; //d 가능
d와 같이 사용하면 된다. 많이 사용할 일이 없을 거 같은데, 알아만 두자.
변환 연산자
using System;
class Point
{
public int x = 0;
public int y = 0;
public override string ToString()
{
return string.Format($"{base.ToString()} x:{x} y:{y}");
}
}
class Program
{
static void Main(string[] args)
{
Point pt = new Point();
int n = pt; //???
}
}
다음과 같은 코드가 있다. Main 메서드에 int n = pt는 가능할까? 사용자가 만든 객체가 int 형으로 변환될 수 있을까?
그냥은 안된다. 명시적으로 변환 연산자를 선언하면 가능해진다.
class Point
{
public int x = 0;
public int y = 0;
public override string ToString()
{
return string.Format($"{base.ToString()} x:{x} y:{y}");
}
public static explicit operator int(Point pt)
{
return pt.x;
}
}
class Program
{
static void Main(string[] args)
{
Point pt = new Point();
int n = (int)pt; //가능
}
}
public static explicit operator int(Point pt) 를 선언하고 int로 반환하고 싶은 내용을 구현한다. Main 메서드에서 int n = pt를 명시적으로 (int)형을 포기해주도록 하자.
그럼 pt = int 형은 가능할까 가능하다.
public static explicit operator Point(int n)이라고 선언해주자. 아래 코드를 보자.
class Point
{
public int x = 0;
public int y = 0;
public override string ToString()
{
return string.Format($"{base.ToString()} x:{x} y:{y}");
}
public static explicit operator int(Point pt)
{
return pt.x;
}
public static explicit operator Point(int n)
{
return new Point();
}
}
class Program
{
static void Main(string[] args)
{
Point pt = new Point();
int n = (int)pt;
pt = (Point)n;
pt = (Point)3;
}
}
위에서 적어준 함수를 적어주고 내부 구현을 한다(여기서는 대충함, 그 내용이 중요한 게 아니니) 그리고 Main 메서드에서 pt = (Point)n; pt = (Point)3; 호출해줘 보았다.
변환 연산자는 크게 코드 적용할 일이 잘 없다. 이런 게 있구나 정도로 알아두자.
'IT > C#(CS)' 카테고리의 다른 글
CS 기초강좌 9. 널 조건부 연산자, 널 접합 연산자 Elvis operator "?" "?[" "??" (14) | 2020.11.15 |
---|---|
CS 기초강좌 8. Nullable ?표현 (15) | 2020.11.14 |
CS 기초강좌 7. 값 타입과 참조 타입의 비교 연산자 == or Equality (4) | 2020.11.13 |
CS 기초강좌 6. 값 타입과 참조 타입, 스택과 힙, struct와 class (4) | 2020.11.11 |
CS 기초강좌 5. C# System.Object에 대하여 (4) | 2020.10.19 |
댓글