BUY THIS BOOK
Add to Cart

Print Book $29.99


Safari Books Online

What is this?

Add to UK Cart

Print Book £17.50

What is this?

Looking to Reprint this content?


C# Essentials
C# Essentials, Second Edition By Ben Albahari, Peter Drayton, Brad Merrill
January 2002
Pages: 216

Cover | Table of Contents | Colophon


Table of Contents

Chapter 1: Introduction
C# is a language built specifically to program the Microsoft .NET Framework. The .NET Framework consists of a runtime environment called the Common Language Runtime (CLR), and a set of class libraries, which provide a rich development platform that can be exploited by a variety of languages and tools.
Programming languages have strengths in different areas. Some languages are powerful but can be bug-prone and difficult to work with, while others are simpler but can be limiting in terms of functionality or performance. C# is a new language designed to provide an optimum blend of simplicity, expressiveness, and performance.
Many features of C# were designed in response to the strengths and weaknesses of other languages, particularly Java and C++. The C# language specification was written by Anders Hejlsberg and Scott Wiltamuth. Anders Hejlsberg is famous in the programming world for creating the Turbo Pascal compiler and leading the team that designed Delphi.
Key features of the C# language include the following:
Component orientation
An excellent way to manage complexity in a program is to subdivide it into several interacting components, some of which can be used in multiple scenarios. C# has been designed to make component building easy and provides component-oriented language constructs such as properties, events, and declarative constructs called attributes.
One-stop coding
Everything pertaining to a declaration in C# is localized to the declaration itself, rather than being spread across several source files or several places within a source file. Types do not require additional declarations in separate header or Interface Definition Language (IDL) files, a property's get/set methods are logically grouped, documentation is embedded directly in a declaration, etc. Furthermore, because declaration order is irrelevant, types don't require a separate stub declaration to be used by another type.
Versioning
C# provides features such as explicit interface implementations, hiding inherited members, and read-only modifiers, which help new versions of a component work with older components that depend on it.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
C# Language
Programming languages have strengths in different areas. Some languages are powerful but can be bug-prone and difficult to work with, while others are simpler but can be limiting in terms of functionality or performance. C# is a new language designed to provide an optimum blend of simplicity, expressiveness, and performance.
Many features of C# were designed in response to the strengths and weaknesses of other languages, particularly Java and C++. The C# language specification was written by Anders Hejlsberg and Scott Wiltamuth. Anders Hejlsberg is famous in the programming world for creating the Turbo Pascal compiler and leading the team that designed Delphi.
Key features of the C# language include the following:
Component orientation
An excellent way to manage complexity in a program is to subdivide it into several interacting components, some of which can be used in multiple scenarios. C# has been designed to make component building easy and provides component-oriented language constructs such as properties, events, and declarative constructs called attributes.
One-stop coding
Everything pertaining to a declaration in C# is localized to the declaration itself, rather than being spread across several source files or several places within a source file. Types do not require additional declarations in separate header or Interface Definition Language (IDL) files, a property's get/set methods are logically grouped, documentation is embedded directly in a declaration, etc. Furthermore, because declaration order is irrelevant, types don't require a separate stub declaration to be used by another type.
Versioning
C# provides features such as explicit interface implementations, hiding inherited members, and read-only modifiers, which help new versions of a component work with older components that depend on it.
Type safety and a unified type system
C# is type-safe, which ensures that a variable can be accessed only through the type associated with that variable. This encapsulation encourages good programming design and eliminates potential bugs or security breaches by making it impossible for one variable to inadvertently or maliciously overwrite another.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Common Language Runtime
Of fundamental importance to the .NET Framework is the fact that programs are executed within a managed execution environment provided by the Common Language Runtime. The CLR greatly improves runtime interactivity between programs, portability, security, development simplicity, and cross-language integration, and provides an excellent foundation for a rich set of class libraries.
Absolutely key to these benefits is the way .NET programs are compiled. Each language targeting .NET compiles source code into metadata and Microsoft Intermediate Language (MSIL) code. Metadata includes a complete specification for a program including all its types, apart from the actual implementation of each function. These implementations are stored as MSIL, which is machine-independent code that describes the instructions of a program. The CLR uses this "blueprint" to bring a .NET program to life at runtime, providing services far beyond what is possible with the traditional approach—compiling code directly to assembly language.
Key features of the CLR include the following:
Runtime interactivity
Programs can richly interact with each other at runtime through their metadata. A program can search for new types at runtime, then instantiate and invoke methods on those types.
Portability
Programs can be run without recompiling on any operating system and processor combination that supports the CLR. A key element of this platform independence is the runtime's JIT ( Just-In-Time) Compiler, which compiles the MSIL code it is fed to native code that runs on the underlying platform.
Security
Security considerations permeate the design of the .NET Framework. The key to making this possible is CLR's ability to analyze MSIL instructions as being safe or unsafe.
Simplified deployment
An assembly is a completely self-describing package that contains all the metadata and MSIL of a program. Deployment can be as easy as copying the assembly to the client computer.
Versioning
An assembly can function properly with new versions of assemblies it depends on without recompilation. Key to making this possible is the ability to resolve all type references though metadata.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Framework Class Library
The .NET Framework provides the .NET Framework Class Library (FCL), which can be used by all languages. The FCL offers features ranging from core functionality of the runtime, such as threading and runtime manipulation of types (reflection), to types that provide high-level functionality, such as data access, rich client support, and web services (whereby code can even be embedded in a web page). C# has almost no built-in libraries; it uses the FCL instead.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
A First C# Program
Here is a simple C# program:
namespace FirstProgram {
  using System;
  class Test {
    static void Main (  ) {
      Console.WriteLine ("Welcome to C#!");
    }
  }
}
A C# program is composed of types (typically classes) that we organize into namespaces. Each type contains function members (typically methods), as well as data members (typically fields). In our program, we define a class named Test that contains a method, named Main, that writes Welcome to C#! to the Console window. The Console class encapsulates standard input/output functionality, providing methods such as WriteLine. To use types from another namespace, we use the using directive. Since the Console class resides in the System namespace, we write using System; similarly, types from other namespaces could use our Test class by including the following statement: using FirstProgram;.
To compile this program into an executable, paste it into a text file, save it as Test.cs, then type csc Test.cs in the command prompt. This compiles the program into an executable called Test.exe. Add the /debug option to the csc command line to include debugging symbols in the output. This will let you run your program under a debugger and get meaningful stack traces that include line numbers.
.NET executables contain a small CLR host created by the C# compiler. The host starts the CLR and loads your application, starting at the Main entry point. Note that Main must be specified as static.
In C#, there are no standalone functions; functions are always associated with a type, or as we will see, instances of that type. Our program is simple and makes use of only static members, which means the member is associated with its type, rather than instances of its type. In addition, we make use of only void methods, which means these methods do not return a value. Of final note is that C# recognizes a method named Main as the default entry point of execution.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 2: C# Language Reference
This chapter walks you through each aspect of the C# language. Many features of C# will be familiar if you have experience with a strongly typed object-oriented language.
Identifiers are names programmers choose for their types, methods, variables, and so on. An identifiermust be a whole word, essentially composed of Unicode characters starting with a letter or underscore. An identifier must not clash with a keyword. As a special case, the @ prefix can be used to avoid such a conflict, but the character isn't considered part of the identifier that it precedes. For instance, the following two identifiers are equivalent:
Korn
@Korn
C# identifiers are case-sensitive, but for compatibility with other languages, you should not differentiate public or protected identifiers by case alone.
A C# program is written by building new types and leveraging existing types, either those defined in the C# language itself or imported from other libraries. Each type contains a set of data and function members, which combine to form the modular units that are the key building blocks of a C# program.
Generally, you must create instances of a type to use that type. Those data members and function members that require a type to be instantiated are called instance members. Data members and function members that can be used on the type itself are called static members.
In this program, we build our own type called Counter and another type called Test that uses instances of the Counter. The Counter type uses the predefined type int, and the Test type uses the static function member WriteLine of the Console class defined in the System namespace:
// Imports types from System namespace, such as Console
using System;
class Counter { // New types are typically classes or structs
  // --- Data members ---
  int value; // field of type int
  int scaleFactor; // field of type int

