Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Определяемый пользователем тип может перегружать предопределенный оператор 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 ++ -- true false |
Операторы true и false должны быть перегружены вместе. |
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 |
Должен быть перегружен в парах следующим образом: == и != , < и > , <= и >= . |
+= , -= , *= /= %= &= \|= ^= <<= >>= >>>= |
Операторы составного присваивания можно перегружать в версиях 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.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 , typeof with |
Нет. |
До 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#: