BUY THIS BOOK
Add to Cart

PDF $30.99

Safari Books Online

What is this?

Looking to Reprint or License this content?


Programming C#
Programming C#, Fourth Edition Building .NET Applications with C# By Jesse Liberty
February 2005
Pages: 666

Cover | Table of Contents | Colophon


Table of Contents

Chapter 1: C# and the .NET Framework
The goal of C# 2.0 is to provide a simple, safe, modern, object-oriented, Internet-centric, high-performance language for .NET development. C# is now a fully mature language, and it draws on the lessons learned over the past three decades. In much the way that you can see in young children the features and personalities of their parents and grandparents, you can easily see in C# the influence of Java, C++, Visual Basic (VB), and other languages, but you can also see the lessons learned since C# was first introduced.
The focus of this book is the C# language and its use as a tool for programming on the .NET platform, specifically and especially with Visual Studio .NET 2005 (full or Express Edition).
Many of the programs in this book are written as console applications (rather than as Windows or web applications) to facilitate concentrating on features of the language instead of being distracted by the details of the user interface.
If you are using Mono or other non-Microsoft versions of C#, you should find that all of the programs in this book work just fine, though we have not tested on anything other than the Microsoft authorized version.
This chapter introduces both the C# language and the .NET platform, including the .NET Framework.
When Microsoft announced C# in July 2000, its unveiling was part of a much larger event: the announcement of the .NET platform. C# 2.0 represents the maturation of that language and coincides with the release of the next generation of tools for .NET.
The .NET platform is a development framework that provides a new application programming interface (API) to the services and APIs of classic Windows operating systems while bringing together a number of disparate technologies that emerged from Microsoft during the late 1990s. This includes COM+ component services, a commitment to XML and object-oriented design, support for new web services protocols such as SOAP, WSDL, and UDDI, and a focus on the Internet, all integrated within the Distributed interNet Applications (DNA) architecture.
Microsoft has devoted enormous resources to the development of .NET and its associated technologies. The results of this commitment to date are impressive. For one thing, the scope of .NET is huge. The platform consists of three product groups:
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
The .NET Platform
When Microsoft announced C# in July 2000, its unveiling was part of a much larger event: the announcement of the .NET platform. C# 2.0 represents the maturation of that language and coincides with the release of the next generation of tools for .NET.
The .NET platform is a development framework that provides a new application programming interface (API) to the services and APIs of classic Windows operating systems while bringing together a number of disparate technologies that emerged from Microsoft during the late 1990s. This includes COM+ component services, a commitment to XML and object-oriented design, support for new web services protocols such as SOAP, WSDL, and UDDI, and a focus on the Internet, all integrated within the Distributed interNet Applications (DNA) architecture.
Microsoft has devoted enormous resources to the development of .NET and its associated technologies. The results of this commitment to date are impressive. For one thing, the scope of .NET is huge. The platform consists of three product groups:
  • A set of languages, including C# and VB, a set of development tools including Visual Studio .NET, a comprehensive class library for building web services and web and Windows applications, as well as the Common Language Runtime (CLR) to execute objects built within this framework
  • Two generations of .NET Enterprise Servers: those already released and those to be released over the next 24-36 months
  • New .NET-enabled non-PC devices, from cell phones to game boxes
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
The .NET Framework
Microsoft .NET supports not only language independence, but also language integration. This means that you can inherit from classes, catch exceptions, and take advantage of polymorphism across different languages. The .NET Framework makes this possible with a specification called the Common Type System (CTS) that all .NET components must obey. For example, everything in .NET is an object of a specific class that derives from the root class called System.Object. The CTS supports the general concept of classes, interfaces, and delegates (which support callbacks).
Additionally, .NET includes a Common Language Specification (CLS), which provides a series of basic rules that are required for language integration. The CLS determines the minimum requirements for being a .NET language. Compilers that conform to the CLS create objects that can interoperate with one another. The entire Framework Class Library (FCL) can be used by any language that conforms to the CLS.
The .NET Framework sits on top of the operating system, which can be any flavor of Windows, and consists of a number of components, currently including:
  • Five official languages: C#, VB, Visual C++, Visual J#, and JScript.NET
  • The CLR, an object-oriented platform for Windows and web development that all these languages share
  • A number of related class libraries, collectively known as the Framework Class Library
