다음을 통해 공유


연산자 오버로드 - 미리 정의된 단항, 산술, 같음 및 비교 연산자

사용자 정의 형식은 미리 정의된 C# 연산자를 오버로드할 수 있습니다. 즉, 피연산자 중 하나 또는 둘 다 해당 형식인 경우 형식은 작업의 사용자 지정 구현을 제공할 수 있습니다. 오버로드 가능한 연산자 섹션에는 오버로드할 수 있는 C# 연산자가 표시됩니다.

키워드를 operator 사용하여 연산자를 선언합니다. 연산자 선언은 다음 규칙을 충족해야 합니다.

  • 여기에는 한정자가 public 포함됩니다.
  • 단항 연산자에는 하나의 입력 매개 변수가 있습니다. 이진 연산자에는 두 개의 입력 매개 변수가 있습니다. 각 경우에 하나 이상의 매개 변수는 형식 T 또는 T?이어야 하고, 연산자 선언이 포함된 형식은 T입니다.
  • 한정자가 포함됩니다 static, 복합 할당 연산자와 같은 +=를 제외하고.
  • 증가(++) 및 감소(--) 연산자는 정적 또는 인스턴스 메서드로 구현할 수 있습니다.

다음 예제에서는 합리적인 숫자를 나타내는 간소화된 구조를 정의합니다. 구조체가 일부 산술 연산자를 오버로드합니다.

public struct Fraction
{
    private int numerator;
    private int denominator;

    public Fraction(int numerator, int denominator)
    {
        if (denominator == 0)
        {
            throw new ArgumentException("Denominator cannot be zero.", nameof(denominator));
        }
        this.numerator = numerator;
        this.denominator = denominator;
    }

    public static Fraction operator +(Fraction operand) => operand;
    public static Fraction operator -(Fraction operand) => new Fraction(-operand.numerator, operand.denominator);

    public static Fraction operator +(Fraction left, Fraction right)
        => new Fraction(left.numerator * right.denominator + right.numerator * left.denominator, left.denominator * right.denominator);

    public static Fraction operator -(Fraction left, Fraction right)
        => left + (-right);

    public static Fraction operator *(Fraction left, Fraction right)
        => new Fraction(left.numerator * right.numerator, left.denominator * right.denominator);

    public static Fraction operator /(Fraction left, Fraction right)
    {
        if (right.numerator == 0)
        {
            throw new DivideByZeroException();
        }
        return new Fraction(left.numerator * right.denominator, left.denominator * right.numerator);
    }

    // Define increment and decrement to add 1/den, rather than 1/1.
    public static Fraction operator ++(Fraction operand)
        => new Fraction(operand.numerator++, operand.denominator);

    public static Fraction operator --(Fraction operand) =>
        new Fraction(operand.numerator--, operand.denominator);

    public override string ToString() => $"{numerator} / {denominator}";

    // New operators allowed in C# 14:
    public void operator +=(Fraction operand) =>
        (numerator, denominator ) =
        (
            numerator * operand.denominator + operand.numerator * denominator,
            denominator * operand.denominator
        );

    public void operator -=(Fraction operand) =>
        (numerator, denominator) =
        (
            numerator * operand.denominator - operand.numerator * denominator,
            denominator * operand.denominator
        );

    public void operator *=(Fraction operand) =>
        (numerator, denominator) =
        (
            numerator * operand.numerator,
            denominator * operand.denominator
        );

    public void operator /=(Fraction operand)
    {
        if (operand.numerator == 0)
        {
            throw new DivideByZeroException();
        }
        (numerator, denominator) =
        (
            numerator * operand.denominator,
            denominator * operand.numerator
        );
    }

    public void operator ++() => numerator++;

    public void operator --() => numerator--;
}

public static class OperatorOverloading
{
    public static void Main()
    {
        var a = new Fraction(5, 4);
        var b = new Fraction(1, 2);
        Console.WriteLine(-a);   // output: -5 / 4
        Console.WriteLine(a + b);  // output: 14 / 8
        Console.WriteLine(a - b);  // output: 6 / 8
        Console.WriteLine(a * b);  // output: 5 / 8
        Console.WriteLine(a / b);  // output: 10 / 4
    }
}

암시적 변환int에서 Fraction로 정의하여 앞의 예제를 확장할 수 있습니다. 그런 다음 오버로드된 연산자는 이러한 두 형식의 인수를 지원합니다. 즉, 분수에 정수를 추가하고 결과적으로 분수를 얻을 수 있습니다.