  // Constructor, used to initialize a type instance
  public Counter(int scaleFactor) { 
    this.scaleFactor = scaleFactor;  
  }
  // Method
  public void Inc(  ) {
    value+=scaleFactor;
  }
  // Property
  public int Count {
    get {return value; }
  }
}
class Test {
  // Execution begins here
  static void Main(  ) {

    // Create an instance of counter type
    Counter c = new Counter(5);
    c.Inc(  );
    c.Inc(  );
    Console.WriteLine(c.Count); // prints "10";

    // Create another instance of counter type
    Counter d = new Counter(7);
    d.Inc(  );
    Console.WriteLine(d.Count); // prints "7";
   }
}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Identifiers
Identifiers are names programmers choose for their types, methods, variables, and so on. An identifiermust be a whole word, essentially composed of Unicode characters starting with a letter or underscore. An identifier must not clash with a keyword. As a special case, the @ prefix can be used to avoid such a conflict, but the character isn't considered part of the identifier that it precedes. For instance, the following two identifiers are equivalent:
Korn
@Korn
C# identifiers are case-sensitive, but for compatibility with other languages, you should not differentiate public or protected identifiers by case alone.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Types
A C# program is written by building new types and leveraging existing types, either those defined in the C# language itself or imported from other libraries. Each type contains a set of data and function members, which combine to form the modular units that are the key building blocks of a C# program.
Generally, you must create instances of a type to use that type. Those data members and function members that require a type to be instantiated are called instance members. Data members and function members that can be used on the type itself are called static members.
In this program, we build our own type called Counter and another type called Test that uses instances of the Counter. The Counter type uses the predefined type int, and the Test type uses the static function member WriteLine of the Console class defined in the System namespace:
// Imports types from System namespace, such as Console
using System;
class Counter { // New types are typically classes or structs
  // --- Data members ---
  int value; // field of type int
  int scaleFactor; // field of type int

