Saturday, 27 October 2012

C# Preprocessing Directives


C# Preprocessing Directives

Preprocessing directives are statements read by the C# compiler during its lexical analysis phase. They can instruct the compiler to include/exclude code or even abort compilation based on the value of preprocessing directives.
A preprocessor directive is identified by the # character that must be the first nonblank character in the line. Blank spaces are permitted before and after the # symbol. Table 2-8 lists the directives that C# recognizes.
Table 2-8. Preprocessing Directives
C# Preprocessing Symbol
Description
#define

#undef

Used to define and undefine a symbol. Defining a symbol makes it evaluate to true when used in a #if directive.
#if

#elif

#else

#endif

Analogues to the C# if, else if, and else statements.
#line
Changes the line number sequence and can identify which file is the source for the line.
#region

#endregion

Used to specify a block of code that you can expand or collapse when using the outlining feature of Visual Studio.NET.
#error

#warning

#error causes the compiler to report a fatal error.
#warning causes the compiler to report a warning and continue processing.


The three most common uses for preprocessing directives are to perform conditional compilation, add diagnostics to report errors and warnings, and define code regions.

Conditional Compilation

The #if related directives are used to selectively determine which code is included during compilation. Any code placed between the #if statement and #endif statement is included or excluded based on whether the #if condition is true or false. This is a powerful feature that is used most often for debug purposes. Here is an example that illustrates the concept:
#define DEBUG

using System;

public class MyApp 

{      

   public static void Main() 

   {

      #if (DEBUG)

         Console.WriteLine("Debug Mode");      

      #else

         Console.WriteLine("Release Mode");

      #endif

   }

}


Any #define directives must be placed at the beginning of the .cs file. A conditional compilation symbol has two states: defined or undefined. In this example, the DEBUG symbol is defined and the subsequent #if (DEBUG) statement evaluates to true. The explicit use of the #define directive permits you to control the debug state of each source file. Note that if you are using Visual Studio, you can specify a Debug build that results in the DEBUG symbol being automatically defined for each file in the project. No explicit #define directive is required.
You can also define a symbol on the C# compile command line using the /Define switch:
csc /Define:DEBUG myproject.cs


Compiling code with this statement is equivalent to including a #Define DEBUG statement in the source code.

Diagnostic Directives

Diagnostic directives issue warning and error messages that are treated just like any other compile-time errors and warnings. The #warning directive allows compilation to continue, whereas the #error terminates it.
#define CLIENT

#define DEBUG

using System;

public class MyApp 

{      

   public static void Main() 

   {

      #if DEBUG && INHOUSE

         #warning Debug is on.  

      #elif DEBUG && CLIENT

         #error Debug not allowed in Client Code.

      #endif

   // Rest of program follows here


In this example, compilation will terminate with an error message since DEBUG and CLIENT are defined.

Code Regions

The region directives are used to mark sections of code as regions. The region directive has no semantic meaning to the C# compiler, but is recognized by Visual Studio.NET, which uses it to hide or collapse code regions. Expect other third-party source management tools to take advantage of these directives.
#region

   // any C# statements

#endregion

Loops


Loops

C# provides four iteration statements: while, do, for, and foreach. The first three are the same constructs you find in C, C++, and Java; the foreach statement is designed to loop through collections of data such as arrays.

while loop

Syntax:
while ( boolean expression ) { body }


The statement(s) in the loop body are executed until the boolean expression is false. The loop does not execute if the expression is initially false.
Example:
byte[] r = {0x00, 0x12, 0x34, 0x56, 0xAA, 0x55, 0xFF};

int ndx=0;

int totVal = 0;

while (ndx <=6) 

{

   totVal +=    r[ndx];   

   ndx += 1;

}




do loop

Syntax:
do { do-body } while ( boolean expression );


This is similar to the while statement except that the evaluation is performed at the end of the iteration. Consequently, this loop executes at least once.
Example:
byte[] r = {0x00, 0x12, 0x34, 0x56, 0xAA, 0x55, 0xFF};

int ndx=0;

int totVal = 0;

do 

{

   totVal += r[ndx];

   ndx += 1;

}

while (ndx <= 6);      


for loop

Syntax:
for ( [initialization]; [termination condition]; [iteration] )

     { for-body }


The for construct contains initialization, a termination condition, and the iteration statement to be used in the loop. All are optional. The initialization is executed once, and then the condition is checked; as long as it is TRue, the iteration update occurs after the body is executed. The iteration statement is usually a simple increment to the control variable, but may be any operation.
Example:
int[] r = {80, 88, 90, 72, 68, 94, 83};

int totVal = 0;

for (int ndx = 0; ndx <= 6; ndx++) {

   totVal += r[ndx];

}


If any of the clauses in the for statement are left out, they must be accounted for elsewhere in the code. This example illustrates how omission of the for-iteration clause is handled:
for   (ndx = 0; ndx < 6; )

{

   totVal += r[ndx];

   ndx++;          // increment here

}


You can also leave out all of the for clauses:
for (;;) { body   }   // equivalent to while(true) { body }


A return, goto, or break statement is required to exit this loop.

foreach loop

Syntax:
foreach ( type identifier in collection )  { body }


The type and identifier declare the iteration variable. This construct loops once for each element in the collection and sets the iteration variable to the value of the current collection element. The iteration variable is read-only, and a compile error occurs if the program attempts to set its value.
For demonstration purposes, we will use an array as the collection. Keep in mind, however, that it is not restricted to an array. There is a useful set of collection classes defined in .NET that work equally well with foreach. We look at those in Chapter 4, "Working with Objects in C#."
Example:
int totVal = 0;

foreach (int arrayVal in r)

{

   totVal += arrayVal;

}


In a one-dimensional array, iteration begins with index 0 and moves in ascending order. In a multi-dimensional array, iteration occurs through the rightmost index first. For example, in a two-dimensional array, iteration begins in the first column and moves across the row. When it reaches the end, it moves to the next row of the first column and iterates that row.

Transferring Control Within a Loop

It is often necessary to terminate a loop, or redirect the flow of statements within the loop body, based on conditions that arise during an iteration. For example, a while (true) loop obviously requires that the loop termination logic exists in the body. Table 2-7 summarizes the principal statements used to redirect the program flow.
Table 2-7. Statements to Exit a Loop or Redirect the Iteration
Statement
Description
Example
break
Redirects program control to the end point of a containing loop construct.
while (true) {

   ndx+=1;

   if (ndx >10) break;

}



continue
Starts a new iteration of enclosing loop without executing remaining statements in loop.
while (ndx <10) {

   ndx +=1;

   if(ndx %2 =1) continue;

   totVal += ndx;

}

goto

identifier;



goto case exp;



goto default;

Directs program control to a label, a case statement within a switch block, or the default statement within a switch block.
The goto may not transfer control into a nested scope—for example, a loop.
public int FindMatch(string myColor)

{

   string[] colorsAvail("blueaqua",

      "red", "green","navyblue");

   int loc;

   int matches=0;

   foreach (colorType in colorsAvail)

   {

      loc = colortype.IndexOf(myColor);

      if (loc >=0) goto Found;

      continue;

Found: 

      matches += 1;

   }   

      return(matches);

}

return

[expression] ;

Returns program control to the method that called the current method. Returns no argument if the enclosing method has a void return type.
public double Area(double w, double l) 

{ 

   return w * l;

}



There are few occasions where the use of a goto statement improves program logic. The goto default version may be useful in eliminating redundant code inside a switch block, but aside from that, avoid its use.

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.