Figure 1-1 breaks down the .NET Framework into its system architectural components.
Figure 1-1: NET Framework architecture
The most important component of the .NET Framework is the CLR, which provides the environment in which programs are executed. The CLR includes a virtual machine, analogous in many ways to the Java virtual machine. At a high level, the CLR activates objects, performs security checks on them, lays them out in memory, executes them, and garbage-collects them. (The Common Type System is also part of the CLR.)
In Figure 1-1, the layer on top of the CLR is a set of framework classes, followed by an additional layer of data and XML classes, plus another layer of classes intended for web services, Web Forms, and Windows Forms. Collectively, these classes make up the FCL, one of the largest class libraries in history and one that provides an object-oriented API for all the functionality that the .NET platform encapsulates. With more than 4,000 classes, the FCL facilitates rapid development of desktop, client/server, and other web services and applications.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Compilation and the MSIL
In .NET, programs aren't compiled into executable files; they are compiled into assemblies that consist of Microsoft Intermediate Language (MSIL) instructions, which the CLR then converts into machine code and executes. The MSIL (often shortened to IL) files C# produces are nearly identical to the IL files other .NET languages produce; the platform is language-agnostic. A key fact about the CLR is that it is common: the same runtime supports development in C# as well as in VB.NET.
C# code is compiled into IL when you build your project. The IL is saved in a file on disk. When you run your program, the IL is compiled again, using the Just In Time (JIT) compiler (a process often called JITing). The result is machine code, executed by the machine's processor.
The standard JIT compiler runs on demand. When a method is called, the JIT compiler analyzes the IL and produces highly efficient machine code, which runs very fast. As the application runs, compilation happens only as needed, and once JIT-compiled, the code is cached for future use. As .NET applications run, they tend to become faster and faster, as the already compiled code is reused.
The CLS means that all .NET languages produce very similar IL code. As a result, objects created in one language can be accessed and derived from another. Thus it is possible to create a base class in VB.NET and derive from it in C#.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
The C# Language
The C# language is disarmingly simple, with only about 80 keywords and a dozen built-in datatypes, but it's highly expressive when it comes to implementing modern programming concepts. C# includes all the support for structured, component- based, object-oriented programming that you expect of a modern language built on the shoulders of C++ and Java, and now with Version 2.0, many of the most important missing ingredients, such as generics and anonymous methods, have been added.
C++ programmers take note: generics are the C# equivalent to Templates, though it turns out that C# generics are a bit simpler and more efficient than C++ templates; they reduce code bloat by reusing shared code at runtime, while giving up a bit of the flexibility available with C++ templates.
The C# language was developed by a small team led by two distinguished Microsoft engineers, Anders Hejlsberg and Scott Wiltamuth. Hejlsberg is also known for creating Turbo Pascal, a popular language for PC programming, and for leading the team that designed Borland Delphi, one of the first successful integrated development environments for client/server programming.
At the heart of any object-oriented language is its support for defining and working with classes. Classes define new types, allowing you to extend the language to better model the problem you are trying to solve. C# contains keywords for declaring new classes and their methods and properties, and for implementing encapsulation, inheritance, and polymorphism, the three pillars of object-oriented programming.
In C#, everything pertaining to a class declaration is found in the declaration itself. C# class definitions don't require separate header files or Interface Definition Language (IDL) files. Moreover, C# supports a new XML style of inline documentation that simplifies the creation of online and print reference documentation for an application.
C# also supports interfaces, a means of making a contract with a class for services that the interface stipulates. In C#, a class can inherit from only a single parent, but a class can implement multiple interfaces. When it implements an interface, a C# class in effect promises to provide the functionality the interface specifies.
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: Getting Started: "Hello World"
It is a time-honored tradition to start a programming book with a "Hello World" program. In this chapter, we create, compile, and run a simple "Hello World" program written in C#. The analysis of this brief program will introduce key features of the C# language.
Example 2-1 illustrates the fundamental elements of a very elementary C# program.
Example 2-1. A simple "Hello World" program in C#
class Hello
{
    static void Main( )
    {
        // Use the system console object
        System.Console.WriteLine("Hello World");
    }
}
Compiling and running this code displays the words "Hello World" at the console. Before we compile and run it, let's first take a closer look at this simple program.
The essence of object-oriented programming is the creation of new types. A type represents a thing. Sometimes the thing is abstract, such as a data table or a thread; sometimes it is more tangible, such as a button in a window. A type defines the thing's general properties and behaviors.
If your program uses three instances of a button type in a window—say, an OK, a Cancel, and a Help button—each button will have a size, though the specific size of each button may differ. Similarly, all the buttons will have the same behaviors (draw, click), though how they actually implement these behaviors may vary. Thus, the details might differ among the individual buttons, but they are all of the same type.
As in many object-oriented programming languages, in C# a type is defined by a class , while the individual instances of that class are known as objects. Later chapters explain that there are other types in C# besides classes, including enums, structs, and delegates, but for now the focus is on classes.
The "Hello World" program declares a single type: the Hello class. To define a C# type, you declare it as a class using the class keyword, give it a name—in this case, Hello—and then define its properties and behaviors. The property and behavior definitions of a C# class must be enclosed by open and closed braces ({} ).
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, Objects, and Types
The essence of object-oriented programming is the creation of new types. A type represents a thing. Sometimes the thing is abstract, such as a data table or a thread; sometimes it is more tangible, such as a button in a window. A type defines the thing's general properties and behaviors.
If your program uses three instances of a button type in a window—say, an OK, a Cancel, and a Help button—each button will have a size, though the specific size of each button may differ. Similarly, all the buttons will have the same behaviors (draw, click), though how they actually implement these behaviors may vary. Thus, the details might differ among the individual buttons, but they are all of the same type.
As in many object-oriented programming languages, in C# a type is defined by a class , while the individual instances of that class are known as objects. Later chapters explain that there are other types in C# besides classes, including enums, structs, and delegates, but for now the focus is on classes.
The "Hello World" program declares a single type: the Hello class. To define a C# type, you declare it as a class using the class keyword, give it a name—in this case, Hello—and then define its properties and behaviors. The property and behavior definitions of a C# class must be enclosed by open and closed braces ({} ).
C++ programmers take note: there is no semicolon after the closing brace.
A class has both properties and behaviors. Behaviors are defined with member methods; properties are discussed in Chapter 3.
A method is a function owned by your class. In fact, member methods are sometimes called member functions. The member methods define what your class can do or how it behaves. Typically, methods are given action names, such as WriteLine( ) or AddNumbers( ). In the case shown here, however, the class method has a special name, Main( ), which doesn't describe an action but does designate to the CLR that this is the main, or first method, for your class.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Developing "Hello World"
There are at least two ways to enter, compile, and run the programs in this book: use the Visual Studio .NET Integrated Development Environment (IDE), or use a text editor and a command-line compiler (along with some additional command-line tools to be introduced later).
Although you can develop software outside Visual Studio .NET, the IDE provides enormous advantages. These include indentation support, Intellisense word completion, color coding, and integration with the help files. Most important, the IDE includes a powerful debugger and a wealth of other tools.
This book tacitly assumes that you'll be using Visual Studio .NET. However, the tutorials focus more on the language and the platform than on the tools. You can copy all the examples into a text editor such as Windows Notepad or Emacs, save them as text files with the extension .cs, and compile them with the C# command-line compiler that is distributed with the .NET Framework SDK (or a .NET-compatible development toolchain such as Mono or Microsoft's Shared Source CLI). Note that some examples in later chapters use Visual Studio .NET tools for creating Windows Forms and Web Forms, but even these you can write by hand in Notepad if you are determined to do things the hard way.
To create the "Hello World" program in the IDE, select Visual Studio .NET from your Start menu or a desktop icon, and then choose File New Project from the menu toolbar. This will invoke the New Project window. (If you are using Visual Studio for the first time, the New Project window might appear without further prompting.) Figure 2-1 shows the New Project window.
Figure 2-1: Creating a C# console application in Visual Studio .NET
To open your application, select Visual C# in the Project Types window, and choose Console Application in the Templates window (if you use the Express Edition of Visual C#, you don't need to perform that first step; go directly to the Console Application).
You can now enter a name for the project and select a directory in which to store your files. Click OK, and a new window will appear in which you can enter the code in Example 2-1, as shown in Figure 2-2.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Using the Visual Studio .NET Debugger
Arguably, the single most important tool in any development environment is the debugger. The Visual Studio debugger is very powerful and it will be well worth whatever time you put into learning how to use it well. That said, the fundamentals of debugging are very simple. The three key skills are:
  • How to set a breakpoint and how to run to that breakpoint
  • How to step into and over method calls
  • How to examine and modify the value of variables, member data, and so forth