  // Constructor, used to initialize a type instance
  public Counter(int scaleFactor) { 
    this.scaleFactor = scaleFactor;  
  }
  // Method
  public void Inc(  ) {
    value+=scaleFactor;
  }
  // Property
  public int Count {
    get {return value; }
  }
}
class Test {
  // Execution begins here
  static void Main(  ) {

    // Create an instance of counter type
    Counter c = new Counter(5);
    c.Inc(  );
    c.Inc(  );
    Console.WriteLine(c.Count); // prints "10";

    // Create another instance of counter type
    Counter d = new Counter(7);
    d.Inc(  );
    Console.WriteLine(d.Count); // prints "7";
   }
}
Each type has its own set of rules defining how it can be converted to and from other types. Conversions between types may be implicit or explicit. Implicit conversions can be performed automatically, while explicit conversions require a cast usingthe C cast operator, ( ):
int x = 123456; // int is a 4-byte integer
long y = x; // implicit conversion to 8-byte integer
short z =(short)x; // explicit conversion to 2-byte integer
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Variables
A variable represents a typed storage location. A variable can be a local variable, a parameter, an array element (see Section 2.11 later in this chapter), an instance field, or a static field (see Section 2.9.2 later in this chapter).
Every variable has an associated type, which essentially defines the possible values the variable can have and the operations that can be performed on that variable. C# is strongly typed, which means the set of operations that can be performed on a type is enforced at compile time, rather than at runtime. In addition, C# is type-safe, which, with the help of runtime checking, ensures that a variable can be operated only via the correct type (except in unsafe blocks; see Section 2.17.2 later in this chapter).
Variables in C# (except in unsafe contexts) must be assigned a value before they are used. A variable is either explicitly assigned a value or automatically assigned a default value. Automatic assignment occurs for static fields, class instance fields, and array elements not explicitly assigned a value. For example:
using System;
class Test {
  int v;
  // Constructors that initialize an instance of a Test
  public Test(  ) {} // v will be automatically assigned to 0
  public Test(int a) { // explicitly assign v a value
     v = a;
  }
  static void Main(  ) {
    Test[] iarr = new Test [2]; // declare array
    Console.WriteLine(iarr[1]); // ok, elements assigned to null
    Test t;
    Console.WriteLine(t); // error, t not assigned
  }
}
The following table shows that, essentially, the default value for all primitive (or atomic) types is zero:
Type
Default value
Numeric
0
Bool
false
Char
'\0'
Enum
0
Reference
null
The default value for each field in a complex (or composite) type is one of these aforementioned values.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Expressions and Operators
An expression is a sequence of operators and operands that specifies a computation. C# has unary operators, binary operators, and one ternary operator. Complex expressions can be built because an operand may itself be an expression, such as the operand (1 + 2) in the following example:
((1 + 2) / 3)
When an expression contains multiple operators, the precedence of the operators controls the order in which the individual operators are evaluated. When the operators are of the same precedence, their associativity determines their order of evaluation. Binary operators (except for assignment operators) are left-associative and are evaluated from left to right. The assignment operators, unary operators, and the conditional operator are right-associative, evaluated from right to left.
For example:
1 + 2 + 3 * 4
is evaluated as:
((1 + 2) + (3 * 4))
because * has a higher precedence than +, and + is a left-associative binary operator. You can insert parentheses to change the default order of evaluation. C# also overloads operators, which means the same operator symbols can have different meanings in different contexts (e.g., primary, unary, etc.) or different meanings for different types.
Table 2-2 lists C#'s operators in order of precedence. Operators in the same box have the same precedence, and operators in italic may be overloaded for custom types (see Section 2.9.8 later in this chapter).
Table 2-2: Operator Precedence Table
Category
Operators
Primary
Grouping:(x)
Member access: x.y
Struct pointer member access: ->
Method call: f(x)
Indexing: a[x]
Post increment: x++
Post decrement:x--
Constructor call: new
Array stack allocation: stackalloc
Type retrieval: typeof
Struct size retrieval: sizeof
Arithmetic check on: checked
Arithmetic check off: unchecked
Unary
Positive value of (passive):
+Negative value of:
-Not:!
Bitwise complement: ~
Pre increment: ++x
Pre decrement:-- x
Type cast: (T)x
Value at address: *
Address of value: &
Multiplicative
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Statements
Execution of a C# program is specified by a series of statements that execute sequentially in the textual order in which they appear. All statements in a procedural-based language such as C# are executed for their effect. The two most basic kinds of statement in C# are the declaration and expression statements. C# also provides flow control statements for selection, looping, and jumping. Finally, C# provides statements for special purposes, such as locking memory or handling exceptions.
So that multiple statements can be grouped together, zero or more statements may be enclosed in braces ({ }) to form a statement block. A statement block can be used anywhere a single statement is valid.
Syntax:
[variable =]? expression;
An expression statement evaluates an expression either by assigning its result to a variable or generating side effects, (i.e., invocation, new, ++, or --). An expression statement ends in a semicolon (;). For example:
x = 5 + 6; // assign result
x++; // side effect
y = Math.Min(x, 20); // side effect and assign result
Math.Min (x, y); // discards result, but ok, there is a side effect
x == y; // error, has no side effect, and does not assign result
Variable declaration syntax:
type [variable [ = expression ]?]+ ;
Constant declaration syntax:
const type [variable = constant-expression]+ ;
A declaration statement declares a new variable. You can initialize a variable at the time of its declaration by optionally assigning it the result of an expression.
The scope of a local or constant variable extends to the end of the current block. You can't declare another local variable with the same name in the current block or in any nested blocks. For example:
bool a = true;
while(a) {
   int x = 5;
   if (x==5) {
      int y = 7;
      int x = 2; // error, x already defined
   }
   Console.WriteLine(y); // error, y is out of scope
}
A constant declaration is like a variable declaration, except that the value of the variable can't be changed after it has been declared:
const double speedOfLight = 2.99792458E08;
speedOfLight+=10; // error
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Organizing Types
A C# program is basically a group of types. These types are defined in files, organized by namespaces, compiled into modules, and then grouped into an assembly.
Generally, these organizational units overlap: an assembly can contain many namespaces, and a namespace can be spread across several assemblies. A module can be part of many assemblies, and an assembly can contain many modules. A source file can contain many namespaces, and a namespace can span many source files. For more information, see Section 3.9 in Chapter 3.
File organization is of almost no significance to the C# compiler: an entire project can be merged into a single .cs file and still compile successfully (preprocessor statements are the only exception to this). However, it's generally tidy to have one type in one file, with a filename that matches the name of the class and a directory name that matches the name of the class's namespace.
Namespace declaration syntax:
namespace name+a {
  using-statement*
  [namespace-declaration | type-declaration]*b
}
a Dot-delimited.
b No delimiters.
A namespace enables you to group related types into a hierarchical categorization. Generally the first name in a namespace name is the name of your organization, followed by names that group types with finer granularity. For example:
namespace MyCompany.MyProduct.Drawing {
  class Point {int x, y, z;}
  delegate void PointInvoker(Point p);
}

