BUY THIS BOOK
Add to Cart

Print Book $49.99


Add to Cart

Print+PDF $64.99

Add to Cart

PDF $39.99

Safari Books Online

What is this?

Add to UK Cart

Print Book £30.99

What is this?

Looking to Reprint or License this content?


Programming C# 3.0
Programming C# 3.0, Fifth Edition By Jesse Liberty, Donald Xie
December 2007
Pages: 607

Cover | Table of Contents | Colophon


Table of Contents

Chapter 1: C# 3.0 and .NET 3.5
The goal of C# 3.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 same 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 C# 3.0 and its use as a tool for programming on the .NET platform, specifically and especially with Visual Studio .NET 2008.
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.
This chapter introduces both the C# language and the .NET platform, including the .NET 3.5 Framework.
Each generation of C# has brought significant additions to the language, with a few standout features. Perhaps the most significant feature added to C# 2.0 was Generics (allowing for an enhancement to type safety when dealing with collections). If so, the most significant addition to C# 3.0 must be the addition of the Language-Integrated Query (LINQ) extensions, which add general-purpose data query extensions to C#; though that is by no means the only enhancement to C#.
Other new features include:
  • Lambda expressions (anonymous delegates on steroids)
  • Extension methods
  • Object initializers
  • Anonymous types
  • Implicitly typed local variables
  • Implicitly typed arrays
  • Expression trees
  • Automatic properties (a small gem)
The fundamental C# language is disarmingly simple, with fewer than 100 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. Version 3.0 has been extended in three very important ways:
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 Evolution of C#
Each generation of C# has brought significant additions to the language, with a few standout features. Perhaps the most significant feature added to C# 2.0 was Generics (allowing for an enhancement to type safety when dealing with collections). If so, the most significant addition to C# 3.0 must be the addition of the Language-Integrated Query (LINQ) extensions, which add general-purpose data query extensions to C#; though that is by no means the only enhancement to C#.
Other new features include:
  • Lambda expressions (anonymous delegates on steroids)
  • Extension methods
  • Object initializers
  • Anonymous types
  • Implicitly typed local variables
  • Implicitly typed arrays
  • Expression trees
  • Automatic properties (a small gem)
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 fundamental C# language is disarmingly simple, with fewer than 100 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. Version 3.0 has been extended in three very important ways:
  • Full support for LINQ—queries against data are now part of the language
  • Full support for the declarative syntax of Windows Presentation Foundation (WPF; for creating rich Windows applications), Work Flow (WF), and Silverlight (for creating cross-platform, cross-browser Rich Internet Applications)
  • Many convenient features added to aid programmer productivity and to work and play well in Visual Studio 2008
