Перегрузка операторов си шарп

Классы и объекты C#: перегрузка операторов и операций преобразования типов

уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

В одной из статей, посвященных работе в C#, мы рассматривали вопрос о перегрузке методов. Вместе с этим, в C# можно перегружать не только методы но и операторы, например, операторы сложения и вычитания, сравнения и так далее. Более того, мы можем сделать перегрузку операций преобразования типов. И сегодня мы рассмотрим некоторые практические примеры, когда нам может пригодиться такая возможность языка программирования C# как перегрузка операторов и операций преобразования типов.

Перегрузка операторов

Перегрузка арифметических операторов в C#

Перегрузка операторов заключается в определении в классе, для объектов которого мы хотим определить оператор, специального метода:

public static возвращаемый_тип operator оператор(параметры)

Например, рассмотрим такой класс:

public class Point < public double X < get; set; >public double Y < get; set; >public Point(double x, double y) < X = x; Y = y; >>

Этот класс определяет точку на плоскости с координатами (X, Y). Нам необходимо обеспечить векторное сложение и векторное вычитание. Компилятор C# умеет складывать, вычитать, сравнивать примитивные типы данных, однако про то, как сравнивать наши собственные классы и объекты он не знает. Технически мы могли бы каждый раз писать что-то наподобие такого:

Point point1 = new Point(10,10); Point point2 = new Point(7,7); Point point3 = new Point(point1.X+point2.X,point1.Y+point2.Y);

И это вполне себе работоспособный код. Однако можно сделать лучше и красивее — переопределить оператор сложения:

public class Point < public double X < get; set; >public double Y < get; set; >public Point(double x, double y) < X = x; Y = y; >//переопределенный оператор сложения public static Point operator +(Point p1, Point p2) < return new Point(p1.X + p2.X, p1.Y + p2.Y); >>

Так как перегружаемый оператор будет использоваться для всех объектов данного класса, то он имеет модификаторы доступа public static . При сложении возвращается объект класса Point . Теперь мы можем сделать наш код более элегантным и понятным:

Point point1 = new Point(10, 10); Point point2 = new Point(7, 7); Point point3 = point1 + point2;//используем перегруженный оператор Console.WriteLine($"X = Y = "); //X = 17 Y = 17

Аналогичным образом можно переопределять и другие арифметические операторы, в том числе операторы сложения, вычитания, умножения, деления и так далее. Например, вот так может выглядеть оператор * для выполнения операция скалярного умножения точки на плоскости:

public static Point operator *(double s, Point point)

И теперь мы можем умножать точку на любое число (выполнять скалярное умножение):

Point point2 = new Point(7, 7); Point point3 = 2.5*point2; Console.WriteLine($"X = Y = "); //X = 17,5 Y = 17,5

Также следует упомянуть, что операторы в C# бывают унарные и бинарные, но в любом случае один из параметров должен представлять тот тип — класс или структуру, в котором определяется оператор.

Перегрузка логических операторов в C#

Немного иначе обстоит дело с перегрузкой логических операторов в C#. Отличие заключается в том, что операторы сравнения должны переопределяться попарно. Парными являются следующие операторы:

Например, переопределим оператор >. Переопределенный оператор в классе Point может быть таким:

public static bool operator >(Point point1, Point point2) < return (point1.X >point2.X) || ((point1.X == point2.X) && (point1.Y > point2.Y)); >

При этом, как только мы переопределим один из парных операторов, компилятор C# сообщит нам об ошибке:

Ошибка CS0216 Для оператора «Point.operator >(Point, Point)» требуется, чтобы был определен соответствующий оператор «

Поэтому, переопределяем и парный оператор
public static bool operator

Мы можем также переопределить операторы true и false . Например, определим их в классе Point :

public class Point < public double X < get; set; >public double Y < get; set; >public Point(double x, double y) < X = x; Y = y; >public static bool operator true(Point p1) < return (p1.X != 0) && (p1.Y != 0); >public static bool operator false(Point p1) < return (p1.X == 0) && (p1.Y == 0); >>

Использовать эти операторы можно следующим образом:

Point point1 = new Point(10, 10); if (point1) //--используем операторы true/false у Point Console.WriteLine("Координаты точки point1 больше нуля"); else Console.WriteLine("Координаты точки point1 равны нулю"); Point point2 = new Point(0, 0); if (point2) //--используем операторы true/false у Point Console.WriteLine("Координаты точки point2 больше нуля"); else Console.WriteLine("Координаты точки point2 равны нулю");

Консольный вывод будет следующим:

Координаты точки point2 равны нулю

Что стоит учитывать при перегрузке операторов в C#

При переопределении операторов в C# следует учитывать следующее:

  1. так как определение оператора представляет собой метод, то этот метод мы также можем перегрузить, то есть создать для него еще одну версию. О том, как перегружать методы в C# мы говорили здесь.
  2. при перегрузке не должны изменяться те объекты, которые передаются в оператор через параметры.

Второй пункт наиболее наглядно демонстрирует перегрузка унарных операторов, например, ++. Например, мы можем определить для класса Point оператор инкремента:

public static Point operator ++(Point p1)

Так как оператор ++ унарный, то он принимает один параметр — объект того класса, в котором данный оператор определен. Несмотря на то, что компилятор C# не предупредит нас об ошибке, это неправильное определение инкремента, так как оператор не должен менять значения своих параметров.

Более корректная перегрузка оператора инкремента будет выглядеть так:

public static Point operator ++(Point p1)

Использование унарного оператора будет таким же, как и при его использовании для простых типов данных. При этом нам не надо определять отдельно операторы для префиксного и для постфиксного инкремента (а также декремента), так как одна реализация будет работать в обоих случаях.

Point point1 = new Point(10, 10); point1++; ++point1; Console.WriteLine($"X = Y = "); //X = 12 Y = 12

Полный список перегружаемых операторов можно найти в документации msdn

При перегрузке операторов также следует помнить, что мы не можем изменить приоритет оператора или его ассоциативность, мы не можем создать новый оператор или изменить логику операторов в типах, который есть по умолчанию в .NET.

Операции преобразования типов

С этой темой перегрузки операторов в C# тесно связана тема перегрузки операторов преобразования типов. В прошлой статье мы рассматривали восходящее и нисходящее преобразование типов. Было бы не плохо иметь возможность определять логику преобразования одних типов в другие. С помощью перегрузки операторов мы можем это делать. Для этого в классе необходимо определить метод, который имеет следующую форму:

public static implicit|explicit operator Тип_в_который_надо_преобразовать(исходный_тип param) < // логика преобразования >

После модификаторов public static идет ключевое слово explicit (если преобразование явное, то есть нужна операция приведения типов) или implicit (если преобразование неявное). Затем идет ключевое слово operator и далее возвращаемый тип, в который надо преобразовать объект. В скобках в качестве параметра передается объект, который надо преобразовать.

Например, пусть у нас есть следующий класс Counter , который представляет секундомер и который хранит количество секунд в свойстве Seconds :

class Counter < public int Seconds < get; set; >public static implicit operator Counter(int x) < return new Counter < Seconds = x >; > public static explicit operator int(Counter counter) < return counter.Seconds; >>

Первый оператор преобразует число — объект типа int к типу Counter . Его логика проста — создается новый объект Counter , у которого устанавливается свойство Seconds . Второй оператор преобразует объект Counter к типу int , то есть получает из Counter число.

Применение операторов преобразования типов в программе может быть следующим:

Counter counter1 = new Counter < Seconds = 23 >; int x = (int)counter1; Console.WriteLine(x); // 23 Counter counter2 = x; Console.WriteLine(counter2.Seconds); // 23

Поскольку операция преобразования из Counter в int определена с ключевым словом explicit , то есть как явное преобразование, то в этом случае необходимо применить операцию приведения типов:

В случае с операцией преобразования от int к Counter операция определена с ключевым словом implicit , то есть как неявная, поэтому в коде выше мы ничего не указывали перед переменной x . Какие операции преобразования делать явными, а какие неявные — решает разработчик по своему усмотрению.

Отметим, что оператор преобразования типов должен преобразовывать из типа или в тип, в котором этот оператор определен. То есть оператор преобразования, определенный в типе Counter , должен либо принимать в качестве параметра объект типа Counter , либо возвращать объект типа Counter . Рассмотрим также более сложные преобразования, к примеру, из одного составного типа в другой составной тип. Допустим, у нас есть еще класс Timer :

class Timer < public int Hours < get; set; >public int Minutes < get; set; >public int Seconds < get; set; >> class Counter < public int Seconds < get; set; >public static implicit operator Counter(int x) < return new Counter < Seconds = x >; > public static explicit operator int(Counter counter) < return counter.Seconds; >//преобразования в Timer и из Timer public static explicit operator Counter(Timer timer) < int h = timer.Hours * 3600; int m = timer.Minutes * 60; return new Counter < Seconds = h + m + timer.Seconds >; > public static implicit operator Timer(Counter counter) < int h = counter.Seconds / 3600; int m = (counter.Seconds % 3600) / 60; int s = counter.Seconds % 60; return new Timer < Hours = h, Minutes = m, Seconds = s >; > >

Класс Timer представляет условный таймер, который хранит часы, минуты и секунды. Класс Counter представляет секундомер, который хранит только количество секунд. Исходя из этого мы можем определить логику преобразования из одного типа к другому, то есть получение из секунд в объекте Counter часов, минут и секунд в объекте Timer .

Применение операций преобразования:

Counter counter1 = new Counter < Seconds = 115 >; Timer timer = counter1; Console.WriteLine($"::"); // 0:1:55 Counter counter2 = (Counter)timer; Console.WriteLine(counter2.Seconds); //115

Итого

Сегодня мы рассмотрели вопросы, связанные с перегрузкой операторов и операций преобразования типов в C#. Перегрузка операторов позволяет определять собственную логику при использовании операторов, например, арифметических или логических при использовании собственных классов. С помощью перегрузки операций преобразования типов мы можем определить логику преобразования одного типа данных (например, класса) в другой.

уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

Источник

Читайте также:  String object javascript example
Оцените статью