Section 2.6.2.1: Nesting namespaces

You may also nest namespace declarations instead of using dots. This example is semantically identical to the previous example:
namespace MyCompany {
  namespace MyProduct {
    namespace Drawing {
      class Point {int x, y, z;}
      delegate void PointInvoker(Point p);
    }
  }
}

Section 2.6.2.2: Using a type with its fully qualified name

The complete name of a type includes its namespace name. To use the Point class from another namespace, you may refer to it with its fully qualified name:
namespace TestProject {
  class Test {
    static void Main(  ) {
      MyCompany.MyProduct.Drawing.Point x;
    }
  }
}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Inheritance
A C# class can inherit from another class to extend or customize that class. A class can inherit only from a single class but can be inherited by many classes, thus forming a class hierarchy. At the root of any class hierarchy is the object class, which all objects implicitly inherit from. Inheriting from a class requires specifying the class to inherit from in the class declaration, using the C++ colon notation:
class Location { // Implicitly inherits from object
  string name;

  // The constructor that initializes Location
  public Location(string name) {
    this.name = name;
  }
  public string Name {get {return name;}}
  public void Display(  ) {
    Console.WriteLine(Name);
  }
}
class URL : Location { // Inherit from Location
  public void Navigate(  ) {
    Console.WriteLine("Navigating to "+Name);
  }
  // The constructor for URL, which calls Location's constructor
  public URL(string name) : base(name) {}
}
URL has all the members of Location and a new member, Navigate:
class Test {
  public static void Main(  ) {
    URL u = new URL("http://microsoft.com");
    u.Display(  );
    u.Navigate(  );
  }
}
The specialized class and general class are referred to as either the derived class and base class or the subclass and superclass.
A class D may be implicitly upcast to the class B it derives from, and a class B may be explicitly downcast to a class D that derives from it. For instance:
URL u = new URL("http://microsoft.com");
Location l = u; // upcast
u = (URL)l; // downcast
If the downcast fails, an InvalidCastException is thrown.

