Chapter 7

 

Operators

 

We all like to express ourselves in different ways. Some smile, some laugh, some screech and some even shout. The C# programming language lays a lot of emphasis on expressions and has one of the largest chapters in its documentation devoted to it. An expression is a sequence of operators and operands providing some computational activity.

 

An expression can be built in n number of ways. It can be as simple as a value resulting in a certain type. It can also consist of a variable with a type. A namespace can also occur in an expression but it must be only at the beginning. A type like a namespace can also occur on the left hand side of an expression. The usual gang of idiots like properties, indexers, properties can also be included in an expression. However, the final result of an expression can never ever be a namespace, type, method group or event access. These can only occur in intermediate results.

 

Expressions are not conjured out of thin air but are made up of only two entities, operators and what we offer them in homage, operands. Operands can include constants, variables, indexers etc. The second entity, operators is what this chapter is dedicated to.

 

 

Checked and Unchecked Operators

 

a.cs

class zzz

{

byte i = 255;

byte j = 256;

public static void Main()

{

byte k = -1;

}

}

 

Compiler Error

a.cs(7,10): error CS0031: Constant value '-1' cannot be converted to a 'byte'

a.cs(4,10): error CS0031: Constant value '256' cannot be converted to a 'byte'

 

A byte can only store values from 0 to 255. We have initialized i to the largest possible value a byte can store i.e. 255. For the second variable, we try and go beyond the range that a byte can handle. Well, the compiler comes back screaming at us with an error. This is the behaviour in the case of variable j. This is enough proof that the compiler checks the values, the variables are being initialized to. For that matter, it could be a local variable created in a function or an instance variable created out of a function. Come what may, C# does not allow you to take a byte and give it a value falling outside its range of 0 to 255.

 

a.cs

class zzz

{

public static void Main()

{

byte k = (byte)256;

byte l = (byte)257;

byte m = (byte)514;

System.Console.WriteLine(k + " " + l + " " + m);

}

}

Compiler Error

a.cs(5,11): error CS0221: Constant value '256' cannot be converted to a 'byte' (use 'unchecked' syntax to override)

a.cs(6,11): error CS0221: Constant value '257' cannot be converted to a 'byte' (use 'unchecked' syntax to override)

a.cs(7,11): error CS0221: Constant value '514' cannot be converted to a 'byte' (use 'unchecked' syntax to override)

 

However, one way out is by using a cast. A cast is some data type within () brackets. The object on the right temporarily is converted to the data type within the cast. It is not like an operation where you get a permanent scar. This conversion is forgotten when you leave the statement. No permanent damage or change is carried out on the object.

 

By casting, we are commanding the compiler to break or overlook its own rules. C# realizes that 256 is larger than 255, so it will keep subtracting the number from 256. The resultant value is the number assigned to the byte variables.

 

To place it more accurately, the compiler divides the number by 256 and the remainder is the assigned value to the variable. Pure mathematics in school revealed that when any number is divided by another number lets say 100, the remainder is always less than 100 (the divisor). This remainder is now a byte that can be equated to the byte variables on the left.

 

The error demands an unchecked modifier while casting.

 

a.cs

class zzz

{

public static void Main()

{

byte k = unchecked((byte)256);

byte l = unchecked((byte)257);

byte m = unchecked((byte)514);

System.Console.WriteLine(k + " " + l + " " + m);

}

}

 

Output

0 1 2

 

a.cs

class zzz {

int b = 1000000;

int c = 1000000;

public static void Main()

{

int j;

zzz a = new zzz();

j = a.abc(a.b,a.c);

System.Console.WriteLine(j);

j = a.pqr(a.b,a.c);

System.Console.WriteLine(j);

a.xyz(a.b,a.c);

}

int abc( int x, int y)

{

return x*y;

}

int pqr( int x, int y)

{

return unchecked(x*y);

}

int xyz( int x, int y)

{

return checked(x*y);

}

}

 

Output

-727379968

-727379968

 

Exception occurred: System.OverflowException: An exception of type System.OverflowException was thrown.

   at zzz.Main()

 

There are two operators in the C# programming language called checked and unchecked. They deal with problems of overflow or underflow in values. In the above examples, the compiler is aware of the problem at compile time. A vast majority of us believe that computers are highly intelligent. The same people also believe in the tooth fairy. What we would like to say very emphatically is that the compilers do not go far enough in understanding what our program is up to. They apply a simple set of rules and if our program fails them, an error is flagged.

 

By default, C# writes the operator unchecked before all our expressions. Thus the functions abc and pqr behave in the same way.

 

The compiler adds a lot of its code to the code we write as well as rewrites a major part of it too. In the function abc or pqr, the compiler does not actually replace the variables with their values while compiling. If it did so, it would come back and inform us about an overflow. The unchecked operator behaves like our parents. They normally let us do what we want as they believe we are old enough to decide our fate. In this case the compiler does assume you know what you are doing and simply gives you the wrong answer of overflow results.

 

Remember the compiler does not hand run your code. If we use the checked operator, the compiler will now behave in a different way at run time but not at compile time. It will now generate an exception if an overflow occurs. Thus, it keeps a watchful eye on your program at runtime. It is advisable to use the checked operator, as we do not know what values our variables may hold. Also, please catch the exception thrown to prevent the error message box showing up as it will scare the shits of the user.

 

a.cs

class zzz

{

const int x = 1000000;

const int y = 1000000;

static int abc() {

return checked(x * y);               

}

static int pqr() {

return unchecked(x * y);

}

static int xyz() {

return x * y;                                         

}

int aaa() {

return x * y;                                         

}

static void Main()

{

}

}

 

Compiler Error

a.cs(6,16): error CS0220: The operation overflows at compile time in checked mode

