Saturday, 27 October 2012

Operators: Arithmetic, Logical, and Conditional

Operators:

Arithmetic, Logical, and Conditional

The C# operators used for arithmetic operations, bit manipulation, and conditional program flow should be familiar to all programmers. This section presents an overview of these operators that is meant to serve as a syntactical reference.

Arithmetic Operators

Table 2-4 summarizes the basic numerical operators. The precedence in which these operators are applied during the evaluation of an expression is shown in parentheses, with 1 being the highest precedence.
Table 2-4. Numerical Operators
Operator
Description
Example
+

-

(3)
Addition
Subtraction
int x = y + 10;
*

/

%

(2)
Multiplication
Division,
Modulo
int x = 60;

int y = 15;

int z = x * y / 2;  // 450

y = x % 29 ;  // remainder is 2

++

--

(1)
Prefix/postfix
Increment/decrement
x = 5;

Console.WriteLine(x++) // x = 5 

Console.WriteLine(++x) // x = 6

~
(1)
Bitwise complement
int x = ~127; // returns -128
>>

<<

(4)
Shift right
Shift left
byte x = 10; // binary 10 is 01010

int result = x << 1; // 20 = 10100

result = x >> 2;     //  5 = 00101


Works with byte, char, short, int, and long
&

|

^

(5-6-7)
Bitwise AND
Bitwise OR
Bitwise XOR
byte x = 12;        //    001100

byte y = 11;        //    001011

int result = x & y; //8 = 001000

result = x ^ y;     //7 = 000111



Core Note
C# does not provide an exponentiation operator. Instead, use the Math.Pow() method to raise a number to a power, and Math.Exp() to raise e to a power.

Conditional and Relational Operators

Relational operators are used to compare two values and determine their relationship. They are generally used in conjunction with conditional operators to form more complex decision constructs. Table 2-5 provides a summary of C# relational and conditional operators.
Table 2-5. Relational and Conditional Boolean Operators
Statement
Description
Example
==

!=

Equality
Inequality
if (x == y) {...}
<

<=

>

>=

Numeric less than
Less than or equal to
Greater than
Greater than or equal to
if (x <= y) {...}
&&

||

Logical AND
Logical OR
if (x == y && y < 30) {...}
If first expression is false, second is not evaluated
&

|

Logical AND
Logical OR
if (x== y | y < 30) {...}
Always evaluates second expression
!
Logical negation
if !(x ==y && y < 30) {...}


Note the two forms of the logical AND/OR operations. The && and || operators do not evaluate the second expression if the first is false—a technique known as short circuit evaluation. The & and | operators always evaluate both expressions. They are used primarily when the expression values are returned from a method and you want to ensure that the methods are called.
In addition to the operators in Table 2-5, C# supports a ?: operator for conditionally assigning a value to a variable. As this example shows, it is basically shorthand for using an if-else statement:
string pass;

int grade=74;

If(grade >= 70) pass="pass"; else pass="fail";

//     expression   ? op1  : op2

pass = (grade >= 70)  ? "pass" : "fail";


If the expression is TRue, the ?: operator returns the first value; if it's false, the second is returned.

Control Flow Statements

The C# language provides if and switch conditional constructs that should be quite familiar to C++ and Java programmers. Table 2-6 provides a summary of these statements.
Table 2-6. Control Flow Statements
Conditional Statement
Example
if (boolean expression) {

   // statements

} else {

   // statements

}

if (bmi < 24.9) {

   weight = "normal";

   riskFactor = 2;

} else {

   weight = "over";

   riskFactor=6;

}

switch (expression) 

{

   case constant expression:

      // statements;

      // break/goto/return()

   case constant expression:

      // statements;

      // break/goto/return()

   default:

      // statements;

      // break/goto/return()

}


  • Constant expression may be an integer, enum value, or string.
  • No "fall through" is permitted. Each case block must end with a statement that transfers control.
switch (ndx)

{

   case 1:

      fabric = "cotton";

      blend = "100%";

      break;

   case 2:  // combine 2 & 3 

   case 3:

      fabric = "cotton";

      blend = "60%";

      break;

   default:  // optional

      fabric = "cotton";

      blend = "50%";

      break;

}



if-else

Syntax:
if ( boolean expression ) statement

if ( boolean expression ) statement1 else statement2