Section 2.7.1.1: as operator

The as operator allows a downcast that evaluates to null to be made if the downcast fails:
u = l as URL;

Section 2.7.1.2: is operator

The is operator can test if an objectis or derives from a specified class (or implements an interface). It is often used to perform a test before a downcast:
if (l is URL)
  ((URL)l).Navigate(  );
Polymorphism is the ability to perform the same operation on many types, as long as each type shares a common subset of characteristics. C# custom types exhibit polymorphism by inheriting classes and implementing interfaces (see Section 2.10 later in this chapter).
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Access Modifiers
To promote encapsulation, a type or type member may hide itself from other types or other assemblies by adding one of the following five access modifiers to the declaration:
public
The type or type member is fully accessible. This is the implicit accessibility for enum members (see Section 2.12 later in this chapter) and interface members (see Section 2.10 later in this chapter).
internal
The type or type member in assembly A is accessible only from within A. This is the default accessibility for nonnested types, so it may be omitted.
private
The type member in type T is accessible only from within T. This is the default accessibility for class and struct members, so it may be omitted.
protected
The type member in class C is accessible only from within C or from within a class that derives from C.
protected internal
The type member in class C and assembly A is accessible only from within C, from within a class that derives from C, or from within A. Note that C# has no concept of protected and internal, in which a type member in class C and assembly A is accessible only from within C or from within a class that derives from C and is within A.
Note that a type member may be a nested type. Here is an example that uses access modifiers:
// Assembly1.dll
using System;
public class A {
  private int x=5;
  public void Foo(  ) {Console.WriteLine (x);}
  protected static void Goo(  ) {}
  protected internal class NestedType {}
}
internal class B {
  private void Hoo (  ) {
    A a1 = new A (  ); // ok
    Console.WriteLine(a1.x); // error, A.x is private
    A.NestedType n; // ok, A.NestedType is internal
    A.Goo(  ); // error, A's Goo is protected
  }
}

// Assembly2.exe (references Assembly1.dll)
using System;
class C : A { // C defaults to internal
  static void Main(  ) { // Main defaults to private
    A a1 = new A(  ); // ok
    a1.Foo(  ); // ok
    C.Goo(  ); // ok, inherits A's protected static member
    new A.NestedType(  ); // ok, A.NestedType is protected
    new B(  ); // error, Assembly 1's B is internal
    Console.WriteLine(x); // error, A's x is private
  }
}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Classes and Structs
Class declaration syntax:
attributes? unsafe? access-modifier?
new?
[ abstract | sealed ]?
class class-name 
[: base-class | : interface+ | : base-class, interface+ ]?
{ class-members }
Struct declaration syntax:
attributes? unsafe? access-modifier?
new?
struct struct-name [: interface+]?
{ struct-members }
A class or struct combines data, functions, and nested types into a new type, which is a key building block of C# applications. The body of a class or struct is comprised of three kinds of members: data, function, and type.
Data members
Includes fields, constants, and events. The most common data members are fields. Events are a special case, since they combine data and functionality in the class or struct (see Section 2.14 later in this chapter).
Function members
Includes methods, properties, indexers, operators, constructors, and destructors. Note that all function members are either specialized types of methods or are implemented with one or more specialized types of methods.
Type members
Includes nested types. Types can be nested to control their accessibility (see Section 2.8 later in this chapter).
Here's an example:
class ExampleClass {
   int x; // data member
   void Foo(  ) {} // function member
   struct MyNestedType  {} // type member
}
Classes differ from structs in the following ways:
  • A class is a reference type; a struct is a value type. Consequently, structs are typically simple types in which value semantics are desirable (e.g., assignment copies a value rather than a reference).
  • A class fully supports inheritance (see Section 2.7 earlier in this chapter). A struct inherits from object and is implicitly sealed. Both classes and structs can implement interfaces.
  • A class can have a destructor; a struct can't.
  • A class can define a custom parameterless constructor and initialize instance fields; a struct can't. The default parameterless constructor for a struct initializes each field with a default value (effectively zero). If a struct declares a constructor(s), all its fields must be assigned in that constructor call.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Interfaces
