기초부터 문법을 공부하던 중 예제를 기반으로 형변환에 대해 간단히 살펴보겠다.
형변환이란
변수를 다른 타입으로 옮기는 것
형 변환이 필요한 이유
void Start()
{
int num1 = 0;
for (int i = 0; i < 10_000_000; i++)
{
num1 += 1;
}
float num2 = 0f;
for (int i = 0; i < 10_000_000; i++)
{
num2 += 1.0f;
}
}
위에 코드를 살펴보면 같은 fot문을 돌려 int 자료형에 천만번 계산을 했을 때와 float 자료형에 천만번 계산을 했을 때 걸리는 시간이 다른 것을 알 수 있다. 결과를 봤을 때 정수형 자료형으로 사용할 수 있는 것을 실수형 자료형으로 계산한다면 큰 차이는 아니지만 비효율적인 시간이 든다.
정수 자료 변환
void TypeCast1()
{
sbyte a = 127;
Debug.Log(a);
int b = (int)a;
Debug.Log(b);
int c = 128;
Debug.Log(c);
sbyte d = (sbyte)c;
Debug.Log(d);
}
위 코드를 실행시키면 다음과 같은 결과가 나온다
d는 왜 128이 아닌 -128로 나오는 것일까? 생각해보면 선언한 sbyte가 표현할 수 있는 수는 -128 ~ 127까지므로 c의 값인 128을 표현할 수 없기 때문이다. 이는 데이터 형식의 최대값보다 큰 값을 넣을 경우 흘러 넘치는 "오버 플로우" 현상이다.
void TypeCast2()
{
int a = 200;
Debug.Log(a);
uint b = (uint)a;
Debug.Log(b);
int c = -30;
Debug.Log(c);
uint d = (uint)c;
Debug.Log(d);
}
d는 왜 -30이 아닌 말도 안되는 숫자가 나올까? uint는 양수만 표현할 수 있다. c값인 -30을 uint로 변환할 경우 데이터 형식의 최소값보다 작은 -30이 들어갔기 때문에 데이터를 넣지 못하고 넘치는 "언더 플로우"가 발생한다.
실수를 정수로 변환
void TypeCast3()
{
float a = 0.9f;
int b = (int)a;
Debug.Log(b);
float c = 1.1f;
int d = (int)c;
Debug.Log(d);
}
실수를 정수로 변환한다면 내림이 발생한다.
숫자를 문자열로, 문자열을 숫자로 변환
void TypeCast4()
{
int a = int.Parse("12345");
Debug.Log($"a : {a}");
float b = float.Parse("123.45");
Debug.Log($"b : {b}");
int c = 13245;
string d = c.ToString();
Debug.Log($"d : {d}");
float e = 123.45f;
string f = e.ToString();
Debug.Log($"f : {f}");
}
int.Parse(문자열)을 이용해 문자열을 숫자로 변환
숫자.ToString()을 이용해 숫자를 문자열로 변환
게임을 제작할 때 많이 사용되는 형변환이다. 예를 들어 UI 텍스트에 있는 골드를 가져와서 더하거나 빼줄 때 텍스트를 가져와 int.Parse()를 사용하여 숫자로 변환 후 연산을 한다.
클래스 형변환
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TypeCasting2 : MonoBehaviour
{
class Monster
{
public void Shout()
{
Debug.Log("Shout");
}
}
class Orc : Monster
{
}
class Troll : Monster
{
}
void Start()
{
Monster monster = new Monster();
Orc orc = (Orc)monster;
orc.Shout();
}
}
new Monster()를 이용해서 monster 객체를 만든 후 (Orc)monster 자식 클래스로 형변환을 하였다. 그리고 orc.Shout()를 실행하면 코드상에선 문제가 없지만 에러가 뜬다. 자식 클래스는 부모 클래스의 함수를 사용할 수 있다. 하지만 orc 인스턴스에서 왜 부모가 가지고 있는 Shout()가 실행이 되지 않을까? 설명하자면 자식 클래스는 부모 클래스 멤버, 함수 + 자기자신 멤버, 함수를 사용할 수 있다. 하지만 부모 클래스는 자식 클래스의 기능을 사용하지 못한다. monster는 부모 클래스로 생성된 인스턴스이다. 즉 자식 클래스에 내용을 알고 있지 않는데 자식 클래스로 형변환을 해버리면 에러가 난다.
결론적으로 쉽게 이해하려면 부모 클래스에서 자식 클래스로 형변환이 안된다.
그럼 반대로 자식 클래스로 인스턴스를 생성하고 부모 클래스로 형변환을 해 Shout()를 실행해보자
void Start()
{
Orc orc = new Orc();
Monster monster = (Monster)orc;
monster.Shout();
orc = (Orc)monster;
orc.Shout();
}
위 코드에서는 자식 클래스 Orc를 통해 orc 인스턴스를 생성하고 부모 클래스로 형변환을 한 뒤, 다시 자식 클래스로 형변환을 한다. 이것이 되는 이유가 뭘까? Orc 클래스는 부모 클래스의 기능을 포함하고 있는 클래스이다. 즉 부모 클래스보다 더 넓은 자료형이기 때문에 부모 클래스로 형변환이 가능하고, 다시 자식 클래스로 형변환이 가능하다.
여기서 이해가 안되는 것이 있었다. 부모 클래스로 형변환을 마치고 다시 자식 클래스로 형변환을 할 때 첫 번째 코드에선 안됐던 Shout()가 어떻게 실행될 수 있을까? 이는 클래스는 참조 개념이라는 것을 통해 이해했다.
Monster monster = new Monster() 코드를 봤을 때 monster는 new Monster()로 생성된 인스턴스를 가르키는 주소값이다.
하지만 그 인스턴스에는 Orc 클래스의 기능이 없으므로 (Orc 클래스보다 더 작은 자료형이므로) 형변환이 안된다. 반대로
Orc orc = new Orc()를 실행하면 orc는 new Orc()로 생성된 인스턴스(자식과 부모를 포함하는 객체)를 가르키는 주소값이 되므로, 부모 클래스로 형변환을 해도 생성된 인스턴스에는 자식 클래스의 기능이 있어 다시 자식 클래스로 형변환이 가능하다라는 것이 내 생각이다.
is, as 키워드
is 키워드 예시
void Start()
{
Monster monster = new Monster();
Orc orc = new Orc();
if (orc is Monster) // orc가 Monster자료형으로 형변환이 가능한가
{
monster = (Monster)orc;
monster.Shout();
}
}
is 키워드는 실제로 형변환을 하는 것이 아니고 형변환을 할 수 있는지 확인 후 true / false를 반환한다.
as 키워드 예시
void Start()
{
Monster monster = new Monster();
Orc orc = new Orc();
monster = orc as Monster; // monster = (Monster)orc;
if(monster != null)
monster.Shout();
}
위 코드를 보면 실질적인 느낌은 monster = orc as Monster와 monster = (Monster)orc가 똑같다. 그럼 as는 왜 필요할까?
아까 작성했던 위 코드를 예로 들자면 코드상으론 에러가 나지 않지만, 실행했을 경우 에러가 발생하고 프로그램이 멈추게 된다. 이를 보완하기 위해서 as를 사용하는 것이다.
as는 형변환을 할 수 있는 확인 후 가능하다면 형변환을 하고, 불가능하다면 null 값을 넣는다.
monster = orc as Monster를 하였을 때 형변환이 된다면 밑에 코드를 실행시켜 Shout()가 실행되고, 반대의 경우라면 monster에 null을 대입하므로 아무것도 실행하지 않는다. 결론적으로 as를 사용하는 이유 형변환에 실패하더라도 프로그램이 멈추지 않고 좀 더 안정적으로 실행된다.
as 사용시 주의점
null을 반환하기 때문에 값 타입 말고 참조 타입일 때 사용해야 한다.
지금까지 위에서 다룬 형변환은 직접 형변환을 명시한 명시적 형변환이다. 하지만 자동으로 형변환 처리를 해주는 암시적 형변환도 있다.
간단한 참고 자료
[C# 기초] 형변환 (암시적, 명시적 형변환, overflow)
1. 형변환 어떤 자료형으로 선언된 변수를 다른 자료형으로 변환하는 것을 형변환이라고 한다. 이 때, 코드에 직접 변환 될 자료형을 입력해야 하는 것을 '명시적 형변환'이라고 하고, 변환 될 자
killu.tistory.com
동영상 강의 참고 자료
https://www.youtube.com/watch?v=ieDgYGBT5Qc&t=173s
'Unity C# > 개념 및 문법 정리' 카테고리의 다른 글
[Unity C#] 객체 지향 (클래스, 추상화) (1) | 2025.01.13 |
---|---|
[Unity C#] C# 2.0 제네릭(Generic) 기초 (0) | 2025.01.11 |
[Unity C#] C# 1.0 컬렉션 (1) | 2025.01.10 |
[Unity C#] 메모리 구조와 GC(Garbage Collection) (1) | 2025.01.09 |
[Unity C#] 의존성 주입 (Dependency Injection) DI (1) | 2025.01.05 |