a.cs(12,8): error CS0220: The operation overflows at compile time in checked mode

a.cs(15,8): error CS0220: The operation overflows at compile time in checked mode

 

C# treats a const variable very differently from a normal one. In the case of a const, the compiler is cent percent sure that the value will never change.

 

Now C# hits the ceiling as in the function abc, it detects an overflow. It gives an error about the impending danger. In the case of function pqr, using unchecked, we have informed the compiler not to bother us as we are old enough to understand what we are doing; even though it is blatantly obvious that we are not in this case. C# bows to our wishes and issues no error message.

 

Under normal circumstances, when one of these operators is not specified for compile time checks, by default,  the reverse of it is applicable for the run time checks. At compile time, the checked operator is on for const variables by default, irrespective of the modifier static. This explains the last two errors. Thus for compile time checks the default is checked for const variables and the compiler will try as hard as possible to check for overflows. However, it will never replace variables with values.

 

 

 

a.cs

class zzz

{

public const int i = checked((int)2147483648);

public const int j = unchecked((int)2147483648);

}

 

Compiler Error

a.cs(3,31): error CS0221: Constant value '2147483648' cannot be converted to a 'int' (use 'unchecked' syntax to override)

 

a.cs

class zzz

{

public int k = unchecked((int)2147483648);

public int l = (int)2147483648;

public int m = checked((int)2147483648);

}

 

Compiler Error

a.cs(4,17): error CS0221: Constant value '2147483648' cannot be converted to a 'int' (use 'unchecked' syntax to override)

a.cs(5,25): error CS0221: Constant value '2147483648' cannot be converted to a 'int' (use 'unchecked' syntax to override)

 

The only difference between the two programs is that in the first program, the variables are const whereas in the second program they are not. The error messages are the same. We could not combine them into one program as at times the compiler stops at the first error message it encounters. Sometimes it does not display all of them.

 

We are trying to cast a number larger than an int to an int. Thus we see the error. Reduce the number by one and the error like a nightmare disappears. This happens only for checked and not unchecked.

 

Remember for compile time checks the default is checked in case you have become absentminded along the way. The ++, --  operators act on overflow in the same way.

 

a.cs

class zzz

{

public static void Main()

{

int k = 65537;

short j = (short)k;

System.Console.WriteLine(j);

short l = checked((short)k);

System.Console.WriteLine(l);

}

}

 

Output

1

Exception occurred: System.OverflowException: An exception of type System.OverflowException was thrown.

   at zzz.Main()

 

The same rule also applies to conversions. We cannot directly convert an int to a short as the short is smaller in range of values than an int. Hence the cast. The compiler is oblivious of the error we have made and when we run the program, the short j gets truncated. This is so because the number gets divided by 65536, but as the default is unchecked no exception is thrown. In the second case, however, we have explicitly used the operator checked.

 

Remember, the rules for a constant and non-constant expression are different. Run time behaviour can be changed by compile time switches. What we've learnt is the default behaviour as of this version. The behaviour can either be checked or unchecked but there is no in between option. The checked and unchecked operators are not nosy parkers. They restrict themselves only to the () brackets. Their domain does not extend beyond that.

 

Is operator

 

a.cs

class yyy

{

}

class zzz

{

public static void Main()

{

yyy a  = new yyy();

if ( a is yyy)

System.Console.WriteLine("yyy");

if ( a is System.String)

System.Console.WriteLine("string");

if ( a is object)

System.Console.WriteLine("object");

}

}

 

Compiler Warning

a.cs(9,6): warning CS0183: The given expression is always of the provided ('yyy') type

a.cs(11,6): warning CS0184: The given expression is never of the provided ('string') type

a.cs(13,6): warning CS0183: The given expression is always of the provided ('object') type

 

Output

yyy

object

 

The 'is' operator is used to determine the run time type of an object or its compatibility with another type. It returns either a true or false, that is, a boolean. As the object a is an instance of class yyy, 'a is yyy' is obviously true. Also, a not being a string will not execute the second System.Writeln. Finally, as all classes derive from object the last if results in a true hence object is displayed.

 

The warnings are issued as even a blind man will tell you that the conditions in the if statement are determinable at run time. Anything that the compiler can't figure out and can be determined at run time is flagged as a warning.

 

a.cs

class yyy

{

}

class zzz

{

public static void Main()

{

if ( yyy is a)

System.Console.WriteLine("yyy");

}

}

 

Compiler Error

a.cs(8,6): error CS0118: 'yyy' denotes a 'class' where a 'variable' was expected

 

The operands to the 'is' operator cannot be reversed. It first requires the name of the object and then the class or type name.

 

a.cs

class yyy

{

}

class xxx

{

}

class zzz

{

public static void Main()

{

zzz a = new zzz();

yyy b = new yyy();

xxx c = new xxx();

a.abc(b);

a.abc(c);

a.abc(20);

a.abc("hi");

}

public void abc(object o)

{

if ( o is yyy)

System.Console.WriteLine("yyy " + o);

if ( o is xxx )

System.Console.WriteLine("yyy " + o);

if ( o is string )

System.Console.WriteLine("yyy " + o);

}

}

 

Output

yyy yyy

yyy xxx

yyy hi

 

The compiler gives us no warnings. Whenever we call a function, we need to be very particular about the data type of the parameter or else it will generate an error. The first exception to this rule can be when objects are cast to each other. The second exception can be with objects that are derived from each other. As all classes derive from the Object class, the parameter set in function abc can be of object type, which means that abc can now receive different parameter types. It will not throw any error.

 

We have three is operators checking whether o is a yyy, xxx or string. The WriteLine function also displays the object. Remember we are checking o with different class names and the if statement will be true only once in each case.   

 