Syntax:
attributes? unsafe? access-modifier?
new?
interface interface-name [ : base-interface+ ]?
{ interface-members }
An interface is like a class, but with these major differences:
  • An interface provides a specification rather than an implementation for its members. This is similar to a pure abstract class, which is an abstract class consisting of only abstract members.
  • A class and struct can implement multiple interfaces; a class can inherit only from a single class.
  • A struct can implement an interface but can't inherit from a class.
Earlier we defined polymorphism as the ability to perform the same operations on many types, as long as each type shares a common subset of characteristics. The purpose of an interface is precisely for defining such a set of characteristics.
An interface is comprised of one or more methods, properties, indexers, and events. These members are always implicitly public and implicitly abstract (therefore virtual and nonstatic).
An interface declaration is like a class declaration, but it provides no implementation for its members, since all its members are implicitly abstract. These members are intended to be implemented by a class or struct that implements the interface.
Here's a simple interface that defines a single method:
public interface IDelete {
  void Delete(  );
}
Classes or structs that implement an interface may be said to "fulfill the contract of the interface." In this example, GUI controls that support the concept of deleting, such as a TextBox or TreeView, or your own custom GUI control, can implement the IDelete interface:
public class TextBox : IDelete {
  public void Delete(  ) {...}
}
public class TreeView : IDelete {
  public void Delete(  ) {...}
}
If a class inherits from a base class, the name of each interface to be implemented must appear after the base-class name:
public class TextBox : Control, IDelete {...}
public class TreeView : Control, IDelete {...}
An interface is useful when you need multiple classes to share characteristics not present in a common base class. In addition, an interface is a good way to ensure that these classes provide their own implementation for the interface member, since interface members are implicitly abstract.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Arrays
Syntax:
type[*]+ array-name = new type [ dimension+ ][*]*;
Note that [*] is the set: [] [,] [,,] ...
Arrays allow a group of elements of a particular type to be stored in a contiguous block of memory. Array types derive from System.Array and are declared in C# using brackets ([]). For instance:
char[] vowels = new char[] {'a','e','i','o','u'};
Console.WriteLine(vowels [1]); // Prints "e"
The preceding function call prints "e" because array indexes start at 0. To support other languages, .NET can create arrays based on arbitrary start indexes, but the FCL libraries always use zero-based indexing. Once an array has been created, its length can't be changed. However, the System.Collection classes provide dynamically sized arrays, as well as other data structures, such as associative (key/value) arrays (see Section 3.4 in Chapter 3).
Multidimensional arrays come in two varieties, rectangular and jagged. Rectangular arrays represent an n-dimensional block; jagged arrays are arrays of arrays:
// rectangular
int [,,] matrixR = new int [3, 4, 5]; // creates one big cube
// jagged
int [][][] matrixJ = new int [3][][];
for (int i = 0; i < 3; i++) {
   matrixJ[i] = new int [4][];
   for (int j = 0; j < 4; j++)
      matrixJ[i][j] = new int [5];
} 
// assign an element
matrixR [1,1,1] = matrixJ [1][1][1] = 7;
For convenience, local and field declarations can omit the array type when assigning a known value, because the type is specified in the declaration:
int[,] array = {{1,2},{3,4}};
Arrays know their own length. For multidimensional arrays, the GetLength method returns the number of elements for a given dimension, which is counted from (the outermost) to the array's Rank-1 (the innermost):
// one-dimensional
for(int i = 0; i < vowels.Length; i++);
// multidimensional
for(int i = 0; i < matrixR.GetLength(2); i++);
All array indexing is bounds-checked by the CLR, with IndexOutOf-RangeException thrown for invalid indexes. As in Java, bounds checking prevents program faults and debugging difficulties while enabling code to be executed with security restrictions.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Enums
Syntax:
attributes? access-modifier?
new?
enum enum-name [ : integer type ]?
{ [attributes? enum-member-name [ = value ]? ]* }
Enums specify a group of named numeric constants:
public enum Direction {North, East, West, South}
Unlike in C, enum members must be used with the enum type name. This resolves naming conflicts and makes code clearer:
Direction walls = Direction.East;
By default, enums are assigned integer constants: 0, 1, 2, etc. You can optionally specify an alternative numeric type to base your enum on and explicitly specify values for each enum member:
[Flags]
public enum Direction : byte {
   North=1, East=2, West=4, South=8
}
Direction walls = Direction.North | Direction.West;
if((walls & Direction.North) != 0)
    System.Console.WriteLine("Can't go north!");
