Перевантаження оператора приведення типу (). Перевантаження операторів 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
⇑
Споріднені теми
- Перевантаження операторів. Загальні відомості. Перевантаження унарних операторів –, !, ++, —
- Перевантаження бінарних операторів. Перевантаження операторів +, –, *, /, %. Вкладення довільної логіки при перевантаженні
- Перевантаження операторів порівняння ==, !=, >, <, >=, <=. Перевантаження логічних побітових операторів &, |, ^
⇑