본문 바로가기

C# 프로그래밍/문법 개념

[C#] 사용자 정의 전환 연산자 (Conversion operator)

반응형

사용자 정의 전환 연산자란?

어떠한 형식에서 다른 형식으로 변환할 때 사용자가 지정한 방식으로 암시적 혹은 명시적으로 변환을 정의하겠다는 의미이다. 의미가 모호할 수 있으니 좀 더 구체적인 예를 들어야 할 것 같다.

 

그전에 형식 변환에 대해 먼저 짚고 넘어가야 한다.


형식 변환이란?

우리는 형식 변환을 할 때 업 캐스팅과 다운 캐스팅 등을 캐스트 연산자와 함께 실행한다.

 

예를 들면 다음과 같은 상황에서

int iNumber = 5;
float fNumber = 5.5f;

fNumber = iNumber;
Debug.Log(fNumber);

iNumber를 fNumber에 담는 것은 int에서 float으로 형식 변환이 이루어졌으며 업 캐스팅이다. 이러한 업 캐스팅은 아무런 에러를 내뱉지 않는다. 그리고 암시적(암묵적)으로 이뤄진다. int인 iNumber는 정수이고 float인 fNumber는 실수이므로 좁은 범위의 정수에서 더 넓은 범위인 실수로의 확장은 암묵적으로 넘어가겠다는 뜻이다.

 

반면

iNumber = fNumber

이러한 상황은 "암시적으로 'float'형식을 'int'형식으로 변환할 수 없습니다"라는 에러가 발생한다. 더 넓은 범위인 실수에서 정수로 변환하려고 하기 때문에 어떤 문제점이 생길 수도 있음을 경고하는 것이다. 

 

이때, 우리는

iNumber = (int)fNumber;
Debug.Log(fNumber);

(int) 같은 캐스트 연산자를 통해 명시적으로 형 변환(다운 캐스팅)을 해주게 되는 것이다. 이렇게 하면 컴파일러가 실수형을 정수형으로 변환하는 것을 알아차리고 소수점 부분을 날려버리며 정수로 바꿔버린다.

 

그런데 만약 int와 float 같은 기본형이 아니라 사용자가 만든 클래스 간에 형식 변환이라면 어떻게 해야 할까?

 

.Net에서는 이러한 변환 역시도 정의할 수 있게 해 놓았다.


어떻게 사용할 수 있을까?

기본적인 문법은 다음과 같다.

public static implicit operator [변환하고 하는 형식]([변환 하려는 형식])

 

여기서 operator는 연산자 오버로드에 필요한 키워드이다.

 

이제 코드를 통해 구체적인 예시를 들어보도록 하겠다.

public class Water
{
    public float liters;
    
    public Water() {}

    public Water(float _liters)
    {
        liters = _liters;
    }

    public void AddFruit()
    {
        //Something...
    }

    public static implicit operator Juice(Water _water)
    {
        _water.AddFruit();
        Juice newJuice = new Juice();
        newJuice.liters = _water.liters * 1.1f;

        return newJuice;
    }
}

위 스크립트는 Water클래스이다. 멤버 변수로 liters를 갖고 있고 물의 용량을 나타낸다.

 

아래는 Juice클래스 이다.

public class Juice
{
    public float liters;
    
    public Juice() {}

    public Juice(float _liters)
    {
        liters = _liters;
    }

    public void Evaporation()
    {
        liters *= 0.4f;
    }

    public static explicit operator Water(Juice _juice)
    {
        _juice.Evaporation();
        Water newWater = new Water();
        newWater.liters = _juice.liters;

        return newWater;
    }
}

마찬가지로 주스의 용량을 알려주는 liters 변수가 있다.

 

우리는 여기서 물을 주스로 바꾸는 것은 물과 과일을 간단히 섞으면 되지만, 주스를 물을 바꾸는 것은 어렵다고 가정하자.

그래서 물을 주스로 바꾸는것은 문제가 발생할 여지가 없지만 주스를 물로 바꾸는것은 문제가 발생할 수 있다.

 

물을 주스로 바꾸는 과정을 코드로 나타내면 다음과 같다.

Water pureWater = new Water();
pureWater.liters = 1.5f;

Juice handMadeJuice = pureWater;
Debug.Log(handMadeJuice.liters);

Water 클래스를 생성하고 업 캐스팅하듯이 변수에다 집어넣으면 된다.

 

아래의 문장을 보자.

public static implicit operator Juice(Water _water)
{
    _water.AddFruit();
    Juice newJuice = new Juice();
    newJuice.liters = _water.liters * 1.1f;

    return newJuice;
}

implicit과 operator 키워드를 사용해서 Water에서 Juice로 형식 변환을 암시적으로 할 때 어떠한 과정을 거치게 될지 정의해두면 해당 과정을 모두 거친 Juice를 반환한다.

 

로그 결과

로그를 찍으면 1.65라는 결과가 나온다.

pureWater의 liters가 1.5였지만 형식 변환을 하며 1.1이 곱해졌기 때문에 1.65가 된 것이다.

 

반대로 Juice를 Water로 바꾸어 보자.

Juice manufacturedJuice = new Juice();
manufacturedJuice.liters = 1.5f;

Water extractedWater = (Water)manufacturedJuice; //혹은 Juice handMadeJuice2 = new Water(1.2f); 도 가능
Debug.Log(extractedWater.liters);

 

주스를 물로 바꾸는 것은 프로그래머가 에러 발생의 소지가 있다고 보았다. 그래서 명시적으로 캐스트 연산자를 통해서만 변환이 가능하게끔 하였다.

public static explicit operator Water(Juice _juice)
{
    _juice.Evaporation();
    Water newWater = new Water();
    newWater.liters = _juice.liters;

    return newWater;
}

이렇게 하면 명시적 변환이 가능해지고 형식 변환을 할 때마다 Evaporation함수를 거치게 된다.

 

로그 결과

1.5에서 0.4가 곱해진 결과이므로 0.6이라는 결과값이 도출된다.

반응형