The [Flags] attribute is optional. It informs the runtime that the values in the enum can be bit-combined and should be decoded accordingly in the debugger or when outputting text to the console. For example:
Console.WriteLine(walls); // Displays "North, West"
Console.WriteLine(walls.ToString("d")); // displays "5"
The System.Enum type also provides many useful static methods for enums that allow you to determine the underlying type of an enum, check if a specific value is supported, initialize an enum from a string constant, retrieve a list of the valid values, and perform other common operations such as conversions. Here is an example:
using System;
public enum Toggle : byte { Off=0, On=1 }
class TestEnum {
  static void Main(  ) {
    Type t = Enum.GetUnderlyingType(typeof(Toggle));
    Console.WriteLine(t); // Prints "System.Byte"

    bool bDimmed = Enum.IsDefined(typeof(Toggle), "Dimmed");
    Console.WriteLine(bDimmed); // Prints "False"

    Toggle tog =(Toggle)Enum.Parse(typeof(Toggle), "On");
    Console.WriteLine(tog.ToString("d")); // Prints "1"
    Console.WriteLine(tog); // Prints "On"

    Array oa = Enum.GetValues(typeof(Toggle));
    foreach(Toggle atog in oa) // Prints "Off=0, On=1"
      Console.WriteLine("{0}={1}", atog, atog.ToString("d")); 
  }
}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Delegates
Syntax:
attributes? unsafe? access-modifier?
new?
delegate
[ void | type ]
delegate-name (parameter-list);
A delegate is a type that defines a method signature so delegate instances can hold and invoke a method or list of methods that match its signature. A delegate declaration consists of a name and a method signature.
Here's an example:
delegate bool Filter(string s);
This declaration lets you create delegate instances that can hold and invoke methods that return bool and have a single string parameter. In the following example a Filter is created that holds the FirstHalfOfAlphabet method. You then pass the Filter to the Display method, which invokes the Filter:
class Test {
  static void Main(  ) {
    Filter f = new Filter(FirstHalfOfAlphabet);
    Display(new String [] {"Ant","Lion","Yak"}, f);
  }
  static bool FirstHalfOfAlphabet(string s) {
    return "N".CompareTo(s) > 0;
  }
  static void Display(string[] names, Filter f) {
    int count = 0;
    foreach(string s in names)
      if(f(s)) // invoke delegate
        Console.WriteLine("Item {0} is {1}", count++, s);
  }
}
Delegates can hold and invoke multiple methods. In this example, we declare a simple delegate called MethodInvoker, which can hold and then invoke the Foo and Goo methods sequentially. The += method creates a new delegate by adding the right delegate operand to the left delegate operand.
using System;
delegate void MethodInvoker(  );
class Test {
  static void Main(  ) {
     new Test(  ); // prints "Foo", "Goo"
  }
  Test(  ) {
    MethodInvoker m = null;
    m += new MethodInvoker(Foo);
    m += new MethodInvoker(Goo);
    m(  );
  }
  void Foo(  ) {
    Console.WriteLine("Foo");
  }
  void Goo(  ) {
    Console.WriteLine("Goo");
  }
}
A delegate can also be removed from another delegate using the -= operator:
Test(  ) {
  MethodInvoker m = null;
  m += new MethodInvoker(Foo);
  m -= new MethodInvoker(Foo);
  m(  ); // m is now null, throws NullReferenceException
}
Delegates are invoked in the order they are added. If a delegate has a non-void return type, then the value of the last delegate invoked is returned. Note that the
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Events
Event handling is essentially a process by which one object can notify other objects that an event has occurred. This process is largely encapsulated by multicast delegates, which have this ability built in.
The FCL defines numerous public delegates used for event handling, but you can also write your own. For example:
delegate void MoveEventHandler(object source, MoveEventArgs e);
By convention, an event delegate's first parameter denotes the source of the event, and the delegate's second parameter derives from System.EventArgs and stores data about the event.
You can define subclasses of EventArgs to include information relevant to a particular event:
public class MoveEventArgs : EventArgs {
  public int newPosition;
  public bool cancel;
  public MoveEventArgs(int newPosition) {
    this.newPosition = newPosition;
  }
}
A class or struct can declare an event by applying the event modifier to a delegate field. In this example, the Slider class has a Position property that fires a Move event whenever its Position changes:
class Slider {
  int position;
  public event MoveEventHandler Move;
  public int Position {
    get { return position; }
    set {
      if (position != value) { // if position changed
        if (Move != null) { // if invocation list not empty
          MoveEventArgs args = new MoveEventArgs(value);
          Move(this, args); // fire event
          if (args.cancel)
            return;
        }
        position = value;
      }
    }  
  }
}
The event keyword promotes encapsulation by ensuring that only the += and -= operations can be performed on the delegate. This means other classes can register themselves to be notified of the event, but only the Slider can invoke the delegate (fire the event) or clear the delegate's invocation list.
You can act on an event by adding an event handler to an event. An event handler is a delegate that wraps the method you want invoked when the event is fired.
In the next example, we want our Form to act on changes made to a Slider's Position. You do this by creating a
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
try Statements and Exceptions
Syntax:
try statement-block
[catch (exception type value?)? statement-block]+ |
finally statement-block |
[catch (exception type value?)? statement-block]+
finally statement-block
The purpose of a try statement is to simplify dealing with program execution in exceptional circumstances. A try statement does two things. First, it lets exceptions thrown during the try block's execution be caught by the catch block. Second, it ensures that execution can't leave the try block without first executing the finally block. A try block must be followed by one or more catch blocks, a finally block, or both.
C# exceptions are objects that contain information representing the occurrence of an exceptional program state. When an exceptional state has occurred (e.g., a method receives an illegal value), an exception object may be thrown, and the call stack is unwound until the exception is caught by an exception handling block.
In the following example, we have an Account class. An exceptional state for the account class is when its balance is below zero, which occurs when the Withdraw method receives a withdraw amount larger than the current balance. Our test class makes our customer tiffanyTaylor perform several actions that involve despositing and withdrawing from an account. When she attempts to withdraw more money than she has in her account, a FundException is thrown, which we will catch so we can notify the user and display her current account balance.
using System;

