[C#] 48. Operator(연산자) 오버로드 사용법
안녕하세요. 명월입니다.
이 글은 C#의 Operator(연산자) 오버로드 사용법에 대한 글입니다.
C#에서 Operator(연산자)란 더하기(+), 빼기(-)의 기호입니다.
링크 - [C#] 04. 연산자
어떤 프로그램을 배우더라도 가장 맨처음 보게 되는 것이 연산자이지 않을까 싶습니다.
그리고 오버로드(overrode)의 뜻은 프로그램에서 재정의라는 뜻입니다.
링크 - [C#] 12. 클래스 상속과 재정의(override)하는 방법과 override와 new의 차이
즉, Operator(연산자) 오버로드라는 것은 연산자를 재정의하는 뜻입니다. 즉, 우리가 사용하는 더하기(+)나 빼기(-)를 단순한 숫자 연산이 아닌 다른 처리로 재정의 할 수 있다는 뜻입니다.
using System;
namespace Example
{
// 예제 클래스
class Node
{
// 데이터 프로퍼티
public int Data
{
get; set;
}
// 생성자
public Node(int data)
{
this.Data = data;
}
// 연산자 더하기(+) 재정의 -> 하나의 Node 인스턴스에 +를 사용할 경우
public static Node operator +(Node a)
{
// 인스턴스 생성(프로퍼티의 값은 같으나 인스턴스가 다른 클래스)
return new Node(a.Data);
}
// 연산자 더하기(+) 재정의 -> +의 연산자에 앞과 뒤에 Node 인스턴스가 배치된 경우
public static Node operator +(Node a, Node b)
{
// 연산자 앞 인스턴스의 Data 값에 뒤 인스턴스의 Data를 더한다.
a.Data = a.Data + b.Data;
// 인스턴스를 리턴
return a;
}
// 연산자 증감(++) 재정의 -> 전치, 후치 관계없다.
public static Node operator ++(Node a)
{
// 새로운 인스턴스를 생성하며 받은 Data 값을 1 증가한다.
return new Node(a.Data + 1);
}
// Operator(연산자)의 재정의(override)는 익명 함수로 설정 가능하다.
// 새로운 인스턴스를 생성하며 Data 값을 뺀다.(참고, 더하기(+)는 인스턴스 생성이 아니지만 빼기(-)는 인스턴스 생성이다.)
public static Node operator -(Node a, Node b) => new Node(a.Data - b.Data);
// 묵시적 형 변환 (묵시적은 강제 캐스팅을 하지 않아도 변환된다.)
public static implicit operator int(Node node)
{
// node 인스턴스의 Data 값을 리턴한다.
return node.Data;
}
// 명시적 형 변환 (명시적은 강제 캐스팅을 해야 한다.)
public static explicit operator Node(int data)
{
// int 형 값을 node 인스턴스를 생성해서 리턴한다.
return new Node(data);
}
// Object 클래스의 ToString 함수를 재정의
public override String ToString()
{
// Data 값을 String 값으로 리턴
return Data.ToString();
}
}
class Program
{
// 실행 함수
static void Main(string[] args)
{
// Node 클래스의 명시적 형 변환으로 인해 explicit operator Node 가 호출. 인스턴스가 생성된다.
Node node = (Node)10;
// Node 클래스의 묵시적 형 변환으로 인해 implicit operator int 가 호출, int 형으로 Data 값이 나온다.
int data = node;
// operator + 새로운 인스턴스 생성
Node node2 = +node;
// operator + node의 인스턴스에 node2의 값을 더한다.
// node와 node3은 같은 인스턴스
var node3 = node + node2;
// 인스턴스 주소 값 출력
Console.WriteLine("node pointer = " + node.GetHashCode());
// node2는 새로운 인스턴스가 생겼기 때문에 다른 인스턴스 주소값
Console.WriteLine("node2 pointer = " + node2.GetHashCode());
// node3은 node 인스턴스의 주소 값 복사를 했기 때문에 node와 주소값이 같다.
Console.WriteLine("node3 pointer = " + node3.GetHashCode());
// 연산자 증감(++) 재정의 새로운 인스턴스가 출력이 되기 때문에 node2는 영향이 없다.
Console.WriteLine((++node2).Data);
// node 인스턴스의 ToString
Console.WriteLine("node result = " + node);
// node2 인스턴스의 ToString
Console.WriteLine("node2 result = " + node2);
// node2에는 새로운 인스턴스가 생성되어 기존 node2.Data에서 node.Data를 뺀다.
node2 -= node;
// 콘솔 출력
Console.WriteLine("node2 result = " + node2);
// 아무 키나 누르면 종료
Console.WriteLine("Press Any key...");
Console.ReadLine();
}
}
}
프로그램에서 쉽게 사용되는 연산자의 재정의입니다.
기존의 정수형(int)과 실수형(float)에서는 더하기(+)나 빼기(-)를 넣으면 사칙 연산이 됩니다만, 클래스에서는 위처럼 재정의하여 사용하게 되면 코드를 많이 줄이는 효과를 줄 수 있습니다.
이 연산기호는 모든 기호가 재정의되는 것은 아닙니다.
참고 - https://docs.microsoft.com/
연산자 | 설명 |
---|---|
+x, -x, !x, ~x, ++, --, true, false | 파라미터가 하나인 연산자로써, 재정의가 가능하다. 여기서 true, false는 두개를 동시에 써야하는 operator로써 bool 형식으로 묵시적 형 변환이 가능한 재정의이다. |
x + y, x - y, x * y, x / y, x % y, x & y, x | y, x ^ y, x << y, x >> y, x == y, x != y, x < y, x > y, x <= y, x >= y | 파라미터가 두개인 연산자로써, 재정의가 가능하다. |
x && y, x || y | 재정의 불가, true, false 재정의로 인식된다. |
a[i], a?[i] | 연산자 재정의가 아닌 인덱서로 인식된다. |
(T)x | 형변환 재정의, explicit 명시적, implicit 묵시적 |
+=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>= | 재정의 불가, 단, 파라미터 두개의 연산자 재정의로 실행되어 대입된다. |
^x, x = y, x.y, x?.y, c ? t : f, x ?? y, x ??= y, x..y, x->y, =>, f(x), as, await, checked, unchecked, default, delegate, is, nameof, new, sizeof, stackalloc, switch, typeof, with | 재정의 불가 |
위 표를 참조하여 연산자 재정의가 허용됩니다.
실무에서는 사실 연산자 재정의를 잘 사용하지 않습니다. 예전 C++에서는 전처리문으로 여러가지 매크로를 만들기 위해서 사용한 적이 있던 걸로 기억되는데 C#에서는 잘 사용하지 않네요.
아마도 가독성의 문제성 때문이지 않을까 생각됩니다. 연산자를 재정의해서 사용하게 되면, 무언가 소스를 해석하기에 암호문처럼 바뀌어 버리지 않을까라고 생각되네요.
예를 들면 +연산자에 빼기 연산을 넣고 -연산자에 더하기 연산을 넣어버리면, 그냥 기호 +,-만 보기에는 더하기 빼기가 헷갈리지 않을까라고 생각되네요. 그렇게 만드는 사람은 없겠지만..
여기까지 C#의 Operator(연산자) 오버로드 사용법에 대한 글이었습니다.
궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.