Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Typ zdefiniowany przez użytkownika może przeciążać wstępnie zdefiniowany operator języka C#. Oznacza to, że typ może zapewnić niestandardową implementację operacji, jeśli jeden lub oba operandy są tego typu. Sekcja Przeciążenia operatorów pokazuje, które operatory języka C# mogą być przeciążone.
Użyj słowa kluczowego operator
, aby zadeklarować operator. Deklaracja operatora musi spełniać następujące reguły:
- Zawiera
public
modyfikator. - Operator jednoargumentowy ma jeden parametr wejściowy. Operator binarny ma dwa parametry wejściowe. W każdym przypadku co najmniej jeden parametr musi mieć typ
T
lubT?
gdzieT
jest typem zawierającym deklarację operatora. - Zawiera
static
modyfikator, z wyjątkiem operatorów przypisania skomplikowanego, takich jak+=
. - Operatory inkrementacji (
++
) i dekrementacji (--
) można zaimplementować jako metody statyczne lub metody instancji.
W poniższym przykładzie zdefiniowano uproszczoną strukturę reprezentującą liczbę racjonalną. Struktura przeciąża niektóre operatory arytmetyczne:
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
}
}
Powyższy przykład można rozszerzyć , definiując niejawną konwersję z int
na Fraction
. Następnie przeciążone operatory obsługują argumenty tych dwóch typów. Oznacza to, że możliwe byłoby dodanie liczby całkowitej do ułamka i uzyskanie ułamka w wyniku.
Możesz również użyć słowa kluczowego operator
do definiowania niestandardowej konwersji typu. Aby uzyskać więcej informacji, zobacz Operatory konwersji zdefiniowane przez użytkownika.
Operatory przeciążalne
W poniższej tabeli przedstawiono operatory, które mogą być przeciążone:
Operatorów | Notatki |
---|---|
+x , -x , , !x , ~x ++ , , -- , , true false |
Operatory true i false muszą być ze sobą przeciążone. |
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 |
Muszą być przeciążone w parach w następujący sposób: == i != , < i > , <= i >= . |
+= , -= , , , *= /= %= &= \|= ^= <<= >>= >>>= |
Operatory przypisania złożonego mogą być przeciążone w języku C# 14 lub nowszym. |
Przeciążony operator przypisania złożonego musi przestrzegać następujących reguł:
- Musi zawierać
public
modyfikator. - Nie może zawierać
static
modyfikatora. - Zwracany typ musi mieć wartość
void
. - Deklaracja musi zawierać jeden parametr, który reprezentuje prawą stronę przypisania złożonego.
Począwszy od C# 14, operatory inkrementacji (++
) i dekrementacji (--
) mogą być przeciążone jako członkowie instancji. Operatorzy wystąpień mogą zwiększyć wydajność, unikając tworzenia nowego wystąpienia. Operator instancji musi przestrzegać następujących reguł:
- Musi zawierać
public
modyfikator. - Nie może zawierać
static
modyfikatora. - Zwracany typ musi mieć wartość
void
. - Nie można zadeklarować żadnych parametrów, nawet jeśli te parametry mają wartość domyślną.
Nie przeciążalne operatory
W poniższej tabeli przedstawiono operatory, których nie można przeciążyć:
Operatorów | Alternatywy |
---|---|
x && y , x || y |
Przeciąż zarówno operatory true i false , jak i operatory & lub | . Aby uzyskać więcej informacji, zobacz Operatory logiczne warunkowe zdefiniowane przez użytkownika. |
a[i] , a?[i] |
Zdefiniuj indeksator. |
(T)x |
Zdefiniuj konwersje typów niestandardowych wykonywane przez wyrażenie rzutowane. Aby uzyskać więcej informacji, zobacz Operatory konwersji zdefiniowane przez użytkownika. |
^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 |
Żaden. |
Przed C# 14 nie można przeciążyć operatorów złożonych. Przeciążenie odpowiedniego operatora binarnego niejawnie przeciąża odpowiedni operator przypisania złożonego.
Rozpoznawanie przeciążenia operatora
Ważne
Ta sekcja dotyczy języka C# 14 i nowszych. Przed wprowadzeniem C# 14 operatory złożone przypisania definiowane przez użytkownika oraz operatory inkrementacji i dekrementacji obiektów nie były dozwolone.
Jeśli x
jest sklasyfikowana jako zmienna w wyrażeniu przypisania złożonego, takim jak x «op»= y
, operatory instancji są preferowane nad jakimikolwiek operatorami statycznymi dla «op»
. Jeśli dla typu «op»=
nie jest zadeklarowany przeciążony operator lub jeśli x
x
nie jest klasyfikowany jako zmienna, są używane operatory statyczne.
Jeśli operator postfiksowy ++
x
nie jest klasyfikowany jako zmienna lub jeśli używane jest wyrażenie x++
, to wystąpienie operator++
zostaje pominięte. W przeciwnym razie wystąpienie operator ++
będzie preferowane. Na przykład
x++; // Instance operator++ preferred.
y = x++; // instance operator++ isn't considered.
Powodem tej reguły jest przypisanie y
do wartości x
przed jej przyrostem. Kompilator nie może określić tego w przypadku implementacji zdefiniowanej przez użytkownika w typie referencyjnym.
Dla operatora prefiksu ++
, jeśli x
jest klasyfikowany jako zmienna w ++x
, operator instancji jest preferowany nad statycznym operatorem jednoargumentowym.
Specyfikacja języka C#
Aby uzyskać więcej informacji, zobacz następujące sekcje specyfikacji języka C#: