Перегрузка оператора приведения типа (). Перегрузка операторов true—false. Перегрузка логических операторов &&, ||
Содержание
- 1. Перегрузка оператора (). Общие сведения
- 1.1. Неявное (implicit) приведение Class1=Class2. Общая форма. Пример
- 1.2. Явное (explicit) приведение типов Class1 = (Class1)Class2. Пример
- 1.3. Пример перегрузки оператора () для типов Line и Rectangle. Демонстрируется явное и неявное приведение типа
- 1.4. Пример перегрузки оператора () для типов Point<=>Circle. Демонстрация приведения для разных видов типов: int<=Point, Circle<=Point, Point<=Circle, double<=Circle
- 1.5. Ограничения, накладываемые на оператор приведения типа ()
- 2. Перегрузка операторов true, false. Пример
- 3. Перегрузка логических операторов &&, ||. Пример
- Связанные темы
Поиск на других ресурсах:
1. Перегрузка оператора (). Общие сведения
Оператор () может быть перегружен в классе в случае приведения из одного типа в другой.
Поскольку класс, в котором реализуется перегрузка оператора () является типом, то приведение может иметь две реализации:
1. Приведение из любого типа в тип нашего класса:
Class1 = Class2;
здесь
- Class1 – класс, в котором перегружен оператор ();
- Class2 – другой клас.
2. Приведение из типа класса, в котором перегружен оператор (), к любому произвольному типу
Class2 = Class1;
здесь
- Class1 – класс, в котором перегружен оператор ();
- Class2 – класс, к которому приводится Class1.
Приведение типа не зависит от того, какой тип приводится, ссылочный или значимый.
Как известно, приведение типов есть:
- явное – это случай, когда возможна потеря данных в результате приведения. Например, приведение int=>short, int=>uint, double=>int и т.п.;
- неявное – случай, когда не нужно указывать явного приведения, поскольку потеря данных не происходит (int=>int, Point=>Point, …).
В зависимости от приведения (явного/неявного) объявление метода, перегружающего оператор приведения () содержит одно из двух слов:
- implicit – задает неявное преобразование. Это преобразование можно задавать без риска утраты точности;
- explicit – задает явное преобразование. Это преобразование используется, если возможна потеря данных или даже возникновение исключительной ситуации.
⇑
1.1. Неявное (implicit) приведение Class1=Class2. Общая форма. Пример
Неявное приведение типа выполняется при выполнении простого присваивания
obj1 = obj2;
здесь
- obj1 – экземпляр некоторого класса Class1, являющийся приемником значения экземпляра obj2;
- obj2 – экземпляр класса-источника Class2.
Учитывая вышесказанное, общая форма использования операторного метода operator()() неявного преобразования (implicit) в классе с именем Class1 имеет вид:
public static implicit operator Class1 (Class2 obj) { // Class1 <= Class2 // ... }
здесь
- Class1 – класс, к которому осуществляется приведение;
- Class2 – класс, выступающий в качестве исходного типа.
Если в классе объявляется неявное приведение типов Class1<=Class2, то допускается также задавать явное приведение в операции присваивания
obj1 = (Class1)obj2;
⇑
1.2. Явное (explicit) приведение типов Class1 = (Class1)Class2. Пример
Явное приведение типов необходимо в случаях, когда происходит потеря данных. Однако это не обязательно при перегрузке оператора () в классе. Способ кодировки метода, перегружающего оператор (), определяется по собственному усмотрению в зависимости от поставленной задачи.
Для двух экземпляров явное приведение типа выглядит следующим образом:
obj1 = (Class1)obj2;
здесь
- obj1 – экземпляр класса Class1, который есть приемником;
- obj2 – экземпляр некоторого класса Class2, который приводится к типу Class1.
При использовании явного приведения типов объявление операторного метода, который перегружает оператор (), имеет вид:
public static explicit operator Class1 (Class2 obj) { // Class1 <= Class2 // ... }
Если в классе объявлено явное (explicit) приведение типа из Class2 в Class1 (Class1<=Class2), то операция неявного присвоения
obj1 = obj2;
без указания явного приведения выдаст ошибку компиляции.
⇑
1.3. Пример перегрузки оператора () для типов Line и Rectangle. Демонстрируется явное и неявное приведение типа
В примере показана перегрузка оператора () для типов Line и Rectangle.
Line => Rectangle Rectangle <= Line
В классе Line формируются поля с координатами крайних точек. Эти точки могут быть восприняты как координаты углов прямоугольника Rectangle. Таким образом, данные не теряются, просто один тип класса конвертируется в другой.
using System; namespace ConsoleApp6 { // Класс, описывающий линию class Line { // Координаты точек линии private double x1, y1, x2, y2; // Конструктор public Line(double x1, double y1, double x2, double y2) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } // Свойства доступа public double X1 { get { return x1; } } public double Y1 { get { return y1; } } public double X2 { get { return x2; } } public double Y2 { get { return y2; } } // Метод, выводящий состояние объекта Line public void Print(string msg) { Console.Write(msg + " => "); Console.WriteLine("( " + x1 + "; " + y1 + ") - ( " + x2 + "; " + y2 + ")"); } // Метод, который перегружает оператор приведения Line <= Rectangle, // используется неявное преобразование (implicit) // Line <= Rectangle public static implicit operator Line(Rectangle r) { return new Line(r.X1, r.Y1, r.X2, r.Y2); } // Явне преобразование (explicit) // Rectangle <= Line public static explicit operator Rectangle(Line l) { return new Rectangle(l.X1, l.Y1, l.X2, l.Y2); } } // Класс, описывающий прямоугольник class Rectangle { // Внутренние поля - координаты углов прямоугольника private double x1, y1, x2, y2; // Конструктор public Rectangle(double x1, double y1, double x2, double y2) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } // Свойства доступа public double X1 { get { return x1; } } public double Y1 { get { return y1; } } public double X2 { get { return x2; } } public double Y2 { get { return y2; } } // Метод, который выводит внутреннее состояние экземпляра класса public void Print(string msg) { Console.WriteLine(msg + " => x1 = " + x1 + "; y1 = " + y1 + "; x2 = " + x2 + "; " + "y2 = " + y2); } } internal class Program { static void Main(string[] args) { // Вызов неявный (implicit) // 1. Реализовать implicit-присваивание // 1.1. Объявить экземпляр класса Rectangle Rectangle r1 = new Rectangle(1, 1, 3, 8); // 1.2. Вызвать оператор приведения типа () Line l1 = r1; // Line <= Rectangle l1.Print("l1"); // 2. Реализовать explicit-присваивание // 2.1. Объявить экземпляр класса Line Line l2 = new Line(5, 8, 1, 3); // 2.2. Выполнить явное присваивание Rectangle r2; r2 = (Rectangle)l2; // Rectangle <= Line r2.Print("r2"); Console.ReadKey(); } } }
Проанализируем вышеприведенный код.
Операторный метод, приводящий из типа Rectangle в тип Line, объявлен с ключевым словом implicit. В этом случае допускается неявное приведение
l1 = r1; // Line <= Rectangle
Операторный метод, выполняющий обратное присвоение из типа Line в тип Rectangle объявлен с ключевым словом explicit. Здесь нужно указывать только явное приведение
r2 = (Rectangle)l2; // Rectangle <= Line
Если не задать явное приведение, то компилятор выдаст ошибку.
После запуска программа выдает следующий результат
l1 => ( 1; 1) - ( 3; 8) r2 => x1 = 5; y1 = 8; x2 = 1; y2 = 3
⇑
1.4. Пример перегрузки оператора () для типов Point<=>Circle. Демонстрация приведения для разных видов типов: int<=Point, Circle<=Point, Point<=Circle, double<=Circle
В примере происходит преобразование между классами Point <=> Circle. В классе Point перегружаются методы
public static implicit operator int(Point pt); public static explicit operator Circle(Point pt);
В классе Circle перегружаются операторы
public static explicit operator double(Circle cr); public static implicit operator Point(Circle cr); public static implicit operator Circle(int value);
Данный пример наглядно показывает возможности перегрузки оператора () для взаимного приведения между значимыми стандартными типами (int, double) и собственными типами (Point, Circle).
Текст демонстрационной программы следующий.
using System; namespace ConsoleApp6 { // Класс, описывающий точку на координатной плоскости class Point { // Внутренние поля класса private double x, y; // Конструктор public Point(double x, double y) { this.x = x; this.y = y; } // Свойства доступа public double X { get { return x; } } public double Y { get { return y; } } // Метод, который выводит координаты точки public void Print(string msg) { Console.WriteLine(msg + " => ( " + x + "; " + y + ")"); } // Методы, перегружающие оператор приведения типа () // int <= Point: obj1 = obj2 public static implicit operator int(Point pt) { return (int)(pt.x + pt.y); // сумма координат } // Circle<=Point: obj1 = (Circle)obj2 public static explicit operator Circle(Point pt) { return new Circle(pt.X, pt.Y, 0.0); } } // Класс, описывающий окружность class Circle { // Внутренние поля private double x, y, radius; // Конструктор public Circle(double x, double y, double radius) { this.x = x; this.y = y; this.radius = radius; } // Свойства доступа к полям класса public double X { get { return x; } } public double Y { get { return y; } } public double Radius { get { return radius; } } // Метод, который выводит внутреннее состояние экземпляра public void Print(string msg) { System.Console.WriteLine(msg + " => x = " + x + "; y = " + y + "; radius = " + radius); } // Методы, которые перегружают оператор () // Явная перегрузка: double<=Circle; obj1 = (double)obj2 public static explicit operator double(Circle cr) { // Вернуть площадь окружности return Math.PI * cr.radius * cr.radius; } // Неявная перегрузка: Point<=Circle; obj1 = obj2 public static implicit operator Point(Circle cr) { return new Point(cr.X, cr.Y); } // Неявная перегрузка: Circle<=int; obj1 = obj2 public static implicit operator Circle(int value) { return new Circle(value, value, value); } } internal class Program { static void Main(string[] args) { // 1. Объявить экземпляры классов Point, Circle Point pt1 = new Point(2, 9); Circle cr1 = new Circle(1, 9, 8); // 2. Продемонстрировать методы приведения // 2.1. int <= Point - неявное int t = pt1; // вызывается Point.operator int(Point) Console.WriteLine("t = " + t); // 2.2. Circle <= Point - явное Circle cr2; cr2 = (Circle)pt1; // вызывается Point.operator Circle(Point) cr2.Print("cr2"); // 2.3. double <= Circle - явное double x = (double)cr1; // вызывается Circle.operator double(Circle) Console.WriteLine("x = " + x); // 2.4. Point <= Circle - неявное Point pt2; pt2 = cr1; // вызывается Circle.operator Point(Circle) pt2.Print("pt2"); // 2.5. Circle <= int - неявное Circle cr3; cr3 = 8; // вызывается Circle.operator Circle(int) cr3.Print("cr3"); Console.ReadKey(); } } }
Результат
t = 11 cr2 => x = 2; y = 9; radius = 0 x = 201.061929829747 pt2 => ( 1; 9) cr3 => x = 8; y = 8; radius = 8
⇑
1.5. Ограничения, накладываемые на оператор приведения типа ()
На оператор приведения типа накладываются следующие ограничения:
1. Если классы образуют иерархию и один из них есть производным от другого, то между этими классами невозможно провести приведение.
2. Приведение можно реализовывать только в одном из типов (классов):
- только в целевом типе;
- только в типе назначения.
⇑
2. Перегрузка операторов true, false. Пример
При перегрузке операторов true и false задается критерий истинности и фальши для типа данных, которым является текущий класс. После перегрузки, объекты этого типа (класса) можно использовать в логических выражениях и операторах if, while, do-while, for.
Операторы true и false перегружаются в паре. Общая форма перегрузки операторов имеет вид
class ClassName { ... public static bool operator true(ClassName obj) { // Код, который возвращает true если истина // ... } public static bool operator false(ClassName obj) { // Код, который возвращает false если истина, // иначе возвращается true // ... } }
Пример.
В примере объявляется класс Rectangle (Прямоугольник), формируемый на основе координат левого верхнего (x1; y1) и правого нижнего углов (x2; y2). В классе перегружаются операторы true и false. Операторы true и false определяют, является (не является) ли прямоугольник квадратом.
using System; namespace ConsoleApp6 { // Класс Прямоугольник class Rectangle { // Внутренние данные - координаты крайних углов прямоугольника private double x1, y1, x2, y2; // Конструктор public Rectangle(double x1, double y1, double x2, double y2) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } // Свойства доступа к полям класса public double X1 { get { return x1; } } public double Y1 { get { return y1; } } public double X2 { get { return x2; } } public double Y2 { get { return y2; } } // Метод, выводящий координаты прямоугольника public void Print(string msg) { Console.Write(msg + " => "); Console.WriteLine("( " + x1 + "; " + y1 + ") - ( " + x2 + "; " + y2 + ")"); } // Операторный метод, перегружающий оператор true public static bool operator true(Rectangle r) { return (Math.Abs(r.x1 - r.x2) == Math.Abs(r.y1 - r.y2)); } // Перегрузка оператора false public static bool operator false(Rectangle r) { return (Math.Abs(r.x1 - r.x2) != Math.Abs(r.y1 - r.y2)); } } internal class Program { static void Main(string[] args) { // 1. Объявить 2 экземпляра класса Rectangle Rectangle r1 = new Rectangle(1, 1, 3, 3); // это квадрат Rectangle r2 = new Rectangle(1, 2, 8, 0); // это не квадрат // 2. Вызов перегруженного оператора true if (r1) Console.WriteLine("r1 is square."); else Console.WriteLine("r1 is not square."); // 3. Вызов перегруженного оператора false if (r2) Console.WriteLine("r2 is square."); else Console.WriteLine("r2 is not square."); Console.ReadKey(); } } }
Результат
r1 is square. r2 is not square.
⇑
3. Перегрузка логических операторов &&, ||. Пример
Логические операторы && и || сами по себе не перегружаются. Однако они могут быть смоделированы в операторах & и | которые можно перегружать. Для этого требуется выполнение следующих условий:
- в классе должны быть перегружены операторы true и false;
- в классе должны быть перегружены логические операторы & и |;
- операторные методы (operator&() и operator|()) должны возвращать тип класса, в котором осуществляется перегрузка;
- параметрами операторных методов, перегружаемых операторами & и | должны быть ссылки на класс, в котором эти методы перегружаются.
Пример.
В следующем примере показывается перегрузка логических операторов &&, ||. Объявляется класс Point3D, реализующий точку в трехмерном пространстве. В классе объявляются:
- внутренние скрытые (private) поля x, y, z являющиеся координатами точки в трехмерном пространстве;
- конструктор Point3D();
- свойства X, Y, Z доступа соответственно к полям x, y, z;
- операторный метод operator true(), который перегружает оператор true. Метод возвращает значение true, если точка не лежит в начале координат (0; 0; 0);
- операторный метод operator false(), который перегружает оператор false. Метод возвращает значение false, если точка находится в начале координат;
- операторный метод operator|(), который перегружает оператор | (логическое «ИЛИ»). Этот операторный метод моделирует работу оператора ||;
- операторный метод operator&(), который перегружает оператор & (логические «И»). Этот операторный метод моделирует работу оператора &&;
- метод Print(), выводящий состояние экземпляра (координаты x, y, z).
using System; namespace ConsoleApp6 { // Класс Point3D - описывает точку в трехмерном пространстве. // Класс демонстрирует перегрузку операторов && и || class Point3D { // Координаты точки private double x, y, z; // Конструктор public Point3D(double x, double y, double z) { this.x = x; this.y = y; this.z = z; } // Свойства доступа к x, y, z public double X { get { return x; } } public double Y { get { return y; } } public double Z { get { return z; } } // Метод, который перегружает оператор true public static bool operator true(Point3D pt) { return !((pt.x == 0) && (pt.y == 0) && (pt.z == 0)); } // Метод, который перегружает оператор false public static bool operator false(Point3D pt) { return (pt.x == 0) && (pt.y == 0) && (pt.z == 0); } // Метод, который перегружает оператор & public static Point3D operator &(Point3D p1, Point3D p2) { if (((p1.x != 0) && (p1.y != 0) && (p1.z != 0)) && ((p2.x != 0) && (p2.y != 0) && (p2.z != 0))) return p2; return new Point3D(0, 0, 0); } // Метод, который перегружает оператор | public static Point3D operator |(Point3D p1, Point3D p2) { if (((p1.x != 0) || (p1.y != 0) || (p1.z != 0)) || ((p2.x != 0) || (p2.y != 0) || (p2.z != 0))) return p2; return new Point3D(0, 0, 0); } } internal class Program { static void Main(string[] args) { // 1. Создать 2 экземпляра класса Point3D Point3D pt1 = new Point3D(1, 1, 7); Point3D pt2 = new Point3D(0, 0, 4); // 2. Вызвать смоделированные перегруженные операторы && та || if (pt1 && pt2) Console.WriteLine("pt1 && pt2 => true"); else Console.WriteLine("pt1 && pt2 => false"); if (pt1 || pt2) Console.WriteLine("pt1 || pt2 => true"); else Console.WriteLine("pt1 || pt2 => false"); Console.ReadKey(); } } }
Результат
pt1 && pt2 => false pt1 || pt2 => true
⇑
Содержание
- Перегрузка операторов. Общие сведения. Перегрузка унарных операторов. –, !, ++, ––
- Перегрузка бинарных операторов. Перегрузка операторов +, –, *, /, %. Вложение произвольной логики при перегрузке
- Перегрузка операторов сравнения ==, !=, <, >, <=, >=. Перегрузка логических побитовых операторов
⇑