Overloading the Logical Operators


As you know, C# defines the following logical operators: &, |, !, &&, and | |. Of these, only the &, |, and ! can be overloaded. By following certain rules, however, the benefits of the short-circuit && and | | can still be obtained. Each situation is examined here.

A Simple Approach to Overloading the Logical Operators

Let’s begin with the simplest situation. If you will not be making use of the short-circuit logical operators, then you can overload & and | as you would intuitively think, with each returning a bool result. An overloaded ! will also usually return a bool result.

Here is an example that overloads the !, &, and | logical operators for objects of type ThreeD. As before, each assumes that a ThreeD object is true if at least one coordinate is non-zero. If all three coordinates are zero, then the object is false.

 // A simple way to overload !, |, and & for ThreeD. using System; // A three-dimensional coordinate class. class ThreeD {   int x, y, z; // 3-D coordinates   public ThreeD() { x = y = z = 0; }   public ThreeD(int i, int j, int k) { x = i; y = j; z = k; }   // Overload |.   public static bool operator |(ThreeD op1, ThreeD op2)   {     if( ((op1.x != 0) || (op1.y != 0) || (op1.z != 0)) |        ((op2.x != 0) || (op2.y != 0) || (op2.z != 0)) )       return true;     else       return false;   }   // Overload &.   public static bool operator &(ThreeD op1, ThreeD op2)   {     if( ((op1.x != 0) && (op1.y != 0) && (op1.z != 0)) &        ((op2.x != 0) && (op2.y != 0) && (op2.z != 0)) )       return true;     else       return false;   }   // Overload !.   public static bool operator !(ThreeD op)   {     if((op.x != 0) || (op.y != 0) || (op.z != 0))       return false;     else return true;   }   // Show X, Y, Z coordinates.   public void show()   {     Console.WriteLine(x + ", " + y + ", " + z);   } } class TrueFalseDemo {   public static void Main() {     ThreeD a = new ThreeD(5, 6, 7);     ThreeD b = new ThreeD(10, 10, 10);     ThreeD c = new ThreeD(0, 0, 0);     Console.Write("Here is a: ");     a.show();     Console.Write("Here is b: ");     b.show();     Console.Write("Here is c: ");     c.show();     Console.WriteLine();     if(!a) Console.WriteLine("a is false.");     if(!b) Console.WriteLine("b is false.");     if(!c) Console.WriteLine("c is false.");     Console.WriteLine();     if(a & b) Console.WriteLine("a & b is true.");     else Console.WriteLine("a & b is false.");     if(a & c) Console.WriteLine("a & c is true.");     else Console.WriteLine("a & c is false.");     if(a | b) Console.WriteLine("a | b is true.");     else Console.WriteLine("a | b is false.");     if(a | c) Console.WriteLine("a | c is true.");     else Console.WriteLine("a | c is false.");   } }

The output from the program is shown here:

 Here is a: 5, 6, 7 Here is b: 10, 10, 10 Here is c: 0, 0, 0 c is false. a & b is true. a & c is false. a | b is true. a | c is true.

In this approach, the &, |, and ! operator methods each return a bool result. This is necessary if the operators are to be used in their normal manner (that is, in places that expect a bool result). Recall that for all built-in types, the outcome of a logical operation is a value of type bool. Thus, having the overloaded versions of these operators return type bool is a rational approach. Unfortunately, this approach works only if you will not be needing the short-circuit operators.

Enabling the Short-Circuit Operators

To enable the use of the && and | | short-circuit operators, you must follow four rules. First, the class must overload & and |. Second, the return type of the overloaded & and | methods must be an object of the class for which the operators are being overloaded. Third, each parameter must be a reference to an object of the class for which the operator is being overloaded. Fourth, the true and false operators must be overloaded for the class. When these conditions have been met, the short-circuit operators automatically become available for use.