a.cs

class yyy

{

}

class xxx

{

}

class zzz

{

public static void Main()

{

zzz a = new zzz();

xxx c = new xxx();

if (c is yyy)

System.Console.WriteLine("yyy " + c);

}

}

Compiler Warning

a.cs(13,5): warning CS0184: The given expression is never of the provided ('yyy') type

 

In the above case, all that we receive is a warning. The object c is an instance of xxx and not yyy. There is no way for the compiler to convert a xxx object into a yyy object. The warning illustrates the wisdom of the compiler.

 

a.cs

class yyy

{

}

class xxx

{

public static explicit operator yyy (xxx a)

{

return new yyy();

}

}

class zzz

{

public static void Main()

{

xxx c = new xxx();

if (c is yyy)

System.Console.WriteLine("yyy " + c);

else

System.Console.WriteLine("false " + c);

}

}

 

Compiler Warning

a.cs(17,5): warning CS0184: The given expression is never of the provided ('yyy') type

 

Output

false xxx

 

In this program, we thought the explicit operator introduced would convert c, an xxx object to yyy. We pleaded hard enough but it just did not help. We replaced the explicit modifier with implicit but the compiler as adamant it is, refused to convert the xxx object into a yyy object. We should have read the warning more carefully.

 

a.cs

class yyy

{

}

class xxx

{

public static implicit operator yyy (xxx a)

{

System.Console.WriteLine("operator");

return new yyy();

}

}

class zzz

{

public static void Main() {

zzz a = new zzz();

xxx c = new xxx();

a.abc(c);

}

public void abc(yyy o)

{

if (o is yyy)

System.Console.WriteLine("yyy " + o);

}

}

 

Compiler Warning

a.cs(21,5): warning CS0183: The given expression is always of the provided ('yyy') type

 

Output

operator

yyy yyy

 

We now reverted to our earlier program where we passed the object as a type yyy class, yet the compiler gives us the same old warning. The compiler can convert an xxx to a yyy thanks to the operator. If we change the parameter in abc from class yyy to object, the is will   still be false.

The return value of an is operator notifies whether the object can be converted to a particular data type or not. The datatype follows the is operator whereas the object precedes it.  The is operator ignores user defined conversions and only looks at reference conversions. It can be used with value and reference types.

 

The as operator

 

a.cs

class zzz

{

public static void Main()

{

string s;

s =  "hi" as string;

System.Console.WriteLine(s);

s =  100 as string;

System.Console.WriteLine(s);

}

}

 

Compiler Error

a.cs(8,6): error CS0039: Cannot convert type 'int' to 'string' via a built-in conversion

 

The as operator behaves like a cast. It converts one data type into another. Thus in the first case, it converts "hi" into a string. As hi can be converted to a string, the value of s is hi. However, a 100 cannot be converted into a string and thus we get an error. A cast normally throws an exception whereas the as operator returns a error.

 

a.cs

class zzz {

public static void Main()

{

int i = 100 as int;

}

}

 

 

Compiler Error

a.cs(4,9): error CS0077: The as operator must be used with a reference type ('int' is a value type)

 

The error message for once makes it very clear that the as operator does not accept a value type like int but requires reference types like string. The earlier rules on user supplied conversions et all apply verbatim here also.

 

The Conditional Operator

 

a.cs

class zzz {

public static void Main() {

int i = 10;

string  j;

j = i >= 20 ? "hi" : "bye";

System.Console.WriteLine(j);

j = i >= 2 ? "hi" : "bye";

System.Console.WriteLine(j);

}

}

 

Output

bye

hi

 

The conditional operator is also called the ternary operator as it takes three operands. The first is the condition to be checked. If it results in true then the answer lies between the ? and :. If it evaluates to false, the answer is within the : to the semi colon. Thus the ? : operator operates like an if else on one line. As the C programming language offered us this operator and C++ followed suit, then how could C# refuse. It is available but use it at your own risk.

 

a.cs

class zzz {

public static void Main() {

int i = 10;

string  j;

j = i >= 20 ? 7 > 3 ? "no"  : "bye" : "yes" ;

System.Console.WriteLine(j);

j = i >= 20 ? 7 < 3 ? "no"  : "bye" : "yes" ;

System.Console.WriteLine(j);

j = i <= 20 ? 7 < 3 ? "no"  : "bye" : "yes" ;

System.Console.WriteLine(j);

j = i <= 20 ? 7 > 3 ? "no"  : "bye" : "yes" ;

System.Console.WriteLine(j);

}

}

 

Output

yes

yes

bye

no

 

The ?: operator is right associative, which means that it gets evaluated from right to left. In the first case, the compiler only sees the condition i >= 20. As it is false everything in between the ? : is ignored and the string j contains yes. In the second case, the same rule applies and the compiler misses the fact that we have changed something from the ? to colon. In the third case, the condition is true, the compiler proceeds to the ? onwards and here sees another conditional operator. As it evaluates to false, the result is bye and in the last case it evaluates to true and thus the result is no.

 

The type of the conditional operator depends on the type of its two operands. In the above case, the type is string as both the operands are strings.

 

a.cs

class zzz

{

public static void Main()

{

object i;

xxx x = new xxx();

yyy y = new yyy();

i =  7 > 3  ? x : y ;

System.Console.WriteLine(i);

}

}

class xxx

{

}

class yyy

{

}

 

Compiler Error

a.cs(8,15): error CS0173: Type of conditional expression can't be determined because there is no implicit conversion between 'xxx' and 'yyy'

 

The compiler tries to convert an xxx type object to a yyy type object but there is no such implicit conversion available.

 

a.cs

class zzz

