C#の演算子オーバーロード:カスタム型で演算子を再定義する
C#で利用可能なほとんどの組み込み演算子を再定義またはオーバーロードできます。これにより、プログラマーはユーザー定義型でも演算子を使用できるようになります。オーバーロードされた演算子は、演算子を定義する記号にキーワードoperatorを続けた特殊な名前を持つ関数です。通常の関数と同様に、オーバーロード演算子には戻り値の型とパラメータリストがあります。
演算子オーバーロードの基本構文
public static 戻り値の型 operator 演算子記号(パラメータリスト) {
// 実装
}
加算演算子(+)のオーバーロード例
public static Box operator+ (Box b, Box c) {
Box box = new Box();
box.length = b.length + c.length;
box.breadth = b.breadth + c.breadth;
box.height = b.height + c.height;
return box;
}
この関数は、ユーザー定義クラスBoxに対する加算演算子(+)を実装します。2つのBoxオブジェクトの属性を加算し、結果のBoxオブジェクトを返します。
完全な実装例
using System;
namespace OperatorOvlApplication {
class Box {
private double length;
private double breadth;
private double height;
public double GetVolume() {
return length * breadth * height;
}
public void SetLength(double len) { length = len; }
public void SetBreadth(double bre) { breadth = bre; }
public void SetHeight(double hei) { height = hei; }
// +演算子のオーバーロード
public static Box operator+ (Box b, Box c) {
Box box = new Box();
box.length = b.length + c.length;
box.breadth = b.breadth + c.breadth;
box.height = b.height + c.height;
return box;
}
}
class Tester {
static void Main(string[] args) {
Box box1 = new Box();
Box box2 = new Box();
Box box3 = new Box();
double volume = 0.0;
box1.SetLength(6.0); box1.SetBreadth(7.0); box1.SetHeight(5.0);
box2.SetLength(12.0); box2.SetBreadth(13.0); box2.SetHeight(10.0);
volume = box1.GetVolume();
Console.WriteLine("Volume of Box1 : {0}", volume); // 210
volume = box2.GetVolume();
Console.WriteLine("Volume of Box2 : {0}", volume); // 1560
// 2つのオブジェクトを加算
box3 = box1 + box2;
volume = box3.GetVolume();
Console.WriteLine("Volume of Box3 : {0}", volume); // 5400
Console.ReadKey();
}
}
}
実行結果:
Volume of Box1 : 210
Volume of Box2 : 1560
Volume of Box3 : 5400
オーバーロード可能・不可能な演算子
| No. | 演算子と説明 |
|---|---|
| 1 | +, -, !, ~, ++, --単項演算子(1つのオペランド)。オーバーロード可能。 |
| 2 | +, -, *, /, %二項演算子(2つのオペランド)。オーバーロード可能。 |
| 3 | ==, !=, <, >, <=, >=比較演算子。オーバーロード可能(対で定義推奨)。 |
| 4 | &&, ||条件論理演算子。直接オーバーロード不可。( true/false演算子と&/|で間接的に制御可能) |
| 5 | +=, -=, *=, /=, %=複合代入演算子。直接オーバーロード不可。(対応する二項演算子のオーバーロードで自動的に使用可能) |
| 6 | =, ., ?:, ->, new, is, sizeof, typeofオーバーロード不可。 |
比較演算子のオーバーロード(対で定義必須)
比較演算子(==/!=, </>, <=/>=)は対でオーバーロードする必要があります。
拡張例:比較演算子とToStringのオーバーロード
using System;
namespace OperatorOvlApplication {
class Box {
private double length, breadth, height;
public double GetVolume() => length * breadth * height;
public void SetLength(double len) => length = len;
public void SetBreadth(double bre) => breadth = bre;
public void SetHeight(double hei) => height = hei;
// +演算子
public static Box operator+ (Box b, Box c) => new Box {
length = b.length + c.length,
breadth = b.breadth + c.breadth,
height = b.height + c.height
};
// == と != (対で必須)
public static bool operator ==(Box lhs, Box rhs) {
if (ReferenceEquals(lhs, null) && ReferenceEquals(rhs, null)) return true;
if (ReferenceEquals(lhs, null) || ReferenceEquals(rhs, null)) return false;
return lhs.length == rhs.length && lhs.height == rhs.height && lhs.breadth == rhs.breadth;
}
public static bool operator !=(Box lhs, Box rhs) => !(lhs == rhs);
// < と > (対で必須)
public static bool operator <(Box lhs, Box rhs) {
if (ReferenceEquals(lhs, null)) return !ReferenceEquals(rhs, null);
return lhs.length < rhs.length && lhs.height < rhs.height && lhs.breadth < rhs.breadth;
}
public static bool operator >(Box lhs, Box rhs) => rhs < lhs;
// <= と >= (対で必須)
public static bool operator <=(Box lhs, Box rhs) => !(rhs < lhs);
public static bool operator >=(Box lhs, Box rhs) => !(lhs < rhs);
// Equals/GetHashCodeもオーバーライドすべき
public override bool Equals(object obj) => obj is Box b && this == b;
public override int GetHashCode() => HashCode.Combine(length, breadth, height);
// 表示用
public override string ToString() => $"({length}, {breadth}, {height})";
}
class Tester {
static void Main(string[] args) {
Box box1 = new Box { length = 6, breadth = 7, height = 5 };
Box box2 = new Box { length = 12, breadth = 13, height = 10 };
Box box3 = box1 + box2;
Box box4 = box3;
Console.WriteLine($"Box1: {box1}");
Console.WriteLine($"Box2: {box2}");
Console.WriteLine($"Box3: {box3}");
Console.WriteLine($"Volume Box3: {box3.GetVolume()}");
Console.WriteLine(box1 > box2 ? "Box1 > Box2" : "Box1 <= Box2");
Console.WriteLine(box1 < box2 ? "Box1 < Box2" : "Box1 >= Box2");
Console.WriteLine(box1 == box2 ? "Box1 == Box2" : "Box1 != Box2");
Console.WriteLine(box3 == box4 ? "Box3 == Box4" : "Box3 != Box4");
Console.ReadKey();
}
}
}
実行結果:
Box1: (6, 7, 5)
Box2: (12, 13, 10)
Box3: (18, 20, 15)
Volume Box3: 5400
Box1 <= Box2
Box1 < Box2
Box1 != Box2
Box3 == Box4
単項演算子のオーバーロード例
public static Box operator-(Box b) { // 単項マイナス
return new Box {
length = -b.length,
breadth = -b.breadth,
height = -b.height
};
}
public static Box operator++(Box b) { // インクリメント
b.length++; b.breadth++; b.height++;
return b;
}
public static bool operator true(Box b) { // true/false演算子(&&/||制御用)
return b.length > 0 || b.breadth > 0 || b.height > 0;
}
public static bool operator false(Box b) => !b;
暗黙的/明示的変換演算子
型変換も演算子オーバーロードとして定義できます。
class Rational {
public int Numerator { get; }
public int Denominator { get; }
// 暗黙的変換(int → Rational)
public static implicit operator Rational(int value)
=> new Rational(value, 1);
// 明示的変換(Rational → double)
public static explicit operator double(Rational r)
=> (double)r.Numerator / r.Denominator;
}
ベストプラクティス
- 演算子の意味論(数学的直感)を保つ(
+は加算的結合、==は値の等価性) - 比較演算子は必ず対で実装し、
Equals/GetHashCodeも整合性を持ってオーバーライドする - 不変型(immutable type)では演算子が新しいインスタンスを返すようにする
- 複雑なロジックを含む演算子は避け、メソッド名で意図を明確にする(例:
Addvs+) - nullチェックを
ReferenceEqualsで行い、無限再帰を防ぐ - 構造体(
struct)でのオーバーロードは値コピーのコストに注意
C言語
- オペレーター関与維持管理:メリットは隠れたコストを上回るか?
- DIY Wall-E 風 Raspberry Pi CD ボックスロボット
- C#演算子完全ガイド:C#における演算子の包括的解説
- C# 三項演算子:条件式を簡潔に書くためのガイド
- C#メソッドオーバーロード完全ガイド:概念、例、ベストプラクティス
- C#コンストラクタオーバーロード完全ガイド:柔軟なクラス設計
- C++演算子オーバーロード:コード例付き実践ガイド
- C++オーバーロード完全マスター:関数と演算子のオーバーロード
- MATLAB基礎マスター:必須構文とコマンド完全ガイド
- Python演算子オーバーロード完全ガイド:__add__、__lt__など特殊メソッドでOOP設計を強化
- Java instanceof演算子完全ガイド:基本から継承・インターフェースまで