머리말
C# 언어는 다른 개발 언어와는 다르게 AS, IS 연산자가 존재한다. 객체를 형변환할 때 개발자의 논리적 오류를 방지할 목적으로 지원되는 예약어인데 숙지하면 매우 유용한 연산자다. 본 포스팅에서는 AS 연산자와 IS 연산자의 개념과 필요성, 활용 방법에 대해서 소개한다.
AS, IS 연산자의 개념
기본 자료형 int, bool, string 등 타입 외에도 클래스 간 캐스팅 작업이 필요한 경우가 있다. 예를 들어 상속관계에 있는 두 클래스에서 자식 클래스는 부모 클래스에 대입될 수 있지만 부모 클래스는 자식 클래스에 대입될 경우 아래와 같이 오류가 발생한다.
암시적으로 'ConsoleApp1.Program.Wookoa' 형식을 'ConsoleApp1.Program.WookoaTistory' 형식으로 변환할 수 없습니다. 명시적 변환이 있습니다. 캐스트가 있는지 확인하세요.
using System;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Wookoa wookoa = new Wookoa();
WookoaTistory tistory = new WookoaTistory();
tistory = wookoa; //오류 발생
}
class Wookoa
{
string _name = "Wookoa";
public void NamePrint()
{
Console.WriteLine(_name);
}
}
class WookoaTistory : Wookoa
{
String _title = "Wookoa:Tistory";
public void TitlePrint()
{
Console.WriteLine(_title);
}
}
}
}
위의 소스를 간략히 설명하자면 Wookoa 클래스를 상속받아서 WookoaTistory 클래스를 정의했다. Main 클래스에서는 부모 클래스인 wookoa 인스턴스를 자식 클래스인 tistory 인스턴스에 대입을 시도했다. 오류가 발생하는 것은 당연하다. WookoaTistory 클래스는 Wookoa 클래스를 상속받아서 필드 및 메서드를 추가로 정의했다. 반대로 생각하면 Wookoa 클래스를 대입할 경우 WookoaTistory 클래스에서 추가로 정의한 필드 및 메서드는 논리적인 오류가 당연하다.
이런 상황에서 오류를 회피하기 위해서는 아래와 같이 명시적으로 캐스팅 연산자를 사용할 텐데 그럼에도 불구하고 컴파일 단계에서 오류를 뱉어낸다.
System.InvalidCastException: ''Wookoa' 형식 개체를 'WookoaTistory' 형식으로 캐스팅할 수 없습니다.'
...
static void Main(string[] args)
{
Wookoa wookoa = new Wookoa();
WookoaTistory tistory = new WookoaTistory();
tistory = (WookoaTistory)wookoa;
}
...
AS, IS 연산자의 필요성
위의 글까지 이해했다면 불쾌할 수 있다. 어차피 오류를 뱉어낸다면 캐스팅 연산자를 사용할 수 없도록 막으면 될 일 아닌가 싶다. 아래의 예와 같이 개발자에 의해서 의도적으로 캐스팅되는 경우를 소개한다. 결론부터 말하자면 부모 클래스를 자식 클래스에 대입하는 경우가 필요하고 그러한 행위를 도와주는 것이 AS, IS 연산자다.
using System;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Wookoa wookoa = new Wookoa();
WookoaTistory tistory = new WookoaTistory();
WookoaTistory tistory2 = new WookoaTistory();
wookoa = tistory;
tistory2 = (WookoaTistory)wookoa;
}
class Wookoa
{
string _name = "Wookoa";
public void NamePrint()
{
Console.WriteLine(_name);
}
}
class WookoaTistory : Wookoa
{
String _title = "Wookoa:Tistory";
public void TitlePrint()
{
Console.WriteLine(_title);
}
}
}
}
위의 소스를 설명하자면 기존의 소스에서 달라진 점은 자식 클래스를 부모 클래스에 대입한 뒤 다시 부모 클래스를 자식 클래스에 대입한다는 점이다. 이때 사용한 것이 캐스팅 연산자 (WookoaTistory)를 사용했다. 위의 소스는 문제없이 작동한다.
클래스는 참조형 변수 타입이기 때문에 주소값을 저장한다는 사실을 기억하면, 먼저 자식 클래스의 주소값을 부모 클래스에 저장한다. 두 번째로 부모 클래스의 값을 명시적으로 형변환해서 제2의 자식 클래스에 저장한다. 여기서 부모 클래스에 저장된 주소 값은 원래 자식 클래스를 가리키던 주소값이기 때문에 컴파일 단계에서도 오류를 뱉어내지 않는다.
단지 wookoa, tistory, tistory2 인스턴스들은 주소값만 주고받은 것이며, 실제로 컴파일할 때 tistory2의 메모리 공간에는 tistory 인스턴스를 가리키는 주소값이 저장되어 있을 것이다. 만약 tistory2 인스턴스가 최종적으로 가리키는 주소 값이 wookoa 인스턴스와 같이 부모 클래스인 경우에는 오류를 뱉어낼 것이다.
AS, IS 연산자의 활용
조금은 억지스러울 수 있겠지만 위의 상황과 같이 부모 클래스를 자식 클래스에 대입하는 경우에 자칫하다가 오류를 발생시킬 수 있다. 오류를 발생시키는 것은 닷넷 환경에서 내부적으로 큰 부하가 발생한다. 그렇기 때문에 오류를 발생시키지 않고 형변환이 가능한지 여부를 체크하기 위한 기능이 필요했다. 그로 인해 AS, IS 연산자가 탄생하게 되었다.
AS 연산자
형변환이 가능하면 형변환을 수행하고, 그렇지 않으면 null 값을 대입하는 연산자다. 아래와 같이 형변환이 가능한지 여부를 체크해서 프로그램을 분기시킬 수 있다. 따라서 형변환이 가능한 경우에만 캐스팅 연산자로 대입시킬 수 있게 된다. 참고로 AS 연산자는 참조형 변수에 대해서만 적용할 수 있으며 참조형 타입으로만 체크가 가능하다.
using System;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Wookoa wookoa = new Wookoa();
WookoaTistory tistory = new WookoaTistory();
tistory = wookoa as WookoaTistory;
if (tistory == null)
Console.WriteLine("형변환 불가능!");
else
Console.WriteLine("형변환 가능!");
}
class Wookoa
{
string _name = "Wookoa";
public void NamePrint()
{
Console.WriteLine(_name);
}
}
class WookoaTistory : Wookoa
{
String _title = "Wookoa:Tistory";
public void TitlePrint()
{
Console.WriteLine(_title);
}
}
}
}
IS 연산자
형변환이 가능한 여부를 불린 형으로 결과 값을 반환한다. AS 연산자와 IS 연산자의 사용 기준은 명확하다. 형변환된 인스턴스가 필요하면 AS 연산자 그렇지 않으면 IS 연산자를 사용하면 된다. MSDN에 따르면 IS 연산자는 C# 7.0부터 지원하며 대부분 시나리오에서는 AS 연산자보다 IS 연산자를 먼저 사용해서 객체의 값을 할당하는 것이 더 적합하다고 한다. 아래의 소스와 같이 캐스팅 여부를 먼저 판단한 뒤 가능할 경우에만 인스턴스를 생성할 수 있기 때문이다.
using System;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Wookoa wookoa = new Wookoa();
if (wookoa is WookoaTistory)
{
Console.WriteLine("형변환 불가능!");
}
else
{
Console.WriteLine("형변환 가능!");
WookoaTistory tistory = (WookoaTistory)wookoa;
}
}
class Wookoa
{
string _name = "Wookoa";
public void NamePrint()
{
Console.WriteLine(_name);
}
}
class WookoaTistory : Wookoa
{
String _title = "Wookoa:Tistory";
public void TitlePrint()
{
Console.WriteLine(_title);
}
}
}
}
꼬리말
다른 언어에 비해 조금은 무거울 수 있는 C# 언어는 친절하게도 캐스팅에 대해 세심한 기능을 제공한다. AS/IS 연산자가 없어도 프로그래밍에 큰 문제는 없겠지만, 조금 더 간결하고 안전한 코딩을 위해서 반드시 이해하면 좋을 것 같다. AS/IS 연산자의 개념과 필요성 그리고 활용 방법에 대해서 소개한 본 포스팅은 이로써 마무리를 짓도록 한다.
소중한 댓글 (0)