This chapter doesn't reiterate the entire debugger documentation, but these skills are so fundamental that it does provide a crash (pardon the expression) course.
The debugger can accomplish the same thing in many ways, typically via menu choices, buttons, and so forth. The simplest way to set a breakpoint is to click in the left margin. The IDE marks your breakpoint with a red dot, as shown in Figure 2-5.
Figure 2-5: A breakpoint
Discussing the debugger requires code examples. The code shown here is from Chapter 5, and you aren't expected to understand how it works yet (though if you program in C++ or Java, you'll probably get the gist of it).
To run the debugger, you can choose Debug Start or just press F5. The program then compiles and runs to the breakpoint, at which time it stops, and a yellow arrow indicates the next statement for execution, as in Figure 2-6.
Figure 2-6: The breakpoint hit
After you've hit your breakpoint it is easy to examine the values of various objects. For example, you can find the value of the variable i just by putting the cursor over it and waiting a moment, as shown in Figure 2-7.
Figure 2-7: Showing a value
The debugger IDE also provides a number of useful windows, such as a Locals window that displays the values of all the local variables (see Figure 2-8).
Figure 2-8: Locals window
Intrinsic types such as integers simply show their value (see i earlier), but objects show their type and have a plus (+) sign. You can expand these objects to see their internal data, as shown in Figure 2-9. You'll learn more about objects and their internal data in upcoming chapters.
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 3: C# Language Fundamentals
Chapter 2 demonstrated a very simple C# program. Nonetheless, that little program was complex enough that I had to skip some of the pertinent details. This chapter illuminates these details by delving more deeply into the syntax and structure of the C# language itself.
This chapter discusses the type system in C#, drawing a distinction between built-in types (int, bool, etc.) versus user-defined types (types you create as classes and interfaces). The chapter also covers programming fundamentals such as how to create and use variables and constants. It then goes on to introduce enumerations, strings, identifiers, expressions, and statements.
The second part of the chapter explains and demonstrates the use of flow control statements, using the if, switch, while, do...while, for, and foreach statements. Also discussed are operators, including the assignment, logical, relational, and mathematical operators. This is followed by an introduction to namespaces and a short tutorial on the C# precompiler.
Although C# is principally concerned with the creation and manipulation of objects, it is best to start with the fundamental building blocks: the elements from which objects are created. These include the built-in types that are an intrinsic part of the C# language as well as the syntactic elements of C#.
C# is a strongly typed language. In a strongly typed language you must declare the type of each object you create (e.g., integers, floats, strings, windows, buttons, etc.), and the compiler will help you prevent bugs by enforcing that only data of the right type is assigned to those objects. The type of an object signals to the compiler the size of that object (e.g., int indicates an object of 4 bytes) and its capabilities (e.g., buttons can be drawn, pressed, and so forth).
C# 1.1 programmers take note: until Version 2, .NET was strongly typed in everything except collections. With the addition of generics, however, it is now easy to create strongly typed collection classes, as shown in Chapter 9.
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
C# is a strongly typed language. In a strongly typed language you must declare the type of each object you create (e.g., integers, floats, strings, windows, buttons, etc.), and the compiler will help you prevent bugs by enforcing that only data of the right type is assigned to those objects. The type of an object signals to the compiler the size of that object (e.g., int indicates an object of 4 bytes) and its capabilities (e.g., buttons can be drawn, pressed, and so forth).
C# 1.1 programmers take note: until Version 2, .NET was strongly typed in everything except collections. With the addition of generics, however, it is now easy to create strongly typed collection classes, as shown in Chapter 9.
Like C++ and Java, C# divides types into two sets: intrinsic (built-in) types that the language offers and user-defined types that the programmer defines.
C# also divides the set of types into two other categories: value types and reference types. The principal difference between value and reference types is the manner in which their values are stored in memory. A value type holds its actual value in memory allocated on the stack (or it is allocated as part of a larger reference type object). The address of a reference type variable sits on the stack, but the actual object is stored on the heap.
C and C++ programmers take note: in C#, there is no explicit indication that an object is a reference type (i.e., no use of the & operator). Also, pointers aren't normally used (but see Chapter 22 for the exception to this rule).
If you have a very large object, putting it on the heap has many advantages. Chapter 4 discusses the various advantages and disadvantages of working with reference types; the current chapter focuses on the intrinsic value types available in C#.
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 and Constants
A variable is a storage location with a type. In the preceding examples, both x and y are variables. Variables can have values assigned to them, and those values can be changed programmatically.
Create a variable by declaring its type and then giving it a name. You can initialize the variable when you declare it, and you can assign a new value to that variable at any time, changing the value held in the variable. This is illustrated in Example 3-1.
Example 3-1. Initializing and assigning a value to a variable
#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace InitializingVariables
{
   class Values
   {
      static void Main( )
      {

         int myInt = 7;
         System.Console.WriteLine("Initialized, myInt: {0}",
            myInt);

         myInt = 5;
         System.Console.WriteLine("After assignment, myInt: {0}",
            myInt);

      }   
   }
}