{

public static void Main()

{

object i;

xxx x = new xxx();

yyy y = new yyy();

i =  7 > 3  ? x : y ;

System.Console.WriteLine(i);

}

}

class xxx

{

public static implicit operator yyy ( xxx a)

{

System.Console.WriteLine("operator");

return new yyy();

}

}

class yyy

{

}

 

 

Output

operator

yyy

 

We do not receive any error as the compiler converts the xxx object to a yyy object using the implicit operator supplied. The implicit operator performs its job by converting the xxx to another data type, in this case yyy. Finally, the ToString function of the object i is called which displays its data type as yyy.

 

a.cs

class zzz

{

public static void Main()

{

object i;

xxx x = new xxx();

yyy y = new yyy();

i =  7 < 3   ? x : y ;

System.Console.WriteLine(i);

}

}

class xxx

{

}

class yyy

{

public static implicit operator xxx ( yyy a)

{

System.Console.WriteLine("operator");

return new xxx();

}

}

 

Output

operator

xxx

 

We have brought about two changes. First, we've changed the condition to return false so that the result now is a yyy object. Next is that the implicit operator is used to convert the data type to xxx. So much done, the compiler now wants to determine the controlling type of the conditional operator. It tries to convert one to another and if it fails, it flags an error. It tries the type conversion from the 2nd operand to 3rd and then vice versa.

 

i =  7 >  3   ? x : y ;

 

If we reverse the logical condition, the resultant output now shows:

 

Output

xxx

 

The compiler has a mind of its own. It realizes that the logical condition is true and the result is x, an xxx object. As it is, i is already an xxx object and the conversion available is more to convert from yyy to xxx, so it doesn't use it. Hence, the operator is not called. When the condition turns out to be false, as seen earlier, the result is a yyy object. This needs conversion so we bring in a user defined operator to do it.

 

a.cs

class zzz

{

public static void Main()

{

object i;

xxx x = new xxx();

yyy y = new yyy();

i =  7 < 3   ? x : y ;

System.Console.WriteLine(i);

}

}

class xxx

{

public static implicit operator yyy ( xxx a)

{

System.Console.WriteLine("operator yyy");

return new yyy();

}

}

class yyy

{

public static implicit operator xxx ( yyy a)

{

System.Console.WriteLine("operator xxx");

return new xxx();

}

}

 

Compiler Error

a.cs(8,16): error CS0172: Type of conditional expression can't be determined because 'xxx' and 'yyy' both implicitly convert to each other  

 

The problems of plenty! We have supplied an operator to convert a yyy object to an xxx object and vice versa. This confuses the life out of the compiler and therefore we get an error as both operators can do the same job. If the compiler chooses any one of them, it could be accused of being impartial. Nor does it like gambling. So it flags an error.

 

a.cs

class zzz

{

public static void Main()

{

xxx i;

xxx x = new xxx();

yyy y = new yyy();

int a = 4, b= 6;

i =  a > b ? x : y ;

System.Console.WriteLine(i);

}

}

class xxx {

public static implicit operator yyy ( xxx a)

{

System.Console.WriteLine("operator");

return new yyy();

}

}

class yyy {

}

Compiler Error

a.cs(9,6): error CS0029: Cannot implicitly convert type 'yyy' to 'xxx'

 

The above program tours you into the mind of a compiler. It gives you a good insight into its workings. The conditional operator has a condition whose result can be estimated only at run time. Thus the compiler cannot fathom whether the type of the operator will be xxx or yyy. The compiler assumes the result to be a yyy object. Under this pretext, the following result of the conditional too has to be stored in a xxx type object. However, there is no such conversion available hence it errs. The compiler could have assumed the conditional to be true thereby resulting in an xxx object. Then no conversions would be required. Well, it performs both these checks, hence it fails just to be on the side of your safety.

 

If we change the data type of i from an xxx to yyy, we get no errors as the compiler assumes that the result of the operator will be a xxx as the worst case possible. It has an operator to convert to a yyy. Move the operator function to the yyy class and the error will now read as given below.

 

Compiler Error

a.cs(9,6): error CS0029: Cannot implicitly convert type 'xxx' to 'yyy'

 

Let us now find out how C# evaluates a conditional operator.

 

a.cs

class zzz

{

public static void Main()

{

yyy a = new yyy();

int i =   a ? 10 : 20 ;

System.Console.WriteLine(i);

}

}

class yyy

{

}

Compiler Error

a.cs(6,11): error CS0029: Cannot implicitly convert type 'yyy' to 'bool'

 

The compiler expects a condition expression with a ternary operator. It tries to convert a yyy object into a bool and realizes that there is no such possibility. So it throws up its hands in despair and gives us the above error.

 

a.cs

class zzz

{

public static void Main()

{

yyy a = new yyy();

int i =   a ? 10 : 20 ;

System.Console.WriteLine(i);

}

}

class yyy

{

public static bool operator true ( yyy a)

{

System.Console.WriteLine("operator true");

return true;

}

public static bool operator false ( yyy a)

{

System.Console.WriteLine("operator false");

return true;

}

}

 

Output

operator true

10

 

We create an operator called true, which the compiler calls to figure out the value of the logical expression. As we returned true in our operator, the conditional operator is considered as true and hence the result is 10.

The operator true is ideal for conditions where we supply an object in place of a condition and require a boolean. This operator converts a yyy to a boolean true . Replace the return true in operator true to return false. The output will be operator true and 20.

 

a.cs

class zzz

{

public static void Main()

{

yyy a = new yyy();

int i =   a ? 10 : 20 ;

System.Console.WriteLine(i);

}

}

class yyy