The C# language was originally 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 (IDEs) 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 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!
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. The .NET platform was, in my view, an object-oriented operating system in disguise, laid on top of the existing operating system.
.NET 3.5 represents a further maturation of that framework and brings with it new ways to create, well, just about everything, while making nothing you've learned obsolete.
You can still create server-only web applications, but with AJAX, you can add client-side controls (and AJAX provides support for much more, including automatic JSON encoding and decoding). You can still create Windows Forms applications for Windows applications, but you can also create richer Windows applications using WPF, which uses a declarative syntax called XAML (explained in some detail in ). That same XAML is used in creating WF applications, which can be used, among other things, as a business layer for your applications.
For a full exploration of the new .NET Framework, please see Programming .NET 3.5 by Jesse Liberty and Alex Horowitz (O'Reilly).
In one of the more exciting additions to the Framework, you can now use that same XAML to produce cross-platform (as of this writing, Windows, Mac, and Unix) and cross-browser (Firefox and Safari) Rich Internet Applications using Microsoft's Silverlight.
For a full exploration of Silverlight, please see my blog at http://silverlight.net/blogs/jesseliberty, and watch for my book, Programming Silverlight (O'Reilly), due in 2008.
All of these development technologies can use C# for the programming logic; C# can be the core for all the programming you do across the board in the development of .NET applications from the Web to the desktop, from thin clients to thick, from Rich Internet Applications to web services.
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, you'll 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.
illustrates the fundamental elements of a simple C# program.
Example . A simple "Hello World" program in C#
  class Hello
    {
        static void Main(string[] args)
        {
            // Use the system console object
            System.Console.WriteLine("Hello World!");
        }
    }
Compiling and running this code displays the words "Hello World!" at the console. Before you compile and run it, let's first take a closer look at this simplest of programs.
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, and 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 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 opening and closing braces ({}).
A class has properties and behaviors. Behaviors are defined with member methods; properties are discussed in .
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, and 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 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 opening and closing braces ({}).
A class has properties and behaviors. Behaviors are defined with member methods; properties are discussed in .
A method (sometimes called a function) is a contained set of operations that are owned by your class. 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.
The CLR calls Main( ) when your program starts. Main( ) is the entry point for your program, and every C# program must have a Main( ) method.
Method declarations are a contract between the creator of the method and the consumer (user) of the method. It is likely that the creator and consumer of the method will be the same programmer, but this doesn't have to be so: it is possible that one member of a development team will create the method, and another programmer will use 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!
Developing "Hello World"
There are at least two ways to enter, compile, and run the programs in this book: use the Visual Studio 2008 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 2008, 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 2008. 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 tool chain such as Mono or Microsoft's Shared Source CLI). Note that some examples in later chapters use Visual Studio 2008 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 2008 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.) shows the New Project window.
Figure : Creating a C# console application in Visual Studio 2008
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 (e.g., HelloWorld), and select a directory in which to store your files. You may also enter the name of the solution containing the project, and select whether you would like Visual Studio 2008 to create a directory for the new solution for you. Click OK, and a new window will appear in which you can enter the code in , as shown in .
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 2008 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 in to learning how to use it well. With 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.
For example, one wonderful debugging tool is a breakpoint: an instruction to the debugger to run your application to a particular line in the code and then stop. 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 : A breakpoint
Discussing the debugger requires code examples. The code shown here is from , 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 : 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 array just by putting the cursor over it and waiting a moment, as shown in .
Figure : 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 : Locals window
Intrinsic types such as integers simply show their value, but objects show their type and have a plus (+
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
demonstrated a very simple C# program. Nonetheless, that little program was complex enough that we 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.
In this chapter, I discuss the type system in C#, covering built-in types such as int and bool, and user-defined types (types you create) such as classes, structs, and interfaces. I also cover programming fundamentals, such as how to create and use variables and constants. I'll then introduce enumerations, strings, identifiers, expressions, and statements.
In the second part of the chapter, I'll explain and demonstrate the use of flow control statements, using the if, switch, while, do...while, for, and foreach statements. You'll also learn about operators, including the assignment, logical, relational, and mathematical operators. I'll finish up with a short tutorial on the C# preprocessor.
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#.
Every variable and object in C# has a "type." There are built-in types (e.g., int), and you may create your own types (e.g., Employee).
When you create an object, you declare its type, and in a statically typed language such as C#, the compiler will "enforce" that typing, giving you an error at compile time (rather than runtime) if you violate the typing by (for example) trying to assign an employee object to an integer variable. This is a good thing; it cuts down on bugs and makes for more reliable code.
In the vast majority of cases, C# is also "manifestly" typed—which means that you explicitly declare the type of the object. There is one exception, which is the use of the keyword var (covered in ). In this case, C# is able to infer the type of the object and thus, rather than being manifest, is actually implicit.
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
Every variable and object in C# has a "type." There are built-in types (e.g., int), and you may create your own types (e.g., Employee).
When you create an object, you declare its type, and in a statically typed language such as C#, the compiler will "enforce" that typing, giving you an error at compile time (rather than runtime) if you violate the typing by (for example) trying to assign an employee object to an integer variable. This is a good thing; it cuts down on bugs and makes for more reliable code.
In the vast majority of cases, C# is also "manifestly" typed—which means that you explicitly declare the type of the object. There is one exception, which is the use of the keyword var (covered in ). In this case, C# is able to infer the type of the object and thus, rather than being manifest, is actually implicit.
Finally, C# is strongly typed, which means that any operation you attempt on any object or variable must be appropriate to that type, or it will cause a compiler error. Once again, this is a good thing; it helps identify bugs reliably at compile time.
In summary, we can say that C# is statically, manifestly, and strongly typed when using most types, except when using the keyword var, at which time it is statically, implicitly, and strongly typed!
Key to all of this is that it is always statically and strongly typed, which means that you must declare your types, and the compiler will then enforce that you use your objects according to their declared types, and this is a good thing.
The C# language itself offers the usual cornucopia of intrinsic (built-in) types you expect in a modern language, each of which maps to an underlying type supported by the .NET CTS. Mapping the C# primitive types to the underlying .NET types ensures that objects you create in C# can be used interchangeably with objects created in any other language compliant with the .NET CTS, such as Visual Basic.
Each built-in type has a specific and unchanging size. lists many of the built-in types offered by C#.
Table : C# built-in value types
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 within a method. In the preceding examples, both x and y are variables. You can assign values to your variables, and you can change those values programmatically.
You create a variable by declaring its type and then giving it a name. You can initialize the variable with a value when you declare it, and you can assign a new value to that variable at any time, changing the value held in the variable. illustrates this.
Example . Initializing and assigning a value to a variable
using System;
using System.Collections.Generic;
using System.Text;




namespace InitializingVariables
{
 class Program
 {
    static void Main(string[] args)
    {


    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 creates a namespace and using directive for every program. To save space, I've omitted these from most of the code examples after this one.
Here, you initialize the variable myInt to the value 7, display that value, reassign the variable with the value 5, and display it again.
VB 6 programmers take note: in C#, the datatype comes before the variable name.
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 key word in the preceding rule is "extra" whitespace. Some whitespace is not extra; it is required to allow the compiler to differentiate one word from another. Thus, if you were to enter:
int myVariable = 5; // no problem
or:
int myVariable=5; // no problem
both would compile, because the spaces between the identifier myVariable, the assignment operator (=), and the literal value 5 are "extra." If, however, you were to enter:
intMyVariable=5;  // error
you would receive a compiler error, because the space between the keyword int and the identifier myVariable is not extra, it is required.
Another exception to the "whitespace is ignored" rule is within strings. 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 typically indifferent.
VB programmers take note: in C# the end-of-line has no special significance; you end statements 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. Virtually every statement ends 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 end. This would be entirely straightforward, and terribly limiting, were it not for branching. There are two types of branches in a C# program: unconditional branches and conditional branches.
Program flow is also affected by looping and iteration statements, which are signaled by the keywords for, while, do, in, and foreach. I discuss iteration later in this chapter. For now, let's consider some of the more basic methods of conditional and unconditional branching.
You can create an unconditional branch 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. illustrates.
Example . Calling a method
using System;




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 also 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.
The second way to create an unconditional branch is with one of the unconditional branch keywords: goto, break, continue, return, or throw. I provide additional information about the first three jump statements later in this chapter. 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!
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 = 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. 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).
VB programmers take note: C# distinguishes between equality (two equals signs) and assignment (one equals sign).
Because myVariable = 57 (read aloud as "assign the numeric value 57 to the variable whose name is myVariable") is an expression that evaluates to 57, it can be used as part of another assignment operator, such as:
mySecondVariable = myVariable = 57;
In this statement, 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.
The value 57 is referred to as a literal value (as opposed to a symbolic value). A symbolic value is one that is housed in a variable, a constant, or an expression. A literal value is the value itself, written in the conventional way.
You can therefore initialize any number of variables to the same value with one statement:
a = b = c = d = e = 20;
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.

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.
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++ programmers 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 though it were left out of your source code.
Had the #if statement failed—that is, if you had tested for an identifier that did not exist—the code between #if and #else would not be compiled, but the code between #else and #endif would be compiled.
Any code not surrounded by #if/#endif is not affected by the preprocessor and is compiled into your program.
You undefine an identifier with #undef. The preprocessor works its way through the code from top to bottom, so the identifier is defined from 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!
Chapter 4: Classes and Objects
In , we discussed the myriad 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.
One very powerful example of the use of "programmer defined types" is the .NET Framework that facilitates common tasks, such as interacting with databases and web sites. The Framework provides entire suites of controls, such as those used to interact with users, and those used to display data, as well as other programmer-defined types that manipulate data but have no visible aspect.
It is this ability to use and create powerful new types that characterizes an object-oriented language. You specify a new type in C# by defining a class. (You can also define types with interfaces, as you will see in .) 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, (eat), bark, (eat some more), and sleep. A particular dog (such as Jesse's dog, Milo) has a specific weight (68 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 within a single, self-contained unit of code. When you want to scroll an item in a Listbox into view, you tell the Listbox control to scroll. How it does so is of no concern to anyone but the person writing 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!
Defining Classes
To create a new class, you first declare it and then define its methods and fields. You declare a class using the class keyword. The complete syntax is as follows:
[attributes] [access-modifiers] class identifier [:[base-class
[,interface(s)]]
{class-body}
This is a formal definition diagram. Don't let it intimidate you. The items in square brackets are optional.
You read this as follows: "a class is defined by an optional set of attributes followed by an optional set of access modifiers followed by the (nonoptional) keyword class, which is then followed by the (nonoptional) identifier (the class name).
"The identifier is optionally followed by the name of the base class, or if there is no base class, by the name of the first interface (if any). If there is a base class or an interface, the first of these will be preceded by a colon. If there is a base class and an interface, they will be separated by a comma, as will any subsequent interfaces.
"After all of these will be an open brace, the body of the class, and a closing brace."
Although this can be confusing, an example makes it all much simpler:
public class Dog : Mammal
{
   // class body here
}
In this little example, public is the access modifier, Dog is the identifier, and Mammal is the base class.
Attributes are covered in ; access modifiers are discussed in the next section. The identifier is the name of the class that you provide. The optional base-class is discussed in . 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 does not 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 created any instances of that class.
When you make an instance of a class, you are said to instantiate the class. The result of instantiating a class is the creation of an instance of the class, known as an object.
What is the difference between a class and an instance of that class (an object)? To answer that question, start with the distinction between 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!
Creating Objects
In , we drew a distinction between value types and reference types. The primitive C# types (int, char, etc.) are value types, and when they are created as standalone variables (not as part of other objects) they 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.
VB 6 programmers take note: although there is a performance penalty in using the VB 6 keywords Dim and New on the same line, in C#, this penalty doesn't exist. Thus, in C#, there is no drawback to using the new keyword when declaring an object variable.
In , 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 compiler 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 doesn't define a constructor. Because 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 null, etc.). lists the default values assigned to primitive types.
Table : Primitive types and their default values
Type
Default value
numeric (int, long, etc.)
0
bool
false
char
'\0' (null)
enum
0
Reference types
null
Typically, you'll want to define your own constructor and provide it with arguments so that the constructor can set the initial state for your object. In , assume that you want to pass in the current year, month, date, and so forth so that the object is created with meaningful data.
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 instance members or static members. Instance members are associated with instances of a type, whereas static members are considered to be part of the class. Thus, there is one instance member for each object instantiated, but there is always exactly one static member, no matter how many objects are created (even if no objects are created).
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 two 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.
VB 6 programmers take note: don't confuse the static keyword in C# with the Static keyword in VB 6 and VB.NET. In VB, the Static keyword declares a variable that is available only to the method in which it was declared. 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.
In addition, static methods may be passed instance members as parameters (or may create such instances themselves within the static method). Because they are scoped to the class—instead of being scoped globally—they have access to the private members of the instances.
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
Because 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 finalizer, which will be called by the garbage collector when your object is destroyed.
The finalizer 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 finalizer; you want this only for handling unmanaged resources. Because there is some cost to having a finalizer, you ought to implement this only on methods that require it (i.e., methods that consume valuable unmanaged resources).
You can't call an object's finalizer directly. The garbage collector will call it for you.
It is not legal to call a finalizer explicitly. Your finalizer 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 IDisposable interface. (You will learn more about interfaces in .) The IDisposable interface requires its implementers to define one method, named Dispose( ), to perform whatever cleanup you consider to be crucial. The availability of Dispose( ) is a way for your clients to say, "Don't wait for the finalizer to be called, do it right now."
If you provide a Dispose( ) method, you should stop the garbage collector from calling your object's finalizer. To do so, call the static method GC.SuppressFinalize( ), passing in the this pointer for your object. Your finalizer can then call your
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 "" 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 variable by reference without first initializing it.
C# also supports the params modifier, which allows a method to accept a variable number of parameters. We discuss the params keyword in .
Methods can return only a single value (though that value can itself be a collection of values). Let's return to the Time class and add a GetTime( ) method, which returns the hour, minutes, and seconds.
Because you can't return three values, perhaps you can pass in three parameters, let the method modify the parameters, and examine the result in the calling method. shows a first attempt at this.
Example . Returning values in parameters
using System;

namespace ReturningValuesInParams
{
  public class Time
  {
    // 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 Time( System.DateTime dt )
    {

      Year = dt.Year;
      Month = dt.Month;
      Date = dt.Day;
      Hour = dt.Hour;
      Minute = dt.Minute;
      Second = dt.Second;
    }
  }

  public class Tester
  {
    static void Main(  )
    {
      System.DateTime currentTime = System.DateTime.Now;
      Time t = new Time( currentTime );
      t.DisplayCurrentTime(  );

      int theHour = 0;
      int theMinute = 0;
      int theSecond = 0;
      t.GetTime( theHour, theMinute, theSecond );
      System.Console.WriteLine( "Current time: {0}:{1}:{2}",
        theHour, theMinute, theSecond );
    }
  }
}

Output:
11/17/2007 13:41:18
Current time: 0:0:0
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 these exact contingencies.
The signature of a method is defined by its name and its parameter list.. Parameter lists can differ by having different numbers or different 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.
Overloaded methods may have different return types, but changing only the return type does not change the signature, and thus is not sufficient to overload the method.
void myMethod(int p1);
string myMethod(int p1, int p2);  // legal

void otherMethod(int p1);
string otherMethod(int p1);  // not legal
In the snippet shown here, myMethod is overloaded by changing the number of parameters, and the two versions may have different return types. On the other hand, the signature of otherMethod is unchanged in the two versions, and changing the return type is not enough to overload the method.
illustrates the Time class with two constructors: one that takes a DateTime object, and the other that takes six integers.
Example . Overloading the constructor
using System;

namespace OverloadedConstructor
{
  public class Time
  {
    // 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(  )
    {
      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 Tester
  {
    static void Main(  )
    {
      DateTime currentTime = DateTime.Now;

      Time t1= new Time( currentTime );
      t.DisplayCurrentTime(  );

      Time t2 = new Time( 2007, 11, 18, 11, 03, 30 );
      t2.DisplayCurrentTime(  );

    }
  }
}
Output:

11/20/2007 17:7:54
11/18/2007 11:3:30
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 the state of the object as though 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: th