Output:
Initialized, myInt: 7
After assignment, myInt: 5
Visual Studio 2005 creates a namespace and using directive (as well as a using region) for every program. To save space, these are left out of most of the code examples, though they are shown in the example code you can download from O'Reilly or LibertyAssociates.com.
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
Statements that evaluate to a value are called expressions. You may be surprised how many statements do evaluate to a value. For example, an assignment such as:
myVariable = 57;
is an expression; it evaluates to the value assigned, which, in this case, is 57.
Note that the preceding statement assigns the value 57 to the variable myVariable. The assignment operator (=) doesn't test equality; rather it causes whatever is on the right side (57) to be assigned to whatever is on the left side (myVariable). All the C# operators (including assignment and equality) are discussed later in this chapter (see "Operators").
Because myVariable = 57 is an expression that evaluates to 57, it can be used as part of another assignment operator, such as:
mySecondVariable = myVariable = 57;
What happens in this statement is that the literal value 57 is assigned to the variable myVariable. The value of that assignment (57) is then assigned to the second variable, mySecondVariable. Thus, the value 57 is assigned to both variables. You can therefore initialize any number of variables to the same value with one statement:
a = b = c = d = e = 20;
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Whitespace
In the C# language, spaces, tabs, and newlines are considered to be "whitespace" (so named because you see only the white of the underlying "page"). Extra whitespace is generally ignored in C# statements. You can write:
myVariable = 5;
or:
myVariable    =                             5;
and the compiler will treat the two statements as identical.
The exception to this rule is that whitespace within strings isn't ignored. If you write:
Console.WriteLine("Hello World")
each space between "Hello" and "World" is treated as another character in the string.
Most of the time the use of whitespace is intuitive. The key is to use whitespace to make the program more readable to the programmer; the compiler is indifferent.
However, there are instances in which the use of whitespace is quite significant. Although the expression:
int x = 5;
is the same as:
int x=5;
it is not the same as:
intx=5;
The compiler knows that the whitespace on either side of the assignment operator is extra, but the whitespace between the type declaration int and the variable name x is not extra, and is required. This is not surprising: the whitespace allows the compiler to parse the keyword int rather than some unknown term intx. You are free to add as much or as little whitespace between int and x as you care to, but there must be at least one whitespace character (typically a space or tab).
VB programmers take note: in C# the end-of-line has no special significance; statements are ended with semicolons, not newline characters. There is no line-continuation character because none is needed.
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
In C# a complete program instruction is called a statement. Programs consist of sequences of C# statements. Each statement must end with a semicolon (;). For example:
int x;     // a statement
x = 23;    // another statement
int y = x; // yet another statement
C# statements are evaluated in order. The compiler starts at the beginning of a statement list and makes its way to the bottom. This would be entirely straightforward, and terribly limiting, were it not for branching. There are two types of branches in a C# program: unconditional branching and conditional branching.
Program flow is also affected by looping and iteration statements, which are signaled by the keywords for , while, do, in, and foreach. Iteration is discussed later in this chapter. For now, let's consider some of the more basic methods of conditional and unconditional branching.
An unconditional branch is created in one of two ways. The first way is by invoking a method. When the compiler encounters the name of a method, it stops execution in the current method and branches to the newly "called" method. When that method returns a value, execution picks up in the original method on the line just below the method call. Example 3-6 illustrates.
Example 3-6. Calling a method
#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace CallingAMethod
{
   class CallingAMethod
   {
      static void Main( )
      {
         Console.WriteLine("In Main! Calling SomeMethod( )...");
         SomeMethod( );
         Console.WriteLine("Back in Main( ).");
      }
      static void SomeMethod( )
      {
         Console.WriteLine("Greetings from SomeMethod!");
      }
   }
}
Output:
In Main! Calling SomeMethod( )...
Greetings from SomeMethod!
Back in Main( ).
Program flow begins in Main( ) and proceeds until SomeMethod() is invoked (invoking a method is sometimes referred to as "calling" the method). At that point, program flow branches to the method. When the method completes, program flow resumes at the next line after the call to that method.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Operators
An operator is a symbol that causes C# to take an action. The C# primitive types (e.g., int) support a number of operators such as assignment, increment, and so forth.
The section titled "Expressions," earlier in this chapter, demonstrates the use of the assignment operator. This symbol causes the operand on the left side of the operator to have its value changed to whatever is on the right side of the operator.
C# uses five mathematical operators: four for standard calculations and a fifth to return the remainder in integer division. The following sections consider the use of these operators.

