Содержание
Операторы могут быть перегружены, чтобы обеспечить более естественный синтаксис для пользовательских типов. В большей степени это актуально для реализации пользовательских структур, представляющих довольно простые типы данных.
Перегружены могут быть следующие операторы:
+ - * / ++ -- ! ~ % & | ^
== != < << >> >
а также операторы автоматического и явного приведения (с помощью ключевых слов implicit
и explicit
), литералы true
и false
, и унарные +
и -
.
Составные операторы присвоения (например, +=
, /=
) автоматически переопределяются при переопределении соответствующих не составных операторов (например, +
, /
).
Операторные методы (Operator Functions)
Перегрузить оператор можно объявив операторный метод. Операторный метод должен быть статическим, и как минимум один из его операндов должен быть того типа, в котором объявлен операторный метод. Объявляется операторный метод с помощью ключевого слова operator
:
1 2 3 4 5 6 7 8 9 10 11 12 | public struct Note { int value; public Note (int semitonesFromA) { value = semitonesFromA; } public static Note operator + (Note x, int semitones) { return new Note (x.value + semitones); } } |
В примере описывается структура, представляющая собой музыкальную ноту. С помощью перегруженного оператора +
мы можем создать новую ноту:
1 2 | Note B = new Note (2); Note CSharp = B + 2; |
Поскольку оператор +
был перегружен, перегружен был и составной оператор +=
:
1 | CSharp += 2; |
Перегрузка операторов равенства и сравнения
Операторы равенства и сравнения обычно переопределяются для структур, реже для классов. При перегрузке этих операторов необходимо соблюдать несколько правил:
- Парность операторов: операторы, представляющие логическую пару: (
== !=
), (< >
),
and (<= >=
), перегружаются одновременно (т.е. если перегружен один оператор, второй автоматически считается также перегруженным). Equals
иGetHashCode
: при перегрузке операторов==
и!=
, также необходимо переопределить методыEquals
иGetHashCode
(определены дляobject
), чтобы коллекции и хэштаблицы могли корректно работать с типом.IComparable
иIComparable<T>
: при перегрузке операторов<
и>
как правило необходимо реализовывать интерфейсыIComparable
иIComparable<T>
.
В продолжение примера структуры Note:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public static bool operator == (Note n1, Note n2) { return n1.value == n2.value; } public static bool operator != (Note n1, Note n2) { return !(n1.value == n2.value); } public override bool Equals (object otherNote) { if (!(otherNote is Note)) return false; return this == (Note)otherNote; } public override int GetHashCode() { return value.GetHashCode(); // Use value's hashcode } |
Автоматическое и явное преобразование
Автоматическое и явное преобразование могут быть перегружены также как операторы. Перегрузка преобразований типов обычно используется для того, чтоб сделать ее более лаконичной и естественной для связанных типов.
В следующем примере объявляется преобразование между структурой Note
(из примера выше) и типом double
(представляющим частоту ноты в герцах):
1 2 3 4 5 6 7 8 9 10 11 12 13 | // преобразование в герцы public static implicit operator double (Note x) { return 440 * Math.Pow (2,(double) x.value / 12 ); } // преобразование из герц public static explicit operator Note (double x) { return new Note ((int) (0.5 + 12 * (Math.Log(x/440) / Math.Log(2)) )); } ... Note n =(Note)554.37; // явное приведение double x = n; // автоматическое преобразование |
Переопределенные преобразования игнорируются операторами as
и is
.