Поделиться через


Перегрузка операторов — предопределенные унарные, арифметические, операторы равенства и сравнения

Определяемый пользователем тип может перегружать предопределенный оператор 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 Операторы true и false должны быть перегружены вместе.
x + y, x - y, x * y, x / y, x % y,
x & y, x | y, x ^ y
x << y, , x >> yx >>> y
x == y, , x != yx < yx > yx <= y,x >= y Должен быть перегружен в парах следующим образом: == и !=, < и >, <= и >=.
+=, -=, *=/=%=&=\|=^=<<=>>=>>>= Операторы составного присваивания можно перегружать в версиях C# 14 и более поздних.

Перегруженный оператор составного назначения должен соответствовать следующим правилам:

  • Он должен включать модификатор public .
  • Он не может включать static модификатор.
  • Возвращаемый тип должен быть void.
  • Объявление должно содержать один параметр, представляющий правую часть составного присваивания.

Начиная с C# 14, операторы инкремента++ и декремента-- можно перегрузить как члены экземпляра. Операторы экземпляров могут повысить производительность, избегая создания нового экземпляра. Оператор экземпляра должен соответствовать этим правилам:

  • Он должен включать модификатор public .
  • Он не может включать static модификатор.
  • Возвращаемый тип должен быть void.
  • Он не может объявлять какие-либо параметры, даже если эти параметры имеют значение по умолчанию.

Не перегруженные операторы

В следующей таблице показаны операторы, которые не могут быть перегружены:

Операторы Альтернативы
x && y, x || y Перегрузите операторы true и false, а также операторы & или |. Дополнительные сведения см. в разделе "Определяемые пользователем условные логические операторы".
a[i], a?[i] Определите индексатор.
(T)x Определите преобразования пользовательских типов, выполняемые выражением приведения. Дополнительные сведения см. в разделе Операторы пользовательского преобразования.
^x, x = y, x.yx?.yc ? t : fx ?? y??= y
x..y, x->y=>f(x)asawaitcheckeduncheckeddefaultdelegateisnameofnew
sizeof, , stackallocswitch, 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#:

См. также