C# Primer: Ramp up quickly

by Fergal Grimes

Author of Microsoft .NET for Programmers

Introduction to C#

This C# primer is intended for programmers who would like to ramp up quickly. C# is the latest in an evolutionary line of C-based programming languages which includes C, C++, and Java. It was used by Microsoft to develop much of the code for the . NET Framework and is considered by many to be the language of choice for . NET development. This series of articles will provide an introduction to the C# language.

If you want more, you'll find almost 100 C# sample programs throughout Microsfot .NET for Programmers, together with a complete case study. These support a learn-by-example approach to supplement the material in these articles.

These articles do not provide complete coverage of the C# language. To do so would require a further book. Instead, the intention is to introduce the important features of the language and equip the reader with the information necessary to understand the main text. At the end of this C# primer series, I will provide a list of resources where you can find further C# tutorials and reference material.

C# language overview

The C# language is an evolution of C and C++, and also has much in common with the Java programming language. Therefore, readers familiar with one or more of these languages will immediately feel comfortable with many features of the C# language, including C# statements, expressions, and operators. C# introduces several modern improvements over C++ in the areas of type safety, versioning, events, and garbage collection. C# also provides full access to operating system and COM API s, and supports an unsafe mode, which enables the use of C-style pointers to manipulate memory. Therefore, C# offers a simpler and safer programming language without sacrificing much of the power and flexibility of C++.

A first .NET program

Without further delay, let's take a look at a simple . NET application. The program shown in listing 1.1 is a simple C# command-line program which greets the user.

 

Listing 1.1 Hello from C#

// file : hello.cs

// compile : csc hello.cs

using System;

class Hello {

  public static void Main() {

    Console.WriteLine("Hello from C#");

  }

}

Every C# program must contain at least one class. In this case, that class is Hello and its Main method is the program's entry point where execution begins. (A member function of a class is known as a method.) To display the greeting, the program calls:

Console.WriteLine("Hello from C#");

This calls the WriteLine method of the Console class, which is contained in the System namespace, to display the message. The System namespace is part of . NET 's Framework class library. We could have coded this call, as follows:

System.Console.WriteLine("Hello from C#");

Instead we declared the System namespace at the start of our program:

using System;

This allows us to omit the namespace name and provides a shorthand for referring to System classes within our program.

This short example demonstrates the use of . NET 's Framework class library, a huge repository of useful classes, which we can use in our . NET applications. These classes are grouped by function and logically arranged in namespaces. We'll look at some common namespaces in a moment.

Compiling the C# Hello program

To compile and test this example, you'll need a copy of the . NET SDK , or Visual Studio . NET. At the time of writing, the SDK could be downloaded from the Microsoft Developer Network site, http://www.msdn.com.

To compile and run this program, open a command window, and use the C# command-line compiler, as shown in figure 1.2.

If csc.exe is not found, you'll have to add the directory where it resides to your path. This directory will depend on the version of . NET you are using and should look like C:\ WINNT \Microsoft.NET\Framework\<.NET Version>.

 

Structure of a C# program

A C# program consists of one or more files, each of which can contain one or more namespaces, which in turn contain types. Examples of types include classes, structs, interfaces, enumerations, and delegates. Listing A.1 illustrates the structure of a C# program.

 

Listing A.1 The structure of a C# program

namespace N1 {

  class C1 {

    // ...

  }

  struct S1 {

    // ...

  }

  interface I1 {

    // ...

  }

  delegate int D1();

  enum E1 {

    // ...

  }

}

namespace N2 {

  class C2 {

    public static void Main(string[] args) {

      // execution starts here

    }

  }

}

 

If no namespace is declared, then a default global namespace is assumed. Note that an executable C# program must include a class containing a Main function member, or method, which represents the program entry point where execution begins. Any command-line arguments are passed as parameters to the Main method in the form of a zero-based array of strings.

To access and use a type, you can use its fully qualified name, which includes its containing namespace name and type name. For example, the following example invokes the WriteLine method of the Console class, which is contained in the System namespace:

System.Console.WriteLine(...);

Alternatively, you can use the using statement to reference the namespace. Thereafter, you can omit the namespace name when referring to the type:

using System;

...

Console.WriteLine(...);

Finally, note the comments in listing A.1. C# uses C-style comments. Therefore, // marks the beginning of a comment which runs to the end of the current line. Multiline comments can be enclosed between /* and */.

Identifiers, variables, and constants

The rules for creating C# identifiers to name program elements are straightforward. We take a look at identifiers and at the declaration of variables and constants next.

Identifiers

Identifiers are used to give names to program elements such as variables, constants, and methods. An identifier must start with a letter or underscore and consist of Unicode characters. Typically, an identifier will consist of letters, underscores, and decimal digits. C# identifiers are case sensitive.