또한 키워드를 operator 사용하여 사용자 지정 형식 변환을 정의합니다. 자세한 내용은 사용자 정의 변환 연산자를 참조하세요.

오버로드가 가능한 연산자

다음 표에서는 오버로드할 수 있는 연산자를 보여줍니다.

운영자 비고
+x, -x, !x, ~x, ++, --, truefalse truefalse 연산자를 함께 오버로드해야 합니다.
x + y, x - y, x * y, x / y, x % y
x & y, x | y, x ^ y
x << y, , x >> yx >>> y
x == y, x != y, x < y, x > y, x <= yx >= y 다음과 같이 쌍으로 오버로드되어야 합니다: ==!=, <>, <=>=.
+=, -=, *=, /=, %=, &=, \|=, ^=<<=, >>=>>>= 복합 할당 연산자는 C# 14 이상에서 오버로드할 수 있습니다.

복합 할당 오버로드된 연산자는 다음 규칙을 따라야 합니다.

  • 한정자를 public 포함해야 합니다.
  • static 수정자를 포함할 수 없습니다.
  • 반환 형식은 .이어야 void합니다.
  • 선언에는 복합 할당의 오른쪽을 나타내는 하나의 매개 변수가 포함되어야 합니다.

C# 14부터는 증가(++) 및 감소(--) 연산자를 인스턴스 멤버로 오버로드할 수 있습니다. 인스턴스 연산자는 새 인스턴스를 만들지 않도록 하여 성능을 향상시킬 수 있습니다. 인스턴스 연산자는 다음 규칙을 따라야 합니다.

  • 한정자를 public 포함해야 합니다.
  • static 수정자를 포함할 수 없습니다.
  • 반환 형식은 .이어야 void합니다.
  • 해당 매개 변수에 기본값이 있더라도 매개 변수를 선언할 수 없습니다.

오버로드할 수 없는 연산자

다음 표에서는 오버로드할 수 없는 연산자를 보여줍니다.

운영자 대안
x && y, x || y truefalse 연산자와 & 또는 | 연산자를 모두 오버로드합니다. 자세한 내용은 사용자 정의 조건부 논리 연산자를 참조하세요.
a[i], a?[i] 인덱서 정의
(T)x 캐스트 식에서 수행하는 사용자 지정 형식 변환을 정의합니다. 자세한 내용은 사용자 정의 변환 연산자를 참조하세요.
^x, x = y, x.y, x?.y, c ? t : f, x ?? y, ??= y
x..y, x->y, =>, f(x), as, await, checked, unchecked, default, delegate, is, nameof, new,
sizeof, stackalloc, switch, typeofwith
없음.

C# 14 이전에는 복합 연산자를 오버로드할 수 없습니다. 해당 이진 연산자를 오버로드하면 해당 복합 할당 연산자가 암시적으로 오버로드됩니다.

연산자 오버로드 해결

중요합니다

이 섹션은 C# 14 이상에 적용됩니다. C# 14 이전에는 사용자 정의 복합 할당 연산자와 인스턴스 증가 및 감소 연산자가 허용되지 않습니다.

와 같은 x복합 할당 식에서 변수로 분류되는 경우 x «op»= y 인스턴스 연산자는 에 대한 «op»정적 연산자보다 선호됩니다. 오버로드된 «op»= 연산자가 형식 x 에 대해 선언되지 않았거나 x 변수로 분류되지 않은 경우 정적 연산자가 사용됩니다.

후위 연산++자에서 x이 변수로 분류되지 않거나 식을 사용하는 경우에는 인스턴스x++가 무시됩니다. 그렇지 않으면 인스턴스 operator ++에 우선권이 부여됩니다. 예를 들면 다음과 같습니다.

x++; // Instance operator++ preferred.
y = x++; // instance operator++ isn't considered.

이 규칙의 이유는 증가하기 전의 y 값에 x이 할당되어야 하기 때문입니다. 컴파일러는 참조 형식의 사용자 정의 구현에 대해 확인할 수 없습니다.

접두사 연산자 ++의 경우, x에서 ++x가 변수로 분류되는 경우 인스턴스 연산자가 정적 단항 연산자보다 더 선호됩니다.

C# 언어 사양

자세한 내용은 C# 언어 사양의 다음 섹션을 참조하세요.

참고하십시오