The following program shows how to properly implement the & and | for the ThreeD class so that the short-circuit operators && and | | are available.

 /* A better way to overload !, |, and & for ThreeD.    This version automatically enables the && and || operators. */ using System; // A three-dimensional coordinate class. class ThreeD {   int x, y, z; // 3-D coordinates   public ThreeD() { x = y = z = 0; }   public ThreeD(int i, int j, int k) { x = i; y = j; z = k; }   // Overload | for short-circuit evaluation.   public static ThreeD operator |(ThreeD op1, ThreeD op2)   {     if( ((op1.x != 0) || (op1.y != 0) || (op1.z != 0)) |        ((op2.x != 0) || (op2.y != 0) || (op2.z != 0)) )       return new ThreeD(1, 1, 1);     else       return new ThreeD(0, 0, 0);   }   // Overload & for short-circuit evaluation.   public static ThreeD operator &(ThreeD op1, ThreeD op2)   {     if( ((op1.x != 0) && (op1.y != 0) && (op1.z != 0)) &        ((op2.x != 0) && (op2.y != 0) && (op2.z != 0)) )       return new ThreeD(1, 1, 1);     else       return new ThreeD(0, 0, 0);   }      // Overload !.   public static bool operator !(ThreeD op)   {     if(op) return false;     else return true;   }   // Overload true.   public static bool operator true(ThreeD op) {     if((op.x != 0) || (op.y != 0) || (op.z != 0))       return true; // at least one coordinate is non-zero     else       return false;   }   // Overload false.   public static bool operator false(ThreeD op) {     if((op.x == 0) && (op.y == 0) && (op.z == 0))       return true; // all coordinates are zero     else       return false;   }   // Show X, Y, Z coordinates.   public void show()   {     Console.WriteLine(x + ", " + y + ", " + z);   } } class TrueFalseDemo {   public static void Main() {     ThreeD a = new ThreeD(5, 6, 7);     ThreeD b = new ThreeD(10, 10, 10);     ThreeD c = new ThreeD(0, 0, 0);     Console.Write("Here is a: ");     a.show();     Console.Write("Here is b: ");     b.show();     Console.Write("Here is c: ");     c.show();     Console.WriteLine();     if(a) Console.WriteLine("a is true.");     if(b) Console.WriteLine("b is true.");     if(c) Console.WriteLine("c is true.");     if(!a) Console.WriteLine("a is false.");     if(!b) Console.WriteLine("b is false.");     if(!c) Console.WriteLine("c is false.");     Console.WriteLine();     Console.WriteLine("Use & and |");     if(a & b) Console.WriteLine("a & b is true.");     else Console.WriteLine("a & b is false.");     if(a & c) Console.WriteLine("a & c is true.");     else Console.WriteLine("a & c is false.");     if(a | b) Console.WriteLine("a | b is true.");     else Console.WriteLine("a | b is false.");     if(a | c) Console.WriteLine("a | c is true.");     else Console.WriteLine("a | c is false.");     Console.WriteLine();     // now use short-circuit ops     Console.WriteLine("Use short-circuit && and ||");     if(a && b) Console.WriteLine("a && b is true.");     else Console.WriteLine("a && b is false.");     if(a && c) Console.WriteLine("a && c is true.");     else Console.WriteLine("a && c is false.");     if(a || b) Console.WriteLine("a || b is true.");     else Console.WriteLine("a || b is false.");     if(a || c) Console.WriteLine("a || c is true.");     else Console.WriteLine("a || c is false.");   } }

The output from the program is shown here:

 Here is a: 5, 6, 7 Here is b: 10, 10, 10 Here is c: 0, 0, 0 a is true. b is true. c is false. Use & and | a & b is true. a & c is false. a | b is true. a | c is true. Use short-circuit && and || a && b is true. a && c is false. a || b is true. a || c is true.

Let’s look closely at how the & and | are implemented. They are shown here:

 // Overload | for short-circuit evaluation. public static ThreeD operator |(ThreeD op1, ThreeD op2) {   if( ((op1.x != 0) || (op1.y != 0) || (op1.z != 0)) |      ((op2.x != 0) || (op2.y != 0) || (op2.z != 0)) )     return new ThreeD(1, 1, 1);   else     return new ThreeD(0, 0, 0); } // Overload & for short-circuit evaluation. public static ThreeD operator &(ThreeD op1, ThreeD op2) {   if( ((op1.x != 0) && (op1.y != 0) && (op1.z != 0)) &      ((op2.x != 0) && (op2.y != 0) && (op2.z != 0)) )     return new ThreeD(1, 1, 1);   else     return new ThreeD(0, 0, 0); }

Notice first that both now return an object of type ThreeD. Pay attention to how this object is generated. If the outcome of the operation is true, then a true ThreeD object (one in which at least one coordinate is non-zero) is created and returned. If the outcome is false, then a false object is created and returned. Thus, in a statement like this:

 if(a & b) Console.WriteLine("a & b is true."); else Console.WriteLine("a & b is false.");

the outcome of a & b is a ThreeD object, which in this case is a true object. Since the operators true and false are defined, this resulting object is subjected to the true operator, and a bool result is returned. In this case the result is true and the if succeeds.

Because the necessary rules have been followed, the short-circuit operators are now available for use on ThreeD objects. They work like this. The first operand is tested by using operator true (for ||) or operator false (for &&). If it can determine the outcome of the operation, then the corresponding & or | is not evaluated. Otherwise, the corresponding overloaded & or | is used to determine the result. Thus, using a && or || causes the corresponding & or | to be invoked only when the first operand cannot determine the outcome of the expression. For example, consider this statement from the program:

 if(a || c) Console.WriteLine("a || c is true.");

The true operator is first applied to a. Since a is true in this situation, there is no need to use the | operator method. However, if the statement were rewritten like this:

 if(c || a) Console.WriteLine("c || a is true.");

then the true operator would first be applied to c, which in this case is false. Thus, the | operator method would be invoked to determine if a was true (which it is in this case).

Although you might at first think that the technique used to enable the short-circuit operators is a bit convoluted, it makes sense if you think about it a bit. By overloading true and false for a class, you enable the compiler to utilize the short-circuit operators without having to explicitly overload either. Furthermore, you gain the ability to use objects in conditional expressions. In general, unless you need a very narrow implementation of & and |, you are better off creating a full implementation.




C# 2.0(c) The Complete Reference
C# 2.0: The Complete Reference (Complete Reference Series)
ISBN: 0072262095
EAN: 2147483647
Year: 2006
Pages: 300

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net