본문 바로가기
IT/C#(CS)

CS 기초강좌 10. 캐스팅(casting), is, as, 변환 연산자

by 신림83 2020. 11. 16.
반응형

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; 호출해줘 보았다.

 

변환 연산자는 크게 코드 적용할 일이 잘 없다. 이런 게 있구나 정도로 알아두자.

 

공부합시다.

반응형

댓글