{

public static implicit operator bool( yyy a)

{

System.Console.WriteLine("operator bool");

return true;

}

public static bool operator true ( yyy a)

{

System.Console.WriteLine("operator true");

return true;

}

public static bool operator false ( yyy a)

{

System.Console.WriteLine("operator false");

return true;

}

}

 

Output

operator bool

10

 

We've learnt from the experts, that is, C#, on how to complicate lives. To convert a yyy object to a boolean value, it first looks for the operator bool. This operator returns a true or false depending upon the parameter given to it. As we are explaining concepts, we return true. If the operator bool is present, like in our case, C# will not look further for an operator true. If it is not available, like in the earlier example, then the operator true gets called. Remember the order is first bool then true. If both are present, fortunately, we get no error.

 

So what happens to operator false? If both of these are not available, will it take operator false? Well, this is not the case. We land up with an error as shown below.

 

Compiler Error

a.cs(12,20): error CS0216: The operator 'yyy.operator false(yyy)' requires a matching operator 'true' to also be defined

 

Try giving only operator true and it will shoot up a similar error.

 

Boolean Operators

 

The if statement, until this moment, is the only known way to make our programs highly intelligent. To add some more intelligence to the if statement within our code, we use the boolean operators. They are the & (and)  and | (or). These operators make the if statement more restrictive. They also add more conditions or intelligence to the if statement. The more complex the if statement, the more dynamic our code becomes.

 

The & sign is a short form for and whereas the | sign stands for the or.

 

a.cs

class zzz

{

public static void Main()

{

int i = 1, j= 2; 

if ( i >= 1 & j > 1)

System.Console.WriteLine("& true");

if ( i >= 1 & j <= 1)

System.Console.WriteLine("& false");

if ( i >= 1 |  j < 1 )

System.Console.WriteLine("| true");

if ( i > 10 |  j >= 1)

System.Console.WriteLine("| again true");

}

}

 

Output

& true

| true

| again true

 

The & in the if make the if true when both conditions are true. As i is greater than equal to 1 and j is greater than 1, the first if is true. Regardless of the first condition being true, the second if evaluates to false because the second condition is false. The | is true if any one of the conditions is true. In the last two if's, even though one of the conditions is true, the expression  evaluates to true.

 

a.cs

class zzz {

public static void Main() {

int i = 1, j= 2; 

if ( i >= 1 && j > 1)

System.Console.WriteLine("&& true");

if ( i >= 1 && j <= 1)

System.Console.WriteLine("&& false");

if ( i >= 1 ||  j < 1 )

System.Console.WriteLine("|| true");

if ( i > 10 ||  j >= 1)

System.Console.WriteLine("|| again true");

}

}

 

Output

&& true

|| true

|| again true

 

We have simply replaced the & with && and ditto for the |. Yet, there seems to be no perceivable difference between them.

Lets us now write a set of programs to understand the difference.

 

a.cs

class zzz

{

public static void Main()

{

int i = 1, j= 2; int k =0; int l =0; 

if ( i >= ++k & j > ++l)

System.Console.WriteLine("& true " + " " + k + " " + l);

if ( i >= ++k & j <= ++l) ;

System.Console.WriteLine("& false " + " " + k + " " + l);

if ( i >= ++k |  j < l++ ) ;

System.Console.WriteLine("| true " + " " + k + " " + l);

k = -1;

if ( i > ++k |  j >= l++)

System.Console.WriteLine("| again true " + " " + k + " " + l);

}

}

 

Output

& true  1 1

& false  2 2

| true  3 3

| again true  0 4

 

a.cs

class zzz {

public static void Main()

{

int i = 1, j= 2; int k =0; int l =0; 

if (i >= ++k && j > ++l)

System.Console.WriteLine("&& true " + " " + k + " " + l);

if (i >= ++k && j <= ++l) ;

System.Console.WriteLine("&& false " + " " + k + " " + l);

if (i >= ++k ||  j < l++) ;

System.Console.WriteLine("|| true " + " " + k + " " + l);

k = -1;

if ( i > ++k ||  j >= l++)

System.Console.WriteLine("|| again true " + " " + k + " " + l);

}

}

Output

&& true  1 1

&& false  2 1

|| true  3 2

|| again true  0 2

 

Two identical programs except the difference between the single and double logical operators and you see two completely different outputs.

 

In the first program, C# simply increments the variable l each time it increments k. A single &, after evaluating the first condition, proceeds to the second condition and increments l. Hence we see k and l with the value of 1and 1 for each of them.

 

Let's take a similar case in the second program. The condition in the if statement reads (i >= ++k && j > ++l). The value of k which is zero first becomes one. The value of i is one and hence the first condition is true as '1 is >= 1'.  C# now goes to the second condition as if this condition is false, the entire expression evaluates to false. It now increments l to 1 and as j is 2, (2>1), thus making the condition true.

 

The second if statement in the second program reads (i >= ++k && j <= ++l). At first k grows from 1 to 2 whereas i remains at 1, hence the condition is false. C# now realizes that it is fruitless evaluating the second condition as evaluating it to true or false will not affect the if result of false anymore. It ignores the second condition, therefore l retains its old value of 1.

 

&& are called short circuit logical operators. A single & will always execute both the conditions and show an increase in the value of l unlike && which distances itself from the second condition on failure of the first.

 

The single | works in a slightly similar way. When the first condition of the or is true, C# considers it to be pointless in evaluating the remaining conditions as the or will remain true irrespective of the result of the second condition.  In the condition (i >= ++k ||  j < l++), variable i still holds 1 and variable k increases to 3. As the first condition is false, C# executes the second as its result can effect the overall result of the compound condition.

 

In the last case, the first condition is true, so no more of the if conditions are read. The variable l restores its original value.

 

a.cs

class zzz