public class FundException : Exception {
  public FundException(decimal balance) :
    base("Total funds are "+balance+" dollars") {}
}

class Account {
  decimal balance;
  public decimal Balance {
    get {return balance;}
  }
  public void Deposit(decimal amount) {
    balance += amount;
  }
  public void Withdraw(decimal amount) {
    if (amount > balance)
      throw new FundException(balance);
    balance -= amount;
  }
}

class Customer {
  Account savingsAccount = new Account(  );
  public void SellBike(  ) {
    savingsAccount.Deposit (1000);
  }
  public void BuyCar(  ) {
    savingsAccount.Withdraw (30000);
  }
  public void GoToMovie(  ) {
    savingsAccount.Withdraw (20);
  }
}

class Test {
  static void Main(  ) {
    Customer tiffanyTaylor = new Customer(  );
    bool todaysTasksDone = false;
    try {
      tiffanyTaylor.SellBike(  );
      tiffanyTaylor.BuyCar(  );
      tiffanyTaylor.GoToMovie(  );
      todaysTasksDone = true;
    }
    catch (FundException ex) {
      Console.WriteLine(ex.Message);
    }
    finally {
      Console.WriteLine(todaysTasksDone);
    }
  }
}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Attributes
Syntax:
[[target:]? attribute-name (
positional-param+ |
[named-param = expression]+ |
positional-param+, [named-param = expression]+)?]
Attributes are language constructs that can decorate code elements (e.g., assemblies, modules, types, members, return values, and parameters) with additional information.
In every language, you specify information associated with the types, methods, parameters, and other elements of your program. For example, a type can specify a list of interfaces that it derives from, or a parameter can specify how its values are to be passed with modifiers such as the ref modifier in C#. The limitation of this approach is that you can only associate information with code elements using the predefined constructs that the language itself provides.
Attributes allow programmers to add to the types of information associated with these code elements. For example, serialization in the .NET Framework uses various serialization attributes applied to types and fields to define how these code elements are serialized. This is more flexible than requiring the language to have special syntax for serialization.
An attribute is defined by a class that inherits (directly or indirectly) from the abstract class System.Attribute. When specifying an attribute on an element, the attribute name is the name of the type. By convention the derived type name ends with the word "Attribute", but this suffix isn't required.
In this example we specify that the Foo class is serializable using the Serializable attribute:
[Serializable]
public class Foo {...}
The Serializable attribute is actually a type declared in the System namespace, as follows:
class SerializableAttribute : Attribute {...}
We could also specify the Serializable attribute using its fully qualified typename, as follows:
[System.SerializableAttribute]
public class Foo {...}
The preceding two examples that use the Serializable attribute are semantically identical.
The C# language and the FCL include a number of predefined attributes. For more information on the other attributes included in the FCL, and on creating your own attributes, see Section 3.11 in Chapter 3.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Unsafe Code and Pointers
C# supports direct memory manipulation via pointers within blocks of code marked unsafe and compiled with the /unsafe compiler option. Pointer types are primarily useful for interoperability with C APIs but may also be used for accessing memory outside the managed heap or for performance-critical hotspots.
For every value type or pointer type V in a C# program, there is a corresponding C# pointer type named V*. A pointer instance holds the address of a value. That value is considered to be of type V, but pointer types can be (unsafely) cast to any other pointer type. Table 2-3 summarizes the principal pointer operators supported by the C# language.
Table 2-3: Principal pointer operators
Operator
Meaning
&
The address-of operator returns a pointer to the address of a value.
*
The dereference operator returns the value at the address of a pointer.