演算子のオーバーロード
ユーザー定義型(自分で作ったクラスなど)は、デフォルトでは組み込み型のように+や-などの演算子を使うことはできないが、独自に演算子を実装することで使えるようになる。
ユーザー定義型で演算子を実装することを、演算子のオーバーロードと呼ぶ。
デフォルトでは参照型では==は参照の比較となるが、==演算子をオーバーロードすることで、データによる比較も可能になる。
組み込み型と同じように扱えることがユーザー定義型の理想であるが、演算子の本来の意味とはかけ離れた実装は避けるべきである。
そのため、実際には演算子のオーバーロードを使う場面は限られる。
ほとんどの演算子は、組み込み型と同じような型でのみ実装すべきである。
演算子を実装する
演算子を実装するには operator キーワードを使用して、以下のように記述する。引数の数はオペランドの数と一致する必要がある。
1 |
public static 戻り値の型 operator演算子 (引数リスト) |
演算子の実装は以下の条件を満たす必要がある。
public
とstatic
修飾子の両方が含まれている。- 単項演算子には、1 つの入力パラメーターがあること。 2 項演算子には、2 つの入力パラメーターがあること。 どちらの場合も、少なくとも 1 つのパラメーターの型が
T
またはT?
であること。(T
は演算子を宣言する型)
また、ペアの演算子(==と!=)(<と>)(<=と>=)(trueとfalse)は両方を定義する必要がある。
&&と||だけは直接定義できないが、&と|、およびtrueとfalseを定義することで、間接的に実装できる。
以下の例では、点のX座標とY座標を記憶するPointクラスに+演算子を実装している。2つのPointクラスを足すと、X座標どうし、Y座標どうしがプラスされる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
internal class Point { public int X { get; set; } public int Y { get; set; } public Point() { } public Point(int x, int y) { X = x; Y = y; } public static Point operator +(Point p1, Point p2) { return new Point(p1.X + p2.X, p1.Y + p2.Y); } } |
1 2 3 4 5 6 |
var p1 = new Point(2, 3); var p2 = new Point { X = 3, Y = 4 }; var p3 = p1 + p2; Console.WriteLine($"{p3.X},{p3.Y}"); //5,7 |
複合代入演算子(+=など)が存在する演算子を定義した場合、自動的に複合代入演算子も定義したことになる。
1 2 3 4 |
var p4 = new Point(2, 3); p4 += new Point(1, 2); //複合代入演算子も定義した+に従った動きになる Console.WriteLine($"{p4.X},{p4.Y}"); //3,5 |
型変換(キャスト)演算子を実装する
明示的な型変換を実装する
キーワードoperatorを使うと、独自の型変換も定義することができる。
明示的な型変換を定義する場合はexplicitキーワードを使用して定義する。
明示的な型変換ではキャスト構文を使用して型変換を行うことができる。
暗黙的な型変換(後述)と明示的な型変換は、どちらか一方しか定義することができない。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
internal class Number { public int Num { get; set; } public Number(int num) { Num = num; } public static explicit operator int(Number n) { return n.Num; } public static explicit operator Number(int n) { return new Number(n); } } |
1 2 3 4 5 |
var num1 = new Number(3); vat int1 = 123; int int2 = (int)num1; var num2 = (Number)int1; |
暗黙的な型変換を実装する
暗黙的な型変換を可能にする場合は、explicitのかわりにimplicitを使用する。
implicitで型変換を定義すると、キャストを使用しなくても型変換をすることができる。(キャスト構文を使用することもできる)
ただし、暗黙的な型変換を定義した場合、プログラマーが意図しないところで自動的に型変換が行われてしまうことがある。
C#の組み込み型ではintからlongへの変換のような、数値昇格の場合のみ、暗黙的な型変換を許可している。
これは昇格(小さな型から大きな型への変換)であれば、情報が失われることがないからである。
自分で型変換を定義するときも、この基準を満たし、明確に意味が分かる場合にのみ暗黙的な型変換を定義するべきである。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
internal class Number { public int Num { get; set; } public Number(int num) { Num = num; } public static implicit operator int(Number n) { return n.Num; } public static implicit operator Number(int n) { return new Number(n); } } |
1 2 3 4 5 |
var num1 = new Number(3); var int1 = 123; Number num2 = int1; //int型変数をNumber型変数に代入(暗黙的な変換) int int2 = num1; //Number型変数をint型変数に代入(暗黙的な変換) |
コメント