{

public static void Main()

{

System.Console.WriteLine( true ^ false);

System.Console.WriteLine( false ^ true);

System.Console.WriteLine( false ^ false);

System.Console.WriteLine( true ^ true);

}

}

 

Output

True

True

False

False

 

The ^ operator works in a similar way to the | operator with one subtle change. If both sides are true, the answer is not true but false. The rest of the conditions work like an or. This is called the logical xor operator. More on this issue a couple of pages later.

 

a.cs

class zzz

{

public static void Main()

{

System.Console.WriteLine( true != false);

System.Console.WriteLine( false != true);

System.Console.WriteLine( false != false);

System.Console.WriteLine( true != true);

}

}

 

Output

True

True

False

False

 

If you observe carefully, the != operator and the ^ operator produce the same result. Let the time of the day decide on the option you choose for a logical comparison. Many a times, like in life, you wonder why C# programming language has certain language features, which are redundant and can duplicated by another set of commands.

 

a.cs

class zzz

{

public static void Main() {

yyy a = new yyy();

yyy b = new yyy();

System.Console.WriteLine( a && b);

}

}

class yyy

{

public static bool operator & (yyy x,yyy y)

{

System.Console.WriteLine("op");

return true;

}

}

 

Compiler Error

a.cs(6,27): error CS0217: In order to be applicable as a short circuit operator a user-defined logical operator ('yyy.operator &(yyy, yyy)') must have the same return type as the type of its 2 parameters.

 

The same error keeps popping up. A large number of operators demand their return type to be the class they are created in. At times, we understand why and at other times, we feel we are not intelligent enough to fathom a higher intellect.

a.cs

class zzz

{

public static void Main()

{

yyy a = new yyy();

yyy b = new yyy();

System.Console.WriteLine( a && b);

}

}

class yyy

{

public static yyy operator & (yyy x,yyy y)

{

System.Console.WriteLine("op");

return new yyy();

}

}

 

Compiler Error

a.cs(7,27): error CS0218: The type ('yyy') must contain declarations of operator true and operator false

 

We do as ordered by the error message and bring in  an operator true and an operator false. These are needed as our answer belongs to the boolean world. To evaluate the  condition in the WriteLine function, we need the above true and false operators. Along with it, we also include the implicit string operator as System.Console.WriteLine takes a string.

 

a.cs

class zzz

{

public static void Main()

{

yyy a = new yyy(1);

yyy b = new yyy(2);

System.Console.WriteLine( a && b);

System.Console.WriteLine( a & b);

}

}

class yyy

{

int j;

public yyy ( int v)

{

j = v;

}

public static yyy operator & (yyy x,yyy y)

{

System.Console.WriteLine("op " + x.j + " " + y.j);

return new yyy(x.j+y.j);

}

public static bool operator true(yyy x)

{

System.Console.WriteLine("true " + x.j);

return true;

}

public static bool operator false(yyy x)

{

System.Console.WriteLine("false " + x.j);

return true;

}

public static implicit operator string (yyy x)

{

System.Console.WriteLine("string " + x.j);

return "yyy " + x.j;

}

}

 

Output

false 1

string 1

yyy 1

op 1 2

string 3

yyy 3

 

We were astonished with the results. Operator & is not called ever. We introduced two new operators for it and & operator itself never gets called. So, we removed the code from our class yyy. When we do so, we get an error.

 

 

Compiler Error

a.cs(7,27): error CS0019: Operator '&&' cannot be applied to operands of type 'yyy' and 'yyy'

 

If we want it to understand the yyy object, we need to overload the && operator. After a while, we realized that our user defined function only implements one & while we had used && in the WriteLine function. We tried to implement the && by adding one more & to our operator. On doing so, we get the following error.

 

Compiler Error

a.cs(12,28): error CS1020: Overloadable binary operator expected

 

Thus we cannot overload the &&. The user-defined operator & handles both the & and the && but with a subtle difference between them. The expression a && b is evaluated as T.false(a) ? a : T.&(a,b). In the above case, object a is represented by the number 1 and the object b by a number 2.

 

C# first calls the operator false using object a with j value as 1. As we are returning true, the answer is object a (from ? to : ) , the string operator is used to produce yyy 1.

 

For the non-short circuit operators, the & is called and life moves on in the fast track like normal as we are returning an object comprised of the sums of objects a and b. This is converted into a string and its values are displayed.

 

Comment the lines for the & as it remains the same always.

 

//System.Console.WriteLine( a & b);

 

Now let the fireworks start. Instead of returning true in the operator false, we now give false.

 

public static bool operator false(yyy x)

{

System.Console.WriteLine("false " + x.j);

return false;

}

We now see the following result"

 

Output

false 1

op 1 2

string 3

yyy 3

 

As the operator false returns false, condition T.false(a) ? a : T.&(a,b) evaluates to false. This calls the operator & with a and b, which creates a new object. Hence, we see op displayed. The explanation for operator & holds true thereafter. The short circuit operator works on the principle that if the operator false returns true, the entire statement is false and no more code is executed.

 

For the or operator |, the rule is a || b and it gets evaluated to T.True(a) ? a : T.|(a,b). We change the & to the | in the operator and also in the WriteLine functions. Make the following changes to Main

 

public static void Main() {

yyy a = new yyy(1);

yyy b = new yyy(2);

System.Console.WriteLine( a || b);

System.Console.WriteLine( a | b);

}

 

…and in class yyy we've changed the & operator to |

 

public static yyy operator | (yyy x,yyy y)

{

System.Console.WriteLine("op " + x.j + " " + y.j);

return new yyy(x.j+y.j);

}

 

With the true operator returning true, we see the following output.

 

Output

true 1

string 1

yyy 1