C# if statements behave as they do in other languages. The only issue you may encounter is how to format the statements when nesting multiple if-else clauses.
// Nested if statements

if (age > 16)                         if (age > 16)

{                                        if (sex == "M")

   if (sex == "M")                          type = "Man";

   {                                     else

      type = "Man";                         type = "Woman" ;

   } else {                           else

      type = "Woman" ;                   type = "child";

   }

} else {  

   type = "child";

}


Both code segments are equivalent. The right-hand form takes advantage of the fact that curly braces are not required to surround single statements; and the subordinate if clause is regarded as a single statement, despite the fact that it takes several lines. The actual coding style selected is not as important as agreeing on a single style to be used.

switch

Syntax:
switch( expression ) {switch block}


The expression is one of the int types, a character, or a string. The switch block consists of case labels—and an optional default label—associated with a constant expression that must implicitly convert to the same type as the expression. Here is an example using a string expression:
// switch with string expression

using System;

public class MyApp 

{

   static void Main(String[] args)

   {

      switch (args[0]) 

      {

         case "COTTON":   // is case sensitive

         case "cotton":

              Console.WriteLine("A good natural fiber.");

              goto case "natural";

         case "polyester":

              Console.WriteLine("A no-iron synthetic fiber.");

              break;

         case "natural":

              Console.WriteLine("A Natural Fiber. ");

              break;

         default:

              Console.WriteLine("Fiber is unknown.");

              break;

      }

   }

}


The most important things to observe in this example are as follows:
  • C# does not permit execution to fall through one case block to the next. Each case block must end with a statement that transfers control. This will be a break, goto. or return statement.
  • Multiple case labels may be associated with a single block of code.
  • The switch statement is case sensitive; in the example, "Cotton" and "COTTON" represent two different values.

Primitives Data Types

Primitives-

The next three sections of this chapter describe features that you'll find in most programming languages: variables and data types, operators, expressions, and statements that control the flow of operations. The discussion begins with primitives. As the name implies, these are the core C# data types used as building blocks for more complex class and structure types. Variables of this type contain a single value and always have the same predefined size. Table 2-3 provides a formal list of primitives, their corresponding core data types, and their sizes.
Table 2-3. C# Primitive Data Types
C# Primitive Type
FCL Data Type
Description
object
System.Object
Ultimate base type of all other types.
string
System.String
A sequence of Unicode characters.
decimal
System.Decimal
Precise decimal with 28 significant digits.
bool
System.Boolean
A value represented as true or false.
char
System.Char
A 16-bit Unicode character.
byte
System.Byte
8-bit unsigned integral type.
sbyte
System.SByte
8-bit signed integral type.
short
System.Int16
16-bit signed integral type.
int
System.Int32
32-bit signed integral type.
long
System.Int64
64-bit signed integral type.
ushort
System.UInt16
16-bit unsigned integral type.
uint
System.UInt32
32-bit unsigned integral type.
ulong
System.UIint64
64-bit unsigned integral type.
single (float)
System.Single
Single-precision floating-point type.
double
System.Double
Double-precision floating-point type.


As the table shows, primitives map directly to types in the base class library and can be used interchangeably. Consider these statements:
System.Int32 age = new System.Int32(17);

int age = 17; 

System.Int32 age = 17;


