ユーザー定義型は、定義済みの C# 演算子をオーバーロードできます。 つまり、オペランドの一方または両方がその型である場合、型は演算のカスタム実装を提供できます。 [オーバーロード可能な演算子] セクションでは、オーバーロードできる C# 演算子を示します。
演算子を宣言するには、 operator
キーワードを使用します。 演算子宣言は、次の規則を満たす必要があります。
-
public
修飾子が含まれています。 - 単項演算子には、1 つの入力パラメーターがあります。 2 項演算子には、2 つの入力パラメーターがあります。 いずれの場合も、少なくとも 1 つのパラメーターに型
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
します。 次に、オーバーロードされた演算子は、これら 2 つの型の引数をサポートします。 つまり、分数に整数を追加し、結果として分数を取得することが可能になります。
また、 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
する必要があります。 - 宣言には、複合代入の右側を表す 1 つのパラメーターを含める必要があります。
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 より前では、複合演算子をオーバーロードすることはできません。 対応する二項演算子をオーバーロードすると、対応する複合代入演算子が暗黙的にオーバーロードされます。
演算子のオーバーロードの解決
Von Bedeutung
このセクションは C# 14 以降に適用されます。 C# 14 より前では、ユーザー定義複合代入演算子とインスタンスインクリメント演算子とデクリメント演算子は許可されていません。
x
x «op»= y
などの複合代入式で変数として分類される場合、インスタンス演算子は、«op»
の静的演算子よりも優先されます。 オーバーロードされた «op»=
演算子が x
の型に対して宣言されていない場合、または x
が変数として分類されていない場合は、静的演算子が使用されます。
後置演算子++
の場合、x
が変数として分類されていないか、式x++
が使用されている場合、インスタンスoperator++
は無視されます。 それ以外の場合は、インスタンス operator ++
が優先されます。 たとえば、
x++; // Instance operator++ preferred.
y = x++; // instance operator++ isn't considered.
このルールの理由は、y
がインクリメントされるx
にの値に割り当てる必要があるためです。 コンパイラは、参照型におけるユーザー定義の実装の場合、それを判断できません。
プレフィックス演算子 ++
の場合、 x
が ++x
の変数として分類されている場合、インスタンス演算子は静的単項演算子よりも優先されます。
C# 言語仕様
詳細については、「C# 言語仕様」の次のセクションを参照してください。
こちらも参照ください
.NET