op 1 2

string 3

yyy 3

 

With the true operator returning false, the results are as follows:

 

Output

true 1

op 1 2

string 3

yyy 3

op 1 2

string 3

yyy 3

 

The | works the same way as the &. There is no change at all. To understand the above output, we need to jog our memory with the fact that a || will only proceed if it meets a false condition at the beginning. If it meets a true condition, it blatantly ignores the following condition as the return value of true or false would not affect the answer. Thus, as in life, if we get what we want we do not push ourselves. We also learnt that a true or false operator is used to figure out the end result of a condition. Also, the object a is always evaluated only once but the object b may or may never be evaluated. An expression to be used as a logical expression must either have an operator bool or a true.

 

Heavy stuff! time to shift our minds to something different and easier.

 

Shift Operators

 

a.cs

class zzz

{

public static void Main()

{

int i,j = 8;

i = j >> 2;

System.Console.WriteLine(i + " " + j);

i = j << 2;

System.Console.WriteLine(i + " " + j);

}

}

 

Output

2 8

32 8

 

The right shift operator >> shifts all the bits to the right. Shifting bits to the right is the same as dividing the value by 2. The bits, that fall off from the right end are replaced with bits having a zero value from the left. The left shift operator works in the reverse way and move bits towards the right, The bits that come in from the right are zero bits and the ones that fall off from the left are forgotten. This is like multiplying the value by 2 for each shift that we make.

 

a.cs

class zzz {

public static void Main()

{

yyy a = new yyy(10);

int i = a >> 2;

System.Console.WriteLine(i);

}

}

class yyy

{

int j ;

public yyy(int p)

{

j = p;

}

public static int operator >> ( yyy a, int b)

{

System.Console.WriteLine(a.j+ " " + b);

return a.j + b;

}

}

 

Output

10 2

12

We are allowed to overload the >> or the << operators as we overload any other operator. The constructor is called with one parameter. The value of this parameter is given to the local variable j. The object is now right shifted by 2 and the result is stored in i.

 

The right shifting passes two parameters in the overloaded operator, that is, the instance of the object and the next is the number if bits to be shifted. As long as we return a data type that is the return value of the operator, what we do in the function is entirely at our discretion. This is the beauty of operator overloading. It's like life. We decide what to make of it and it is nobody's business to interfere in it. For an int, the shift count is not what we specify but count & 0x1f and for a long, count & 0x3f. Count is the number of bits to shift. God only knows why the above restriction. If the shift count is zero, the answer does not change. Also, these operators cannot cause an overflow and produce the same results in checked and unchecked mode.

 

++ and -- operators

 

The increment operator ++ can be used in two contexts with a variable. Prefix which means before and postfix which means after. The results are normally the same but differ in a very subtle way.

 

a.cs

class zzz {

public static void Main()

{

int i,j;

j = 1;

i = ++j;

System.Console.WriteLine(i + " " + j);

i = j++;

System.Console.WriteLine(i + " " + j);

}

}

 

Output

2 2

2 3

The ++ before a variable is called the prefix increment operator. Here, the variable j, which has a value of 1, first becomes 2 and then this value is stored in i. Thus, at the end of the statement, both i and j share the same value.

 

In the case of the postfix increment operator, the ++ appears after the variable name.

 

i=++j, in simple language reads as, initialize i to  the existing value of the variable j that is 2. The minute you reach the end of the statement, increment the value of the variable j by 1. While the statement is being executed, j has a value of 2, outside the statement j has a value of 3.

 

There are a large number of cases where the postfix operator makes our programming life easier. Can we not have an operator that makes the rest of life better?

 

a.cs

class zzz {

public static void Main() {

yyy a = new yyy();

System.Console.WriteLine(++a.i);

}

}

class yyy

{

public int i

{

get {return 5;}

set {}

}

}

 

Output

6

 

The postfix and prefix operators can also work with a property, provided the property has both a get and set accessor. If we remove the set accessor from the above example we receive the following error message.

Compiler Error

a.cs(6,28): error CS0200: Property or indexer 'yyy.i' cannot be assigned to -- it is read only

 

…and if we remove the get accessor and only have the set accessor.

 

Compiler Error

a.cs(6,28): error CS0154: The property or indexer 'yyy.i' cannot be used in this context because it lacks the get accessor

 

The above errors are obvious as a ++ operator with a variable, i.e. writing i++, is a short form of writing i = i + 1.

 

In such a case, we first need to read the value of a variable, therefore we need a get accessor and then we change the value of the same variable. So we require a set accessor too. The indexer also works in the same fashion where the compiler does not even bother to have a separate error message for them. The error message for the property and indexer are merged into one. A lazy programmer !!!.

 

The predefined data types that understand the ++ and -- are sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, and any other enum type. What works for a ++ also works in the same way for a --.

 

a.cs

class zzz

{

public static void Main()

{

}

}

class yyy

{

public static int operator ++ (yyy a)

{

}

}

 

 

Compiler Error

a.cs(9,19): error CS0559: The parameter and return type for ++ or -- operator must be the containing type

 

The ++ operator belongs to a class and hence must return an object that is an instance of the same class. The ++ operator in our code must return a yyy and not an int.

 

a.cs

class zzz

{

public static void Main()

{

yyy a = new yyy(3);

int i;

i = ++a;

System.Console.WriteLine(i);

i = a++;

System.Console.WriteLine(i);

}

}

class yyy

{

int j;

public yyy( int p)

{

j = p;

}

public static yyy operator ++ (yyy a)

{

yyy b = a;

System.Console.WriteLine("++ " + a.j);

b.j++;

return b;

}

public static implicit operator int (yyy a)

{

System.Console.WriteLine("int " + a.j);

return a.j;

}

}

 