They all generate exactly the same Intermediate Language (IL) code. The shorter version relies on C# providing the keyword int as an alias for the System.Int32 type. C# performs aliasing for all primitives.
Here are a few points to keep in mind when working with primitives:
  • The keywords that identify the value type primitives (such as int) are actually aliases for an underlying structure (struct type in C#). Special members of these structures can be used to manipulate the primitives. For example, the Int32 structure has a field that returns the largest 32-bit integer and a method that converts a numeric string to an integer value:
    int iMax = int.MaxValue;     // Return largest integer 
    
    int pVal = int.Parse("100"); // converts string to int
    
    

    The C# compiler supports implicit conversions if the conversion is a "safe" conversion that results in no loss of data. This occurs when the target of the conversion has a greater precision than the object being converted, and is called a widening conversion. In the case of a narrowing conversion, where the target has less precision, the conversion must have explicit casting. Casting is used to coerce, or convert, a value of one type into that of another. This is done syntactically by placing the target data type in parentheses in front of the value being converted: int i = (int)y;.
    short i16 = 50;    // 16-bit integer
    
    int i32 = i16;     // Okay: int has greater precision
    
    i16 = i32;         // Fails: short is 16 bit, int is 32
    
    i16 = (short) i32; // Okay since casting used
    
    

  • Literal values assigned to the types float, double, and decimal require that their value include a trailing letter: float requires F or f; double has an optional D or d; and decimal requires M or m.
    decimal pct = .15M; // M is required for literal value
    
    
The remainder of this section offers an overview of the most useful primitives with the exception of string, which is discussed later in the chapter.

decimal

The decimal type is a 128-bit high-precision floating-point number. It provides 28 decimal digits of precision and is used in financial calculations where rounding cannot be tolerated. This example illustrates three of the many methods available to decimal type. Also observe that when assigning a literal value to a decimal type, the M suffix must be used.
decimal iRate = 3.9834M;         // decimal requires M 

iRate = decimal.Round(iRate,2);  // Returns 3.98

decimal dividend = 512.0M;

decimal divisor = 51.0M;

decimal p = decimal.Parse("100.05");

// Next statement returns remainder = 2

decimal rem = decimal.Remainder(dividend,divisor);


bool

The only possible values of a bool type are true and false. It is not possible to cast a bool value to an integer—for example, convert true to a 1, or to cast a 1 or 0 to a bool.
bool bt = true;

string bStr = bt.ToString(); // returns "true"

bt = (bool) 1;               // fails


char

The char type represents a 16-bit Unicode character and is implemented as an unsigned integer. A char type accepts a variety of assignments: a character value placed between individual quote marks (' '); a casted numeric value; or an escape sequence. As the example illustrates, char also has a number of useful methods provided by the System.Char structure:
myChar =  'B';       // 'B' has an ASCII value of 66

myChar = (char) 66;  // Equivalent to 'B'

myChar = '\u0042';   // Unicode escape sequence

myChar = '\x0042';   // Hex escape sequence

myChar = '\t';       // Simple esc sequence:horizontal tab

bool bt;

string pattern = "123abcd?";

myChar = pattern[0];                  // '1'

bt = char.IsLetter(pattern,3);        // true  ('a')

bt = char.IsNumber(pattern,3);        // false

bt = char.IsLower(pattern,0);         // false ('1')

bt = char.IsPunctuation(pattern,7);   // true  ('?')

bt = char.IsLetterOrDigit(pattern,1); // true

bt = char.IsNumber(pattern,2);        // true  ('3')

string kstr="K";

char k = char.Parse(kstr);


byte, sbyte

A byte is an 8-bit unsigned integer with a value from 0 to 255. An sbyte is an 8-bit signed integer with a value from –128 to 127.
byte[] b = {0x00, 0x12, 0x34, 0x56, 0xAA, 0x55, 0xFF};

string s = b[4].ToString(); // returns 170

char myChar = (char) b[3];


short, int, long

These represent 16-, 32-, and 64-bit signed integer values, respectively. The unsigned versions are also available (ushort, uint, ulong).
short i16 = 200;

i16 = 0xC8 ;     // hex value for 200

int i32 = i16;   // no casting required 


single, double

These are represented in 32-bit single-precision and 64-bit double-precision formats. In .NET 1.x, single is referred to as float.
  • The single type has a value range of 1.5 x 10 –45 to 3.4 x 1038 with 7-decimal digit precision.
  • The double type has a value range of 5 x 10–324 to 1.7 x 10308 with 15- to 16-decimal digit precision.
  • Floating-point operations return NaN (Not a Number) to signal that the result of the operation is undefined. For example, dividing 0.0 by 0.0 results in NaN.
  • Use the System.Convert method when converting floating-point numbers to another type.
float xFloat = 24567.66F;

int xInt = Convert.ToInt32(xFloat);  // returns 24567

int xInt2 = (int) xFloat;

if(xInt == xInt2) {  }               // False

string xStr = Convert.ToString(xFloat);

single zero = 0;

if (Single.IsNaN(0 / zero)) {  }     // True

double xDouble = 124.56D;


Note that the F suffix is used when assigning a literal value to a single type, and D is optional for a double type.

Using Parse and TryParse to Convert a Numeric String

The primitive numeric types include Parse and tryParse methods that are used to convert a string of numbers to the specified numeric type. This code illustrates:
short shParse  = Int16.Parse("100");

int iParse     = Int32.Parse("100");

long lparse    = Int64.Parse("100");

decimal dParse = decimal.Parse("99.99");

float sParse   = float.Parse("99.99");

double dbParse = double.Parse("99.99");


TRyParse, introduced in .NET 2.0, provides conditional parsing. It returns a boolean value indicating whether the parse is successful, which provides a way to avoid formal exception handling code. The following example uses an Int32 type to demonstrate the two forms of tryParse:
int result;

// parse string and place result in result parameter

bool ok = Int32.TryParse("100", out result); 

bool ok = Int32.TryParse("100", NumberStyles.Integer, null,

                         out result);


In the second form of this method, the first parameter is the text string being parsed, and the second parameter is a NumberStyles enumeration that describes what the input string may contain. The value is returned in the fourth parameter.

Primitives-

The next three sections of this chapter describe features that you'll find in most programming languages: variables and data types, operators, expressions, and statements that control the flow of operations. The discussion begins with primitives. As the name implies, these are the core C# data types used as building blocks for more complex class and structure types. Variables of this type contain a single value and always have the same predefined size. Table 2-3 provides a formal list of primitives, their corresponding core data types, and their sizes.
Table 2-3. C# Primitive Data Types
C# Primitive Type
FCL Data Type
Description
object
System.Object
Ultimate base type of all other types.
string
System.String
A sequence of Unicode characters.
decimal
System.Decimal
Precise decimal with 28 significant digits.
bool
System.Boolean
A value represented as true or false.
char
System.Char
A 16-bit Unicode character.
byte
System.Byte
8-bit unsigned integral type.
sbyte
System.SByte
8-bit signed integral type.
short
System.Int16
16-bit signed integral type.
int
System.Int32
32-bit signed integral type.
long
System.Int64
64-bit signed integral type.
ushort
System.UInt16
16-bit unsigned integral type.
uint
System.UInt32
32-bit unsigned integral type.
ulong
System.UIint64
64-bit unsigned integral type.
single (float)
System.Single
Single-precision floating-point type.
double
System.Double
Double-precision floating-point type.


As the table shows, primitives map directly to types in the base class library and can be used interchangeably. Consider these statements:
System.Int32 age = new System.Int32(17);

int age = 17; 

System.Int32 age = 17;


They all generate exactly the same Intermediate Language (IL) code. The shorter version relies on C# providing the keyword int as an alias for the System.Int32 type. C# performs aliasing for all primitives.
Here are a few points to keep in mind when working with primitives:
  • The keywords that identify the value type primitives (such as int) are actually aliases for an underlying structure (struct type in C#). Special members of these structures can be used to manipulate the primitives. For example, the Int32 structure has a field that returns the largest 32-bit integer and a method that converts a numeric string to an integer value:
    int iMax = int.MaxValue;     // Return largest integer 
    
    int pVal = int.Parse("100"); // converts string to int
    
    

    The C# compiler supports implicit conversions if the conversion is a "safe" conversion that results in no loss of data. This occurs when the target of the conversion has a greater precision than the object being converted, and is called a widening conversion. In the case of a narrowing conversion, where the target has less precision, the conversion must have explicit casting. Casting is used to coerce, or convert, a value of one type into that of another. This is done syntactically by placing the target data type in parentheses in front of the value being converted: int i = (int)y;.
    short i16 = 50;    // 16-bit integer
    
    int i32 = i16;     // Okay: int has greater precision
    
    i16 = i32;         // Fails: short is 16 bit, int is 32
    
    i16 = (short) i32; // Okay since casting used
    
    

  • Literal values assigned to the types float, double, and decimal require that their value include a trailing letter: float requires F or f; double has an optional D or d; and decimal requires M or m.
    decimal pct = .15M; // M is required for literal value
    
    
The remainder of this section offers an overview of the most useful primitives with the exception of string, which is discussed later in the chapter.

decimal

The decimal type is a 128-bit high-precision floating-point number. It provides 28 decimal digits of precision and is used in financial calculations where rounding cannot be tolerated. This example illustrates three of the many methods available to decimal type. Also observe that when assigning a literal value to a decimal type, the M suffix must be used.
decimal iRate = 3.9834M;         // decimal requires M 

iRate = decimal.Round(iRate,2);  // Returns 3.98

decimal dividend = 512.0M;

decimal divisor = 51.0M;

decimal p = decimal.Parse("100.05");

// Next statement returns remainder = 2

decimal rem = decimal.Remainder(dividend,divisor);


bool

The only possible values of a bool type are true and false. It is not possible to cast a bool value to an integer—for example, convert true to a 1, or to cast a 1 or 0 to a bool.
bool bt = true;

string bStr = bt.ToString(); // returns "true"

bt = (bool) 1;               // fails


char

The char type represents a 16-bit Unicode character and is implemented as an unsigned integer. A char type accepts a variety of assignments: a character value placed between individual quote marks (' '); a casted numeric value; or an escape sequence. As the example illustrates, char also has a number of useful methods provided by the System.Char structure:
myChar =  'B';       // 'B' has an ASCII value of 66

myChar = (char) 66;  // Equivalent to 'B'

myChar = '\u0042';   // Unicode escape sequence

myChar = '\x0042';   // Hex escape sequence

myChar = '\t';       // Simple esc sequence:horizontal tab

bool bt;

string pattern = "123abcd?";

myChar = pattern[0];                  // '1'

bt = char.IsLetter(pattern,3);        // true  ('a')

bt = char.IsNumber(pattern,3);        // false

bt = char.IsLower(pattern,0);         // false ('1')

bt = char.IsPunctuation(pattern,7);   // true  ('?')

bt = char.IsLetterOrDigit(pattern,1); // true

bt = char.IsNumber(pattern,2);        // true  ('3')

string kstr="K";

char k = char.Parse(kstr);


byte, sbyte

A byte is an 8-bit unsigned integer with a value from 0 to 255. An sbyte is an 8-bit signed integer with a value from –128 to 127.
byte[] b = {0x00, 0x12, 0x34, 0x56, 0xAA, 0x55, 0xFF};

string s = b[4].ToString(); // returns 170

char myChar = (char) b[3];


short, int, long

These represent 16-, 32-, and 64-bit signed integer values, respectively. The unsigned versions are also available (ushort, uint, ulong).
short i16 = 200;

i16 = 0xC8 ;     // hex value for 200

int i32 = i16;   // no casting required 


single, double

These are represented in 32-bit single-precision and 64-bit double-precision formats. In .NET 1.x, single is referred to as float.
  • The single type has a value range of 1.5 x 10 –45 to 3.4 x 1038 with 7-decimal digit precision.
  • The double type has a value range of 5 x 10–324 to 1.7 x 10308 with 15- to 16-decimal digit precision.
  • Floating-point operations return NaN (Not a Number) to signal that the result of the operation is undefined. For example, dividing 0.0 by 0.0 results in NaN.
  • Use the System.Convert method when converting floating-point numbers to another type.
float xFloat = 24567.66F;

int xInt = Convert.ToInt32(xFloat);  // returns 24567

int xInt2 = (int) xFloat;

if(xInt == xInt2) {  }               // False

string xStr = Convert.ToString(xFloat);

single zero = 0;

if (Single.IsNaN(0 / zero)) {  }     // True

double xDouble = 124.56D;


Note that the F suffix is used when assigning a literal value to a single type, and D is optional for a double type.

Using Parse and TryParse to Convert a Numeric String

The primitive numeric types include Parse and tryParse methods that are used to convert a string of numbers to the specified numeric type. This code illustrates:
short shParse  = Int16.Parse("100");

int iParse     = Int32.Parse("100");

long lparse    = Int64.Parse("100");

decimal dParse = decimal.Parse("99.99");

float sParse   = float.Parse("99.99");

double dbParse = double.Parse("99.99");


TRyParse, introduced in .NET 2.0, provides conditional parsing. It returns a boolean value indicating whether the parse is successful, which provides a way to avoid formal exception handling code. The following example uses an Int32 type to demonstrate the two forms of tryParse:
int result;

// parse string and place result in result parameter

bool ok = Int32.TryParse("100", out result); 

bool ok = Int32.TryParse("100", NumberStyles.Integer, null,

                         out result);


In the second form of this method, the first parameter is the text string being parsed, and the second parameter is a NumberStyles enumeration that describes what the input string may contain. The value is returned in the fourth parameter.