You cannot use a C# keyword as an identifier. However, you may prefix an identifier with the @ character to distinguish it from a keyword:

object @this; // prevent clash with "this" keyword

Although C# identifiers are case sensitive, you should generally not distinguish public members by case alone. Apart from encouraging confusion, the cross language nature of . NET means that your types may be reused by a case insensitive language such as Visual Basic.

Variables

A C# variable represents a location in memory where an instance of some type is stored. The C# type system is really just a layer on top of .NET's language-independent type system, which we explore in detail in chapter 2. In particular, we explore the differences between value and reference types, so we won't repeat that discussion here. Briefly, value types are the simple types such as int , long , and char , which are common to most programming languages. You can also create your own value types. Objects, strings, and arrays are examples of reference types.

Value types can be directly declared and initialized:

bool bln = true;

byte byt1 = 22;

char ch1 = 'x', ch2 = '\u0066';

decimal dec1 = 1.23M;

double dbl1 = 1.23, dbl2 = 1.23D;

short sh = 22;

int i = 22;

long lng1 = 22, lng2 = 22L;

sbyte sb = 22;

float f = 1.23F;

ushort us1 = 22;

uint ui1 = 22, ui2 = 22U;

ulong ul1 = 22, ul2 = 22U, ul3 = 22L, ul4 = 2UL;

Note that you can explicitly specify the type of a literal value by appending a suffix such as U for unsigned, or L for long. You can also specify a character value using a Unicode escape sequence. For example, '\u0061' is the letter a .

Normally, reference types are created using the new keyword:

object o = new System.Object();

However, although the string type is a reference type, it can be directly initialized:

string s = "Hello!";

C# supports C-style escape sequences in strings:

string s1 = "Hello\n"; // ends with newline character

string s2 = "Hello\tthere!"; // contains embedded tab character

Escape sequences begin with a \ (backslash) character. Therefore, if your string otherwise contains a \, you'll need to double it:

string s3 = "C:\\WINNT";

C# also provides the verbatim string for this purpose. To create a verbatim string literal, include the @ character before the opening quote:

string s4 = @"C:\WINNT";

This causes any escape sequences within the string to be ignored.

In C# both the string and char types use 2-byte Unicode characters.

There are no global variables in C#. Therefore, all variables are either member variables of a class or struct, or they are local variables created within the scope of a method.

Constants

C# provides the const modifier which can be used in front of a declaration to create program constants:

const int min = 1;

const int max = 100;

const int range = max - min;

Constants are typically initialized with a literal value. They can also be given the value of an expression, as we do with the range constant above, provided that the compiler can evaluate the expression at compile time. Therefore, the following would generate a compiler error because the value of the expression assigned to i cannot be known until run time:

System.Random r = new System.Random();

const int i = r.Next(1, 7); // error - compiler cannot evaluate

Arrays

Arrays in C# are zero-based and, for the most part, work like they do in other common programming languages. The array type is a reference type:

string[] a;

This declares an array of strings, a, but does not allocate space for any elements. The array name serves as a reference to the array. This is similar to C/C++ where the array name is a pointer to the first array element. Note that the type of a , in this example, is string[] . In other words, unlike C-style arrays, the square brackets are part of the type declaration.

To create an array and allocate space for array elements use:

string[] a = new string[100];

This defines an array of strings, a , and allocates space for 100 string elements. The index of the first element is zero while the last index is 99.

Arrays can be directly initialized:

string[] a1 = {"cat", "dog", "mouse", "horse"};

int[] a2 = {1, 2, 3};

The first line creates an array with four string elements and initializes their values with the strings in curly braces. The second line creates and initializes a three-element array of integers.

We can have multi-dimensional arrays:

string[,] ar = {

  {"cat", "rabbit"},

  {"dog", "fox"},

  {"mouse", "horse"}

};