Section 3.6.2.1: Simple arithmetical operators (+, -, *, /)

C# offers operators for simple arithmetic: the addition (+), subtraction (-), multiplication (*), and division (/) operators work as you might expect, with the possible exception of integer division.
When you divide two integers, C# divides like a child in fourth grade: it throws away any fractional remainder. Thus, dividing 17 by 4 returns the value 4 (17/4 = 4, with a remainder of 1). C# provides a special operator (modulus, %, which is described in the next section) to retrieve the remainder.
Note, however, that C# does return fractional answers when you divide floats, doubles, and decimals.

Section 3.6.2.2: The modulus operator (%) to return remainders

To find the remainder in integer division, use the modulus operator (%). For example, the statement 17%4 returns 1 (the remainder after integer division).
The modulus operator turns out to be more useful than you might at first imagine. When you perform modulus n on a number that is a multiple of n, the result is 0. Thus 80%10 = 0 because 80 is an even multiple of 10. This fact allows you to set up loops in which you take an action every
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Preprocessor Directives
In the examples you've seen so far, you've compiled your entire program whenever you compiled any of it. At times, however, you might want to compile only parts of your program—for example, depending on whether you are debugging or building your production code.
Before your code is compiled, another program called the preprocessor runs and prepares your program for the compiler. The preprocessor examines your code for special preprocessor directives, all of which begin with the pound sign (#). These directives allow you to define identifiers and then test for their existence.
#define DEBUG defines a preprocessor identifier, DEBUG. Although other preprocessor directives can come anywhere in your code, identifiers must be defined before any other code, including using statements.
C and C++ programmer take note: the C# preprocessor implements only a subset of the C++ preprocessor and doesn't support macros.
You can test whether DEBUG has been defined with the #if statement. Thus, you can write:
#define DEBUG

//... some normal code - not affected by preprocessor

#if DEBUG
   // code to include if debugging
#else
  // code to include if not debugging
#endif

//... some normal code - not affected by preprocessor
When the preprocessor runs, it sees the #define statement and records the identifier DEBUG. The preprocessor skips over your normal C# code and then finds the #if - #else - #endif block.
The #if statement tests for the identifier DEBUG, which does exist, and so the code between #if and #else is compiled into your program—but the code between #else and #endif is not compiled. That code doesn't appear in your assembly at all; it is as if it were left out of your source code.
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 4: Classes and Objects
Chapter 3 discussed the myriad primitive types built into the C# language, such as int, long, and char. The heart and soul of C#, however, is the ability to create new, complex, programmer-defined types that map cleanly to the objects that make up the problem you are trying to solve.
It is this ability to create new types that characterizes an object-oriented language. You specify new types in C# by declaring and defining classes. You can also define types with interfaces, as you will see in Chapter 8. Instances of a class are called objects. Objects are created in memory when your program executes.
The difference between a class and an object is the same as the difference between the concept of a dog and the particular dog who is sitting at your feet as you read this. You can't play fetch with the definition of a dog, only with an instance.
A Dog class describes what dogs are like: they have weight, height, eye color, hair color, disposition, and so forth. They also have actions they can take, such as eat, walk, bark, and sleep. A particular dog (such as my dog Milo) has a specific weight (62 pounds), height (22 inches), eye color (black), hair color (yellow), disposition (angelic), and so forth. He is capable of all the actions of any dog (though if you knew him you might imagine that eating is the only method he implements).
The huge advantage of classes in object-oriented programming is that they encapsulate the characteristics and capabilities of an entity in a single, self-contained, and self-sustaining unit of code. When you want to sort the contents of an instance of a Windows listbox control, for example, tell the listbox to sort itself. How it does so is of no concern; that it does so is all you need to know. Encapsulation, along with polymorphism and inheritance, is one of three cardinal principles of object-oriented programming.
An old programming joke asks, how many object-oriented programmers does it take to change a light bulb? Answer: none, you just tell the light bulb to change itself. (Alternate answer: none, Microsoft has changed the standard to darkness.)
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Defining Classes
To define a new type or class, first declare it, and then define its methods and fields. Declare a class using the class keyword. The complete syntax is as follows:
[attributes] [access-modifiers] class identifier [:base-class [,interface(s)]]
{class-body}
Attributes are covered in Chapter 8; access modifiers are discussed in the next section. (Typically, your classes will use the keyword public as an access modifier.) The identifier is the name of the class that you provide. The optional base-class is discussed in Chapter 5. The member definitions that make up the class-body are enclosed by open and closed curly braces ({}).
C and C++ programmers take note: a C# class definition doesn't end with a semicolon, though if you add one, the program will still compile.
In C#, everything happens within a class. So far, however, we've not instantiated any instances of that class; that is, we haven't created any objects. What is the difference between a class and an instance of that class? To answer that question, start with the distinction between the type int and a variable of type int. Thus, while you would write:
int myInteger = 5;
you wouldn't write:
int = 5;
You can't assign a value to a type; instead, you assign the value to an object of that type (in this case, a variable of type int).
When you declare a new class, you define the properties of all objects of that class, as well as their behaviors. For example, if you are creating a windowing environment, you might want to create screen widgets (more commonly known as controls in Windows programming) to simplify user interaction with your application. One control of interest might be a listbox, which is very useful for presenting a list of choices to the user and enabling the user to select from the list.
Listboxes have a variety of characteristics—for example, height, width, location, and text color. Programmers have also come to expect certain behaviors of listboxes: they can be opened, closed, sorted, and so on.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Creating Objects
In Chapter 3, a distinction was drawn between value types and reference types. The primitive C# types (int, char, etc.) are value types, and are created on the stack. Objects, however, are reference types, and are created on the heap, using the keyword new , as in the following:
Time t = new Time();
t doesn't actually contain the value for the Time object; it contains the address of that (unnamed) object that is created on the heap. t itself is just a reference to that object.
VB6 programmers take note: while there is a performance penalty in using the VB6 keywords Dim and New on the same line, in C# this penalty has been removed. Thus, in C# there is no drawback to using the new keyword when declaring an object variable.
In Example 4-1, notice that the statement that creates the Time object looks as though it is invoking a method:
Time t = new Time();
In fact, a method is invoked whenever you instantiate an object. This method is called a constructor, and you must either define one as part of your class definition or let the CLR provide one on your behalf. The job of a constructor is to create the object specified by a class and to put it into a valid state. Before the constructor runs, the object is undifferentiated memory; after the constructor completes, the memory holds a valid instance of the class type.
The Time class of Example 4-1 doesn't define a constructor. If a constructor is not declared, the compiler provides one for you. The default constructor creates the object but takes no other action.
Member variables are initialized to innocuous values (integers to 0, strings to the empty string, etc.). Table 4-2 lists the default values assigned to primitive types.
Table 4-2: Primitive types and their default values
Type
Default value
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Using Static Members
The members of a class (variables, methods, events, indexers, etc.) can be either instance members or static members. Instance members are associated with instances of a type, while static members are considered to be part of the class. You access a static member through the name of the class in which it is declared. For example, suppose you have a class named Button and have instantiated objects of that class named btnUpdate and btnDelete. Suppose as well that the Button class has a static method SomeMethod(). To access the static method, you write:
Button.SomeMethod();
rather than:
btnUpdate.SomeMethod( );
In C#, it is not legal to access a static method or member variable through an instance, and trying to do so will generate a compiler error (C++ programmers, take note).
Some languages distinguish between class methods and other (global) methods that are available outside the context of any class. In C# there are no global methods, only class methods, but you can achieve an analogous result by defining static methods within your class.
VB6 programmers take note: don't confuse the static keyword in C# with the Static keyword in VB6 and VB.NET. In VB, the Static keyword declares a variable that is available only to the method it was declared in. In other words, the Static variable is not shared among different objects of its class (i.e., each Static variable instance has its own value). However, this variable exists for the life of the program, which allows its value to persist from one method call to another.
In C#, the static keyword indicates a class member. In VB, the equivalent keyword is Shared.
Static methods act more or less like global methods, in that you can invoke them without actually having an instance of the object at hand. The advantage of static methods over global, however, is that the name is scoped to the class in which it occurs, and thus you don't clutter up the global namespace with myriad function names. This can help manage highly complex programs, and the name of the class acts very much like a namespace for the static methods within 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!
Destroying Objects
Since C# provides garbage collection, you never need to explicitly destroy your objects. However, if your object controls unmanaged resources, you will need to explicitly free those resources when you are done with them. Implicit control over unmanaged resources is provided by a destructor , which will be called by the garbage collector when your object is destroyed.
C and C++ programmers take note: a destructor is not necessarily called when an object goes out of scope, but rather, when it is garbage-collected (which may happen much later). This is known as nondeterministic finalization.
The destructor should only release resources that your object holds on to, and should not reference other objects. Note that if you have only managed references, you don't need to and should not implement a destructor; you want this only for handling unmanaged resources. Because there is some cost to having a destructor, you ought to implement this only on methods that require it (that is, methods that consume valuable unmanaged resources).
You can't call an object's destructor directly. The garbage collector will call it for you.
C#'s destructor looks, syntactically, much like a C++ destructor, but it behaves quite differently. Declare a C# destructor with a tilde as follows:
~MyClass(){}
In C#, this syntax is simply a shortcut for declaring a Finalize( ) method that chains up to its base class. Thus, when you write:
~MyClass()
{ 
   // do work here
}
the C# compiler translates it to:
protected override void Finalize()
{
   try
   {
      // do work here.
   }
   finally
   {
      base.Finalize( );
   }
}
It is not legal to call a destructor explicitly. Your destructor will be called by the garbage collector. If you do handle precious unmanaged resources (such as file handles) that you want to close and dispose of as quickly as possible, you ought to implement 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!
Passing Parameters
By default, value types are passed into methods by value. (See the section entitled "Method Arguments," earlier in this chapter.) This means that when a value object is passed to a method, a temporary copy of the object is created within that method. Once the method completes, the copy is discarded. Although passing by value is the normal case, there are times when you will want to pass value objects by reference. C# provides the ref parameter modifier for passing value objects into a method by reference, and the out modifier for those cases in which you want to pass in a ref variable without first initializing it. C# also supports the params modifier, which allows a method to accept a variable number of parameters. The params keyword is discussed in Chapter 9.
Methods can return only a single value (though that value can be a collection of values). Let's return to the Time class and add a GetTime() method, which returns the hour, minutes, and seconds.
Java programmers take note: in C#, there's no need for wrapper classes for basic types like int (integer). Instead, use reference parameters.
Because we can't return three values, perhaps we can pass in three parameters, let the method modify the parameters, and examine the result in the calling method. Example 4-7 shows a first attempt at this.
Example 4-7. Returning values in parameters
#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace ReturningValuesInParams
{
   public classTime
   {
      // private member variables
      private int Year;
      private int Month;
      private int Date;
      private int Hour;
      private int Minute;
      private int Second;

      // public accessor methods
      public void DisplayCurrentTime( )
      {
         System.Console.WriteLine( "{0}/{1}/{2} {3}:{4}:{5}",
            Month, Date, Year, Hour, Minute, Second );
      }

      public int GetHour( )
      {
         return Hour;
      }

      public void GetTime( int h, int m, int s )
      {
         h = Hour;
         m = Minute;
         s = Second;
      }

      // constructor
      public 
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Overloading Methods and Constructors
Often you'll want to have more than one function with the same name. The most common example of this is to have more than one constructor. In the examples shown so far, the constructor has taken a single parameter: a DateTime object. It would be convenient to be able to set new Time objects to an arbitrary time by passing in year, month, date, hour, minute, and second values. It would be even more convenient if some clients could use one constructor, and other clients could use the other constructor. Function overloading provides for exactly these contingencies.
The signature of a method is defined by its name and its parameter list. Two methods differ in their signatures if they have different names or different parameter lists. Parameter lists can differ by having different numbers or types of parameters. For example, in the following code the first method differs from the second in the number of parameters, and the second differs from the third in the types of parameters:
void myMethod(int p1);
void myMethod(int p1, int p2);
void myMethod(int p1, string s1);
A class can have any number of methods, as long as each one's signature differs from that of all the others.
Example 4-9 illustrates our Time class with two constructors: one that takes a DateTime object, and the other that takes six integers.
Example 4-9. Overloading the constructor
#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace OverloadedConstructor
{
   public classTime
   {
      // private member variables
      private int Year;
      private int Month;
      private int Date;
      private int Hour;
      private int Minute;
      private int Second;

      // public accessor methods
      public void DisplayCurrentTime( )
      {
         System.Console.WriteLine( "{0}/{1}/{2} {3}:{4}:{5}",
            Month, Date, Year, Hour, Minute, Second );
      }

      // constructors
      public Time( System.DateTime dt )
      {
         Year = dt.Year;
         Month = dt.Month;
         Date = dt.Day;
         Hour = dt.Hour;
         Minute = dt.Minute;
         Second = dt.Second;
      }

      public Time( int Year, int Month, int Date,
           int Hour, int Minute, int Second )
      {
         this.Year = Year;
         this.Month = Month;
         this.Date = Date;
         this.Hour = Hour;
         this.Minute = Minute;
         this.Second = Second;
      }
   }

   public class 
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Encapsulating Data with Properties
Properties allow clients to access class state as if they were accessing member fields directly, while actually implementing that access through a class method.
This is ideal. The client wants direct access to the state of the object and doesn't want to work with methods. The class designer, however, wants to hide the internal state of his class in class members, and provide indirect access through a method.
By decoupling the class state from the method that accesses that state, the designer is free to change the internal state of the object as needed. When the Time class is first created, the Hour value might be stored as a member variable. When the class is redesigned, the Hour value might be computed or retrieved from a database. If the client had direct access to the original Hour member variable, the change to computing the value would break the client. By decoupling and forcing the client to go through a method (or property), the Time class can change how it manages its internal state without breaking client code.
Properties meet both goals: they provide a simple interface to the client, appearing to be a member variable. They are implemented as methods, however, providing the data-hiding required by good object-oriented design, as illustrated in Example 4-11.
Example 4-11. Using a property
#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace UsingAProperty
{
   public classTime
   {
      // private member variables
      private int year;
      private int month;
      private int date;
      private int hour;
      private int minute;
      private int second;

      // public accessor methods
      public void DisplayCurrentTime( )
      {

         System.Console.WriteLine(
            "Time\t: {0}/{1}/{2} {3}:{4}:{5}",
            month, date, year, hour, minute, second );
      }

      // constructors
      public Time( System.DateTime dt )
      {
         year = dt.Year;
         month = dt.Month;
         date = dt.Day;
         hour = dt.Hour;
         minute = dt.Minute;
         second = dt.Second;
      }

      // create a property

      public int Hour
      {
         get
         {
            return hour;
         }

         set
         {
            hour = value;
         }
      }
   }

   public class 
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
readonly Fields
You might want to create a version of the Time class that is responsible for providing public static values representing the current time and date. Example 4-12 illustrates a simple approach to this problem.
Example 4-12. Using static public constants
#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace StaticPublicConstants
{
   public classRightNow
   {
      // public member variables
      public static int Year;
      public static int Month;
      public static int Date;
      public static int Hour;
      public static int Minute;
      public static int Second;

      static RightNow( )
      {
         System.DateTime dt = System.DateTime.Now;
         Year = dt.Year;
         Month =