Overloading the cast operator (). Overloading true–false operators. Overloading the logical operators &&, ||
Contents
- 1. Overloading the cast operator (). General information
- 1.1. Implicit cast Class1=Class2. General form. Example
- 1.2. Explicit cast Class1 = (Class1)Class2. Example
- 1.3. An example of operator overloading () for Line and Rectangle types. Demonstrates explicit and implicit type cast
- 1.4. An example of operator overloading () for types Point<=>Circle. Demonstration of types cast: int<=Point, Circle<=Point, Point<=Circle, double<=Circle
- 1.5. Restrictions imposed on the type cast operator ()
- 2. Overloading true, false operators. Example
- 3. Overloading of logical operators &&, ||. Example
- Related topics
Search other resources:
1. Overloading the cast operator (). General information
The () operator can be overloaded in a class when cast from one type to another.
Since the class in which the overload of the operator () is implemented is a type, the cast can have two implementations:
1. Cast from any type to the type of our class:
Class1 = Class2;
here
- Class1 – class in which operator () is overloaded;
- Class2 – another class.
2. Cast from the type of the class in which the operator () is overloaded to any arbitrary type
Class2 = Class1;
here
- Class1 – class in which operator () is overloaded;
- Class2 – class to which Class1 is cast.
Type cast is independent of whether the type being cast is a reference type or a value type.
As you know, type cast is:
- explicit – the case where data loss is possible as a result of the cast. For example, cast int=>short, int=>uint, double=>int, etc.;
- implicit – the case when it is not necessary to specify an explicit cast, since no data loss occurs (int=>int, Point=>Point, …).
Depending on the cast (explicit/implicit), the declaration of a method that overloads the cast operator () contains one of two words:
- implicit – specifies an implicit conversion. This conversion can be specified without the risk of loss of precision;
- explicit—specifies an explicit conversion. This conversion is used if there is a possibility of data loss or even an exception.
⇑
1.1. Implicit cast Class1=Class2. General form. Example
An implicit cast is performed when performing a simple assignment
obj1 = obj2;
here
- obj1 – an instance of some class Class1, which is the receiver of the value of an instance of obj2;
- obj2 – an instance of the source class Class2.
Given the above, the general form of using the implicit operator method operator()() in a class named Class1 is:
public static implicit operator Class1 (Class2 obj) { // Class1 <= Class2 // ... }
here
- Class1 – the class to which the cast is performed;
- Class2 – the class that acts as the initial type.
If an implicit cast Class1<=Class2 is declared in a class, then it is also allowed to specify an explicit cast in the assignment operation
obj1 = (Class1)obj2;
⇑
1.2. Explicit cast Class1 = (Class1)Class2. Example
Explicit type cast is necessary in cases where data loss occurs. However, this is not necessary when overloading the () operator in a class. The method of encoding a method that overloads the operator () is determined at one’s own discretion, depending on the task. For two instances, an explicit cast looks like this:
obj1 = (Class1)obj2;
here
- obj1 – an instance of class Class1, which is the receiver;
- obj2 – an instance of some class Class2 that is cast to type Class1.
When using explicit type cast, the declaration of an operator method that overloads the () operator is:
public static explicit operator Class1 (Class2 obj) { // Class1 <= Class2 // ... }
If a class declares an explicit cast from Class2 to Class1 (Class1<=Class2), then the implicit assignment operation
obj1 = obj2;
without specifying an explicit cast will give a compilation error.
⇑
1.3. An example of operator overloading () for Line and Rectangle types. Demonstrates explicit and implicit type cast
The example shows an overload of the () operator for the Line and Rectangle types.
Line => Rectangle Rectangle <= Line
In the Line class, fields with coordinates of extreme points are formed. These points can be thought of as the coordinates of the corners of the Rectangle. Thus, no data is lost, just one class type is converted to another.
using System; namespace ConsoleApp6 { // A class that describes a line class Line { // Coordinates of line points private double x1, y1, x2, y2; // Constructor public Line(double x1, double y1, double x2, double y2) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } // Access properties public double X1 { get { return x1; } } public double Y1 { get { return y1; } } public double X2 { get { return x2; } } public double Y2 { get { return y2; } } // Method that outputs the state of the Line object public void Print(string msg) { Console.Write(msg + " => "); Console.WriteLine("( " + x1 + "; " + y1 + ") - ( " + x2 + "; " + y2 + ")"); } // Method that overloads the Line <= Rectangle cast operator, // the implicit conversion is performed // Line <= Rectangle public static implicit operator Line(Rectangle r) { return new Line(r.X1, r.Y1, r.X2, r.Y2); } // Explicit conversion // Rectangle <= Line public static explicit operator Rectangle(Line l) { return new Rectangle(l.X1, l.Y1, l.X2, l.Y2); } } // Class that describes a rectangle class Rectangle { // Internal fields - coordinates of rectangle corners private double x1, y1, x2, y2; // Constructor public Rectangle(double x1, double y1, double x2, double y2) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } // Access properties public double X1 { get { return x1; } } public double Y1 { get { return y1; } } public double X2 { get { return x2; } } public double Y2 { get { return y2; } } // Method that outputs the internal state of an instance of a class 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 call // 1. Implement implicit assignment // 1.1. Declare the object of Rectangle class Rectangle r1 = new Rectangle(1, 1, 3, 8); // 1.2. Call the type cast operator () Line l1 = r1; // Line <= Rectangle l1.Print("l1"); // 2. Implement explicit assignment // 2.1. Declare an instance of the Line class Line l2 = new Line(5, 8, 1, 3); // 2.2. Perform explicit assignment Rectangle r2; r2 = (Rectangle)l2; // Rectangle <= Line r2.Print("r2"); Console.ReadKey(); } } }
Let’s analyze the above code.
The operator method that casts from the Rectangle type to the Line type is declared with the implicit keyword. In this case, an implicit cast is allowed.
l1 = r1; // Line <= Rectangle
The operator method that performs the reverse assignment from the Line type to the Rectangle type is declared with the explicit keyword. Here you need to specify only an explicit cast
r2 = (Rectangle)l2; // Rectangle <= Line
If you do not specify an explicit cast, the compiler will generate an error.
After running the program gives the following result
l1 => ( 1; 1) - ( 3; 8) r2 => x1 = 5; y1 = 8; x2 = 1; y2 = 3
⇑
1.4. An example of operator overloading () for types Point<=>Circle. Demonstration of types cast: int<=Point, Circle<=Point, Point<=Circle, double<=Circle
The example converts between Point <=> Circle classes. The following methods are overloaded in the Point class
public static implicit operator int(Point pt); public static explicit operator Circle(Point pt);
In the Circle class, the following operators are overloaded
public static explicit operator double(Circle cr); public static implicit operator Point(Circle cr); public static implicit operator Circle(int value);
This example clearly shows the possibilities of overloading the operator () for co-casting between significant standard types (int, double) and native types (Point, Circle).
The text of the demo program is as follows.
using System; namespace ConsoleApp6 { // Class describing a point on a coordinate plane class Point { // Internal class fields private double x, y; // Constructor public Point(double x, double y) { this.x = x; this.y = y; } // Access properties public double X { get { return x; } } public double Y { get { return y; } } // Method that prints the coordinates of a point public void Print(string msg) { Console.WriteLine(msg + " => ( " + x + "; " + y + ")"); } // Methods that overload the cast operator () // int <= Point: obj1 = obj2 public static implicit operator int(Point pt) { return (int)(pt.x + pt.y); // sum of coordinates } // Circle<=Point: obj1 = (Circle)obj2 public static explicit operator Circle(Point pt) { return new Circle(pt.X, pt.Y, 0.0); } } // A class that describes a circle class Circle { // Internal fields private double x, y, radius; // Constructor public Circle(double x, double y, double radius) { this.x = x; this.y = y; this.radius = radius; } // Properties for accessing class fields public double X { get { return x; } } public double Y { get { return y; } } public double Radius { get { return radius; } } // Method that outputs the internal state of instance public void Print(string msg) { System.Console.WriteLine(msg + " => x = " + x + "; y = " + y + "; radius = " + radius); } // Methods that overload the () operator // Explicit overload: double<=Circle; obj1 = (double)obj2 public static explicit operator double(Circle cr) { // Return the area of a circle return Math.PI * cr.radius * cr.radius; } // Implicit overload: Point<=Circle; obj1 = obj2 public static implicit operator Point(Circle cr) { return new Point(cr.X, cr.Y); } // Implicit overload: 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. Declare instances of classes Point, Circle Point pt1 = new Point(2, 9); Circle cr1 = new Circle(1, 9, 8); // 2. Demonstrate cast methods // 2.1. int <= Point - implicit int t = pt1; // calls Point.operator int(Point) Console.WriteLine("t = " + t); // 2.2. Circle <= Point - explicit Circle cr2; cr2 = (Circle)pt1; // calls Point.operator Circle(Point) cr2.Print("cr2"); // 2.3. double <= Circle - explicit double x = (double)cr1; // calls Circle.operator double(Circle) Console.WriteLine("x = " + x); // 2.4. Point <= Circle - implicit Point pt2; pt2 = cr1; // calls Circle.operator Point(Circle) pt2.Print("pt2"); // 2.5. Circle <= int - implicit Circle cr3; cr3 = 8; // calls Circle.operator Circle(int) cr3.Print("cr3"); Console.ReadKey(); } } }
Result
t = 11 cr2 => x = 2; y = 9; radius = 0 x = 201.061929829747 pt2 => ( 1; 9) cr3 => x = 8; y = 8; radius = 8
⇑
1.5. Restrictions imposed on the type cast operator ()
The following restrictions apply to the cast operator:
1. If classes form a hierarchy and one of them is derived from the other, then it is impossible to cast between these classes.
2. The cast can only be implemented in one of the types (classes):
- only in the target type;
- only in the destination type.
⇑
2. Overloading true, false operators. Example
Overloading the true and false operators sets a test for true and false for the data type, which is the current class. After overloading, objects of this type (class) can be used in logical expressions and if, while, do-while, for statements.
The true and false operators are overloaded in pairs. The general form of operator overloading is
class ClassName { ... public static bool operator true(ClassName obj) { // Code that returns true if true // ... } public static bool operator false(ClassName obj) { // Code that returns false if true, otherwise returns true // ... } }
Example.
The example declares a class Rectangle, formed on the basis of the coordinates of the upper left (x1; y1) and bottom right corners (x2; y2). The class overloads the true and false operators. The true and false operators determine whether a rectangle is (or isn’t) a square.
using System; namespace ConsoleApp6 { // Class Rectangle class Rectangle { // The internal data is the coordinates of the extreme corners of the rectangle private double x1, y1, x2, y2; // Constructor public Rectangle(double x1, double y1, double x2, double y2) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } // Field access properties of the class public double X1 { get { return x1; } } public double Y1 { get { return y1; } } public double X2 { get { return x2; } } public double Y2 { get { return y2; } } // A method that displays the coordinates of a rectangle public void Print(string msg) { Console.Write(msg + " => "); Console.WriteLine("( " + x1 + "; " + y1 + ") - ( " + x2 + "; " + y2 + ")"); } // Operator method that overloads the true operator public static bool operator true(Rectangle r) { return (Math.Abs(r.x1 - r.x2) == Math.Abs(r.y1 - r.y2)); } // Overloading the false operator 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. Declare 2 instances of the Rectangle class Rectangle r1 = new Rectangle(1, 1, 3, 3); // this is a square Rectangle r2 = new Rectangle(1, 2, 8, 0); // this is not a square // 2. Call the overloaded operator true if (r1) Console.WriteLine("r1 is square."); else Console.WriteLine("r1 is not square."); // 3. Call the overloaded operator false if (r2) Console.WriteLine("r2 is square."); else Console.WriteLine("r2 is not square."); Console.ReadKey(); } } }
Result
r1 is square. r2 is not square.
⇑
3. Overloading of logical operators &&, ||. Example
The logical operators && and || themselves are not overloaded. However, they can be modeled in the & and | that can be overloaded. This requires the following conditions to be met:
- in the class the operators true and false must be overloaded;
- the logical operators & and | must be overloaded in the class;
- operator methods (operator&() and operator|()) must return the type of the class in which the overload is performed;
- parameters of operator methods must be references to the class in which these methods are overloaded.
Example.
The following example shows the overloading of the logical operators &&, ||. The Point3D class is declared, which implements a point in three-dimensional space. In the class are declared:
- internal hidden (private) fields x, y, z which are coordinates of a point in three-dimensional space;
- constructor Point3D();
- properties X, Y, Z of access, respectively, to the fields x, y, z;
- the operator method operator true(), which overloads the operator true. The method returns true if the point is not at the origin (0; 0; 0);
- operator method operator false(), which overloads operator false. The method returns false if the point is at the origin;
- operator method operator|(), which overloads the operator | (logical “OR”). This operator method models the operation of the || operator;
- operator method operator&(), which overloads operator & (logical “AND”). This operator method models the operation of the && operator;
- the Print() method that displays the state of the instance (x, y, z coordinates).
using System; namespace ConsoleApp6 { // Class Point3D - describes a point in three-dimensional space. // The class demonstrates overloading of the && and || operators class Point3D { // Point coordinates private double x, y, z; // Constructor public Point3D(double x, double y, double z) { this.x = x; this.y = y; this.z = z; } // Properties of access to x, y, z. public double X { get { return x; } } public double Y { get { return y; } } public double Z { get { return z; } } // Method that overloads the true operator public static bool operator true(Point3D pt) { return !((pt.x == 0) && (pt.y == 0) && (pt.z == 0)); } // Method that overloads the false operator public static bool operator false(Point3D pt) { return (pt.x == 0) && (pt.y == 0) && (pt.z == 0); } // Method that overloads operator & 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); } // Method that overloads operator | 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. Create 2 instances of the Point3D class Point3D pt1 = new Point3D(1, 1, 7); Point3D pt2 = new Point3D(0, 0, 4); // 2. Invoke the simulated overloaded operators && and || 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(); } } }
Result
pt1 && pt2 => false pt1 || pt2 => true
⇑
Related topics
- Operators overloading. General information. Overloading of unary operators –, !, ++, —
- Overloading binary operators. Operator overloading +, –, *, /, %. Nesting arbitrary logic on overload
- Overloading comparison operators ==, !=, <, >, <=, >=. Overloading boolean bitwise operators &, |, ^
⇑