Output

++ 3

int 4

4

++ 4

int 5

5

 

In the ++ operator of the class, we return an object b, which is an instance of class yyy. We first initialize b to the parameter passed and then display the value of j. System.Console.WriteLine shows ++3

 

The next line increments the member j by one thereby storing the value of 4 in j and then the object is returned. Since the value is stored in an int variable, the implicit int function nows comes into picture. The int operator converts the yyy to an int by returning the value of the member j.  Within int, the display line will show 'int 4' and finally the WriteLine in Main will display 4.

 

However, in the second case of a++, the same thing happens. The identical operator gets called and we get the same result. Our reasoning was that the value of the yyy object would be 4 and not 5 as the ++ was used in the postfix context. But, there is no difference at all.

 

Thus, in the user defined operators, the ++ works the same whether it is used in a prefix or a postfix context. We do not have two different operators and our operators do not know which context they are being called in. There is only one operator being used but in two dissimilar ways.

 

Logical Negation ! Operator

 

a.cs

class zzz

{

public static void Main()

{

System.Console.WriteLine(!true);

System.Console.WriteLine(!false);

}

}

 

Output

False

True

 

This operator only acts on a bool. It reverses the condition. A true becomes false and a false becomes true.

 

a.cs

class zzz {

public static void Main()

{

yyy a = new yyy();

int i = !a;

System.Console.WriteLine(i);

}

}

class yyy

{

public static int operator ! (yyy a)

{

return 1;

}

}

 

Output

1

 

The negation operator can be overloaded only for objects. This object becomes a parameter. The important thing here is that there is no rule stating the data type of the return value. In the above case we are returning an int for all that C# cares. Common sense demands that it should be an int but… One case where C# should have enforced a sensible rule but does not. May be we need to wait till the next version arrives.

 

 

 

Complement operator

 

a.cs

class zzz {

public static void Main()

{

int i = 32768;

int j = ~i;

System.Console.WriteLine(j);

}

}

 

Output

-32769

 

The bitwise operator ~ negates the bits. The zeros become one and the ones becomes zeros. This operator is used when you want to reverse the bits in a byte.

 

Arithmetic Operators

 

a.cs

class zzz {

public static void Main() {

double i = 3 * 2.0;

int j = (int)3 * 2.0;

System.Console.WriteLine(2 * (float)3.0);

System.Console.WriteLine(2 * 3.0);

System.Console.WriteLine(3.0 * (double)2);

}

}

 

Compiler Error

a.cs(4,10): error CS0029: Cannot implicitly convert type 'double' to 'int'

 

The multiplication * operator can multiply a float or a double with an int. The second line has a number with decimal places i.e. a double multiplied with an integer. It does the multiplication and the answer is obviously a double. The multiplication goes through but the double value in no way can be held in an int. The fault, dear Brutus, lies not in the stars, but in the internal conversion from a double into an int.

 

Let us understand more of the above conversions.

 

Every time we create a variable, C# allocates some memory for it. We can determine this memory by using the sizeof operator in an unsafe context. The size of the basic data types are as follows, double 8, float 4, long 8, int 4, uint 4, char 2, byte 1.

 

a.cs

class zzz

{

public static void Main()

{

double i = 0; float j = 0;

i = j;

j = i;

int k =0; long l =0;

k = l;

l = k;

byte m = 0;

m = k;

k = m;

}

}

 

Compiler Error

a.cs(7,5): error CS0029: Cannot implicitly convert type 'double' to 'float'

a.cs(9,5): error CS0029: Cannot implicitly convert type 'long' to 'int'

a.cs(12,5): error CS0029: Cannot implicitly convert type 'int' to 'byte'

 

C# has a very simple rule when it comes to equating objects belonging to the simple data types. It does not like any waste. Hence, we can equate a larger data type to a smaller data type. Here, larger and smaller refer to the amount of memory a data type is allocated at the time of creation.

On equating a larger data type to a smaller one, there is no reason for loss of data as in the memory, variable on the left is large enough to hold the value stored of the right.

 

int = byte

 

However in the reverse case, the variable on the right will theoretically store a larger value than the left side variable. As the variable on the left can store a smaller value, there is a possibility of loss of data.

 

byte=int

 

Thus an int can be equated to a byte but a byte cannot be equated to an int. The range of values an int can store is by far larger than what a byte can handle.

 

a.cs

class zzz {

public static void Main() {

int i;

i = 10/0;

}

}

 

Compiler Error

a.cs(4,5): error CS0020: Division by constant zero

 

Whenever any number is divided by zero, the answer, as taught to us in school, is infinity. No computer can ever evaluate this expression as it holds no definition. Thus, one of the million error checks that a compiler reviews is divide by zero.

 

a.cs

class zzz {

public static void Main()

{

int i; int j=0;

i = 10/j;

}

}

Output

Exception occurred: System.DivideByZeroException: Attempted to divide by zero.

   at zzz.Main()

 

At times, compilers can be exasperating. We love calling them all sorts of names. Here we have very clearly, in front of the world, initialized the variable j to zero. The compiler knows about it but seems to turn a blind eye to it. For some reason, as we mentioned earlier, the compiler ignores alll variable values until the program is executed. This now throws an exception at run time and since we haven't caught it, we see the error.

 

a.cs

class zzz

{

public static void Main()

{

System.Console.WriteLine(10/3);

System.Console.WriteLine((double)10/3);

System.Console.WriteLine((double)(int)10/3);

System.Console.WriteLine(10/3.0);

System.Console.WriteLine(10.0/3);

}

}

 

Output

3

3.33333333333333

3.33333333333333

3.33333333333333

3.33333333333333

 

When we divid