C# 연산자 오버로딩 마스터하기: 사용자 정의 타입을 위한 연산자 재정의
C#에서 제공하는 내장 연산자 대부분을 재정의(Overload)하거나 오버로딩(Overloading)할 수 있습니다. 프로그래머가 사용자 정의 타입에도 연산자를 사용할 수 있게 해줍니다. 오버로드된 연산자는 operator 키워드 뒤에 연산자 기호가 오는 특수 이름을 가진 함수입니다. 일반 함수처럼 반환 타입과 매개변수 리스트를 가집니다.
연산자 오버로딩 구현
예: Box 클래스에 + 연산자 오버로딩
public static Box operator+ (Box b, Box c)
{
Box box = new Box();
box.length = b.length + c.length;
box.breadth = b.breadth + c.breadth;
box.height = b.height + c.height;
return box;
}
두 Box 객체의 속성을 더해 결과 Box 객체를 반환합니다.
완전한 구현 예제
using System;
namespace OperatorOvlApplication
{
class Box
{
private double length, breadth, height;
public double GetVolume() { return length * breadth * height; }
public void SetLength(double len) { length = len; }
public void SetBreadth(double bre) { breadth = bre; }
public void SetHeight(double hei) { height = hei; }
// + 연산자 오버로딩
public static Box operator+ (Box b, Box c)
{
Box box = new Box();
box.length = b.length + c.length;
box.breadth = b.breadth + c.breadth;
box.height = b.height + c.height;
return box;
}
}
class Tester
{
static void Main(string[] args)
{
Box Box1 = new Box();
Box Box2 = new Box();
Box Box3 = new Box();
double volume = 0.0;
Box1.SetLength(6.0); Box1.SetBreadth(7.0); Box1.SetHeight(5.0);
Box2.SetLength(12.0); Box2.SetBreadth(13.0); Box2.SetHeight(10.0);
volume = Box1.GetVolume();
Console.WriteLine("Box1 부피: {0}", volume); // 210
volume = Box2.GetVolume();
Console.WriteLine("Box2 부피: {0}", volume); // 1560
// 오버로드된 + 연산자 사용
Box3 = Box1 + Box2;
volume = Box3.GetVolume();
Console.WriteLine("Box3 부피: {0}", volume); // 5400
Console.ReadKey();
}
}
}
오버로딩 가능/불가능 연산자
| 번호 | 연산자 및 설명 |
| 1 | +, -, !, ~, ++, -- 단항 연산자 (피연산자 1개) - 오버로딩 가능 |
| 2 | +, -, *, /, % 이항 연산자 (피연산자 2개) - 오버로딩 가능 |
| 3 | ==, !=, <, >, <=, >= 비교 연산자 - 오버로딩 가능 (쌍으로 구현 권장) |
| 4 | &&, || 조건부 논리 연산자 - 직접 오버로딩 불가 (하지만 true/false, &, | 연산자 오버로딩으로 간접 지원) |
| 5 | +=, -=, *=, /=, %= 복합 할당 연산자 - 직접 오버로딩 불가 (해당 이항 연산자 오버로딩 시 자동 지원) |
| 6 | =, ., ?:, ->, new, is, sizeof, typeof 오버로딩 불가능 |
비교 연산자 오버로딩 확장 예제
public static bool operator == (Box lhs, Box rhs)
{
if (ReferenceEquals(lhs, null) && ReferenceEquals(rhs, null)) return true;
if (ReferenceEquals(lhs, null) || ReferenceEquals(rhs, null)) return false;
return lhs.length == rhs.length && lhs.height == rhs.height && lhs.breadth == rhs.breadth;
}
public static bool operator != (Box lhs, Box rhs)
{
return !(lhs == rhs); // == 연산자 활용
}
public static bool operator < (Box lhs, Box rhs)
{
return lhs.length < rhs.length && lhs.height < rhs.height && lhs.breadth < rhs.breadth;
}
public static bool operator > (Box lhs, Box rhs)
{
return lhs.length > rhs.length && lhs.height > rhs.height && lhs.breadth > rhs.breadth;
}
public static bool operator <= (Box lhs, Box rhs)
{
return lhs.length <= rhs.length && lhs.height <= rhs.height && lhs.breadth <= rhs.breadth;
}
public static bool operator >= (Box lhs, Box rhs)
{
return lhs.length >= rhs.length && lhs.height >= rhs.height && lhs.breadth >= rhs.breadth;
}
// ToString 오버라이드로 출력 편의성 향상
public override string ToString()
{
return String.Format("({0}, {1}, {2})", length, breadth, height);
}
확장 예제 실행 결과
Box 1: (6, 7, 5)
Box 2: (12, 13, 10)
Box1 부피: 210
Box2 부피: 1560
Box 3: (18, 20, 15)
Box3 부피: 5400
Box1 is not greater than Box2
Box1 is less than Box2
Box1 is not greater or equal to Box2
Box1 is less or equal to Box2
Box1 is not equal to Box2
Box3 is equal to Box4
연산자 오버로딩 모범 사례
- 대칭성 유지:
== 오버로딩 시 !=도 반드시 구현
Equals()와 GetHashCode()도 함께 오버라이드
IComparable<T> 인터페이스 구현 고려
- 수학적 의미에 맞게 직관적으로 구현 (예: +는 교환법칙 만족)
- 불변(immutable) 타입일 경우 새 인스턴스 반환, 가변 타입일 경우 기존 인스턴스 수정 후 반환
- null 체크 필수 (
ReferenceEquals 사용 권장)
주의: 연산자 오버로딩은 남용하면 코드 가독성을 해칠 수 있습니다. 수학적 연산이 자연스러운 타입(벡터, 행렬, 복소수, 금액 등)에만 제한적으로 사용하세요.