Перегрузка операторов в C#: пользовательские типы и кастомная логика
Что такое перегрузка операторов
C# позволяет переопределять поведение большинства встроенных операторов для пользовательских типов (классов/структур). Перегруженный оператор — это статический метод со специальным именем: operator +, operator == и т.д.
Синтаксис
public static ВозвращаемыйТип operator Оператор(Тип1 операнд1[, Тип2 операнд2])
{
// реализация
}
- Оператор должен быть
public static - Хотя бы один операнд должен быть типа содержащего класса/структуры
- Унарные операторы — один операнд, бинарные — два
Пример: сложение Box
public class Box
{
public double Length { get; set; }
public double Breadth { get; set; }
public double Height { get; set; }
public double Volume => Length * Breadth * Height;
// Перегрузка +
public static Box operator +(Box b1, Box b2)
{
return new Box
{
Length = b1.Length + b2.Length,
Breadth = b1.Breadth + b2.Breadth,
Height = b1.Height + b2.Height
};
}
}
// Использование
Box b1 = new Box { Length = 6, Breadth = 7, Height = 5 };
Box b2 = new Box { Length = 12, Breadth = 13, Height = 10 };
Box b3 = b1 + b2; // Length=18, Breadth=20, Height=15
Console.WriteLine(b3.Volume); // 5400
Перегружаемые и неперегружаемые операторы
| Категория | Операторы | Можно перегрузить? |
|---|---|---|
| Унарные | + - ! ~ ++ -- true false | ✅ Да |
| Бинарные арифметические | + - * / % | ✅ Да |
| Сравнения | == != < > <= >= | ✅ Да (парами: ==/!=, </>/<=/>=) |
| Логические | && || | ❌ Нет (но работают через &, |, false, true) |
| Присваивания | = += -= *= /= %= ... | ❌ Нет (составные используют перегруженные бинарные) |
| Другие | [] () ?. ?. ? : new is as sizeof typeof | ❌ Нет (кроме индексаторов this[] и приведения explicit/implicit) |
Парные операторы сравнения
Если перегружаете ==, ОБЯЗАТЕЛЬНО перегрузите != (и наоборот). Аналогично для </>, <=/>=. Иначе — warning.
Также рекомендуется переопределять Equals() и GetHashCode() при перегрузке ==.
Полный пример: Box с множественными операторами
public class Box
{
public double Length { get; set; }
public double Breadth { get; set; }
public double Height { get; set; }
public double Volume => Length * Breadth * Height;
public static Box operator +(Box a, Box b) => new Box
{
Length = a.Length + b.Length,
Breadth = a.Breadth + b.Breadth,
Height = a.Height + b.Height
};
public static bool operator ==(Box a, Box b)
{
if (ReferenceEquals(a, null) && ReferenceEquals(b, null)) return true;
if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) return false;
return a.Length == b.Length && a.Breadth == b.Breadth && a.Height == b.Height;
}
public static bool operator !=(Box a, Box b) => !(a == b);
public static bool operator <(Box a, Box b) => a.Volume < b.Volume;
public static bool operator >(Box a, Box b) => a.Volume > b.Volume;
public static bool operator <=(Box a, Box b) => a.Volume <= b.Volume;
public static bool operator >=(Box a, Box b) => a.Volume >= b.Volume;
public override bool Equals(object obj) => this == (obj as Box);
public override int GetHashCode() => HashCode.Combine(Length, Breadth, Height);
public override string ToString() => $"({Length}, {Breadth}, {Height})";
}
Явное и неявное приведение (conversion operators)
public struct Digit
{
public byte Value { get; }
public Digit(byte v) => Value = v;
// Неявное приведение int → Digit (без потерь)
public static implicit operator Digit(int d) => new Digit((byte)d);
// Явное приведение Digit → int (с потенциальными потерями/проверкой)
public static explicit operator int(Digit d) => d.Value;
}
// Использование
Digit d = 5; // implicit
int i = (int)d; // explicit required
Лучшие практики
- Перегружайте операторы, когда это семантически очевидно (математические типы: векторы, матрицы, комплексные числа, деньги)
- Соблюдайте математические законы:
a + b == b + a,(a + b) + c == a + (b + c) - Всегда перегружайте пары сравнения вместе
- Переопределяйте
Equals/GetHashCodeпри== - Не перегружайте операторы для ссылочных типов, если это не ожидаемая семантика значений
- Для
++/--возвращайте новый объект (типы-значения) или изменённый (reference types — с осторожностью)
Язык C
- Операторы C# — исчерпывающее руководство по операторам в C#
- Тернарный оператор в C#: краткое руководство по условным выражениям
- Освоение перегрузки методов в C#: концепции, примеры и лучшие практики
- Освоение перегрузки конструкторов в C#
- Перегрузка операторов в Python: практическое руководство
- Оператор instanceof в Java: полное руководство с примерами
- DIY робот в стиле Wall‑E на Raspberry Pi из коробки от CD
- Оператор-вовлеченное обслуживание: перевешивают ли выгоды скрытые затраты?
- Перегрузка операторов в C++: практическое руководство с примерами кода
- Перегрузка в C++: функции и операторы — подробное руководство