This declares a two-dimensional (3 x 2) array and initializes it. (C/C++ programmers will find C#'s multi-dimensional array syntax a little different.) We can also have arrays of arrays:

int[][] matrix;

Array elements must be of the same type. However, we can declare an array of type object and put anything in it:

object[] ar = {3, "cat", 2.45};

This may not be particularly useful since you may need to cast to the correct type when accessing an element:

string animal = (string)ar[1];

Expressions and operators

A C# expression consists of a sequence of operators and their operands. If you are a C or C++ programmer you'll be pleased to find that most C# operators look familiar and retain their original C-like meanings. In this section, we explore the full list of C# operators.

Arithmetic operators

C# provides all the usual arithmetic operators for addition, subtraction, multiplication, division, and so forth, as seen in table A.1.

 

Table A.1 C# arithmetic operators

Operator

Description

Examples

+

Unary Plus

+a

-

Unary Minus

-a

++

Increment

++a or a++

- -

Decrement

- -a or a- -

+

Addition

a + b

-

Subtraction

a - b

*

Multiplication

a * b

/

Division

a / b

%

Remainder

a % b

 

For non-C programmers, the increment (++) and decrement (--) operators may be new. Each comes in pre and post forms. Where a pre-increment operator appears inside an expression, the increment operation takes place before the expression is evaluated. With a post-increment operator, the expression is evaluated first. The same rules apply to both forms of the decrement operator. Table A.2 shows some examples.

 

Table A.2 Using the increment operators

i Before

Assignment Expression

j After

i After

3

j = ++i;

4

4

3

j = i++;

3

4

3

j = - -i;

2

2

3

j = i- -;

3

2

 

Relational operators

The C# relational operators are the same as those found in C and C++.

 

Table A.3 C# relational operators

Operator

Description

Example

==

Equality

a == b

!=

Inequality

a != b

<

Less Than

a < b

<=

Less Than or Equal To

a <= b

>

Greater Than

a > b

>=

Greater Than or Equal To

a >= b

 

Visual Basic programmers should note that C# uses a double equals, == , to test for equality and a single equals, = , for assignment. Also, inequality is denoted by != instead of <> .

Logical operators

The logical operators also owe their heritage to C/C++.

 

Table A.4 C# logical operators

Operator

Description

Example

!

Negation

!a

&

Bitwise And

a & b

|

Bitwise Or

a | b

^

Exclusive Or (XOR)

a ^ b

~

Bitwise Complement

~ a

&&

Logical And

a && b

||

Logical Or

a || b

 

Bit-shifting operators

The << and >> operators perform left and right bitwise shifts on integral arguments:

int i1 = 32;

int i2 = i1 << 2; // i2 == 128

int i3 = i1 >> 3; // i3 == 4

Assignment in C#

Table A.5 presents the C# assignment operators. Like C/C++, C# provides compound assignment operators of the form a op= b . In general, a op= b , where op is an arithmetic operator, is just a convenient shorthand for a = a op b .

 

Table A.5 C# assignment operators

Operator

Expression

Expression Value (a==3 and b==7)

=

a = b

7

+=

a += b

10

-=

a -= b

-4

*=

a *= b

21

/=

a /= b

0

%=

a %= b

3

&=

a &= b

3

|=

a |= b

7

>>=

a >>= b

0

<<=

a <<= b

384

 

Miscellaneous operators

C# also includes the conditional, ? , operator found in C/C++:

min = a < b ? a : b;

This is just shorthand for the if-else statement:

if (a < b)

min = a;

else

min = b;

In addition to operators already described, C# includes the following miscellaneous operators:

  • . (Dot)--For member access as in args.Length
  • () (Cast)--For type conversion
  • [] (Indexing)--For indexing arrays, pointers, properties, and attributes
  • new--For creating new objects
  • typeof--For obtaining the runtime type of an object
  • is--For comparing the runtime type of two objects
  • sizeof--For obtaining the size of a type in bytes
  • checked, unchecked-- For checking arithmetic overflow at runtime
  • * (Pointer Indirection)--For obtaining the variable to which a pointer points
  • -> (Pointer Member Access)-- p->m is the same as (*p).m
  • & (Address Of)--Returns the address of its operand

A note about operator precedence and associativity

Operator precedence determines the order in which individual operators are evaluated. For example, a+b*c is evaluated as a+(b*c) because the * operator has higher precedence than the + operator.

When an operand occurs between two operators with the same precedence, the associativity of the operators controls the order in which the operations are performed. Except for the assignment operators, all binary operators are left-associative, meaning that operations are performed from left to right. The assignment operators and the conditional operator are right-associative.

For the most part, operator precedence and associativity in C# follow the C/C++ tradition. (You'll find a complete list in the . NET SDK and Visual Studio . NET documentation.) Since few programmers can remember the rules anyway, it is best to use parentheses to explicitly convey your intentions. For example, a+b*c first multiplies b by c and then adds the result to a , but (a+b)*c first adds a to b and then multiplies the result by c .

C# statements

C# statements can span more than one line, and are terminated by a semicolon. In addition, statements can be grouped into statement blocks, where a block is a sequence of statements enclosed in curly braces ( { and } ). You can use a statement block wherever a single statement is valid:

int i, j;

// a single statement...

i = 1;

// a statement block...

{

  j = 2;

  i = i + j;

}

if

The if statement is used to branch based on some condition:

if (i < 5)

System.Console.WriteLine("i < 5");

This example displays a message if i is less than 5. An if statement can include an else clause which will be executed if the test condition is false:

if (i < 5)

System.Console.WriteLine("i < 5");

else

System.Console.WriteLine("i >= 5");

Note that, where there are two or more statements governed by the if or else condition, the statements must be enclosed in curly braces to form a statement block:

if (i < 5) {

  System.Console.WriteLine("i < 5");

  System.Console.WriteLine("i is smaller");

} else {

  System.Console.WriteLine("i >= 5");

  System.Console.WriteLine("i is not smaller");

}

This is a case of the general rule, mentioned earlier, that you can use a statement block wherever a single statement is valid.

do

The do statement is used for repetition:

int i = 1;

do

  System.Console.WriteLine(i++);

while(i <= 5);

This example displays the digits 1 to 5. Since the loop condition is tested at the bottom of the loop, a do loop is always executed at least once.

while

The while statement is also used for repetition:

int i = 1;

while(i <= 5)

  System.Console.WriteLine(i++);

This example also displays the digits 1 to 5. In this case, the loop condition is tested at the top of the loop.

for

The for statement is also used for repetition and will be familiar to C/C++ programmers. Unlike while and do, it is typically used when the number of iterations is known at the start of the loop:

for (int i = 1; i <= 5; i++)

  System.Console.WriteLine(i);

This example also displays the digits 1 to 5. The for loop contains three expressions separated by semicolons and enclosed in parentheses. The first is the initializing expression which is executed once before looping begins. In this case, it declares an integer, i , and initializes it to 1. The second expression contains the looping condition. In this example, it checks if i is less than or equal to 5. The loop terminates when this condition becomes false. The third expression is known as the iterator expression and is evaluated after each iteration. In this case, it increments the variable, i .

Note that you can code an infinite loop by omitting the loop expressions:

for (;;) {

  // infinite loop

  ...

}

You can also insert multiple expressions in place of the initializing and iterator expressions:

for (int i=1, j=2; i <= 5; i++, j+=2) {

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

}

This example initializes the integers i and j and also updates them at the end of each iteration.

continue

The continue statement is used within a loop to skip the remainder of the current iteration and begin the next:

for (int i = 1; i <= 5; i++) {

  if (i == 3)

  continue;

  System.Console.WriteLine(i);

}

This loop displays the digits 1, 2, 4, and 5. The digit 3 is skipped.

break

The break statement is used to break out of a loop:

for (int i = 1; i <= 5; i++) {

   if (i == 3)

   break;

   System.Console.WriteLine(i);

}

This example displays the digits 1 and 2. The remaining loop iterations are skipped.

switch

The switch statement is slightly different from its C/C++ counterpart:

uint i = 2;

switch(i) {

   case 0:

     goto case 2;

   case 1:

     goto case 2;

   case 2:

     System.Console.WriteLine("i < 3");

     break;

   case 3:

    System.Console.WriteLine("i == 3");

    break;

  default:

    System.Console.WriteLine("i > 3");

    break;

}

This example displays the message: i < 3 . The switch statement evaluates the expression in parentheses and uses the result to select from among several cases. In this example, the result is 2 and the statement following case 2 is executed. Note that the break statements are required since C#, unlike C/C++, does not allow fall through from one case to the next. Instead, C# provides the goto case (as seen in the example) and goto default , to allow multiple cases to execute the same statement block. Omitting the break statements causes a compiler error.

The default case, which is executed when no other case matches, is not required, although it is good practice to include it.

foreach

The foreach statement provides a convenient way to iterate over an array or collection:

int[] arr = {2, 4, 6, 8};

foreach (int i in arr)

  System.Console.WriteLine(i);

This example displays the integers 2, 4, 6, and 8.

return

The return statement terminates execution of the current method and returns control to the caller. It can optionally return a value to the caller. The following example illustrates:

class Add {

  public static void Main() {

    System.Console.WriteLine("2+3=" + add(2, 3));

  }

  private static int add(int i, int j) {

    return i + j;

  }

}

This short program contains a method called add which accepts two integer arguments, adds them, and returns the result.

goto

The goto statement transfers control to a statement marked with a label:

using System;

public class SomeClass {

public static void Main (string[] args) {

  if (args.Length == 0) {

    Console.WriteLine("No args provided... aborting!");

    goto end;

  }

  Console.WriteLine("First arg is " + args[0]);

  end: return;

  }

}

This example uses goto to jump to the end of the program if no arguments are provided on the command line. The target of the goto is the return statement prefixed with an end label. We can choose any name for a label, as long as it is not a C# keyword.

throw

The throw statement throws an exception. It is typically used to signal an error or abnormal condition:

if (val > max)

  throw new Exception("value exceeds maximum");

We'll look more closely at exceptions later in this C# primer series.

By now, you should be able to compile a C# application, and have a better appreciation for the C# basics. You've seen the basic structure, explored some operators, and used some of the primitives including identifiers, constants and variables. In the next article, we'll take a look at the classes, stuctures and important name spaces of C#. If you would like to know more about C# or the Microsoft .NET platform, check out the book, Microsoft .NET for Programmers, or Manning's Dot Net Corner community pages. You can reach the next article in this series here.