Você está na página 1de 967

Introduction to C#

C# Professional Skills Development 1-1


Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Introduction to C#
Objectives
Learn about the history of C# and .NET.
Understand the goals of C#.
Provide a context for .NET and C#.
Introduction to C#
1-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Welcome to C#
This course teaches you how to build Windows and Web applications using C#
and the .NET platform. .NET represents a significant innovation in the
development of Windows and Web programming, and C# is the language of
choice for use with the .NET platform.
The Goals of C#
C# is designed to be:
Simple
Safe
Object-oriented
Internet-centric
High performance
C# is a simple language with only 80 keywords and a dozen types. It is a typesaf
e
language, which means that it enlists the compiler in helping you find
bugs while you develop your program.
C# builds on the lessons learned from C++ and Java to provide a fully objectorie
nted
approach to design and programming. Object-oriented languages
allow you to create new types that reflect the objects in your problem domain.
This allows you to manage very complex projects and build programs that are
easy to maintain and extend.
C# is highly Internet-centric; the goal of C# and .NET is to integrate the Web
into desktop applications and to facilitate the development of Web
applications.
Finally, C# does not sacrifice the high performance characteristics of the C
family of languages.
Introducing the .NET Platform
The .NET platform was introduced in July 2000 at the Microsoft Professional
Developers Conference (PDC). .NET created quite a sensation; here was a
revolutionary approach that provided an object-oriented, component-based
layer on top of Windows to facilitate desktop and Web application
development. This was essentially a whole new API for Windows and for the
Web, presented as a tightly integrated object-oriented framework.
Welcome to C#
C# Professional Skills Development 1-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
As time went on we came to discover that .NET was an umbrella for a number
of new technologies emerging from Microsoft, including:
COM+
ADO.NET
ASP+ (now ASP.NET)
XML
Web Service Protocols
SOAP
WSDL
UDDI
COM+ provides transaction support and messaging for both desktop and Web
applications. While the COM+ technology was developed independently of
.NET it is fully integrated into the .NET platform.
ADO.NET is the successor technology to ADO and provides an objectoriented
approach to database interaction. ADO.NET differs from ADO in that
it is centered around a disconnected dataset, allowing for greater scalability i
n
the development of database applications.
ASP.NET is the replacement technology for ASP, which is Microsofts Web
Application development technology. ASP.NET provides a highly scalable,
object-oriented, component-based Rapid Application Development
environment for building Web applications.
XML is the open standards technology for document markup. While all of
.NET is built on XML, it is not necessary to become proficient in XML to
work with .NET. Visual Studio.NET and the supporting technologies
encapsulate and hide the gritty details of XML.
Microsoft has made a strong commitment, however, to building .NET on top
of open standards such as SOAP: the Simple Object Access Protocolan
open, lightweight XML-based standard for the exchange of information in a
decentralized and distributed environment.
New Tools
.NET provides a number of new tools, including a suite of new languages
(VB.NET, C#, etc.). One of the most powerful new tools is the new integrated
development environment: Visual Studio .NET. Visual Studio .NET is an
extension of the rapid application development environment previously found
in Visual Basic 6, combined with the best elements of FrontPage and the other
Microsoft development tools.
Introduction to C#
1-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
In addition, the new .NET platform provides a comprehensive class library to
support many common programming tasks.
New Languages
.NET offers a number of new programming languages, the most important of
which are Visual Basic .NET and C#. Visual Basic .NET offers a Visual
Basic-like syntax for a fully object oriented language. C# offers a C++-like
syntax for the .NET platform.
The focus of this course will be on C#. C# is a revolutionary advance in the Cla
nguage
family that previously included C and C++ and, arguably, Java. C#
learns lessons from all three, maintaining the performance of C, the overall
syntax of C++, and borrowing many innovations from Java, such as garbage
collection for memory management.
The .NET goal is to provide a language independent development
environment. It is possible to write .NET classes in any language that supports
the Common Language Specification (CLS). The CLS dictates the
characteristics of any conforming .NET language.
All CLS languages are integrated with one another, allowing you to create a
class in C# and derive from it in Visual Basic .NET, treating both classes
polymorphically. In addition, you can throw an exception in a Visual Basic
.NET class and catch it in a C# class.
All of these concepts: deriving classes, polymorphism, and throwing
exceptions will be explained as you go forward.
CTS and CLS
The Common Language Specification (CLS) declares the functionality that
any .NET language must support. Part of the CLS is the definition of a
Common Type System (CTS). Thus, every language within .NET will support
the same set of intrinsic (built-in) types.
Currently there are four official languages, though Microsoft expects many
more to be forthcoming:
C#
Visual Basic .NET
C++ with managed extensions
JScript.NET
Any CLS language can be used to interact with the Framework Class Library.
The class library offers a comprehensive suite of useful objects for your
Welcome to C#
C# Professional Skills Development 1-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
programming. Youll learn how the Framework Class Library works in great
detail throughout this class.
Common Language Runtime (CLR)
The CLR offers an object-oriented platform for Windows and Web
development. It is the CLR that interprets your code and acts as an
intermediary between your .NET application and the underlying platform.
The CLR houses the Just In Time (JIT) Compiler and the intrinsic types as
well as support for exceptions.
.NET Architecture
The .NET Framework is built on top of the CLR, which in turn sits on top of
the Windows platform, as shown in Figure 1.
Introduction to C#
1-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 1. .NET architecture.
The .NET Framework classes are built on top of the CLR and provide
extensive low-level support for your application. The Framework Classes
provide the plumbing you would otherwise write yourself, including classes
for threads, streams, security, and so forth.
ADO.NET is the database support technology for .NET. ADO.NET is built on
top of the Framework Classes and provides a high level abstraction of database
tables and database relationships. Youll learn about ADO.NET in greater
detail later in this course.
On top of all of this technology lies the support for building Windows
applications, Web applications, and Web Services. Web Services are Web
applications without a user interface. Web Services provide data and
functionality to other Web applications (e.g., a Web Service might provide a
stock ticker symbol given the name of a company).
C# is the programming language of choice for every layer of this architecture.
Much of the CLR, and nearly all of the .NET Framework, is written in C#. C#
Welcome to C#
C# Professional Skills Development 1-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
is ideal for writing ADO.NET applications and certainly C# is the right
language for building Windows or Web applications.
MSIL
The programs you write in C# are not actually compiled into executable code.
Instead your source code (the English text you write) is compiled into an
intermediate form called Microsoft Intermediate Language (MSIL) or IL for
short. The reason that all .NET languages can interoperate is that the IL code
produced by C# is nearly identical to the IL code produced by Visual Basic
.NET.
Key Terms
Source code The English-like text that you type into a file.
MSIL Microsoft Intermediate Language (MSIL) is the intermediate
form your program is transformed into by the compiler. The
.NET CLR (Common Language Runtime) transforms the MSIL
code into executable code when your program is run.
History of C#
C# was created by Anders Hejlsberg and Scott Wiltamuth. Hejlsberg is the
creator of Turbo Pascal and Borlands Delphi. They built C# on the shoulders
of C, C++, and Java, consciously borrowing the best aspects of these
languages and adding innovations of their own to facilitate .NET development.
Because C# was specifically built for .NET and is designed to work in the
.NET environment, this course focuses on building .NET applications with this
powerful language.
Introduction to C#
1-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Hello World
It is traditional in programming books to begin with a program that prints the
words Hello World to the monitor. This course will be no exception. Youll
start by examining a simple Hello World program.
Source Code
A program consists of source code that you type into a file. You compile the
source code file into IL (Microsoft Intermediate Language).
You run your program and the CLR turns the IL into executable code. This is
known as Just In Time compiling. The compiled code is then cached in
memory; the next time you run that block of code it is ready to go.
Lets look at a very first program:
namespace HelloWorld
{
class HelloWorld
{
static void Main()
{
// use the system console object
System.Console.WriteLine("Hello world");
}
}
}
Though it is very simple, this program illustrates many concepts in C#
programming. Lets take it apart, piece-by-piece.
One of the first things you should notice is the keyword class in the third line
:
class HelloWorld
A class creates a new type. A type is a thing. The world is filled with things;
and we as humans cant help noticing them. Your desk, phone, and chair are
things. Your dog, sister, and children are things. The sky is a thing, the idea
of
Hello World
C# Professional Skills Development 1-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
love and honor are things. We are a thing-oriented species, and C# is a thingori
ented
language.
Some things are concrete, like dogs and desks, and some things are abstract,
like love and freedom. C# is able to represent both concrete and abstract
things.
A class defines a new thing. An object is an instance of a class. Just as dog
describes an idea but Fido is an instance of a dog, a class describes a type and

an object is an instance of that type. The class button tells you what buttons a
re
like, the edit, cancel, and delete buttons are instances of button, in other wor
ds,
they are objects.
Classes defined new types. From a C# perspective, a type describes the size
and capabilities of objects. C# provides a number of built-in types such as int,

long, and double, and you are free to create your own types.
Methods
Lets look inside the body of the HelloWorld class:
static void Main()
{
// use the system console object
System.Console.WriteLine("Hello world");
Every class has properties and behaviors. In fact, in C# everything that
happens, happens within a class. So class is not only a keyword, it is a key
concept.
The behavior of a class is defined by its methods (member functions). Methods
are typically given action names such as AddNumber or WriteLine. Every
program must have a special method, Main. Main does not have an action
name, but it does have a special role in every program: it is the entry point.
That is, the program begins by calling the Main method.
Main must be marked with the keyword static. Youll understand the meaning
of the static keyword later in this course; for now you just need to know that
Main() must be marked as static.
A method has a name, parameters, and a return type. If the method does not
return a value it is marked void. You call a method and when it completes, it
returns.
Introduction to C#
1-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
For example, if you add a method, myMethod, you will indicate its parameters
within parentheses, and its return type before the name of the method.
void myMethod(int param1, string param2)
{
// do work here
}
The method name is myMethod. The return type is void. This method takes
two parameters. The first is an int (integer) and the second is a string.
Somewhere else in your code you might invoke that method.
int x = 5;
string myString = Hello;
myMethod(x,myString);
You declare a couple of local variables: x of type int and myString of type
string and then pass these variables as parameters to the method MyMethod.
You can declare local variables within any method. These variables are local
to the method itself and are destroyed with the method ends. In addition, you
can pass in parameters, as you saw here, and these parameters act just like
local variables within the method.
Youll learn about variables, strings, integers, and parameters in great detail
later in this course. The key concept, however, is that methods are useful when
you want to provide behavior for your class. Typically, methods are used to
manipulate the class data or to interact with other objects.
Comments
In this simple example, you see that there is one line that begins with a pair o
f
slashes:
// use the system console object
This is a comment, which is a note to the programmer that does not affect the
program in any way. C# supports three styles of comments:
C-style comments /* */
C++ style comments //
XML documentation comments ///
Hello World
C# Professional Skills Development 1-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
C-style comments begin with a slash-star /* and end with a star-slash */.
Everything between the open and end comment marks is commented out. Cstyle
comments are very useful for marking large blocks of source code as
comments, and most C# programmers reserve them for commenting out
code they want to temporarily disable.
The most common type of comment used for documenting C# code is the C++
style double slash //. Everything from the double slash to the end of the line i
s
a comment. C++ comments can start in the middle of a line, allowing you to
add a comment to the same line as the code itself.
The third type of comment is the XML documentation style comment. Youll
learn more about this powerful feature later in this course.
Comments are a very powerful technique when used well. The key to
commenting your code is to write about the purpose of a given line of code,
rather than restating what the line of code does. Well-written code is also well

commented. Youll be surprised how often youll return to code you wrote and
scratch your head thinking, What the devil was I trying to accomplish here?
Comments are also very useful for the programmer who inherits your program
and needs to understand how it works. Over time you will find that a
programmer can never add too many comments.
Console
The last line of the HelloWorld program does all the work:
System.Console.WriteLine("Hello world");
The first thing to note is that this example is a console application. Console
applications run in a console window, often called a DOS box.
The console is your monitor and a console application simply writes to your
console. Console applications are very simple, with virtually no support for
Graphical User Interfaces (GUIs). Youll use Console applications a lot early
in this course, because they greatly simplify development and allow you to
concentrate on the issues at hand, rather than fussing with complex UI issues.
In C# the console (monitor) is managed by a console object. WritLine is a
method of the Console class. You pass a string as a parameter to the WriteLine
method, which in this case is the string Hello World. This string is then
printed to the monitor.
Introduction to C#
1-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Namespaces
Notice that Console is preceded by System. System is the namespace within
which the Console class resides. The Framework Class Library has thousands
of useful classes, and each class has a number of methods. That makes for tens
of thousands, perhaps hundreds of thousands of names of methods and classes.
Namespaces help divide the world of these names to simplify working with
classes and to prevent clashes between the names of methods in different parts
of the Framework.
The dot operator (.) is used to access classes within a namespace. For example,
you access the Console class within the System namespace with
System.Console.
The dot operator is also used to access methods within a class. For example,
you access the WriteLine method within the Console class with
Console.WriteLine().
Building The Application
C# Professional Skills Development 1-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Building The Application
There are two ways to build the Hello World application. The easiest way is
within Visual Studio .Net. Youll be spending a lot of time within the Visual
Studio .NET integrated development environment (IDE), but to prove that
there is nothing magical about the IDE youll build this first application from
the command line.
Try It Out!
1. Open Notepad and copy in the program as shown in Figure 2.
Figure 2. Notepad with Hello World.
2. Save the program as HelloWorld.cs.
3. Open the Visual Studio .NET command prompt from the Start window.
This opens a command window set up properly for the .NET compiler.
4. Navigate to the directory in which you stored your program.
5. Enter the command csc HelloWorld.cs This builds the application.
6. Type dir to see a directory of the current folder. You should see at least
the following two files:
HelloWorld.cs
HelloWorld.exe
Introduction to C#
1-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
7. Enter HelloWorld from the command prompt. The program should run
and print the words Hello World! to your monitor.
Building Applications in Visual
Studio .NET
Youve built the application in Notepad and compiled it at the command line.
Next you will build the same application from within Visual Studio .NET.
With such a simple application as this, Visual Studio .NET does not provide
much obvious additional value; and the code it will create will be a bit more
complicated. But Visual Studio .NET does offer tremendous advantages with
anything but the simplest of code.
1. To get started, open Visual Studio .NET by clicking the Start button.
2. Click on New Project. This opens the New Project dialog box as shown in
Figure 3. Be sure to select a Visual C# project in the Project Types
window and a Console Application in the Templates window. You may
name your program anything you like, and store it in whatever location is
convenient for you. When you are ready, click OK.
Figure 3. The New Project dialog box.
Building The Application
C# Professional Skills Development 1-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
3. Visual Studio .NET sets up your application and opens a series of
Windows to facilitate writing your code, as shown in Figure 4. Notice that
the code window offers a starting point for the Main method.
Figure 4. The Hello World application.
4. Within the body of Main, delete the comment and add the following code:
Console.WriteLine("Hello World");
5. Notice that Visual Studio .NET attempts to help you choose the right
method from Console, and offers supporting information about the
parameters expected for WriteLine. Your code should now look like
Figure 5.
Introduction to C#
1-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 5. Hello World.
6. Note that the Main method is written somewhat differently by Visual
Studio .NET. In this version of Main() there is an argument named args.
Youll ignore this for now. This is useful when calling a C# application
from the command line, but is not relevant to the current program.
7. Save this program by pressing CTRL+S or by clicking the Save button, as
shown in Figure 6.
Figure 6. The Save button.
8. Build this program by pressing CTRL+SHIFT+B or by selecting Build-
>Build from the menu. You should see Build: 1 succeeded, 0 failed, 0
skipped in the Output window.
9. Run the program by pressing CTRL+F5 or selecting Debug->Start
Without Debugging from the menu. You should see a console window
open and the text display, as shown in Figure 7.
Figure 7. Running the program.
Building The Application
C# Professional Skills Development 1-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
10. Notice that Visual Studio .NET keeps the window open and adds the
prompt Press any key to continue. This prompt is provided for you by the
IDE, and you do not need to modify your program to create this prompt.
Introduction to C#
1-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
The .NET platform supports a Common Language Specification
detailing how languages must behave. The CLS includes a Common
Type Specification (CTS) detailing the types for all .NET languages.
C# is ideally suited for work with the .NET platform.
The Hello World program illustrates the fundamentals of writing a C#
program.
The steps are: write the program, compile it and run it.
The output of compiling the program is Microsoft Intermediate
Language (MSIL). The Common Language Runtime (CLR) turns the
IL code into an executable program in a process known as Just In
Time Compiling.
Building The Application
C# Professional Skills Development 1-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. What is the Common Language Specification?
2. What is ADO.NET?
3. What is MSIL?
4. What are the three types of Comments?
Introduction to C#
1-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. What is the Common Language Specification?
The Common Language Specification (CLS) declares the
functionality that any .NET language must support
2. What is ADO.NET?
ADO.NET is the successor technology to ADO; it provides an
object model for interacting with back end data providers such as
relational databases
3. What is MSIL?
MSIL is Microsoft Intermediate Language. When you compile
your program what is produces is an MSIL file that is then
compiled by the Just In Time (JIT) compiler. The MSIL produced
by C# is virtually identical to the MSIL produced by other CLScompliant
languages
4. What are the three types of comments?
C# supports C-style comments (/* */) and C++ style comments
(//) as well as the new XML documentation comments (///)
Language Fundamentals
C# Professional Skills Development 2-1
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Language
Fundamentals
Objectives
Learn about variable declaration, assignment, and initialization.
Understand literal, symbolic, and enumerated constants.
Discover the meaning of type.
Understand statements and expressions.
Language Fundamentals
2-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Variables
Variables are, technically, named typed storage locations. What this means is
that you can create variables to hold values of a specific type, and you can giv
e
these variables names. For example, you can declare:
int myAge; // declare an integer variable
myAge = 25; // assign a value to the variable
Here youve created a variable named myAge and youve declared that it will
hold an int. An int is an integer variable. You then, subsequently assigned the
value 25 to that variable.
Variable values can be changed as the program progresses. You can assign
new values to a variable at any time.
myAge = 46; // how did that happen?
Declare, Assign, Initialize
When you allocate space for the variable, you are said to declare the variable.
When you give that variable a value, you are said to assign to it. You can
combine these two steps into one; this is known as initializing the variable.
Rather than writing,
int myAge; // declare an integer variable
myAge = 25; // assign a value to the variable
you can combine these steps into a single initialization:
int myAge = 25; // initialize an integer variable
You can declare more than one variable on a line at a time, as long as they all
have the same type:
int myFirstVar, mySecondVar, myThirdVar;
Variables
C# Professional Skills Development 2-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
You can even initialize more than one variable on a line at the same time;
again assuming they all are of the same type:
int myFirstVar = 5, mySecondVar = 7, myThirdVar = 9;
WriteLine Substitution Parameters
When you call WriteLine to print a value, you can use substitution parameters.
These are numbers, starting with 0 and counting up, enclosed in braces {}.
You place the value to substitute for the parameter after the close quote,
separated by commas:
Console.WriteLine(My Age is {0}, myAge);
In this example, {0} is the substitution parameter, and the value in the variabl
e
myAge will be substituted, causing the following to print:
My Age is 25
You can have more than one substitution parameter, as long as you count up
from zero, and you ensure that the substituted values are listed in the order th
ey
are numbered.
int intOne = 5, intTwo = 7, intThree=9;
Console.WriteLine(
"one: {0}, two: {1}, three: {2}, one again: {0}",
intOne, intTwo, intThree);
In this WriteLine statement there are four substitutions, using only three
substitution parameters. The first, {0} is repeated at the end of the statement,

and continues to refer to the very first substitution parameter (intOne). The
result of running this code is shown in Figure 1.
See Substitution
Parameters.sln
Language Fundamentals
2-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 1. Substitution parameters.
Notice that the first value (5) is shown twice. The {0} substitution parameter
refers to the first value after the string (intOne) and you may use that as ofte
n
as you like within the statement.
Definite Assignment
While you are not required to initialize or even assign to your variable, you
may not use a variable that has not been either initialized or assigned a value.

That is, you may not pass that variable to a method, nor may you assign that
variable to another.
In the next listing, you create a variable and dont initialize it. You then try t
o
pass that value to Console.WriteLine, but this generates an error, because you
cant use a variable doesnt have a value assigned to it.
namespace DefiniteAssignment
{
class Class1
{
static void Main()
{
int firstValue;
int secondValue;
// unassigned local variable used - compile error
Console.WriteLine("FirstValue: {0}", firstValue);
// unassigned local variable used - compile error
secondValue = firstValue;
Both the call to Console.WriteLine and the assignment to secondValue will
fail because firstValue does not have a value assigned to it. This is a great
feature of the language. Assigning an uninitialized value can make it difficult
See Definite
Assignment.sln
Variables
C# Professional Skills Development 2-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
to find bugs, but having this problem found by the compiler makes your life
much easier. The error you will receive when you compile is shown in
Figure 2.
Figure 2. Compile an error using an unassigned variable.
You can comment out the two offending lines, and then add the following code
to test whether the program compiles:
// assign a value
firstValue = 7;
// pass assigned value to WriteLine
Console.WriteLine("FirstValue: {0}", firstValue);
// okay now to assign
secondValue = firstValue;
Console.WriteLine("SecondValue: {0}", secondValue);
By assigning a value to firstValue you can then use that variable both in
method calls and on the right-hand side of an assignment statement.
Language Fundamentals
2-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Constants
Constants allow you to name a value that will not change while your program
is running. C# supports three types of constants:
Literal constants
Symbolic constants
Enumerations
Literal Constants
A literal constant is just a number you type into your code. For example:
int x = 46;
The value 46 is a literal constant. The numerals 46 represent the value forty-si
x
and they cannot represent any other value. You cant define 46 to represent the
value twenty-eight, no matter how hard you try.
C# programmers try to avoid using literal constants. They are often referred to
as magic numbers. They are magic because they cause magical bugs in your
code.
Often youll use the same value in more than one place in your code. If you
use literals, and change the value in one place, you must remember to change it
in all the others or magical bugs begin to appear.
The alternative to literal constants is symbolic constants.
Symbolic Constants
Symbolic constants are very much like variables, except that their value never
changes. Like a variable, a symbolic constant is a named, typed value, but the
point of a symbolic constant is to take the place of a literal constant. Removin
g
the magic numbers from your program makes your program easier to
maintain and understand. Instead of writing the following:
int x = 100;
You might write:
Constants
C# Professional Skills Development 2-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
// declare a constant
const int BoilingPointCelsius = 100;
//
int x = BoilingPointCelsius;
There are a number of advantages to using the constant. First, if you use the
value many times in your code, you can change the value of the constant and
be assured that the new value will be used throughout the program once you
recompile.
Second, a well-named symbolic constant is itself a form of documentation. It is
far more informative to state that you are assigning the BoilingPointCelsius
constants value, then to simply state that you are assigning the value 100. In
fact, if you choose your variable name well, the code is entirely transparent:
int maxTemperature = BoilingPointCelsius;
The well-named variable and the symbolic constant make the intent of the
code nearly self-evident. You can clarify obscure code with comments, but
maintaining comments is difficult and error prone (the code changes and you
forget to update the comments). It is far better for the code itself to be cryst
al
clear.
Symbolic Constants Must Be Initialized
Remember that constants cannot be assigned while the program is running.
You must, therefore, initialize the constant when you create it. The following
will not compile:
const int BoilingPoint;
BoilingPoint = 100; // nope, must initialize
The only way to change the value for a constant is to stop the program, edit the

source code, and then recompile.
Enumerations
An enumeration is a named set of constants. Each constant is assigned a value.
Enumerations are typically based on int (and if you dont specify otherwise, int
is used by default). You name an enumeration and put the enumerated
constants within braces, as shown here:
Language Fundamentals
2-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
enum Color
{
Red,
Orange,
Yellow,
Green,
Blue,
Indigo,
Violet
}
A comma separates each value. If you do not assign a value, the first
enumerated value will be 1, and each subsequent value will count up. You are
free to set values explicitly for some or all.
enum Temperature
{
FreezingPoint = 0,
WickedCold = 10,
WayCold,
RoomTemperature = 72,
Boiling = 100
}
In the Temperature enumeration, all the values are assigned except for the
enumerated value WayCold, whose value is set to 11 (one more than the
previous).
You can use enumerated constants in many of the places you would use a
symbolic constant, but if you want to get the underlying integer value you
must cast to an int:
Console.WriteLine(Freezing point: {0},
(int) Temperature.FreezingPoint);
Notice that you access the FreezingPoint enumerated value by specifying the
enumeration Temperature, just like accessing a property of a class. The
enumeration Temperature creates a scope, or context, for the enumeration.
Constants
C# Professional Skills Development 2-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
1. Open Visual Studio .Net.
2. Click New Project. Set the location to someplace convenient for your test
program.
3. Choose Visual C# Projects from Project Types and then choose Console
Application from Templates as shown in Figure 3.
Figure 3. Creating new project.
4. Name your test program VariablesAndConstants and click OK.
5. Navigate to the Main() method, where youll find the following comment:
//
// TODO: Add code to start application here
//
6. Remove the comment and type in the following code:
Language Fundamentals
2-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
int numberDogs = 1;
int numberCats = 3;
int numberAnimals = numberDogs + numberCats;
Console.WriteLine("numberDogs: {0} + numberCats: {1} =
numberAnimals: {2}",
numberDogs, numberCats, numberAnimals);
Youve created three variables, assigned a value to each of the first two,
and then assigned their sum to the third. Finally, youve printed out these
values using WriteLine substitution parameters.
7. Add the following code:
const int maxAnimalsAllowed = 10;
int numberAnimalsWeCanStillGet = maxAnimalsAllowed -
numberAnimals;
Console.WriteLine("We can still get {0} more animals",
numberAnimalsWeCanStillGet);
Youve now added a constant value, maxAnimalsWeCanGet and used
that constant to compute the value of the variable
numberAnimalsWeCanStillGet. While these variable names are long,
they are self-explanatory and worth the extra typing.
8. Add the following enumeration within the class declaration, but before the
Main method.
Constants
C# Professional Skills Development 2-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
class VariablesAndConstants
{
enum Temperature
{
WickedCold,
OneDegreeWarmer,
FreezingPoint = 32,
Comfortable = 72,
BoilingPoint = 212
}
static void Main(string[] args)
{
This adds the enumeration Temperature, which you can then refer to
within the program.
9. Add the following code to use the Temperature enumeration:
Console.WriteLine(
"Wicked cold: {0}, one degree warmer: {1}",
(int)Temperature.WickedCold,
(int)Temperature.OneDegreeWarmer);
Console.WriteLine(
"The difference between boiling and freezing: {0}",
Temperature.BoilingPoint -
Temperature.FreezingPoint);
Here youve used the enumerated constants both for displaying to the
monitor and also for computation.
Language Fundamentals
2-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Types
C# is strongly typed. That means that when you create an object, you must
declare its type. The type tells the compiler the size of the object in memory
(i.e., how many bytes of memory the object occupies). The type also tells you
what values the object may hold and it tells you the objects possible behavior.
A strongly typed language does not allow you to just assign values to a
variable willy-nilly; each variable has a type, and the values you assign must
be appropriate to that type. This is a terrific feature in a language, because i
t
enlists the compiler in helping you find bugs.
If you declare that myAge will hold an int (integer) and then try to assign a
date to that variable, the compiler will catch the error. Compiler errors are
preferred over run-time errors.
Compile Errors vs. Run-Time Errors
Compile errors occur each time the program is compiled. If the error exists, the

compiler will find it. Run-time errors get past the compiler, but show up when t
he
program is running. Run-time errors are not reliable; they may occur with some r
uns of
the program, but not with others. Compile errors (sometimes referred to as compi
letime
errors) are caught by the developer. Your Quality Assurance team may catch runti
me
errors, but if not, they will certainly be caught by your customers. Compile tim
e
errors are easier to find, easier to fix, and less embarrassing.
C# offers a number of built-in types, and you can create your own types using
the class keyword (covered later in this course).
The built-in, or intrinsic types are shown in Table 1. Notice that each C# type
corresponds to an underlying .NET type. The .NET type is part of the
Common Type System and is consistent across all .NET languages.
NOTE Boolean values are always true or false, and ints are always
4 bytes.
Types
C# Professional Skills Development 2-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Type Size .NET Description
byte 1 byte Unsigned (0-255)
char 1 char Unicode characters
bool 1 Boolean true or false (note, no other values!)
sbyte 1 Sbyte Signed (-128 to 127)
ushort 2 Uint16 Unsigned (0 to 65,535)
int 4 Int32 -2,147,483,647 to 2,147,483,647
uint 4 UInt32 Unsigned (0 to 4,294,967,295)
float 4 Single ~+/- 1.5*10-45 to ~+/-3.4*1038
double 8 Double ~+/-1.5*10-324 to ~+/-1.7*10308
(15-16 significant digits)
decimal 8 Decimal Fixed-precision 28 digits and position of
decimal point
long 8 Int64 Signed 9,223,372,036,854,775,808 to
9,223,372,036,854,775,807
ulong 8 Uint64 Unsigned from 0 to 0xFFFFFFFFFFFFFFFF
Table 1. The intrinsic types.
Strings
In C# (unlike C and C++) a string is a first class member of the language. A
string is a set of letters, typically a text sentence or phrase. You can declare

strings and assign to them just as you would any other type. You can also
initialize strings, combining the declaration and assignment in a single step:
string myString; // declare
myString = Hello; // assign
string myOtherString = World; // initialize
Strings are used throughout C# programs. Console applications use strings for
output, while Windows applications often assign strings to the text field of
labels, text boxes, and other UI controls. Strings are also used to store text
provided by the user, and can be stored in a database in character, text, or
memo fields.
Language Fundamentals
2-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Writing C# Programs
A C# program consists of source code. The source code is, essentially, stylized
text files that follow precise rules. Even if you dont know C#, if you have any
programming experience at all you can typically read through a C# program
and guess at a great deal of what is going on.
Identifiers
C# programs abound in identifiers. An identifier is just the name you provide
for types, methods, variables, constants, objects, and so forth.
There are strict C# rules for which characters can be used in identifiers, but
you can avoid problems and also make programs that are easier to read by
limiting yourself strictly to characters, and when required, numerics.
The convention, in C# is to use camel notation for variable names and to use
Pascal notation for methods and most other identifiers.
Camel Notation
In camel notation, multiple words are put together into a single word. Each of t
he
original words begins with a capital letter, except the first letter. Examples o
f camel
notation include myInt, yourName, temperatureOutside, and allTheWorld.
Pascal Notation
Pascal notation is just like camel notation, except that the first word is also
capitalized.
Examples of Pascal notation include DrawWindow, MoveLeft, and DeleteContents.
Hungarian Notation
Hungarian Notation is named after Charles Simonyi, a Hungarian who was a
senior programmer at Microsoft. The idea of Hungarian notation is to prepend
variable names with one or more letters to indicate type. For example, you
might write:
Writing C# Programs
C# Professional Skills Development 2-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
int iAge, iWeight;
The lowercase i at the beginning of each of these identifiers indicates that the

value is an integer. Hungarian notation can be useful in simple procedural
languages, but in object-oriented languages such as C#, in which you will be
defining new types, Hungarian notation begins to break down.
Many C# programmers use a limited form of Hungarian notation to indicate
the type of user controls when building Web or Windows applications.
TextBox txtCompanyName;
Button btnCommand;
RadioButton rbYes, rbNo;
This can be helpful when working with many controls on a Windows or Web
form, but is not a standard convention and can cause confusion when
programmers evolve different conventions.
C# Statements
In C# any complete instruction is a statement. All statements must end with a
semicolon. The following are valid C# statements:
int x; // a definition
x = 23; // an assignment
int y = x; // initialization
int z = SomeMethod(x); // a method call
a = b = c = SomeOtherMethod(); // multiple assignments
Anywhere you can use a statement, you can use a block. A block is a set of
statements surrounded by a pair of braces. For example, the if statement is
followed by a statement:
if (someCondition)
x = 5; // statement
The if statement can also be followed by a block:
Language Fundamentals
2-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
if (someCondition)
{
x = 5;
y = 7;
SomeMethod();
z = 18;
}
The syntax of the if statement is covered later in this course, but the key poin
t
here is that wherever a statement is legal you may use a block instead.
Expressions
Any statement that evaluates to a value, is called an expression. It can be very

surprising how many statements are actually expressions as well. For example,
every assignment or initialization is an expression; the statement evaluates to
the assigned value.
x = 57; // evaluates to 57
int y = x; // evaluates to the value of x
int a = b = c = x;
HERE HERE HERE
This last statement consists of a series of expressions as shown in the
following:
c = x;
evaluates to the value of x. That value is then assigned to b and again evaluate
s
to the value of x. Finally, that expression is assigned to a, and again the resu
lt
evaluates to the value of x. It is because the assignment evaluates to the value

of whatever is on the right-hand side that you can chain the assignments
together. It is as if you wrote:
c = x;
b = c;
int a = b;
Writing C# Programs
C# Professional Skills Development 2-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
All of these may be combined to a single statement, precisely because each is
also an expression.
Whitespace
Whitespace is any separator within your code, such as a space, tab or new line.
It is called whitespace because it is the whitespace between the black
characters on a printed page.
The C# compiler ignores extra whitespace, so anywhere you can use a space,
you can also use a series of spaces, tabs, or new line characters. As far as the

C# compiler is concerned, the following two statements are identical:
int myAge = 5; // statement 1
int myAge =
5; // statement 2
The extra spaces, tabs, and new lines are totally ignored by the compiler.
Judicious use of whitespace can make your code much easier to read. While
you may not yet know how a for statement works, you can easily see that the
second of the following examples is easier to understand and read, than the
first:
// statement 1
for (int x=5;x<10;x++){Console.WriteLine(x: {0},x);y++;}
// statement 2
for ( int x = 5; x < 10; x++ )
{
Console.WriteLine(x: {0}, x);
y++;
}
Whitespace can help with readability and can help create programs that are
easier to maintain.
Not All Spaces Are Optional
While you can choose to add additional spaces between identifiers, some
spaces are not optional. The compiler will be confused and will issue an error
Language Fundamentals
2-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
if you do not add a whitespace after a type declaration, for example. The
following two lines are not identical as far as the compiler is concerned:
int x = 5; // declare an int
intx=5; // need space after int
Because the word int is a keyword, it needs a space after it.
Whitespace in quoted strings is also not ignored. The following two lines will
not print identical strings:
Console.WriteLine(Hello world); // note space
Console.WriteLine(Helloworld); // note no space
The first of these will print Hello world, while the second will print
Helloworld. Which one is considered to be the error is entirely up to the
designer, but they are not interchangeable.
Writing C# Programs
C# Professional Skills Development 2-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
Variables are named typed storage locations.
Each variable must have a type and the values assigned to that variable
must be consistent with the type.
You can combine declaration and assignment in initialization.
C# requires definite assignmentvariables must have a value before
they are used.
Constants come in three flavors: literal, symbolic, and enumerated.
Literal constants are just numbers (e.g., 52).
Symbolic constants are like variables, except they must be initialized
and they cannot change their value while the program is running.
Enumerated constants allow you to create a set of related constants
under a common enumeration identifier.
A type tells you the size in memory and the behavior of an object.
C# is strongly typedobjects have a specific type and the type limits
the values they can store.
Strings in C# are first class types, and are fully supported by the
language.
Identifiers are the names you assign to variables, constants, methods,
etc. By convention, variables are identified with camel notation and
virtually everything else with Pascal notation.
C# instructions are called statements. Any statement that returns a
value is an expression.
The C# compiler ignores most extra whitespace.
Language Fundamentals
2-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
(Review questions and answers on the following pages.)
Writing C# Programs
C# Professional Skills Development 2-21
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. What is a variable and when would you use one?
2. What are the three types of constants?
3. What is the difference between a symbolic constant and a variable?
4. What is the difference between an expression and a statement?
5. When does the compiler not ignore whitespace?
Language Fundamentals
2-22 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. What is a variable and when would you use one?
A variable is a named memory location in which you can
temporarily store a value. You might use a variable to hold a
value entered by the user.
2. What are the three types of constants?
Literal (a number), symbolic (a name), and enumerated (a set of
constants with a shared enumeration name)
3. What is the difference between a symbolic constant and a variable?
A symbolic constants value may not change while the program is
running.
4. What is the difference between an expression and a statement?
An expression is a statement that evaluates to a value.
5. When does the compiler not ignore whitespace?
The compiler will not ignore whitespace that separates a
keyword from surrounding text, and will not ignore whitespace
within a string.
Writing C# Programs
C# Professional Skills Development 2-23
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 2:
Language
Fundamentals
Lab 2:
Language Fundamentals
2-24 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 2 Overview
In this lab youll learn how to create variables, use constants, and display these

values to the console.
To complete this lab, youll need to work through two exercises:
Using Variables
Using Constants
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-bystep

instructions.
Using Variables
C# Professional Skills Development 2-25
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Using Variables
Objective
In this exercise, youll create a new Console application and create some
variables. You will then display the variables to the console.
Things to Consider
What is a variable?
How might the value in a variable change during the execution of the
program?
Step-by-Step Instructions
1. Open Visual Studio .NET.
2. Click New Project. Set the location to someplace convenient for your test
program.
3. Choose Visual C# Projects from Project Types and then choose Console
Application from Templates as shown in Figure 4.
Figure 4. Creating the new project.
Lab 2:
Language Fundamentals
2-26 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
4. Navigate to the Main() method. Remove the comment and type in the
following code:
int numberDeskTops = 8;
int numberLapTops = 2;
int numberComputers = numberDeskTops + numberLapTops;
Console.WriteLine("This office has {0} computers.",
numberComputers);
Console.WriteLine(
"Of them {0} are desktops and {1} are laptops",
numberDeskTops, numberLapTops);
5. Compile and build the application.
Using Constants
C# Professional Skills Development 2-27
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Using Constants
Objective
In this exercise, youll create a program using a constant and an enumeration.
Things to Consider
When would you use an enumeration rather than a simple constant?
When would you use constants rather than variables?
Step-by-Step Instructions
1. Create a new console application named UsingConstants.
2. Create an enumeration for shapes.
enum numSides
{
circle = 0,
triangle = 3,
square,
pentagon,
octagon = 8
};
3. Add the following code to Main.
const int myAge = 46;
Console.WriteLine(
"I am {0} years old and I know that...", myAge);
Console.WriteLine(
"A figure with 3 sides is a {0}, " +
" but if it has 5 it is a {1}",
numSides.triangle, numSides.pentagon);
Lab 2:
Language Fundamentals
2-28 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Compile and run the application. It should look like Figure 5.
Figure 5. Running the lab.
Branching
C# Professional Skills Development 3-1
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Branching
Objectives
Differentiate between conditional and unconditional branching.
Understand how method calls interrupt the execution path
unconditionally, and how methods return values.
Understand how the if statement allows conditional branching.
See how the else statement and nested branching add flexibility and
how the switch statement adds clarity.
Discover how looping works in C# and see how the various looping
constructs facilitate writing code that is easy to understand.
Branching
3-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Unconditional Branching
The normal course of events is for a program to execute in the order it was
written. Each statement is executed, one after another. In tiny sample
applications, that may be the entire story: the program begins, each statement
is executed, and then the program ends, but if that were all there was, programs

would be very limited and nearly useless.
In any real program, the logic of the program branches. That branching can
happen unconditionally, when a method is called, or conditionally, based on
the state of some object in the program.
Method Calls
There are a number of ways a program can branch conditionally, and many of
these will be discussed later in this course. The most common unconditional
branch is a method.
The normal course of events is for a program to execute in the order it is
written. A method begins and each statement is executed in turn until the
method ends.
NOTE The terms method and function can be used interchangeably. C
programmers tend to use the term function, while C++ and Java
programmers tend to use the term method.
When a method is called, or invoked, the processing of the program branches
to the start of the new method. When the method returns (finishes), processing
resumes on the line immediately following the invocation of the method, back
in the original calling method.
Figure 1 illustrates that when the method returns, processing resumes on the
line after the method call. Processing begins with the execution of Statement1
and then continues to the next statement, which is a method call for Function2.
At this point, execution branches to Function2.
Unconditional Branching
C# Professional Skills Development 3-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 1. A branching diagram.
Within Function2 the first statement executes and then processing goes to the
second statement, which again is a method call to Function3. In Function3 the
first statement, Statement1 executes, followed by Statement2 and Statement3,
in that order. When the function ends or when it hits a return statement,
execution returns to the calling method, in this case Function2. Within
Function2 execution resumes on the line immediately following the call to
Function3; in this case Statement3.
When Function2 ends, again execution resumes in the calling method, in this
case Function1. Statements 3 and 4 are executed and then processing branches
on the call to Function4.
Each statement in Function4 executes in order. Statement1 is followed by
Statement2, which in turn is followed by Statement3 and then Statement4.
When Function4 completes, execution returns to Function1 at Statement4.
Branching
3-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
You can see the order of execution illustrated with a simple example.
1. Open a new project in Visual Studio .NET.
2. Under Project Types choose Visual C# Projects and within Templates
choose Console Application. Name the project UnconditionalBranching
as shown in Figure 2.
Figure 2. Creating the new project.
3. Replace the comment in Main with the following two lines of code:
Console.WriteLine("I'm in Main!");
FunctionOne();
4. Add the following methods to the program, just below Main, but within
the braces for Class1:
Unconditional Branching
C# Professional Skills Development 3-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
static void FunctionOne()
{
Console.WriteLine("I'm in functionOne!");
FunctionTwo();
}
static void FunctionTwo()
{
Console.WriteLine("I'm in functionTwo!");
}
5. Compile the program with SHIFT+CTRL+B. You should not receive any
errors.
6. Execute the program with CTRL+F5. Each method is invoked in turn, as
shown in Figure 3.
Figure 3. Running the test branching program.
7. You can see the actual steps of invoking methods by putting a breakpoint
next to the first executable line in Main(). You set the breakpoint by
clicking in the margin next to the line you want to break on. A red dot
appears, and the line is highlighted as shown in Figure 4.
Figure 4. Setting the breakpoint.
8. Execute to the breakpoint by pressing F5.
Branching
3-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
TIP: F5 runs the program in the debugger, while CTRL+F5 runs without the
debugger.
9. Program executes to the breakpoint and then stops. Depending on how
your environment is set up, you should now see a set of windows similar
to those shown in Figure 5. In the main code window you see the current
line (about to be executed) indicated with a yellow arrow.
At the bottom of the screen may be a pair of useful windows. The Locals
window shows the value of local variables. Youll learn more about this
window later in the course.
The lower right-hand corner has the Call Stack window, which shows the list
of methods that led to the current method (more about this shortly).
Figure 5. Running to the breakpoint.
10. You can step through this program using F11. Pressing once executes the
first statement, which writes to the output console. If you switch to the
output console (click on it on the taskbar, or use ALT+TAB to switch
among your applications) youll see that the line has been displayed to the
console.
Unconditional Branching
C# Professional Skills Development 3-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
11. Step into the call to FunctionOne by pressing F11. The yellow current
statement pointer jumps down into FunctionOne and the CallStack now
shows two methods listed, FunctionOne() and Main(), as shown in
Figure 6. This indicates that you are currently in FunctionOne, called by
Main.
Figure 6. Step into FunctionOne.
12. You can continue stepping through the program, executing each line in
turn, and branching unconditionally on the method calls.
Returning Values from Method Calls
Methods can return a value to the calling method. To do this, you must declare
the type of value the method will return. Void indicates that the method does
not return any value at all. So far, all the methods youve seen have returned
void.
Branching
3-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
Follow these steps to create a method that returns an integer value, and youll
use that value in the calling method.
1. Create a new console application and call it ReturnValues.
2. Replace the code in Main with the following:
Console.WriteLine("I'm in Main!");
FunctionOne();
3. Add two methods. FunctionOne returns void, but Doubler returns an int
value:
static void FunctionOne()
{
Console.WriteLine("I'm in functionOne!");
int x = 5;
int y;
y = Doubler(x);
Console.WriteLine("x was {0} and y is {1}", x,y);
return;
}
static int Doubler(int originalValue)
{
int newValue = originalValue * 2;
return newValue;
}
4. Put a breakpoint in Main() where you call FunctionOne and run to that
breakpoint, as shown in Figure 7.
5. Step into FunctionOne. You declare a local variable x and initialize its
value to 5. You then declare a variable y and assign to y the result of
calling Doubler, passing in x. Step until that line is current.
Unconditional Branching
C# Professional Skills Development 3-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 7. In ReturnValues ready to call FunctionOne.
6. Hover the cursor over the x. Youll see that the value of x is displayed in a
small window, and is also shown in the Locals window, as shown in
Figure 8.
7. Step into Doubler, and examine the value of the parameter, originalValue.
You can see in the Locals window that the value is 5, as youd expect.
This is the value of x, the argument to Doubler as called from
FunctionOne.
Branching
3-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 8. Examining the value of x before invoking Doubler.
8. Within Doubler a new value is created and initialized to twice the
parameter. It is this value that is returned in the return statement.
9. Examine the value of newValue before the method returns. Youll see in
the Locals window that it is 10.
10. Continue stepping out of Doubler and back into FunctionOne. Step
through the assignment to y and then examine y; it is now set to 10.
The return value of Doubler was assigned to the local variable y. This is a
common idiom with method calls.
Conditional Branching
C# Professional Skills Development 3-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Conditional Branching
Far more interesting than unconditional branching, is conditional branching.
With conditional branching, your program learns to make decisions based on
the conditions of the moment.
In real life you decide whether or not to open your umbrella based on local
conditions. For example, if it is raining, you open the umbrella. If it is not
raining, you do not (yes, there are always exceptions, but this is the general
rule with umbrellas).
In C# you might express this idea with the following code:
if (isRaining)
OpenUmbrella();
The if Statement
The if statement tests a Boolean value or an expression that evaluates to a
Boolean. Often that expression is a method call that returns a Boolean value. If

the value evaluates true, the body of the if statement executes, if not, the
statement is skipped.
The body of an if statement can be a single statement, or as noted earlier, can
be a block of statements enclosed within braces.
else
If statements can also have an else clause. If the expression evaluates true, th
en
the body of the if statement is executed and the body of the else statement is
skipped. If the expression evaluates false, then the body of the if statement is

skipped and the body of the else statement is executed.
if ( myAge > 40 )
Console.WriteLine(You are over 40);
else
Console.WriteLine(Youth abounds!);
In this example, if the variable myAge is 50 then the Boolean expression
myAge > 40 will evaluate true, and the statement You are over 40 will be
displayed. If the value of myAge is 40 or less, however, then the if statement
Branching
3-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
will fail, and the else statement will execute, displaying Youth abounds! on the

console.
A Nested if
You can nest an if statement within another if statement to test multiple
conditions. For example, you can test the ambient temperature to see if water is

solid, liquid, or gaseous with the following code:
if ( temperature > freezingPoint )
{
if ( temperature < boilingPoint )
Console.WriteLine(Water is liquid);
else
Console.WriteLine(Water is gaseous);
}
else
ConsoleWriteLine(Water is solid);
The outer if statement tests whether the current temperature is greater than the

freezingPoint. If it is not, the else statement executes and the string, Water i
s
solid is displayed.
If the outer if statement evaluates true (that is, if the current temperature is

greater than the freezing point), then the block of statement executes. Within
that block, you see an inner, nested if statement. This inner if tests whether t
he
current temperature is less than the boiling point, and if so it displays the
message Water is liquid. Otherwise, if the temperature is greater than the
boiling point, the string Water is gaseous is displayed.
Nested if statements work well for situations where one condition depends on
another. For this particular example, however, you can write code that is easier

to understand by using another construct: the switch statement.
Switch
The switch statement allows you to choose among a series of values, taking
action on whichever value matches. Switch statements are typically used when
you branch, or take other actions, based on one of a series of values.
The formal syntax for a switch statement is as follows:
Conditional Branching
C# Professional Skills Development 3-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
switch (expression)
{
case constant-expression:
statement
jump-statement
[default: statement]
}
The easiest way to see how a switch statement is used is with an example. In
the following example, you will switch on the value entered by a user.
using System;
namespace SwitchStatements
{
class SwitchStatements
{
static void Main()
{
// print the menu of choices
Console.Write(
"(1) Walk (2) Run (3) Crawl (4) Falling
[1,2,3,4]: ");
// read the user's choice from the keyboard
string choice = Console.ReadLine();
// convert the choice to an integer
int menuChoice = Convert.ToInt32(choice);
// switch on the choice and choose
// the appropriate action
switch (menuChoice)
{
case 1:
Console.WriteLine("Walking...");
break;
case 2:
See Switch
Statements.sln
Branching
3-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Console.WriteLine("Running...");
goto case 4;
case 3:
Console.WriteLine("Crawling...");
break;
case 4:
Console.WriteLine("Falling...");
break;
} // end switch
} // end Main
} // end class SwitchStatements
} // end namespace SwitchStatements
The example begins by printing a menu to the console:
Console.Write(
"(1) Walk (2) Run (3) Crawl (4) Falling [1,2,3,4]: ")
The user chooses a value of 1, 2, 3, or 4. You read the users choice with the
ReadLine() method of Console, which reads a single line (ended by pressing
enter) from the console.
You then assign that string value to an integer by calling the ToInt32 method
of the Convert object:
string choice = Console.ReadLine();
int menuChoice = Convert.ToInt32(choice);
NOTE ToInt32 is a static method of the Convert object, as explained later
in this course. For now, you can just treat this as a magic ability to
convert strings to integers.
You are ready to switch on the menuChoice value, taking the appropriate
action depending on the users selection. If the user entered 1 to indicate that
he wants to walk, you will match the case where the value is 1. In that case you

will take whatever action is appropriate. To keep this example simple, youll
print a message to the console:
Conditional Branching
C# Professional Skills Development 3-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
switch (menuChoice)
|{
case 1:
Console.WriteLine("Walking...");
break;
The keyword break indicates that the handling of the case is completed and
processing can fall out of the switch statement.
If the user entered 2 to indicate that they want to run, then the first case wou
ld
be skipped and the second case would match:
case 2:
Console.WriteLine("Running...");
goto case 4;
This case also prints a message to the console. This time, however, rather than
ending with break this statement ends with goto. Goto indicates that you want
to continue as if another case had matched, in this case 4. The result of
choosing 2 (run) is that both case 2 and case 4 will execute (in that order).
Thus, the output will be Running Falling which is not inappropriate.
Default
The switch statement may have a default case that will execute if none of the
other choices do. You can add a default case to the code shown just below the
fourth case:
Branching
3-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
switch (menuChoice)
{
case 1:
Console.WriteLine("Walking...");
break;
case 2:
Console.WriteLine("Running...");
goto case 4;
case 3:
Console.WriteLine("Crawling...");
break;
case 4:
Console.WriteLine("Falling...");
break;
default:
Console.WriteLine(
"I'm sorry, that is not a valid choice");
break;
} // end switch
If the user enters an invalid value (e.g., 6), the default case executes and the

string Im sorry, that is not a valid choice displays.
Looping
C# Professional Skills Development 3-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Looping
One of the principal mechanisms for writing useful programs is the ability to
loop through a set of instructions, repeating each instruction until a condition
is
met.
C# supports a number of looping instructions, including:
goto
while
dowhile
for
foreach
Each of these constructs, except the foreach loop is illustrated in the
following example. Youll learn more about the foreach loop later in this
course when collections are covered.
using System;
namespace Looping
{
class Looping
{
static void Main()
{
// goto
Console.WriteLine("goto...");
int i = 0;
repeat:
Console.Write("{0} ", i);
i++;
if (i < 10)
goto repeat;
// while
Console.WriteLine("\n\nWhile...");
Branching
3-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
i = 0;
while (i < 10)
{
Console.Write("{0} ", i);
i++;
}
// do...while
Console.WriteLine("\n\nDo While...");
i = 0;
do
{
Console.Write("{0} ", i);
i++;
} while (i < 10);
// for
Console.WriteLine("\n\nFor...");
for (i = 0; i < 10; i++)
{
Console.Write("{0} ", i);
}
Console.WriteLine("\n");
}
}
}
The best way to analyze this demonstration program is section by section.
goto
The venerable goto statement is not much respected these days (except when
used within a switch statement as shown above), but it is the root of all
looping. Using goto is quite straightforward, and requires only three steps:
1. Create a label.
2. Perform some work.
3. Goto the label if some condition is met.
Looping
C# Professional Skills Development 3-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Set up your state by initializing the integer variable i to zero. Then create th
e
label: repeat and begin work. In this case, the work is nothing more than
printing out the value of i and then incrementing i. The ++ operator adds one to

the value of i. Test the value of i, and if it is less than ten, go to the label
and
loop through printing and incrementing the value again:
// goto
Console.WriteLine("goto...");
int i = 0;
repeat:
Console.Write("{0} ", i);
i++;
if (i < 10)
goto repeat;
The result of running just this block of code is shown in Figure 9.
Figure 9. The goto loop.
To see how this works, put a breakpoint at the initialization of i and step
through the loop. Each time through, youll see i incremented and then the test
in the if statement. Each time that i is less than 10 the goto executes, returni
ng
you to the repeat label. Once i is 10, the if statement fails and the program
ends.
The Trouble with goto
Goto has a terrible reputation with computer scientists, and for good reason. If

you imagine that there is a line drawn in your code indicating the order of
execution, goto statements can cause the line of execution to loop over itself.
Repeated goto statements, sprinkled liberally throughout the code can create
loops within loops that look not unlike a plate of spaghetti.
This is why goto statements are said to create spaghetti code. The truth is
somewhat more complicated: goto statements are not inherently evil. You can
write fine, structured code containing goto. The problem is that goto
Branching
3-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
statements also allow you to write horrible, unstructured code. The solution is
to use more structured constructs, such as while, do while, and for loops.
while
The while loop shown in the listing is a good example of how you can
accomplish the same goal you did with the goto statement, but in a more
structured statement.
The semantics of a while statement are: While this condition remains true, take
this action. While i is less than ten, print its value.
i = 0;
while (i < 10)
{
Console.Write("{0} ", i);
i++;
}
NOTE We use the value i for counting variables in this and many other
examples. This is a long tradition in computer programming, and
in fact it goes all the way back to an early programming language:
Fortran. In Fortran the only legal counting variables were i, j, k,
and l. Many programmers continue this tradition of using i, j, k,
and l for simple counting variables, even if they dont know why.
What is appealing about this code is that the braces enclose the entire action,
and the while statement sets the condition. You can see how this works and
there is no jumping all over the code willy-nilly. The condition is tested, and
if
it evaluates true (i is less than ten) the action within the braces is executed.
The
result of running the while loop is shown in Figure 10.
Looping
C# Professional Skills Development 3-21
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 10. Executing the while loop.
You can see that the output from the while loop is identical to the goto, but th
e
code is somewhat cleaner. In large programs the code is infinitely cleaner.
dowhile
The while loop has one feature that may represent a limitation in certain
circumstances. The condition (i < 10) is tested before the loop is executed.
Notice that just before testing the while loop, youve assigned the value 0 to i.
This is necessary because the previous test of goto left i with the value of 10.
If
you comment out the assignment just before running the while loop, the results
are dramatically different, as shown in Figure 11.
Figure 11. Commenting out the assignment to i.
This time the while loop does not print any values at all, because the test (i <

10) fails, and the body never executes.
There are times when you want to ensure that your loop runs at least once. For
this, use a do..while loop. A do..while loop works just like a while loop, excep
t
Branching
3-22 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
that the test is executed after the statement executes. In most circumstances,
this wont make any difference. If you initialize the i to 0 each time, the loops
will run identically, as shown in Figure 12.
Figure 12. While and DoWhile when i = 0.
If, however, you remove the initialization of i to 0 both in the while and the
do..while loops, the difference is shown, as seen in Figure 13.
Looping
C# Professional Skills Development 3-23
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 13. While and do while when i = 10.
This time i is not set to 0, it is left at 10. The while loop test fails, so not
hing is
printed. The do..while loop executes once and then the value is tested. Since
the test fails, the do..while loop is not executed again.
for Loops
Most loops, in one way or another, replicate these steps:
1. Set an initial condition.
2. Test the condition and if the test evaluates true, execute the body of the
loop.
3. Change the condition.
4. Test the condition and if the test evaluates true, execute the body of the
loop.
5. Repeat Steps 3 and 4 while the condition remains true.
The for loop combines Steps 2 through 4 into a single statement.
Branching
3-24 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The head of the for loop (within the parentheses) is divided into three sections
,
separated by semicolons. The first section sets the initial conditions. The
second section is the test, and the third section is the action to take after th
e
body of the for statement executes.
for (i = 0; i < 10; i++)
{
Console.Write("{0} ", i);
}
In the code shown, the variable i is initialized to 0 in the first section. This

section only executes once; the first time the loop is run. The second section i
s
the test; the variable is tested to ensure its value is less than 10. If so, the
body
of the for loop (within the braces) is run.
When the body completes, the third section executes (i++) and then the test is
executed again. If the test evaluates true, the body is run again. This repeats
until the test fails.
You can leave any of the sections blank and can move the initialization out of
the for loop:
i = 0;
for (; i < 10; i++)
{
Console.Write("{0} ", i);
}
You can move the action into the body of the loop:
Console.WriteLine("\n\nFor...");
i = 0;
for (; i < 10; )
{
Console.Write("{0} ", i);
i++;
}
You can, if you are particularly ornery, even move the test into the body of the

loop:
Looping
C# Professional Skills Development 3-25
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
i = 0;
for (;; )
{
Console.Write("{0} ", i);
i++;
if (i >= 10)
break;
}
This does somewhat undermine the point of the for loop, but it also illustrates
quite dramatically that each part of the for loop is optional. You must hold its

place with the semicolons, and you can break out of a for loop (or any loop)
with the break keyword.
Branching
3-26 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
Program execution continues in the order of the program statements,
unless interrupted by a branching statement.
Branching can be conditional or unconditional. Unconditional
branching is most often accomplished with a method call.
When a method is invoked, program execution branches to the top of
the. When the method returns, execution resumes with the statement in
the calling method that immediately follows the method invocation.
A value may be returned by a method and that value may be used
locally within the calling method.
Conditional branching can be accomplished with the if statement. If
statements may have an else clause and may also have other if
statements nested within them.
The switch statement may be used to selectively branch based on a
particular value.
C# programs can loop using a number of constructs. The most
primitive looping construct is the if statement, but C# also supports the
while, do..while, and for loop.
Looping
C# Professional Skills Development 3-27
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. What is the difference between a conditional and an unconditional branch?
2. What is the return type to indicate that no value will be returned by a
method?
3. When is an else statement executed?
4. What is the disadvantage of goto?
5. Why would you use do..while rather than while?
6. What is the advantage of using a for loop rather than a while loop?
Branching
3-28 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. What is the difference between a conditional and an unconditional branch?
A conditional branch depends on the Boolean state of an
expression being tested. An unconditional branch occurs no
matter what the state of the program.
2. What is the return type to indicate that no value will be returned by a
method?
The return type to indicate that no value is returned is void.
3. When is an else statement executed?
An else statement is executed when the expression tested by its
if statement evaluates false. You cannot have an else statement
without an if statement.
4. What is the disadvantage of goto?
Programs using goto branch n ways that are hard to trace when
examining the code, and create code that is difficult to
understand and maintain.
5. Why would you use do..while rather than while?
The do..while loop is guaranteed to run at least once, since the
test executes after the body of the loop. Use do..while anytime
you must ensure that the body of the loop will execute at least
once.
6. What is the advantage of using a for loop rather than a while loop?
The for loop combines the initialization, test, and update of the
trackng value into a single header statement. for loops are
generally cleaner and easier to understand.
Looping
C# Professional Skills Development 3-29
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 3:
Branching
Lab 3:
Branching
3-30 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 3 Overview
In this lab youll learn how to work with conditional statements and how to
work with loops.
To complete this lab, youll need to work through two exercises:
Branching Your Program Using Conditionals
Repeating Work with Looping
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-bystep

instructions.
Branching Your Program Using Conditionals
C# Professional Skills Development 3-31
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Branching Your Program Using
Conditionals
Objective
In this exercise, youll create code that branches based on a condition.
Things to Consider
When would you use if/else vs. switch?
How might you exit a program quickly if you cannot continue?
Step-by-Step Instructions
1. Create a new console application.
2. Within Main, ask the user for his/her age. If the user is under 21, write a
message and exit the program:
using System;
namespace Conditionals
{
class GuessIt
{
static void Main()
{
Console.Write("Enter your age: ");
// read the user's age from the keyboard
string strAge = Console.ReadLine();
// convert it to an integer
int age = Convert.ToInt32(strAge);
Lab 3:
Branching
3-32 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
// don't let them continue if they're not of age
if (age < 21)
{
Console.WriteLine(
"Oh, you are just too young to play this
game!");
return;
}
else
Console.WriteLine(
"Step right up, and watch carefully....");
3. Prompt the user to guess a number between 1 and 10. Convert the value to
an integer and switch on the value, printing appropriate messages based on
how close they are to the value that the computer is thinking of (4).
Console.Write(
"Enter a number between 1 and 10: ");
// read the user's guess from the keyboard
string strGuess = Console.ReadLine();
// convert the guess to an integer
int guess = Convert.ToInt32(strGuess);
// switch on the guess and choose
// the appropriate action
switch (guess)
{
case 1:
case 2:
Console.WriteLine("Nope, way too low...");
break;
case 3:
Console.WriteLine("Very close...");
break;
case 4:
Branching Your Program Using Conditionals
C# Professional Skills Development 3-33
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Console.WriteLine("You got it!!");
break;
case 5:
Console.WriteLine("You just missed it!");
break;
case 6:
case 7:
Console.WriteLine("A bit high...");
break;
case 8:
case 9:
case 10:
Console.WriteLine("Way too high!");
break;
default:
Console.WriteLine(
"I'm sorry, that is not a valid guess");
break;
} // end switch
} // end Main
} // end class GuessIt
} // end namespace
Lab 3:
Branching
3-34 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Repeating Work with Looping
Objective
In this exercise, youll add loops to the previous game to make it more
interesting.
Things to Consider
What kind of loop will work best to allow the user to keep playing
after each guess?
What is the condition for ending the loop?
Step-by-Step Instructions
1. Reopen the previous exercise.
2. Modify the code to declare the integer variable guess before you prompt
for the number, and initialize it to 0.
int guess = 0;
3. Create the while loop, testing the new value guess, and continuing the loop
until guess is equal to 4.
int guess = 0;
while (guess != 4)
{
Console.Write("Enter a number between 1 and 10: ");
// read the user's guess from the keyboard
string strGuess = Console.ReadLine();
// note that you no longer declare guess here
guess = Convert.ToInt32(strGuess);
Repeating Work with Looping
C# Professional Skills Development 3-35
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
4. The rest of the code is unchanged. Remember to close the while loop (with
a closing brace).
default:
Console.WriteLine(
"I'm sorry, that is not a valid guess");
break;
} // end while
} // end switch
} // end Main
} // end class GuessIt
} // end namespace
5. Compile and run your program as shown in Figure 14.
Figure 14. Running the looping application.
Lab 3:
Branching
3-36 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Operators
C# Professional Skills Development 4-1
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Operators
Objectives
Understand the various categories of operators.
Explore how relational operators and logical operators work together.
Use short circuit evaluation.
Use parentheses to change the order of precedence.
Operators
4-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Operator Types
C# supports a number of operators, including:
Relational operators (>, <, ==)
Assignment (=)
Mathematical (*,/,-,+)
Logical (&&, ||, !)
When you looked at if statements (and many of the branching statements) you
tested for certain conditions, such as whether two values were equal or one
was greater than another. These are examples of relational operators.
The assignment operator may be the operator used most in C#. You will often
find yourself assigning the value of one object to another, or assigning to an
object the value returned by a method.
Mathematical operators are used less frequently, but many programs involve
simple arithmetic, and nearly all C# programs use the increment operator (++)
in loops.
Logical operators are used when you are testing whether either or both of two
(or more) conditions are true.
Relational Operators
The relational operators include:
Equals (==)
Not Equals (!=)
Greater than (>)
Greater than or equal to (>=)
Less than (<)
Less than or equal to (<=)
WARNING! It is a common mistake to write the assignment operator, which is a
single equal sign (=), when you mean to use the equals operator,
which is a pair of equal signs with no space between them (==).
Operator Types
C# Professional Skills Development 4-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Relational operators always evaluate to a Boolean (either true or false). Given
two values and a relational operator, the entire expression is either true or it
is
not. For example:
7 < 5; // evaluates false
7 > 5; // evaluates true
5 > 5; // evaluates false
5 >= 5; // evaluates true
You will almost never compare two literal constants in this way; typically
youll evaluate two variables or objects:
myAge < yourAge
And typically, youll do this evaluation within a test. Most often youll use an
if statement or a while or for statement:
if ( myAge < yourAge )
// take some action
while ( turnsLeft > 0 )
// take some action
for ( i = 0; i < 10; i++ )
// take some action
In each of these cases, the evaluated expression will return true or false.
Mathematical Operators
The mathematical operators generally work the way youd expect. The
addition operator (+) adds two values, the subtraction operator (-) returns the
difference between two values. When you multiply two values (*) the
expression evaluates to their product:
z = x * y; // assign to z the product of x and y
Operators
4-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Division
There are no surprises with addition, subtraction, or multiplication. When you
divide two int values, however, the result is what youd get in fourth grade:
that is, the quotient is truncated! You will remember that back in fourth grade,

the problem (13/5) had the answer 2, with a remainder of 3. That is, 5 went
into 13 twice, with 3 left over. C# integer division works the same way.
In the following expression, x has the value 2. The remainder is lost.
int x = 13 / 5;
When you divide doubles and floats, of course, you do get a decimal result, to
the limits of the precision of the type.
Modulus
You can get at the remainder value in integer division, by using the modulus
operator (%). Modulus returns only the remainder. The following expression
evaluates to 3, because 13/5 is 2 with a remainder of 3, and % returns only the
remainder:
int x = 13 % 5
It turns out that modulus is quite useful for finding every nth occurrence in a
loop.
For example, to find every tenth occurrence in a loop, you might write
something like the following:
for (int i = 11; i <= 100; i++)
{
Console.Write("{0} ",i);
if (i % 10 == 0)
Console.WriteLine("\t {0}", i);
}
This code writes the values 11 through 100. After every ten values, it writes a
tab and then writes the number followed by a new line. The result is shown in
Figure 1.
See Operator.sln
Operator Types
C# Professional Skills Development 4-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 1. The modulus operator.
The first time through the loop, i has the value 11. The modulus operator
returns the value 1 and the if statement fails:
if (i % 10 == 0)
11 % 10 is 1 because 11 / 10 is 1 with 1 left over. The second time through the
loop i is 12, and 12 % 10 is 2, again the if fails, two is not equal to zero. Th
is
continues until i is 20. At that point the modulus operator returns 0 (20 % 10)
which is equal to zero, and so the if statement is executed.
Concatenation
The addition operator (+) is also used to concatenate strings:
string s1 = hello;
string s2 = world;
string s3 = s1 + s2;
The result will be that s3 contains the string hello world. This has become such

a common idiom in programming that few find it confusing.
NOTE The space at the beginning of string s2 allows the concatenated
string to be hello world rather than helloworld.
Operators
4-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Self-Applied Operators
It is common to want to increment a variable or multiply a variable by a value,
and then assign that value back to the variable itself.
In C# you can, as you do in other languages, write the resulting value back to
the variable:
myVariable = myVariable + 5;
If myVariable had the value 7 before this statement, it would now contain the
value 12. Similarly, you can write:
int a = 5, b = 12;
a = a * b;
After these two statements, variable a will have the value 60.
This is such a common idiom, that C# provides a shorthand. The statement,
a = a * b;
is exactly equivalent to the statement,
a *= b;
The latter is just shorthand for multiply myself times b and reassign the value
back to me. This shorthand can be applied to any of the mathematical
operators:
a += b; // add b
a -= b; // subtract b
a *= b; // multiply by b
a /= b; // divide by b
a %= b; // modulus b
Operator Types
C# Professional Skills Development 4-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Increment and Decrement
Adding or subtracting a single value to a variable is such a common
occurrence that C# supports operators that do exactly that: the increment
operator adds one to a value, and the decrement operator subtracts one. In
addition, each of these comes in two flavors: prefix and postfix.
Prefix vs. Postfix Operator
The semantics of the prefix increment operator (++x) are increment and then
fetch. The semantics of the postfix increment operator (x++) are fetch and
then increment.
You can see the difference by examining the following lines of code:
int a = 5, b = 7, c = 9, d = 11;
a = ++b; // a = 8, b = 8
c = d++; // c = 11, d = 12
The first line initializes four int variables. The second line assigns to a the
result of the prefix increment operator on b. The prefix operator increments b
and then returns the value, and the incremented value is assigned to a. Since b
started out as 7, the incremented value is 8, and both a and b now have the
value of 8.
The third line assigns to c the result of calling the postfix operator on d. The

semantics here are to return the current value of d (11) and then to increment d
.
Thus, c has 11 and d has 12.
Logical Operators
C# supports three logical operators:
&& (Logical AND) evaluates true if both operand expressions are
true.
|| (Logical OR) evaluates true if either operand expression is true.
! (Logical NOT) evaluates true if the operand expression is false.
One way to understand these operators is to examine their impact given a test
case. If you assume that you have two integer variables, x and y, and that x has

the value 5 and y has the value 12 then the following code will evaluate false,
because while x does equal 5, y does not equal 7 and for the logical AND to
evaluate true, both operands must be true.
Operators
4-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
(x == 5) && (y == 7)
On the other hand, the following expression does evaluate true, because even
though y does not equal 7, x does equal 5 and logical OR evaluates true if
either operand is true. You can read this as x is 5 or y is 7 and the answer to
that is true.
(x==5) || (y == 7)
Finally, the following expression will evaluate true, precisely because x is not

equal to 3. That is, the inner expression x == 3 evaluates false, and so the
entire expression !(x==3) evaluates true. It is as if you said, It is false that
x is
equal to 3. That statement is true, it is false that x is 3.
!(x == 3)
Short Circuit Evaluation
The logical operators are typically used in tests, often in if statements, so yo
u
might see code like the following:
if ( (x == 3) && (y == 12) )
The body of the if statement will be executed only if the logical statement
evaluates true, which in this case it will do only if x is equal to three and y
is
equal to 12.
It turns out that the compiler may not need to evaluate the right side of a
logical expression if the answer can be determined from the left side alone. For

example, in a logical AND statement (&&) there is no need to evaluate the
right side of the expression if the left side is false. Since they must both be
true, if the left is false then the entire statement is false regardless of the
value
on the right.
Similarly, in an OR statement, if the left statement is true, then it doesnt
matter whether the right statement is true or not, the logical OR condition is
satisfied if either side is true.
You can test short circuit evaluation by creating a couple of methods that
return Boolean values:
Operator Types
C# Professional Skills Development 4-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
static bool ReturnsTrue()
{
Console.WriteLine("In ReturnsTrue().");
return true;
}
static bool ReturnsFalse()
{
Console.WriteLine("In ReturnsFalse().");
return false;
}
The first of these, returnsTrue always returns the value true, and the second,
returnsFalse always returns false. You can now set up test conditions:
if (ReturnsTrue() || ReturnsFalse())
Console.WriteLine ("or statement worked!");
If you run this code, the call to ReturnsFalse will never be executed, as shown
in Figure 2. The first half of the OR statement evaluates true, and so the OR
condition is satisfied. There is no reason to call ReturnsFalse; it doesnt matter

whether it returns true or false; the expression is already true, and the if
statement executes.
Figure 2. Testing the OR with short circuit evaluation.
If you reverse the order of the test the second method must be tested:
//if (ReturnsTrue() || ReturnsFalse())
if (ReturnsFalse() || ReturnsTrue())
Console.WriteLine ("The || statement worked!");
Operators
4-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The first method runs and returns false. Now the OR statement must execute
the right side to see if it will return true. It does, so the OR statement is
satisfied, as shown in Figure 3.
Figure 3. Reversing the test.
Similarly, you can set up an AND statement that will take advantage of shortcirc
uit
evaluation:
if (ReturnsFalse() && ReturnsTrue())
Console.WriteLine ("The && statement worked!");
else
Console.WriteLine("Nope, the && statement failed.");
In this case, once the first method call, ReturnsFalse returns a value of false,

there is no point in checking the second method call. Whether or not it returns
true, the entire if statement has already failed its test, because the AND
statement must evaluate true for both operands. The result is that the else
clause is invoked, as shown in Figure 4.
Figure 4. Short circuit evaluation of the AND statement.
Operator Types
C# Professional Skills Development 4-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
To get a good handle on operators, youll create a simple console application.
1. Open a new console application, and name it Operators.
2. Set up a series of variables.
int intA, intB;
float floatA, floatB;
intA = 12;
intB= 7;
floatA = 12;
floatB= 7;
3. You are now ready to explore the arithmetic operators for addition,
subtraction, multiplication, and division.
Console.WriteLine("Arithmetic operators...");
Console.WriteLine("intA + intB: {0}", intA + intB);
Console.WriteLine("intA - intB: {0}", intA-intB);
Console.WriteLine("intA * intB: {0}", intA * intB);
Console.WriteLine("intA / intB: {0}", intA / intB);
Console.WriteLine("intA % intB: {0}", intA % intB);
Console.WriteLine(
"\nfloatA + floatB: {0}", floatA + floatB);
Console.WriteLine(
"floatA - floatB: {0}", floatA-floatB);
Console.WriteLine(
"floatA * floatB: {0}", floatA * floatB);
Console.WriteLine(
"floatA / floatB: {0}", floatA / floatB);
Console.WriteLine(
"floatA % floatB: {0}", floatA % floatB);
See operators.sln
Operators
4-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
4. Compile and run the program. The results are shown in Figure 5, but there
are a few things to note.
The console output for the first float variable begins with \n:
Console.WriteLine(
"\nfloatA + floatB: {0}", floatA + floatB);
The \n character sequence is an escape sequence that represents a new
line character. That is why there is an extra vertical space between the int
examples and the float examples.
Note also that the output is identical for addition, subtraction, and
multiplication, but the int example uses integer division (with truncation),
and the float shows the decimal value.
Figure 5. The arithmetic operators.
5. Add the following code to the program:
Operator Types
C# Professional Skills Development 4-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
int x = 5;
int z = 0;
Console.WriteLine("\n\nx: {0}", x);
z = ++x;
Console.WriteLine("int z = ++x; x: {0}, z: {1}", x, z);
Console.WriteLine("\n\nx: {0}", x);
z = x++;
Console.WriteLine("z = x++; x: {0}, z: {1}", x, z);
Console.WriteLine("\n\nx: {0}", x);
z = x--;
Console.WriteLine("z = x--; x: {0}, z: {1}", x, z);
Here you create two integer variables, x and z, initializing x to 5 and z to
0. You then apply the prefix increment operator to x and assign the value
to z, displaying the result. You then apply the postfix increment operator
and finally the postfix decrement operator.
6. To simplify the output, comment out the earlier code, and run the program.
The results are shown in Figure 6. You can see the differing impact of
prefix and postfix. In the first example, x is incremented before its value is
fetched, so both x and z have the incremented value of 6.
Figure 6. Increment and decrement.
In the second example, the value of x (6) is fetched and assigned to z, and
then x is incremented to 7.
Operators
4-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
In the third example, the value in x (7) is assigned to z, and then x is
decremented to 6.
7. Return to the code and comment out what you have so far, adding the
following:
int testValue = 7;
if (testValue == 6)
Console.WriteLine("testValue is 6!");
if (testValue != 6)
Console.WriteLine("testValue is not 6!");
if (testValue > 6)
Console.WriteLine("testValue is greater than 6");
if (testValue >= 6)
Console.WriteLine(
"testValue is equal to or greater than 6");
8. Compile and run this test code to see the impact of the relational operators.

The results are shown in Figure 7.
Figure 7. Relational operators.
The program begins by initializing x to the value 7. The first test fails
because testValue is not equal to 6 and so nothing is printed:
if (testValue == 6)
Console.WriteLine("testValue is 6!");
Operator Types
C# Professional Skills Development 4-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The second test succeeds, and the message is printed:
if (testValue != 6)
Console.WriteLine("testValue is not 6!");
The final two tests succeed as well; the value 7 is both greater than 6 and also

greater than or equal to 6.
Operators
4-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Operator Precedence
Operators are evaluated in a very strict order as established by the rules of
precedence. You may remember some of these rules from high school algebra.
For example, multiplication has a higher precedence than addition:
nt x = 8 + 5 * 3;
After this statement executes, x will have the value 23 rather than 39, because
the order of operations is 5*3 (15) plus 8 (23), rather than 8+5 (13) times 3
(39).
Every operator has its own precedence level, and operators with higher
precedence levels are invoked first.
Using Parentheses
You can change the order of precedence by using parentheses. For example:
int x = (8 + 5) * 3;
In this example, x evaluates to 39. The parentheses are evaluated first, and the

operation is 8 + 5 (13) times 3 or 39.
Using parentheses makes your program easier to read and maintain. You may
want to use parentheses even when they dont change the order of operations,
if only to document your intent.
Preprocessor
C# Professional Skills Development 4-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Preprocessor
Preprocessor directives are not, strictly speaking, operators, but they act as
special instructions to the C# compiler and precompiler. The preprocessor is an
operation that runs before your program is compiled.
Preprocessor directives allow you to provide special instructions to the
compiler, which is useful when you want to control what portions of your
program are compiled.
Preprocessor directives begin with the character #. This must be the very first
character on the line, and there must be no space between the # and the
directive name. For example, the directive might be:
#define
The #define directive defines an identifier within the preprocessor. You can
then test whether that identifier has been defined using #if and #endif. If the
test succeeds, the code within the if/endif block will be compiled; otherwise it

will not be compiled and will not be in the executable code.
#define Debug
#if Debug
// compile the code here
// only if debug is defined
#endif
You can also add an else clause to the if statement:
#define Debug
#if Debug
// compile the code here
// only if debug is defined
#else
// compile this code only if
// debug is NOT defined
#endif
Operators
4-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Finally, you can add elif clauses to create complex switch-like statements:
#define Debug
#if Debug
// compile the code here
// only if debug is defined
#elif Test
// compile if debug is not defined
// but test is
#else
// compile this code only if
// debug and test are both NOT defined
#endif
Typically, preprocessor directives are used when you want to mark a section of
code to be compiled only if an identifer exists. Note that the identifier does n
ot
have to have a specific value, it just has to be defined.
You might use preprocessor directives when you want to mark out an area of
code to be compiled only when debugging. You might also use it for
compiling different versions for different customers or platforms.
Preprocessor
C# Professional Skills Development 4-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
C# supports a wide variety of operators, including relational,
mathematical, and logical operators.
The mathematical operators act as you might expect, except that
integers perform integer division, truncating the result. You can find
the remainder in integer division by using the modulus (%) operator.
You can concatenate two strings using the addition operator (+).
C# provides both a prefix and a postfix increment (and decrement)
operator. The semantics of prefix are increment and then fetch, while
the semantics of post fix are fetch and then increment.
The Logical operators && and || can be short circuited.
All operators have precedence, but the order of evaluation can be
overridden by using parentheses.
Operators
4-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
(Review questions and answers on the following pages.)
Preprocessor
C# Professional Skills Development 4-21
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. What is a practical use for the modulus operator?
2. What is the difference between integer multiplication and multiplication
with floats?
3. What is the difference between using the prefix increment operator vs. the
postfix increment operator?
4. How can you change the order in which operators are evaluated?
5. What is the code to test if DEBUG has been defined for the preprocessor?
Operators
4-22 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. What is a practical use for the modulus operator?
The modulus operator may be used during loops to find intervals.
When you modulus a number with an exact multiple the result is
0. You can test for that condition to take action every nth
iteration.
2. What is the difference between integer multiplication and multiplication
with floats?
None, it was a trick question. Multiplication, addition, and
subtraction are identical; integer division however, is quite
different from division with floats.
3. What is the difference between using the prefix increment operator vs. the
postfix increment operator?
The prefix operator increments the value before it is assigned,
while the postfix operator assigns the original value and then
increments.
4. How can you change the order in which operators are evaluated?
Use parentheses. Parentheses have the highest precedence and
are evaluated first.
5. What is the code to test if DEBUG has been defined for the preprocessor?
#if Debug
//code here
#endif
Preprocessor
C# Professional Skills Development 4-23
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 4:
Operators
Lab 4:
Operators
4-24 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 4 Overview
In this lab youll learn how to use operators in your program
To complete this lab, youll need to work through two exercises:
Using Mathematical Operators
Comparing Values with the Comparison Operators
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-bystep

instructions.
Using Mathematical Operators
C# Professional Skills Development 4-25
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Using Mathematical Operators
Objective
In this exercise, youll use mathematical operators to manipulate values.
Things to Consider
What is the difference between division with integers and floats?
What does modulus do with integers and floats?
Step-by-Step Instructions
1. Open the project named Operators.sln in the Operators folder. This
project has been prepared for you.
2. Fill in the values for the WriteLine statements. For example, the value you
might fill in for the first WriteLine would be:
Console.WriteLine("firstInt + secondInt: {0}",
firstInt + secondInt);
3. Fill in the operator in the if statement, replacing the question mark. Your
goal is to have the if statement execute every fifth value.
4. Compile and run the program.
Lab 4:
Operators
4-26 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Comparing Values with the
Comparison Operators
Objective
In this exercise, youll work with the comparison operators.
Things to Consider
What is the difference between > and >=?
Step-by-Step Instructions
1. Open the program named Comparison Operators.sln in the Comparison
Operators folder.
2. This project has been created for you, but you must fill in the missing
operators. Replace the question marks with the appropriate operators. For
example, you might replace the first question mark with the equals (==)
operator:
if (comparisonValue ? 6)
Console.WriteLine("comparisonValue is 6!");
3. Be sure to add the decrement operator where the comment shows.
comparisonValue?; // decrement the comparison value
4. Compile and run the program, as shown in Figure 8.
Comparing Values with the Comparison Operators
C# Professional Skills Development 4-27
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 8. Running the comparison program.
Lab 4:
Operators
4-28 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Classes and Objects
C# Professional Skills Development 5-1
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Classes and
Objects
Objectives
Understand how to create new types.
Discover the distinction between classes and objects.
Use access modifiers.
Add methods to classes.
Add constructors to classes and overload constructors.
Use private fields to enforce data hiding.
Distinguish between reference and value types.
Pass by reference and use out parameters.
Understand non-deterministic finalization.
Use properties to support data hiding.
Classes and Objects
5-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Creating New Types
Traditional procedural programming languages provide a set of data types, just
as C# does. These include int, long, char, string, and so forth. The innovation
of object-oriented languages like C#, however, is that you can add your own
types to represent the things in your problem domain.
Problem Domain
The problem domain is the set of issues you are trying to model or solve in your

program. For commercial programs the problem domain is often a business area (su
ch
as sales records or marketing issues). The problem domain can be just about any
task
youve set yourself in programming; if you are working on a simulation of warfare,

then the battle characteristics, weaponry, and terrain will all be within your p
roblem
domain.
This ability to extend the language with new types is very powerful. You
create new types in C# with the class keyword. Classes have behavior and they
have state. You model the behavior using methods (sometimes called
functions) and you model the state with member variables (sometimes called
fields). You provide access to the class state through properties.
Class vs. Object
In C# you can draw a distinction between a class, which is the definition of a
new type, and an object, which is an instance of that type. For example, Dog is
a class (it describes the Dog type) but Fido, Milo, and Rover are all objects:
they are instances of the class Dog.
Similarly, Button is a class, but in Figure 1 the three buttons, Save, Cancel, a
nd
Delete are all objects; instances of class Button.
Figure 1. Three buttons.
Creating New Types
C# Professional Skills Development 5-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Declaring a Class
You declare a class with the class keyword. Here is the formal definition
syntax:
[attributes] [access modifiers]
class identifier [:base class]
[class body]
Attributes are discussed in the advanced portion of this course. Access
modifiers are discussed later in this chapter. The keyword class appears
followed by an identifier: the name of your class (e.g., Button or Dog). The
base class is discussed in a future chapter when you learn about inheritance.
The class body is the set of statements that define the members (methods,
fields, and properties) of the class.
Access Modifiers
The possible access modifiers for the class (and for members of the class) are:
public
private
protected
internal
protected internal
Public access means that the class can be seen by any method of any class.
Private access means that the class (or method) can only be seen by methods of
the class in which it is created. Classes can be nested, but private classes are
an
advanced topic taken up later in this course. Private methods and certainly
private fields are common.
Most fields and some methods are marked private to indicate that they are
visible only to methods of the class in which they are created. The most
common case is for methods and properties to be public and for fields to be
private.
Protected is just like private except that the protected member is visible to
classes derived from this one. Youll learn about protected access in the
chapter on inheritance.
Internal access is like private except that the internal members are visible to
any class in this assembly. The assembly is the basic unit of creating a
Classes and Objects
5-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
program. Assemblies are implemented as either executable programs or as dlls.
When you mark a class or method as internal you are saying that it should be
visible to the classes in this assembly, but not to classes in other assemblies.

Protected internal is the union of both internal and protected: that is the clas
s
(or method) is private except to derived classes and also classes in the current

assembly.
Class Members
C# Professional Skills Development 5-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Class Members
Classes consist of methods, fields, and properties. Properties are discussed in
a
subsequent chapter. Methods are responsible for implementing the behavior of
the class, and fields hold the state of the class.
Methods
A class methods implement the behavior of that class. All methods in C#
belong to one or another class. A method can take parameters, but that is not
required. A method may have any number of parameters, but each parameter
must declare its type. It is possible for a method to return a value, in which
case the type of the value returned must be declared.
// return int, take no parameters
public int myMethod();
// return double, take two parameters
// the first parameter is an int
// the second parameter is a string
public double SecondMethod(int p1, string p2);
If the method does not return a value, the return type is void.
// returns void (no return value)
// takes one parameter: an integer
public void ThirdMethod(int x);
Constructor
A constructor is a special method of a class, used to create a valid instance of

the class. The constructor must have the same name as the class itself.
Constructors have no return value, not even void. Their purpose is to create a
valid instance, and they are called by the compiler; your code never explicitly
calls a constructor.
For example, if you create an Employee class, you might give it a constructor
like the following:
Classes and Objects
5-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public class Employee
{
private int empAge;
private string empName;
public Employee(int age, string name)
{
empAge = age;
empName = name;
}
}
Here the constructor is passed two parameters, and it uses these parameters to
set private members of the class.
Default Constructor
A constructor may have no parameters at all, in which case it is referred to as
the default constructor. It turns out that if you create a class with no
constructor at all, the compiler will provide a default constructor for you, tha
t
does nothing. It is as if you wrote the following:
public Employee()
{
}
Notice that you are able to create your own default constructor, in which case
one will not be provided for you; if you create any constructors at all you will

not get a default constructor from the compiler. The default constructor that
you create may do work:
public Employee()
{
age = 35; // set a member
}
It is easy to be confused about the meaning of default a constructor. To be
clear, the default constructor is any constructor that takes no parameters,
whether you create it explicitly or it is provided by the compiler.
Class Members
C# Professional Skills Development 5-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The this Keyword
The this keyword refers to the current object. Within a constructor, the this
keyword refers to the object being created. It is common to name private
member variables with the same name as the parameters passed to the
constructor; in such a case you can use the this keyword to differentiate the
member variable from the parameter:
public class Employee
{
private int age;
private string name;
public Employee(int age, string name)
{
this.age = age;
this.name = name;
}
}
In this example, this.age refers to the member variable, while age refers to the

parameter. Similarly, this.name refers to the member variable, while name
refers to the parameter.
Every method has a hidden this parameter that is a reference to the current
object. You can use the this keyword in any method, though most of the time it
is not necessary. If you have a method that updates a member variable you do
not need the this keyword unless there is ambiguity:
public void SomeClassMethod(string name)
{
age = 57; // no ambiguity
this.name = name; // resolve ambiguity
}
In this example, SomeClassMethod is a method of the employee class. It can
access the age member variable without the this keyword, because there is no
ambiguity. However, when it tries to access the name member variable, it must
use the this keyword to differentiate the member from the parameter.
Classes and Objects
5-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Fields
Fields are often called member variables or even class variables. The purpose
of a field is to hold the state of the object. You declare a field much as you
would declare a local variable within a method:
private int age;
private string name;
The difference is that you need to declare the access level (see access levels,
above). Typically, member fields will be private. This allows for data hiding
and supports encapsulation of your class. This is covered later in this chapter
when Properties are discussed in detail.
Fields Can Be Initialized
It is possible to initialize a field within the definition of the class. For exa
mple,
if you want to ensure that the field yearsOfService is initialized to 1, you can

declare it as follows:
private int yearsOfService = 1;
If the yearsOfService is not otherwise set in the constructor, its value will be
1
when the object is created. This is an easy way to give objects default values
for their fields.
Instantiating Objects
An objects constructor is called when the object is instantiatedthat is, when
an object is created (an instance is made). You instantiate an object with the
new keyword.
Employee emp1 = new Employee(); // instantiate an Employee
This is a common programming idiom in C#:
Type identifier = new Type();
You can create a Dog object with the following:
Class Members
C# Professional Skills Development 5-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Dog milo = new Dog();
If your constructor takes parameters, you pass them inside the parentheses:
// instantiate an employee
// pass in the age and name
Employee emp1 = new Employee(25, Jim);
Once you have an instance, you may call methods on that object:
emp1.DisplayInfo(); // call a method
Try It Out!
Lets look at an example that instantiates a class using the new keyword.
1. Open a new console application project in Visual Studio .NET and name it
Employee.
2. Rename Class1 to Employee.
3. Instantiate an Employee object in the static method Main by adding the
following code to Main:
int age = 46;
string name = "John Doe";
Employee emp1 = new Employee();
emp1.SomeMethod(age, name);
4. Add the SomeMethod method to the Employee class. Copy the following
method after the Main method, but still within the Employee class:
See Employee.sln
Classes and Objects
5-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
// params act as local variables
public void SomeMethod(int paramOne, string paramTwo)
{
Console.WriteLine("Received 2 params: {0} and {1}",
paramOne, paramTwo);
Console.WriteLine("paramOne++ = {0}",paramOne);
}
5. Examine the program. The static method Main() instantiates an Employee
object, and then calls an instance method on that object. The method
invoked takes two parameters, and treats them as local variables,
incrementing one and then printing them to the console.
6. Compile and run the program.
Overloading the Constructor
You can create more than one constructor. This is known as overloading the
constructor and allows you to provide different options for instantiating the
object.
You can overload the constructor (or any method) by modifying the signature.
The signature is the name and parameter list. You may vary the number or the
types of the parameters (or both). You can have one constructor that takes a
single string parameter, another that takes an int (modifying the type), and a
third that takes two strings (modifying the number).
Try It Out!
Lets look at an example that instantiates a class using an overloaded
constructor.
1. Open a new console application project in Visual Studio .NET and name it
Employee2.
2. Rename Class1 to Employee.
3. Give the class the following member fields:
See Employee2.sln
Class Members
C# Professional Skills Development 5-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
private int age;
private string name;
private int yearsOfService = 1;
4. Create two constructors: a default constructor and one that takes two
parameters. In the default constructor, set the age and name to reasonable
default values:
public Employee()
{
age = 21;
this.name = "John Doe";
}
public Employee(int age, string name)
{
this.age = age;
this.name = name;
}
5. Add a method to display the employees name, age, and years of service:
public void DisplayEmployeeInfo()
{
Console.WriteLine(
"{0} is {1} years old",
name, age );
Console.WriteLine(
"{0} has worked here {1} year(s)",
name, yearsOfService);
}
6. You are now ready to instantiate a couple of Employee objects, using both
the default constructor and the overloaded second constructor that takes
two parameters:
Classes and Objects
5-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
static void Main(string[] args)
{
int age = 46;
string name = "Jesse Liberty";
Employee emp1 = new Employee(age, name);
emp1.DisplayEmployeeInfo();
Employee emp2 = new Employee();
emp2.DisplayEmployeeInfo();
}
7. Compile and run the program. The results should show that the first
Employee object was created with the parameters you passed in, while the
second Employee object used the default constructor, as shown in
Figure 2.
Figure 2. Running the overloaded constructors.
Static and Non-Static Methods
Class methods and members come in two flavors: static and non-static. Nonstatic
methods are often called instance methods. You invoke instance methods
on an object (an instance of the class) and you invoke static methods on the
class itself.
Static methods are a compromise on the C concept of global methods. C#
has no global methods, but it is convenient at times to be able to invoke a
method without having a particular instance of a class. Youve seen many
static methods at work already. When you call WriteLine() you invoke it not
on an instance of Console, but on the Console class itself:
Console.WriteLine(hello world);
Class Members
C# Professional Skills Development 5-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Notice that you do not have an instance of Console. You never wrote:
Console myConsole = new Console(); // not needed
myConsole.WriteLine(); // nope!
The WriteLine method is conceived to be a method of the class, and is
declared to be a static method. You can call the method on the class itself,
rather than on an object (instance) of the class.
Instance methods are methods of an object, and typically impact the object
itself. Your Employee class might declare a method to increment the salary
field:
public class Employee
{
private int salary; // field
public BumpSalary(int increment)
{
salary += increment;
}
}
The salary method takes an int value and increases the private member salary
by that amount. You invoke this method on an instance (not on the class). This
allows you to change the salary for a particular employee.
// create an employee, pass in age, name, initial salary
Employee joe = new Employee(50, Joe, 100);
joe.BumpSalary(50); // increase Joes salary
Static Main()
Every program must have a static Main() method in one of the classes in the
program. The Main() method is the entry point for the program; it is the
method called by the operating system.
Static methods can only call other static methods unless an instance is
provided or created.
Classes and Objects
5-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Static Fields
Like static methods, static fields belong to the class, rather than to an instan
ce
of the class. You can use static fields to keep track of data that is instance
independent. For example, it is common to use static member fields as a
counter to track how many instances of the class have been created.
You access static fields with static methods.
Try It Out!
The best way to see how to work with static fields is with an example.
1. Open a new console application project in Visual Studio .NET and name it
StaticMembers.
2. Rename Class1 to Employee.
3. Give the class two private members, one static and one not.
private int age;
static private int numEmployees = 0;
4. Create a constructor that initializes the instance variable. Have the
constructor update the static member to indicate that another object has
been created. Note that you must access the static member through the
class, rather than through the instance:
public Employee(int age)
{
this.age = age;
Employee.numEmployees++;
}
5. You access the static member numEmployees with a static method. Add
the following method to the class:
See Static
Members.sln
Class Members
C# Professional Skills Development 5-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public static int GetNumEmployees()
{
return numEmployees;
}
6. Add an instance method to display information about the employee, and
within that method, access the number of employees through the static
method:
public void DisplayEmployeeInfo()
{
Console.WriteLine(
"This employee is {0} years old",
age );
Console.WriteLine(
"{0} employees have been created",
Employee.GetNumEmployees());
}
7. Create a test method to instantiate Employees and display their
characteristics and the number of Employees created.
static void Main(string[] args)
{
int age = 46;
Employee emp1 = new Employee(age);
emp1.DisplayEmployeeInfo();
Employee emp2 = new Employee(35);
emp2.DisplayEmployeeInfo();
Employee emp3 = new Employee(21);
emp3.DisplayEmployeeInfo();
}
8. Compile and run the program. Note that the static member is able to track
the number of objects created, as shown in Figure 3.
Classes and Objects
5-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 3. Using static fields.
Value vs. Reference
C# Professional Skills Development 5-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Value vs. Reference
Like many other languages, C# differentiates between the intrinsic types (e.g.,
int, long, string) and the types you create (classes). C# also differentiates,
however, between value types and reference types.
When you instantiate an integer variable, its value is placed on the stack. The
variable is a named location on the stack. The stack location contains the
actual value of the integer.
Stack and Heap
The stack is an area of memory set aside for value types. The stack is of limite
d size,
and values are added to the stack as needed. Values are popped off the stack as th
ey
are destroyed.
The heap is an undifferentiated area of memory that is used for creating user-de
fined
types (classes). When an object is created on the heap a reference is returned,
and the
built-in garbage collector cleans objects off the heap when there are no longer
active
references to the object.
When you instantiate a class, however, the object is instantiated on the heap,
and what you actually have on the stack is a reference to that object.
House myHouse = new House();
The House object is created on the heap, and myHouse is actually a reference
to that object, as shown in Figure 4. myVar is an integer variable (a value type
)
whose value is on the stack. myHouse is a reference to an instance of a class (a

reference type) instantiated on the heap.
Classes and Objects
5-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 4. Classes are instantiated on the heap.
All intrinsic types (int, long, char) are value types. All classes are reference

types.
Passing Parameters
Parameters are passed to methods by value. This simple statement has
profound implications.
When an object is passed by value, a copy is made. When you pass an int, a
copy of the int is made and used in the method. When you pass an object,
however, what is made is a copy of the reference. The copy of the reference
still refers to the same object on the heap.
So, reference types are effectively passed by referencethat is, you still have
access to the original object on the heap, because the copy of the reference
refers to that same object.
Suppose you have a method, MethodOne that creates two variables: an int
(myInt) and a Cat object, (frisky) as shown in Figure 5.
Value vs. Reference
C# Professional Skills Development 5-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 5. Creating two objects.
In this example, frisky is a reference to the Cat object created on the heap. Th
at
cat object has two fields, secretVal whose value is 3 and Power whose value is
0.
Suppose you now call a method, Method2 passing in the integer variable and
the Cat object as shown in Figure 6.
Figure 6. Passing by reference and by value.
Both parameters are passed by value, and so a copy is made. The copy of
magicLevel cannot affect the value of myInt back in the calling method. The
Cat reference boots however, does refer to the same object as frisky and so
changes made to that object will be seen back in MethodOne.
You can change the cat frisky because you have a reference to the object to
which frisky refers. You simplify this statement by saying that you have a
reference to frisky, but that is just shorthand for having a reference to the
object to which frisky refers.
Classes and Objects
5-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
You cannot change the variable myInt in MethodOne however, because you do
not have a reference to it; you have a local copy of its value.
Try It Out!
You can prove to yourself that int values are passed by value by trying to write

a method to swap two values.
1. Open a new console application project in Visual Studio .NET and name it
Swap.
2. Rename Class1 to Swap.
3. Start by creating a method to swap two integers. This method takes two
parameters, left and right. It then creates a temporary variable, and assigns
the value in the parameter left to that temporary variable. It then assigns
the value in right to left, and the value in the temporary variable to right,
thus swapping the values. The method displays the value in left and right
both before and after the swap.
static void DoSwap(int left, int right)
{
int temp;
Console.WriteLine("Swap before. left: {0}, right: {1}",
left, right);
temp = left;
left = right;
right = temp;
Console.WriteLine(
"\nSwap after. left: {0}, right: {1}",
left, right);
}
4. Create a test of the DoSwap method in the Main() method. Pass in two
values, printing their value both before and after the swap.
See Swap.sln
Value vs. Reference
C# Professional Skills Development 5-21
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
static void Main()
{
int x = 5;
int y = 7;
Console.WriteLine(
"Main before. x: {0}, y: {1}",
x, y);
DoSwap(x, y);
Console.WriteLine(
"Main after. x: {0}, y: {1}",
x, y);
}
5. Compile and run the program. The results are shown in Figure 7. You can
see that the swap method shows that the values are swapped, but the values
are not swapped back in Main. This is consistent with the idea that the int
variables are passed by value.
Figure 7. Running swap with value types.
Passing by Reference
In this case, youd like the int to be passed by reference. You can accomplish
this in C# by adding the ref keyword both to the method and to the invocation
of the method.
Classes and Objects
5-22 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
To see the ref keyword at work, youll return to the previous example, but
make a couple of simple changes:
1. Modify the DoSwap method, changing the parameters to add the ref
keyword:
static void DoSwap(ref int left, ref int right)
2. Modify the invocation of DoSwap, adding the ref keyword:
DoSwap(ref x, ref y);
3. Compile and run this program. As you can see, this makes all the
difference; the values are changed back in Main, as shown in Figure 8.
Figure 8. Using the ref keyword.
The keyword ref instructs the compiler to treat the parameter as passed by
reference. A copy is not made; a reference to the original value is passed.
The out Keyword
At times, you will pass parameters to a method so as to retrieve multiple values

out of that method. Normally, methods can return only one value (the return
value), but by passing in parameters by reference and manipulating those
values, you can extract multiple values out of a method.
You can accomplish this by passing in values by ref, using the ref keyword.
The problem is that you must initialize the values, because C# requires definite

assignment. You cannot just declare a variable and then pass it by ref without
See Swap.sln
Value vs. Reference
C# Professional Skills Development 5-23
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
first initializing it. If, however, the entire reason you created the variable w
as
to use it to retrieve values from the method, then the value you initialize it w
ith
will be meaningless, and it is annoying (and misleading) to initialize the
variable with a meaningless value. If your code does not make sense then it
is hard to maintain, and when you fill in dummy values, you make the code
harder to understand.
To solve this problem, C# offers the out keyword. When you are passing
values to a method by reference only to extract a value set within the method,
you can use the out keyword rather than the ref keyword. Out and ref are
identical in every way except this: variables passed with the out keyword do
not need to have an initial valid value. You can declare a variable and pass it
using out without initializing the value.
Lets look at an example that illustrates how to use the out keyword. In this
program you create a class Employee, and give that class three private member
variables: baseLevel, bonusLevel, and profitSharingLevel.
public class Employee
{
private int baseLevel;
private int bonusLevel;
private int profitSharingLevel;
The constructor for this class takes values for the three private member
variables, and sets them accordingly:
public Employee(
int baseLevel,
int bonusLevel,
int profitSharingLevel)
{
this.baseLevel = baseLevel;
this.bonusLevel = bonusLevel;
this.profitSharingLevel = profitSharingLevel;
}
You can imagine that this class would have methods that affect these values by
interacting with other systems, databases, and perhaps data available over the
Web. The example uses a very simple method named WorkMethod that stands
in for that work.
See PassByRef.sln
Classes and Objects
5-24 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public void WorkMethod()
{
// get some data from the database
// do some calculations
baseLevel++;
bonusLevel *= 4;
profitSharingLevel = 7;
}
The method that illustrates getting a value by reference is getLevels. Again,
this stands in for a method that might set values based on database tables or
other computation. In this case, youll compute the new values based on the
member variables and constants hard wired in to keep the program simple:
public int GetLevels(
out int extendedBase,
out int extendedProfitSharingLevel)
{
extendedBase = baseLevel + bonusLevel;
extendedProfitSharingLevel =
baseLevel * profitSharingLevel + 327;
// show what you'll return!
Console.WriteLine (
"returning these values: {0}, {1}, {2}",
extendedBase, extendedProfitSharingLevel,
baseLevel + bonusLevel + profitSharingLevel);
return this.baseLevel + this.bonusLevel +
this.profitSharingLevel;
}
This method takes two parameters by reference, marked with the keyword out:
extendedBase and extendedProfitSharingLevel. These parameters are not used
in the method at all except as (effectively) return values. The incoming values
are completely ignored; allowing you to use the out keyword.
The extendedbase value is set based on two of the class fields:
Value vs. Reference
C# Professional Skills Development 5-25
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
extendedBase = baseLevel + bonusLevel;
The second parameter is set based on the member field baseLevel multiplied
by the member field profitSharingLevel plus the addition of a constant 327.
extendedProfitSharingLevel =
baseLevel * profitSharingLevel + 327;
Both of these parameters act as a mechanism to extract these new values from
the method. Finally, the method returns a value based on summing the three
member variables:
return this.baseLevel + this.bonusLevel +
this.profitSharingLevel;
When the GetLevels method is invoked, you must pass in two values. Because
of the out keyword, you need not initialize these variables:
static void Main(string[] args)
{
Employee fred = new Employee(10,20,30);
fred.WorkMethod();
int newExtendedBase;
int newExtendedProfitSharing;
int newTotal =
fred.GetLevels(
out newExtendedBase,
out newExtendedProfitSharing
);
Console.WriteLine (
"new total: {0}", newTotal);
Console.WriteLine (
"new base: {0}, newProfit: {1}",
newExtendedBase, newExtendedProfitSharing);
}
Classes and Objects
5-26 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
You can see that newExtendedBase and newExtendedProfitSharing are never
initialized, and no value is assigned to them before they are passed as
parameters to GetLevels.
Compile and run this program. The results show that the values created in
GetLevels are available to the variables back in Main, as shown in Figure 9.
Figure 9. Using the out keyword.
If you change the out keyword in the method call and the method itself back to
ref, you will get a compile error, as shown in Figure 10. Weve circled and
highlighted the change to ref in the calling method as well as in the GetLevels
method, and circled and highlighted the error received by the compiler, shown
in the bottom (Task List) window.
Value vs. Reference
C# Professional Skills Development 5-27
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 10. A compile error using the ref keyword.
Properties
As discussed earlier, private member variables are normally kept private. This
supports data hiding: the idea that the internal state of your class should not
be
visible to your clients. If you hide the details of class state, you can change
how you manage that state without breaking your clients.
Classes and Objects
5-28 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
TIP: A client is any other class that interacts with your class. If a
change to your program causes code in the client to stop
working, you are said to have broken the client. It is a goal of
object-oriented programming that you can change your class
without breaking the clients of your class.
Heres why data hiding helps. Suppose you keep the employees age as a field
in your Employee class. If you make that field public, other classes may access
the Employees age directly.
You might decide to get rid of the age field, and compute the Employees age
based on the Employees birth date. If the age field was public, making this
change will break those clients that are currently using this field.
As the designer of the class, you want your clients to access the age field
through a method. Today that method might just return the value of the
member field, tomorrow it might compute the age. If your clients are calling
the method, they will not break when you change the method; they expect to
get back an age, and they will get one.
The problem is that your clients really want to interact with the age as a field
,
not as a method. It is cleaner to write,
currentAge = myEmployee.age;
rather than
currentAge = myEmployee.GetAge();
It isnt a big deal, but writing cleaner code makes it easier to maintain and
more reliable. This dilemmathat the designer of a class wants to present data
through methods, and the client of the class wants to access data as a fieldis
solved by properties.
Declaring a Property
A property looks to the designer of the class just like a method, but it looks t
o
the client of the class just like a field. The designer is free to change the
implementation without breaking the client (the implementing class would
need to be recompiled, but the client class would not).
Property statements have two parts: a get part for retrieving the value, and a s
et
part for setting the value. The set part has an implicit parameter: value, which

is the value being set.
Value vs. Reference
C# Professional Skills Development 5-29
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
For example, you can add an Age property to the Employee class, to provide
public access to the age value:
public class Employee
{
private int age; // private member variable
public int Age // property note capital A
{
get
{
return age; // can change later to compute
}
set
{
age = value; // use implicit parameter
}
}
}
To the designer, the property Age looks just like a method. The designer can
change the get accessor to compute the age and then recompile without
breaking any clients accessing this property:
get
{
ComputeAge(birthDate); // change how you get age
}
To the client, however, this property looks just like a field:
Employee emp = new Employee(5); // initialize employee
int currentAge = emp.Age; // access property
//
emp.Age = 27; // set property
The value assigned to the property (27) is passed into the set accessor as the
value implicit parameter.
Classes and Objects
5-30 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Read-Only and Write-Only Properties
You can create a read-only parameter by not creating a set accessor. Similarly,
you can create a write-only parameter by creating a set, but not a get accessor.

Try It Out!
The best way to see how properties are used is to create a small test program.
1. Open a new console application project in Visual Studio .NET and name it
Properties.
2. Rename Class1 to Tester.
3. Create an Employee class with two private member variables.
public class Employee
{
private int baseLevel;
private string name;
4. Provide a constructor that sets these two fields:
public Employee(string name, int baseLevel)
{
this.name = name;
this.baseLevel = baseLevel;
}
5. Create properties to match the two private member variables.
See Properties.sln
Value vs. Reference
C# Professional Skills Development 5-31
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public int BaseLevel
{
get
{
return baseLevel;
}
set
{
baseLevel = value;
}
}
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
6. You are now able to access the state of the Employee class through the
properties youve created. Instantiate a couple of Employee objects in
Main().
static void Main()
{
Employee fred =
new Employee("Fred", 2);
Employee joe =
new Employee("Joe", 5);
7. You can access the state of the fred instance through the get properties.
Classes and Objects
5-32 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Console.WriteLine(
"{0}'s base: {1}",
fred.Name,fred.BaseLevel);
8. Similarly you can set a value using the set accessor.
joe.BaseLevel = 12;
// use get to see the new values
Console.WriteLine(
"{0}'s base: {1}",
joe.Name,joe.BaseLevel);
9. To really see the accessors at work, set breakpoints in the get and set
accessors for the two properties, and set a breakpoint in the first WriteLine
in Main(). Run the application to the first breakpoint (WriteLine) and step
into the get method, as shown in Figure 11. You can see that you have
stepped into the get accessor for Name just by using the Name property in
a place that would retrieve the value.
Value vs. Reference
C# Professional Skills Development 5-33
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 11. Stepping into the accessor.
10. Continue stepping through the code until you set the new value for
baseLevel. Step into the accessor, and you can see that the set accessor is
automatically called when you are assigning a new value.
Classes and Objects
5-34 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Destroying Objects
One of the most powerful innovations in C# and .NET is the availability of
garbage collection. This means that, unlike with C and C++, in C# you do not
need to destroy objects when you are done with them; the CLR will clean up
after you.
Managing Resources
If your program works only with managed resources, you do not have to do
anything at all to ensure that your objects are destroyed. The garbage
collection is automatic. If you create an object, it will be destroyed as soon a
s
there are no references to that object; that is, as soon as it is no longer need
ed.
If you do manage unmanaged resources, you will need to ensure that they are
destroyed. You do this by providing a finalize method. Only release the
resources your object holds, and only implement this if you have unmanaged
resources. Even then, you never invoke finalize directly; it is called for you b
y
the garbage collector.
Non-Deterministic Finalization
Since you never call finalize yourself, you can never be certain exactly when
your object will be destroyed. This is generally not a problem, but if you
manage scarce resources (such as files or connections to a database) you may
need to ask your client to help you close those resources.
The idiom for doing so is not to call finalize directly, but rather to implement

the IDispose interface. Interfaces are covered in a later chapter, but to
implement IDispose you have only to implement the Dispose method. The
interface simply dictates the return type, name, and parameters of the Dispose
method.
void Dispose()
Your client classes call Dispose, and that is your opportunity to shut down and
destroy the precious resources you manage.
Some classes will not want a Dispose method; theyll want a close method. For
example, if your class represents a file, your client will expect to close the f
ile
rather than dispose it. No problem, just create a public method close and have
that call your private method Dispose.
Destroying Objects
C# Professional Skills Development 5-35
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The Using Idiom
It is easy for your clients to forget to call Dispose on your class. To facilita
te
this, C# supports a using statement. Heres an example of its use:
using (Font theFont = new Font(Ariel, 10.0f))
{
// use the font here
} // the compiler calls Dispose on the font
You create the resource within the parentheses and use the object within the
braces. When the closing brace is reached, the Dispose method is called on the
resource.
Classes and Objects
5-36 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
C# provides the ability to extend its intrinsic types with user-defined
types.
You define a type with the keyword class.
Instances of classes are called objects.
You control access to classes and their members with access modifiers
such as public, private, protected, and internal.
Classes consist of methods, member fields, and properties.
The constructor is a special method responsible for creating a valid
instance of the class.
The this keyword refers to the current object.
All methods may be overloaded by changing their signature.
Static methods belong to the class rather than to an instance.
Intrinsic types are value types; user-defined types are reference types.
You can pass a value type by reference using either the ref or the out
keywords.
C# will provide garbage collection, but destruction of objects is nondeterminist
ic.
Destroying Objects
C# Professional Skills Development 5-37
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. What is the difference between a class and an object?
2. What is the difference between private and public access?
3. What is the this keyword and how is it used?
4. What is a default constructor?
5. What does it mean to overload a constructor?
6. What is the difference between a field and a property?
7. What is the difference between a static method and an instance method?
8. What is the difference between a value type and a reference type?
9. What is the difference between pass by reference and pass by value?
10. What is the difference between the ref keyword and the out keyword?
Classes and Objects
5-38 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. What is the difference between a class and an object?
A class defines a new type, an object is an instance of that type
2. What is the difference between private and public access?
Public members of a class are accessible from any method of any
class. Private members are accessible only from methods of their
own class.
3. What is the this keyword and how is it used?
The keyword this refers to the current object. It is provided as a
hidden argument to all instance methods, and can be used at any
time to refer to the current object. It is often used to distinguish
between member fields and parameters.
4. What is a default constructor?
A default constructor is any constructor that takes no arguments.
5. What does it mean to overload a constructor?
When you overload a constructor (or any method) you provide
more than one constructor with the same name, but with a
different number (or type) of parameters.
6. What is the difference between a field and a property?
A field is a simple data value. A property appears to a client as a
field, but to the implementor as a method, and may access the
value either from a field, or from a database (or file, etc.) or it
may compute the value.
7. What is the difference between a static method and an instance method?
A static method is a method of the class, and does not have a this
pointer. An instance method is a method of an object, and refers
to the instance data of that object.
8. What is the difference between a value type and a reference type?
Value types are created on the stack (all the intrinsic types are
value types). Reference types are created on the heap and a
reference to the object is held on the stack. All user-created
types (classes) are reference types.
9. What is the difference between pass by reference and pass by value?
When you pass an argument to a method it is passed by value,
and a copy is made. If you use the ref (or out) keyword, the
argument is passed by reference, and no copy is made; the
parameter acts as a reference to the original value.
10. What is the difference between the ref keyword and the out keyword?
These two keywords work identically, except that the variable
used as an out parameter need not be initialized.
Destroying Objects
C# Professional Skills Development 5-39
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 5:
Classes and
Objects
Lab 5:
Classes and Objects
5-40 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 5 Overview
In this lab youll learn to overload constructors and to pass values by
reference.
To complete this lab, youll need to work through two exercises:
Overload a Constructor
Pass a Value by Reference
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-bystep

instructions.
Overload a Constructor
C# Professional Skills Development 5-41
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Overload a Constructor
Objective
In this exercise, youll overload a constructor. You are provided with a default
constructor and youll add a second constructor that takes two parameters.
Things to Consider
Why would you need two constructors for one object?
How can overloaded constructors help create optional parameters?
Step-by-Step Instructions
1. Open OverloadedConstructor.sln in the OverloadedConstructor folder.
2. Add a constructor that takes a parameter for the age and a second
parameter for the name.
public Cat(int age, string name)
{
this.age = age;
this.name = name;
}
3. Add test code in Main, to test this constructor.
int age = 4;
string name = "Frisky";
Cat frisky = new Cat(age, name);
frisky.DisplayCatInfo();
4. Build and run the program. It should look like Figure 12.
Lab 5:
Classes and Objects
5-42 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 12. Final exercise results.
Pass a Value by Reference
C# Professional Skills Development 5-43
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Pass a Value by Reference
Objective
In this exercise, youll create a program to demonstrate that intrinsic types are
passed by value by default and that classes are passed by reference. First, you
will finish the Cat class already started for you. You will then create a Swap
method that you will overload three times: once to take integers by value, once
to take integers by reference, and once to take Cat objects.
Things to Consider
How do you demonstrate that a value has been passed by reference?
How can you cause value types to be passed by reference?
What does it mean to pass an object by reference? What can you do
with that object?
Step-by-Step Instructions
1. Open PassByRef.sln in the PassByRef folder.
2. Fill in the constructor.
public Cat(int age)
{
this.age = age;
}
3. Create an Age property for the Cat object.
Lab 5:
Classes and Objects
5-44 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public int Age
{
get { return age; }
set
{
age = value;
}
}
4. Fill in the parameters for the Swap method that takes two integers by
value.
public void Swap(int first, int second
{
int temp = first;
first = second;
second = temp;
}
5. Fill in the parameters for the Swap method that takes two integers by
reference and fill in the method.
public void Swap(ref int first, ref int second)
{
int temp = first;
first = second;
second = temp;
}
6. Create the method for the Swap that takes two Cat objects.
public void Swap(Cat c1, Cat c2)
{
int temp = c1.Age;
c1.Age = c2.Age;
c2.Age = temp;
}
Pass a Value by Reference
C# Professional Skills Development 5-45
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
7. Invoke the three Swap methods in Run.
public void Run()
{
int valOne = 5;
int valTwo = 10;
Console.WriteLine("Pass by value...");
Console.WriteLine("ValOne: {0}, ValTwo: {1}",
valOne, valTwo);
Swap(valOne, valTwo);
Console.WriteLine("ValOne: {0}, ValTwo: {1}",
valOne, valTwo);
Console.WriteLine("\nPass by reference...");
Console.WriteLine("ValOne: {0}, ValTwo: {1}",
valOne, valTwo);
Swap(ref valOne, ref valTwo);
Console.WriteLine("ValOne: {0}, ValTwo: {1}",
valOne, valTwo);
Console.WriteLine("\nPass a user defined type...");
Cat boots = new Cat(2);
Cat frisky = new Cat(3);
Console.WriteLine("boots is {0} while frisky is {1}",
boots.Age, frisky.Age);
Swap(boots,frisky);
Console.WriteLine("boots is {0} while frisky is {1}",
boots.Age, frisky.Age);
}
8. Build and run the program. The output should look like Figure 13.
Lab 5:
Classes and Objects
5-46 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 13. The Pass by Reference exercise results.
Inheritance
C# Professional Skills Development 6-1
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Inheritance
Objectives
Use inheritance to implement specialization.
Chain up to base classes to implement reuse.
Use virtual functions to implement polymorphism.
Box and unbox intrinsic types to treat them as objects.
Inheritance
6-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Creating Hierarchies
It is a human characteristic to organize the things in the world into hierarchie
s.
Give us a random collection of objects and well start categorizing them. In
fact, this is a great party game: ask a person to empty his pockets. Then tell
that person to create three piles. Give no further advice, and refuse all
questions. Nearly everyone will, in fact, make three piles, organizing their
stuff into categories of their own choosing.
Categorization is the first step towards creating hierarchies. You see
hierarchies everywhere. The most familiar, of course, is the taxonomy of life
used by scientists: Kingdom, Phyla, Class, Order, Family, Genus, Species.
This is just a formal hierarchical organization of all the types of life.
Every hierarchy represents generalization and specialization. The taxonomy
that classifies life notes that some types of life share certain characteristics

(generalization) and that each sub-type specializes the type above it, as shown
in Figure 1.
Figure 1. Animal hierarchy.
The Gerbil, Cow, Dog, Horse, and Cat types all share certain characteristics
(they suckle their young, who are born live, and they have hair). These are
characteristics generalized in the Mammal class, and you know that all
Mammals share these characteristics.
Creating Hierarchies
C# Professional Skills Development 6-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
As you move down the hierarchy, you see greater specialization. A dog is a
special kind of mammal (one that whines, barks, begs for food) as is a cat (for
example, a cat is a mammal that is totally indifferent to human approval).
Continuing down the specialization hierarchy you find retrievers (dopey
animals eager to chase tennis balls) and terriers (yappy animals eager to chase
retrievers).
You can see the same kind of generalization/specialization in any welldesigned
hierarchy. For example, Windows, and many other windowing
systems, treat all the controls (widgets) that you might use on a form or in a
Graphical User Interface as though they are all types of windows, as shown in
Figure 2.
Figure 2. Window hierarchy.
In this example, Text, Button, and List share characteristics that are
generalized in Control. These characteristics might include the ability to be
used on a form, or to raise certain kinds of events. Similarly, controls, dialog

boxes, and documents share characteristics such as being drawable and having
a location; these commonalities are generalized in Window.
The Button type is specialized in command buttons and check boxes. These
two types are both buttons, but they behave differently when clicked (the
command button fires an event, the check box is checked or unchecked and
has its state changed).
Inheritance
6-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The key point is that as you move up the hierarchy you see greater
generalization, and as you move down the hierarchy, you see greater
specialization, as shown in Figure 3.
Figure 3. Specialization and generalization.
Implementing Specialization
In C#, specialization is implemented with inheritance. When you specialize a
class you create a new class based on the first. The first class is called the b
ase
class (e.g., Control), while the specialized class (Button) is called the derive
d
class. Button derives from Control; Control is the base class of Button. The
syntax for inheritance is:
public class ListBox : Window
The colon separates the new class (on the left) from its base class (on the
right). In this example, ListBox derives from (inherits from/specializes)
Window, and Window is the base class for ListBox.
A derived class inherits all the members of the base, and can have its own
members (fields, properties, methods) as well. In addition, the derived class
must have its own constructor.
Creating Hierarchies
C# Professional Skills Development 6-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
TIP: The purpose of a derived class is to specialize (extend) the base class.
For example, you might have a class Person, with a field age, as shown in the
following code and illustrated in Figure 4.
public class Person
{
private int age;
Person(int age)
{
this.age = age;
}
}
Figure 4. A base class.
Notice that the Person constructor sets the age member.
You can derive a new class from Person, specializing the characteristics of a
person as an Employee. An Employee is a Person, but it is a special kind of
person, who works for a firm. The Employee will have an age, but will add
new fields, such as baseLevel and bonusLevel. This is shown in the following
code, as well as illustrated in Figure 5.
Inheritance
6-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 5. Deriving Employee from Person.
Chaining Up to Base
It is the job of the Employee constructor to set its member variables, baseLevel

and bonusLevel, but how will Persons age field be set? The answer is to
initialize or chain up to the base class constructor, using the keyword base.
public Employee : Person
{
public Employee(age, baseLevel, bonusLevel) :
base(age)
{
this.baseLevel = baseLevel;
this.bonusLevel = bonusLevel;
}
}
You chain up to the base by placing a colon after the arguments, and then
invoking the base constructor with the keyword base, followed by the
arguments for the base constructor in parentheses.
Creating Hierarchies
C# Professional Skills Development 6-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Inheritance Implements is-a
In C#, inheritance is used to implement the is-a relationship; that is the
relationship of specialization. A Dog is-a Mammal, a ListBox is a Window,
and so forth.
This relationship is distinguished from the has a relationship that is maintaine
d
by having one class have a reference to an object of another type, and the
composed of relationship that is created by member fields.
So, a List-Box is-a window. It might have a document that it is connected to,
and it might be composed of an array of strings.
Polymorphism
You can treat a derived type as if it were an instance of its base type. This is

known as polymorphism. Polymorphism comes from the root parts poly
indicating many and morph indicating forms. A polymorphic object can have
many forms.
For example, you can treat list boxes, buttons, drop downs, and text fields all
as if they were controls. You can place them on a form and tell each to draw
itself. Each of these objects implements the Draw method differently (a list
box draws its list, a button draws a rectangle with its text inside, and so fort
h).
You are treating control (the base class) polymorphicallyin all its forms.
In C#, polymorphism is implemented with the keyword virtual. You add this
keyword to a method to indicate that you expect it to be overridden in the
derived classthat is, you expect the derived class to have a method with the
same name (e.g., Draw()) that acts somewhat differently than that method in
the base class.
You indicate your intention to override the method in the derived class by
explicitly marking the method with the keyword override.
Implementing Polymorphism
To see how inheritance and polymorphism work, youll create a simple
console application that demonstrates treating objects polymorphically. To get
started, youll need a base class: Employee.
The purpose of the employee class is to represent any employee in your
corporation. For this demonstration youll strip down the employee class and
provide it only with three fields:
Inheritance
6-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public class Employee
{
protected string name;
protected int bonusLevel;
protected int profit = 0;
The constructor will initialize the two fields that do not have an initializatio
n
value:
public Employee (string name, int bonusLevel)
{
this.name=name;
this.bonusLevel = bonusLevel;
}
Employee will create two virtual methods. By declaring these methods as
virtual, you express your expectation that they will be overridden in derived
classes. For example, the first method is TakeProfits. You expect that
specialized kinds of Employees might use a different formula to take profits
for the year, but this base method will provide a default way of computing an
employees profits. In a real application this would probably involve working
with a database and computing complex formulas; for this example youll just
multiply the bonusLevel by a constant:
public virtual void TakeProfits()
{
profit += (bonusLevel * 500);
Console.WriteLine("{0} profit this year: {1}",
name, profit);
}
The second virtual method simulates booking a trip on an airline. For this to
work youll need a TravelAgent class. TravelAgent will have only a single
method, BookFlight that will take a constant indicating the type of seating the
Employee wants (coach, business class or first class). To indicate this,
TravelAgent will have an enumeration:
Creating Hierarchies
C# Professional Skills Development 6-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
class TravelAgent
{
// an enum scoped to TravelAgent
public enum FlightType
{
coach,
businessClass,
firstClass
}
The method BookFlight takes two parameters, a value of the enumerated
FlightType and a string indicating the passengers name. Again, in a real
application this method would actually book the flight; for now youll just
display the choice to the console:
public void BookFlight(FlightType type, string name)
{
string output = " will be flying ";
switch (type)
{
case FlightType.coach:
output += "coach";
break;
case FlightType.businessClass:
output += "business class";
break;
case FlightType.firstClass:
output += "first class";
break;
}
Console.WriteLine(name + output);
}
The employee method PlanBizTrip can now instantiate a TravelAgent and
book a flight:
Inheritance
6-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public virtual void PlanBizTrip()
{
TravelAgent agent = new TravelAgent();
agent.BookFlight(TravelAgent.FlightType.coach, name);
}
Deriving a New Class
The Manager class is a specialized form of Employee, one that represents
managers within the company. Managers have a manager level:
public class Manager : Employee
{
int managerLevel = 1;
While the manager level is initialized to 1, it can be changed in the constructo
r.
Note that the constructor for a manager must take all the parameters expected
for an Employee so that it can initialize its base class:
public Manager(
string name,
int bonusLevel,
int managerLevel):
base(name,bonusLevel) // chain up
{
this.managerLevel = managerLevel;
}
You can see here that the first two parameters are passed to the constructor for

the base class (Employee), while the third parameter (managerLevel) is used to
initialize the member field.
The Manager class inherits both the virtual methods from Employee. As the
author of the Manager class you are free to override the behavior of the
Employee class, if you wish to specialize the behavior in your derived class.
Begin by overriding the virtual method PlanBizTrip.
To do so, you must match the signature (name and parameters) of
Employee.PlanBizTrip, but rather than writing virtual you will write override.
Creating Hierarchies
C# Professional Skills Development 6-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The body of the method may be entirely different than the body in the base
class. In this case, the Manager will fly first class:
public override void PlanBizTrip()
{
TravelAgent agent = new TravelAgent();
agent.BookFlight(
TravelAgent.FlightType.firstClass,
name);
}
It is possible that you will want to invoke the base class method from within
your override. You do this with the keyword base, as shown in the override of
TakeProfits. The manager will increase his profit by multiplying the
managerLevel value (not available in the Employee class) by a constant, and
then invoking the base method to reuse the work done there:
public override void TakeProfits()
{
profit += managerLevel * 750;
base.TakeProfits();
}
Treating Employees Polymorphically
You are now ready to treat references to Employee polymorphically, whether
they actually refer to an instance of the base class Employee or to the derived
class Manager.
To begin, create an instance of Employee and an instance of Manager:
public class Tester
{
public static void Main()
{
Employee e1 = new Employee("Fred", 2);
Manager m1 = new Manager("Joe", 3,5);
You can create an array of Employee objects, in which you store your two
objects.
Inheritance
6-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
NOTE Arrays are a standard ingredient in C# and in many programming
languages. Arrays are discussed in detail in a future chapter. You
can think of an array as a simple collection of objects that are all
of the same type. You access an object in the array using the index
operator. Indices start at 0 and count up, so the fourth item in an
array named myArray is accessed at myArray[3].
Notice that you may safely store a Manager in an array of Employees, because
a Manager is-an Employee. This is the essence of the inheritance relationship:
Employee[] EmpArray = new Employee[2];
EmpArray[0] = e1;
EmpArray[1] = m1;
You can now iterate through the array using a foreach loop. The foreach loop
extracts each employee in turn and assigns it to the reference e, which you can
then use to access properties and methods of the employee. In this case you
will invoke the two methods:
foreach (Employee e in EmpArray)
{
// treat all the members as employees
e.PlanBizTrip();
e.TakeProfits();
}
The output shows that the objects are treated polymorphically, as shown in
Figure 6. You can see that the Employee Fred uses the base methods, while the
Manager Joe uses the Manager methods. The client (Main) does not know or
care which is a Manager, as far as the client is concerned it has a collection o
f
Employees. It calls the virtual method and the right thing happens.
Creating Hierarchies
C# Professional Skills Development 6-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 6. Treating employees polymorphically.
Note that calling foreach in this way is exactly like calling the methods on
each indexed value explicitly, as illustrated by adding this block of code:
Console.WriteLine(
"\nAgain, using explicit array indexing...");
EmpArray[0].PlanBizTrip();
EmpArray[0].TakeProfits();
EmpArray[1].PlanBizTrip();
EmpArray[1].TakeProfits();
The result of adding this code is shown in Figure 7. The results are the same,
this time, however, you are invoking the methods directly, rather than through
the enumerator.
Figure 7. Accessing array members explicitly.
Inheritance
6-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The Object Class
In C# every class is considered to derive (ultimately) from Object. That is,
Object is the root of all class hierarchies. The object class has a number of
useful methods, some of the most important of which are shown in Table 1.
Method Description
Equals Are two objects the same?
ToString Return a string representation of the object
Finalize Clean up resources
Table 1. Important object class methods.
When a method expects an object to be passed as an argument, you are free to
pass in any type at all. Since all types derive from object, they can be treated

polymorphically. That is, if the method expects an object and you pass in an
Employee, that will work fine because an Employee is an object. What
happens, however, if you pass in an integer? It turns out that C# treats value
types as if they derive from object as well.
What actually happens, however, is that the integer is boxedthat is an
object is created that holds the value of the integer. Boxing happens
automatically. You do not have to take any action to create the boxed integer;
it is done for you by the CLR.
When you unbox an object back to its original type, you must explicitly cast
the object to the original type. If you box an int, when you unbox it you must
cast the object to an int. This is illustrated in Figure 8.
In Figure 8 the integer variable i has the value 123. When you pass variable i
to a method that expects an object, the integer is boxed. A new object is
created on the heap that contains the value of the integer, and a reference (o)
is
created.
Later, when you assign the value in o back to an integer you must explicitly
cast back to an int and the value is unboxed back to the integer j.
The Object Class
C# Professional Skills Development 6-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 8. Boxing and unboxing an integer.
The goal of boxing is to allow the intrinsic types to have the same behavior as
user-defined types (classes); that is, everything in C# is assumed to derive
from object.
Inheritance
6-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
Inheritance implements specialization.
Common features should be factored to base classes.
Reuse can be accomplished by chaining up to base class methods.
Virtual functions allow you to treat objects polymorphically.
When you override a virtual method you must use the keyword
override.
All classes derive from object.
Intrinsic types derive from object as well.
When you pass an intrinsic type to a method expecting an object, the
intrinsic type is implicitly boxed.
You must explicitly cast to unbox a boxed object.
The Object Class
C# Professional Skills Development 6-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. How do you implement specialization in C#?
2. What is the syntax for deriving class D from base class B?
3. What is the difference between private and protected access?
4. How do you mark a base class method to support polymorphism?
5. How do you mark a method in a derived class that overrides a virtual
method in the base class?
6. Which needs an explicit cast: boxing or unboxing?
Inheritance
6-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. How do you implement specialization in C#?
Specialization is implemented in C# using inheritance. To
accomplish this you derive the more specialized class from the
more general class.
2. What is the syntax for deriving class D from base class B?
To derive class D from base class B you write a colon to the right
of the derived class identifier, followed by the base class
identifier:
public class Derived : Base
3. What is the difference between private and protected access?
Private members are available only to the methods of the class in
which they are created, protected members are available to the
creating class and any class derived from the creating class.
4. How do you mark a base class method to support polymorphism?
Methods that will be used to support polymorphism are marked
virtual. This indicates that the method author expects the method
to be overridden in derived classes.
5. How do you mark a method in a derived class that overrides a virtual
method in the base class?
When you override a method in a derived class, you must use the
keyword override.
6. Which needs an explicit cast: boxing or unboxing?
Boxing is implicit, but when you unbox the value you must
explicitly cast it back to its original type.
The Object Class
C# Professional Skills Development 6-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 6:
Inheritance
Lab 6:
Inheritance
6-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 6 Overview
In this lab youll learn how to use polymorphism and inheritance.
To complete this lab, youll need to work through two exercises:
Implementing Polymorphism
Passing Objects as Parameters
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-bystep

instructions.
Implementing Polymorphism
C# Professional Skills Development 6-21
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Implementing Polymorphism
Objective
In this exercise, youll create a Student class and derive from that a more
specialized MathMajor class. You will create virtual methods in Student that
youll override in MathMajor. Weve created a Registrar class for you, which
you will want to review in the starter program. When you call BookClass, you
pass in your major and your name. The BookClass method assigns you to the
appropriate math class based on your major.
Things to Consider
Why derive one class from another?
What is the semantic implication of marking a method virtual?
Why would you override a virtual method?
Step-by-Step Instructions
1. Open the starter program named Polymorphism.sln in the Polymorphism
folder. The Student class is started for you. Implement the constructor to
assign the name, passed in as a parameter, to the member variable name.
public class Student
{
protected string name;
private int numCredits = 0;
public Student (string name)
{
this.name=name;
}
2. Implement the BookClasses method of Student. Delegate the work to the
BookClass method of the Registrar.
Lab 6:
Inheritance
6-22 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public virtual void BookClasses()
{
Registrar registrar = new Registrar();
registrar.BookClass(
Registrar.Major.LiberalArts, name);
}
3. Finish the AddCredits method. This method increments the numCredits
member variable by the number of credits passed in, and displays an
appropriate message. The second parameter describes the subject these
credits are for, and is used in the message displayed.
public virtual void AddCredits(
int numCredits, Registrar.Major subject)
{
this.numCredits = numCredits;
Console.WriteLine(
"{0} has added {1} credits in {2}",
name, numCredits, subject);
}
4. Implement a MathMajor class that derives from Student.
public class MathMajor : Student
{
5. Provide a member variable in MathMajor to keep track of how many
credits the Student is taking in mathematics. Call the member variable
numMathCredits.
int numMathCredits = 0;
6. Implement the constructor to take two parameters. The first parameter will
be the students name. The second will be the number of math credits the
student currently has on record.
Implementing Polymorphism
C# Professional Skills Development 6-23
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public MathMajor(
string name,
int numMathCredits):
base(name)
{
this.numMathCredits = numMathCredits;
}
7. Override the BookClasses method to pass Mathematics as the major.
public override void BookClasses()
{
Registrar r = new Registrar();
r.BookClass(Registrar.Major.Mathematics, name);
}
8. Override AddCredits to check if the subject is Mathematics, and if so,
increment this students numMathCredits value.
public override void AddCredits(
int credits, Registrar.Major subject)
{
base.AddCredits(credits, subject);
if ( subject == Registrar.Major.Mathematics )
{
numMathCredits += credits;
Console.WriteLine(
"Now registered for {0} math credits",
numMathCredits);
}
}
9. In the Run method of Tester create an instance of Student and an instance
of MathMajor and put them both in an array of Students.
Lab 6:
Inheritance
6-24 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Student s1 = new Student("Fred");
MathMajor m1 = new MathMajor("Joe",20);
Student[] StudentArray = new Student[2];
StudentArray[0] = s1;
StudentArray[1] = m1;
10. Iterate over the array, and call the two methods of Student.
foreach (Student e in StudentArray)
{
// treat all the members as employees
e.BookClasses();
e.AddCredits(3,Registrar.Major.Mathematics);
}
11. Compile, build, and run the program. Your output should look like
Figure 9.
Figure 9. Running the program.
Passing Objects as Parameters
C# Professional Skills Development 6-25
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Passing Objects as Parameters
Objective
In this exercise, youll create a method that takes an object as a parameter.
Youll then pass in two objects: one a user-defined type and the other an
intrinsic type.
Things to Consider
What does it mean that everything in C# derives from Object?
How can intrinsic types derive from Object?
Step-by-Step Instructions
1. Create a new console application.
2. Create a class, Cat. Do not explicitly derive this class from any other class
.
class Cat
{
3. Give the Cat class two member variables: age and name.
private int age;
private string name;
4. Create a constructor to initialize the age and name.
public Cat(int age, string name)
{
this.age = age;
this.name = name;
}
Lab 6:
Inheritance
6-26 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
5. Override the ToString method to return a string that includes the Cat
objects name and age.
public override string ToString()
{
return "I am " + name +
", and I am " + age +
" years old.";
}
6. Create a Tester class.
class Tester
{
7. Give Tester a DisplayObject method that takes an object as its parameter.
In this method, invoke ToString on the object passed in as a parameter.
public void DisplayObject(object o)
{
Console.WriteLine("Displaying object {0}",
o.ToString());
}
8. Create a Run method for Tester. In Run, create a Cat instance and pass it
to DisplayObject.
public void Run()
{
Cat frisky = new Cat(3,"Frisky");
DisplayObject(frisky);
9. In Run create an integer variable, initialize it and pass it to DisplayObject
.
int x = 25;
DisplayObject(x);
Passing Objects as Parameters
C# Professional Skills Development 6-27
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
10. Put a breakpoint on the first call to DisplayObject and step into the call.
Watch as your user-defined class (which was not explicitly derived from
Object) is passed to DisplayObject, as shown in Figure 10.
Figure 10. Stepping into the DisplayObject method.
11. Note the type of this is System.Object, but the true type of the object is
Cat.
12. Step through in the debugger and see the int passed to DisplayObject.
13. Your output should look like Figure 11.
Lab 6:
Inheritance
6-28 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 11. Output from displaying both objects.
Operator Overloading
C# Professional Skills Development 7-1
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Operator
Overloading
Objectives
Understand why you overload operators in user-defined classes.
Establish rules about when and how to overload operators.
Examine implicit and explicit type conversion.
Walk through a detailed example using operator overloading.
Operator Overloading
7-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Why Overload Operators
A principal goal in C# and object-oriented languages in general, is to allow
user-defined types (classes) to have all the functionality of intrinsic types.
There should be little difference in interacting with an Employee than in
interacting with an int.
One of the key features of the intrinsic types (int, double, char, etc.) is that
they
support the use of operators. So, you know that you can write:
int a, b = 5, c = 10;
a = b + c;
After these two lines of code run, the variable a will hold the value 15 (the su
m
of b and c). If you look closely at these lines of code, you will realize that t
wo
operators are being invoked. First the addition (+) operator is invoked. The
expression b + c evaluates to 15. Second, the assignment operator (=) is
invoked, assigning the value 15 to a.
For user-defined types to act like intrinsic types, they must be able to support

these operators when appropriate. For many types, operator overloading is not
appropriate (what does it mean to add two Employees?) but for other classes,
operator overloading can greatly simplify your code. Imagine, for example,
that youve created a class to represent Roman numerals:
class Roman
{
//
}
You would like to be able to add variables of type Roman just as you might
add integers.
Roman a, b = 15, c = 10;
a = b + c;
Console.WriteLine(a: {0}, a);
Why Overload Operators
C# Professional Skills Development 7-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
You would like the third statement to display:
a: XXV
To accomplish this, you must implement the addition and assignment
operators. When you implement an operator you are said to overload that
operator.
Without operator overloading you would have to create an Add method for
Roman, and then you would write:
a = b.Add(c);
In fact, without operator overloading you could not use the assignment
operator, and so youd have to write:
a.Assign(b.Add(c));
This statement is a lot less intuitive than:
a = b + c;
Operator Overloading and the CLS
The Common Language Specification does not mandate operator overloading
and some languages, like Visual Basic .NET do not support it. If you do
provide an overloaded operator, you should supply a method that does the
same work as well. That way, if someone derives from your class or interacts
with it from another language they will still obtain the necessary functionality
.
For example, if you overload the equality operator (==) be sure to overload
Object.Equals().
Creating Overloaded Operators
Implement operator overloading in C# using static methods. Binary operators
(such as addition, subtraction, etc.) take two parameters. You would
implement the addition operator for your Roman class like this:
Operator Overloading
7-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public static Roman operator+ (Roman lhs, Roman rhs)
{ // }
The method begins with an accessor declaration (public), followed by the
keyword static. The return type is Roman (adding two Roman objects returns a
new Roman object). The two parameters are both instances of Roman.
It is a convention to name the two arguments lhs (left-hand side) and rhs
(right-hand side). When you write:
b + c;
the operator+ is invoked and the Roman on the left-hand side of the + sign is
assigned to lhs, while the Roman on the right-hand side of the + sign is
assigned to rhs.
Danger, Will Robinson!
Operator overloading can clarify your code, as shown earlier. It can also be a
recipe for writing miserable, confusing, obscure, and difficult to maintain
code. You can, if you are particularly malicious, write the addition operator (+
)
and have it subtract two values! There are no rules that determine what you
must do inside the implementation of an operator, except common sense.
Long experience with operator overloading dictates the following guidelines:
Use operator overloading sparingly.
Mimic only the natural use of an operator (subtraction operator
should subtract).
The one exception to the natural use of operator is to implement the
convention that the + operator may be used for concatenation (as is
done with Strings).
Each operator should do one simple thing.
Matched Pairs
C# dictates that some operators must be implemented in matched pairs. For
example, if you implement the greater than operator (>) you must implement
the less than operator (<). Similarly, if you implement the greater than or
equals operator (>=) you must implement the less than or equals operator (<=).
Why Overload Operators
C# Professional Skills Development 7-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
One interesting matched pair is that if you implement the equals (==) operator,
you must implement the not equals operator (!=).
Type Conversion
In C#, conversion from one type to another (casting) may be implicit or it may
be explicit. A type may be converted implicitly when there is no chance that
data will be lost:
int intValue = 5;
long longValue = intValue; // implicit conversion
longValue++;
intValue = (int) longValue; // explicit
In the code shown here the conversion from int to long is implicit. The long
can accommodate any possible int value, i.e., no value can be lost. When you
convert back to an int, however, it is possible for a value to be lost (the long

holds values up to 9,223,372,036,854,775,808, while the int can hold values
only up through 2,147,483,647).
Notice that the implicit conversion requires no work, you just assign the int
value to the long variable. The explicit conversion is accomplished by putting
the type you want to convert to inside parentheses. What actually happens is
that longValue is cast to an int, and that int value is then assigned to intValu
e.
You can provide type conversions for your classes. Once again this assists with
allowing your users to interact with your classes just as if they were intrinsic

types.
You provide conversion with the keywords implicit and explicit. For example,
you might create an explicit conversion from a float to a Roman as follows:
public static explicit Roman(float theFloatValue)
{
create new Roman value here
}
The conversion from float to Roman must be explicit, because a float may
have a fractional part that will be lost in the Roman.
In some ways the explicit and implicit operators are like constructors. The
syntax is to declare the access level (public) followed by the keyword static
and then the keyword explicit. You then declare the type you are converting to
Operator Overloading
7-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
(Roman). The parameter is an object of the type you want to convert from, in
this case a float. Within the body of the operator you make the conversion,
returning a new Roman object based on the argument (the float).
Implement the implicit conversion in exactly the same way:
public static implicit float(Roman r)
{
create new float value here
}
In this case you are converting from a Roman to a float. Here the conversion
can be implicit because a float can hold any value that can be held in a Roman.
A Detailed Example
In this next example, youll create a Roman class to explore operator
overloading in detail.
To understand how this program works, you need to examine the relationship
between integer values (1,2,3) and Roman numerals (I, II, III). The
conversion can be tricky at points. Table 1 indicates the key values to watch
for.
Why Overload Operators
C# Professional Skills Development 7-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Integer Value Roman Numeral
1 I
2 II
3 III
4 IV
5 V
6 VI
7 VII
8 VIII
9 IX
10 X
40 XL
50 L
90 XC
100 C
101 CI
400 CD
500 D
900 CM
1000 M
Table 1. Roman numeral to Arabic numeral conversions.
You can see the pattern. You add an I for each one, a V for each 5, an X for
each 10, an L for each 50, a C for each 100, a D for each 500, and an M for
each 1,000. This would be straightforward except that the fours and nines (40,
90, 400, 900) present a challenge. For example, 4 is not IIII, it is IV. That is
,
one less than 5. Similarly, 9 is IX. Forty carries on with ten less than 50 (XL)

and 90 is ten less than 100 (XC). The value 900 is written as one hundred less
than one thousand (CM).
Much of the work in dealing with Roman numerals is in converting integers to
Roman and back. To simplify this work and to simplify the class, youll store
the value of the Roman as an integer, and convert it only when required.
public class Roman
{
private int val;
Operator Overloading
7-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The private member val holds the integer value of the Roman numeral. When
you add two Romans or do other math with Romans you will just manipulate
the integer values. When you display the Roman, however, you will convert it
to a string based on the rules for writing Roman numerals.
The constructor for a Roman numeral takes an int as a parameter:
public Roman(int val)
{
Console.WriteLine("In Roman Constructor(int)");
this.val = val;
}
This allows you to create a Roman numeral with the value IX by writing:
Roman r1 = new Roman(9);
You can imagine overloading the constructor to allow you to pass in a Roman
numeral as a string:
Roman r2 = new Roman(IX);
This is left as an exercise for the ambitious student!
Overriding ToString
Virtually all the remaining methods are operator overloads, except for the
method ToString. ToString is a method of Object, and Roman overrides the
base methods implementation to return a Roman numeral as a string. If the
value of the Roman is 49, ToString will return the string XLIX.
Creating the string is done in two steps:
1. Create temporary values.
2. Use the temporary values to create the string.
For example, if the number you were representing was 1950, the Roman
numeral would be DMDL. The first D represents 1,000, the MD represents
900, and the L represents 50.
Why Overload Operators
C# Professional Skills Development 7-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
To build this number you need to know how many thousands you have (1), and
how many nine hundreds you have. The number of 900s can only be 1 or 0. No
number is thought of as having more than one 900 in it. For example, 1800 has
no 900s, it has one thousand, one five hundred and three hundreds. This is not
a mathematical rule, it is the syntax of Roman numerals.
Certain values (900, 500, 90, 50, 9, 5) can have only the values 1 or 0. There
can be only one of these or none of these in any given number.
You also need to know the total number of thousands, hundreds, tens, and
ones. You compute all of this using the division and modulus operator:
// comments show how 3899 would be converted
int numThousands = val / 1000; // 3
int remainder = val % 1000; // 899
int numNineHundreds = remainder / 900; // 0
remainder %= 900; // 899
int numFiveHundreds = remainder / 500; // 1
remainder %= 500; // 399
int numHundreds = remainder / 100; // 3
remainder %= 100; // 99
int numNineties = remainder / 90; // 1
remainder %= 90; // 9
int numFifties = remainder / 50; // 0
remainder %= 50; // 9
int numTens = remainder / 10; // 0
remainder %= 10; // 9
int numNines = remainder / 9; // 1
remainder %= 9; // 0
int numFives = remainder / 5; // 0
int numOnes = remainder % 5; // 0
The comments indicate how this works. Given the value 3,899 the
numThousands will be 3. 3,899 / 1000 returns 3, because this is integer
division and the remainder is simply truncated away.
To get the remainder and compute the rest of the value, you use the modulus
operator: 3,899 % 1000. The result is the remainder after the division, or 899.
You can now compute the number of 900s by dividing the 899. The result is 0
(899 / 900 is 0, remainder 899).
Operator Overloading
7-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
You compute the number of 500s in the same way (1 with a remainder of 399).
The number of hundreds is 3.
Continuing on, assemble the values for 3,899 shown in Table 2.
Value Number in 3,899
1000s 3
900s 0
500s 1
100s 3
90s 1
50s 0
10s 0
9s 1
5s 0
1s 0
Table 2. The special values for 3,899.
You are now ready for Step 2, assembling the string:
string output = "[" + val + "]: ";
StringBuilder s = new StringBuilder(output);
for (int i = 0; i < numThousands; i++)
s.Append( 'M' );
if (numNineHundreds == 1)
s.Append("CM");
if (numFiveHundreds == 1)
s.Append( 'D' );
if (numHundreds == 4)
s.Append( "CD" );
else
for(int i = 0; i < numHundreds; i++)
s.Append( 'C' );
if (numNineties == 1)
Why Overload Operators
C# Professional Skills Development 7-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
s.Append("XC");
if (numFifties == 1)
s.Append( 'L' );
if (numTens == 4)
s.Append( "XL" );
else
for(int i = 0; i < numTens; i++)
s.Append( 'X' );
if (numNines == 1)
s.Append("IX");
if (numFives == 1)
s.Append( 'V' );
if (numOnes == 4)
s.Append( "IV" );
else
for (int i = 0; i < numOnes; i++)
s.Append( 'I' );
Notice that there is special processing for the 4s. That is, if the value is 400
,
the value is represented as CD rather than as CCCC:
if (numHundreds == 4)
s.Append( "CD" );
else
for(int i = 0; i < numHundreds; i++)
s.Append( 'C' );
If the number of hundreds is not 4 it must be less than 4, and so you write a C
for each hundred. The same logic is applied to 40 and 4:
Operator Overloading
7-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
if (numTens == 4)
s.Append( "XL" );
else
for(int i = 0; i < numTens; i++)
if (numOnes == 4)
s.Append( "IV" );
else
for (int i = 0; i < numOnes; i++)
s.Append( 'I' );
Overloading Operators
With ToString in place, you are ready to overload various operators to make
your Roman numeral behave like an int. The first is the addition operator. You
want to be able to add two Roman values to one another:
public static Roman operator+(Roman lhs, Roman rhs)
{
Console.WriteLine("In operator+");
return (lhs.val + rhs.val);
}
The logic here is that given two Roman numerals, you can extract their val
fields (with the underlying values), add these, and return a new Roman. The
result is an int, but the return value is marked as a Roman. For this to work,
you need an implicit conversion from int to Roman:
public static implicit operator Roman(int val)
{
Console.WriteLine("In implicit conversion from int");
return new Roman(val);
}
The implicit conversion operator works by explicitly creating a new Roman,
passing in the integer value. The constructor takes an int, so this works fine.
Semantically this makes sense; given an int, it is safe to implicitly cast to a
Roman, as a Roman can represent any value held by an int.
Why Overload Operators
C# Professional Skills Development 7-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
While looking at the conversion operators, you might as well go ahead and
create the conversion from a Roman to an int. Again, this can safely be
implicit:
public static implicit operator int(Roman r)
{
Console.WriteLine("In implicit conversion to int");
return r.val;
}
When converting to and from a float, a little more thought is required. While it

is safe to implicitly convert from a Roman numeral to a float, the reverse is no
t
true:
// convert roman to float
public static implicit operator float(Roman r)
{
Console.WriteLine("In implicit conversion to float");
return (float) r.val;
}
// explicit conversion float -> roman
public static explicit operator Roman(float val)
{
Console.WriteLine("In explict conversion from float");
return new Roman((int) val);
}
You need an equality operator to test whether two Romans are equal. If you
implement this, however, you must implement the not-equals operator and you
should override the Equals method:
Operator Overloading
7-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
// overload equality operator.
// strategy: compare the integer values
public static bool operator==(Roman lhs, Roman rhs)
{
Console.WriteLine("In operator ==");
return (lhs.val == rhs.val);
}
// overload the not equals operator
// strategy - test operator ==
public static bool operator !=(Roman lhs, Roman rhs)
{
Console.WriteLine("In operator !=");
return !(lhs==rhs);
}
// provided for language compatibility
// check to make sure it is a Roman object
// if so, delegate to operator ==
public override bool Equals(object o)
{
Console.WriteLine("In method Equals");
if (! (o is Roman) )
{
return false;
}
return this == (Roman) o;
}
The equality operator tests the underlying integer values of the two Roman
numerals. If these are equal then the objects are equal. The strategy with the
not-equal operator is to return the logical inverse of the equality operator,
using operator ! on the Boolean value returned from the equality operator.
return !(lhs==rhs);
Why Overload Operators
C# Professional Skills Development 7-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
This works by evaluating lhs==rhs, which returns a Boolean value and then
applying operator ! which returns the inverse. So, if lhs==rhs evaluates true,
then !(lhs==rhs) returns false.
The logic of the Equals method is to first test that the object passed to the
method is of type Roman. If so, then it delegates responsibility to the
overloaded equality operator. This is a very common idiom.
If you are overriding Equals and ToString, you might as well go whole hog
and override GetHashCode, the third significant method of Object:
public override int GetHashCode()
{
return val;
}
GetHashCode is used by hashtable collections (explained in a subsequent
chapter), and simply requires an integer value. Here you provide the
underlying value of the Roman numeral.
Testing The Program
To test the program, first provide a number of values to translate and display.
Then add code to test the overloaded operators:
public void Run()
{
Console.WriteLine("\n creating new Roman(9)");
Roman r1 = new Roman(9);
Console.WriteLine("r1: {0}", r1.ToString());
Console.WriteLine("\n creating new Roman(999)");
r1 = new Roman(999);
Console.WriteLine("r1: {0}", r1.ToString());
Console.WriteLine("\n creating new Roman(567)");
r1 = new Roman(567);
Console.WriteLine("r1: {0}", r1.ToString());
Console.WriteLine("\n creating new Roman(1423)");
Roman r2 = new Roman(1423);
Operator Overloading
7-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Console.WriteLine("r2: {0}", r2.ToString());
// addition will call implicit int converter
// and then Roman constructor
Console.WriteLine("\n r3 = r1 + r2...");
Roman r3 = r1 + r2;
Console.WriteLine("r1 + r2 = r3: {0}", r3.ToString());
// convert 5 to Roman
// add the two int vals (call int to roman)
// construct a Roman answer
Console.WriteLine("\n r4 = r3 + 5...");
Roman r4 = r3 + 5;
Console.WriteLine("r3 + 5 = r4: {0}", r4.ToString());
Console.WriteLine("\n r4 = r3 + 7.5f...");
Roman r5 = r3 + (Roman) 7.5f;
Console.WriteLine("r3 + 7.5f = r5: {0}",
r5.ToString());
}
The output from this is shown in Figure 1. First create the value 9, and display

its value (IX). You can see that you have stepped into the constructor to do
this.
You then create 999 to test its value (CMXCIX). The value 567 is translated as
DLXVII and 1,423 is MCDXXIII. All of these are correct.
Then add the value in r1 to the value in r2 and print the resulting value of
1,990 (MCMXC). To accomplish this, step into operator + as you might
expect, but then you see that you are in the implicit conversion from int to
Roman, and then you are in the Roman constructor.
Why Overload Operators
C# Professional Skills Development 7-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 1. Testing the Roman class.
In the next text, adding a Roman (r3) to an integer (5), you step not only into
the implicit conversion and the constructor, but then into operator +, back into

the implicit conversion and then into the constructor.
To understand why this is happening, youll need to set some breakpoints and
step through the code. Start by setting a breakpoint on line 185 as shown in
Figure 2. Run to the breakpoint.
Operator Overloading
7-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 2. Setting a breakpoint.
You are in the middle of the program. All of the simple tests have run, as you
can see by switching to the output window, shown in Figure 3. The line youve
stopped on will display the next action, r3 = r1 + r2. Step over the WriteLine
with F10. The line is displayed to the console and you are ready to step into th
e
assignment and addition:
Roman r3 = r1 + r2;
Why Overload Operators
C# Professional Skills Development 7-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 3. Running to the breakpoint.
Press F11 to step into the operator+ method. This is what youd expect; the
order of operations should be add, then assign. After the line that displays tha
t
you are in operator+ you should see the return statement. This returns the sum
of the two underlying values. Whoa! You are taken to the implicit conversion
operator that takes an int and returns a Roman. Why?
You are here because the sum of lhs.val + rhs.val is an integer, but the operato
r
+ method returns a Roman. To do so, the compiler tries to convert the int to a
Roman. Since you have defined such a conversion operator, it is invoked, as
shown in Figure 4.
Operator Overloading
7-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 4. Stepping into the conversion operator.
Pay particular attention to the call stack (circled and highlighted in Figure 4)
.
You see that you have come to the implicit conversion operator from the
Addition operator. Look at the locals window, the value of val is 1990; the
sum of the values of the two roman numerals lhs and rhs. You can prove this to
yourself by double-clicking on the second line in the call stack. This takes you

back to operator+. In the Locals window you find lhs and rhs, and you can
click on the + sign next to them to find their values, as shown in Figure 5. The

values are highlighted in Figure 5. You see that the value of lhs was 567 and
the value of rhs was 1423. The sum of 567 + 1423 is 1990.
Why Overload Operators
C# Professional Skills Development 7-21
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 5. Examining the values in the calling method.
Returning to the Implicit conversion operator, you can now step into the
constructor that takes an int. Youll see that the value 1990 is passed in. You
will then pop out of the operators and return to the test method, where you will

display the value of r3. This invokes ToString.
Step into this method. The value of val is 1990. You can watch the conversion,
as you step through. The Locals window displays all you need to know about
this value, as shown in Figure 6. You can see that there is 1 one thousand, 1
nine hundred, one ninety and no other values. That is the information needed
to display this value as a Roman numeral.
Operator Overloading
7-22 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 6. Evaluating 1990.
As you continue to step through the method, you can watch the string being
built. If you want to see the value of s as you are appending to it, click on th
e +
sign to the left of s in the Locals window. The m_Stringvalue property will
display the current value of the string as you modify it, as shown in Figure 7.
Each value is added in turn, building the Roman numeral as it goes.
Why Overload Operators
C# Professional Skills Development 7-23
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 7. Building the output string.
Converting an int
In the next part of the example, add an int to a Roman numeral. Step through
this as well. Youll see that the very first method invoked is the conversion
from int to Roman. You cant call operator+ unless you have two Romans, so
the compiler attempts to convert the integer 5 to a Roman. Because you have
an implicit conversion operator, it is able to do so. The conversion operator
invokes the constructor. This explains the first two lines of the output for thi
s
part of the example. The rest of this test is identical to the one you just walk
ed
through: the addition of two Romans.
The final part of the test is to add a Roman to a float:
Roman r5 = r3 + (Roman) 7.5f;
Notice that you must explicitly cast the float to a Roman, because the decimal
part (.5) will be truncated. This invokes the explicit cast operator, which
Operator Overloading
7-24 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
simply casts the float value to an int, and then invokes the constructor, as
shown in Figure 8.
Figure 8. The explicit float conversion.
From here, the rest of this example is just like the previous example, adding
two Romans.
Why Overload Operators
C# Professional Skills Development 7-25
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
Operator overloading facilitates creating user-defined types that
behave like intrinsic types.
The CLS does not require operator overloading, so be cautious when
implementing classes that will be used with languages that do not
support this language feature.
Operator overloading can be used to create very confusing code; be
careful and parsimonious in your use of operator overloading.
Some operators are matched pairs (e.g., > and <). If you overload one,
you must overload the other.
If you overload the equality operator (==) be sure to override
object.Equals.
Type conversion can be implicit (if no value may be lost) or explicit.
Operator Overloading
7-26 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
(Review questions and answers on the following pages.)
Why Overload Operators
C# Professional Skills Development 7-27
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. Do all .NET languages support operator overloading?
2. When should you avoid using operator overloading?
3. What is the matched pair for operator ==?
4. What is the difference between implicit and explicit casting?
5. Should overloaded operators be static or instance members of the class?
Operator Overloading
7-28 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. Do all .NET languages support operator overloading?
No. It is not part of the Common Language Specification, and
many .NET languages, such as Visual Basic .NET do not support it.
2. When should you avoid using operator overloading?
Do not use operator overloading when the semantics of the class
do not match the expected semantics of the operator. It is safe to
use the addition operator for adding the key underlying values,
but much more suspect to add two obscure values, and far less
acceptable to have the addition operator do work that is
unrelated to addition.
3. What is the matched pair for operator ==?
The matched pair for the equality operator (==) is the not-equals
operator (!=).
4. What is the difference between implicit and explicit casting?
In implicit conversion no cast must be shown; the compiler will
invoke the implicit conversion with no other help from the
developer. Explicit conversion requires that the developer write
the type explicitly.
5. Should overloaded operators be static or instance members of the class?
Overloaded operators must be static members of the class.
Why Overload Operators
C# Professional Skills Development 7-29
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 7:
Operator
Overloading
Lab 7:
Operator Overloading
7-30 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 7 Overview
In this lab youll learn how to overload operators, including implicit and
explicit type conversions.
To complete this lab, youll need to work through two exercises:
Implementing the Equality Operator
Overloading Conversion Operators
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-bystep

instructions.
Implementing the Equality Operator
C# Professional Skills Development 7-31
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Implementing the Equality Operator
Objective
In this exercise, youll reproduce the Roman numeral class used as an example
within the lesson. It will be your job to overload the equality operator and the

not-equals operator.
Things to Consider
Remember that if you overload the equality operator you will want to
overload the Equals method.
Can you use operator== to simplify operator !=?
Why would the implementation of operator== be different from the
implementation of the Equals method?
Step-by-Step Instructions
1. Open the project named EqualityOperatorStarter.sln in the
EqualityOperatorStarter folder. Notice that the Roman class was provided
for you, and the ToString and HashCode methods have been created. The
implementation of these methods is identical to that shown in the
exercises.
2. Start by creating the constructor. You need only assign the parameter value
to the private member variable val.
Console.WriteLine("In Roman Constructor(int)");
this.val = val;
3. Create an equality operator. Think through what the parameters should be.
Add a line of code that calls the console to announce that you are in the
equality operator.
Lab 7:
Operator Overloading
7-32 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
// overload equality operator.
// strategy: compare the integer values
public static bool operator==(Roman lhs, Roman rhs)
{
Console.WriteLine("In operator ==");
return (lhs.val == rhs.val);
}
4. Override the Equals method from Object. Think through what parameter
this method takes and how the method differs from the equality operator.
// takes an object (required by virtual method signature)
// check to make sure it is a Roman object
// if so, delegate to operator ==
public override bool Equals(object o)
{
Console.WriteLine("In method Equals");
if (! (o is Roman) )
{
return false;
}
return this == (Roman) o;
}
5. Create a non-equality operator. Can you delegate some of the work to the
equality operator?
// overload the not equals operator
// strategy - test operator ==
public static bool operator !=(Roman lhs, Roman rhs)
{
Console.WriteLine("In operator !=");
return !(lhs==rhs);
}
6. In the Run method of the Tester class, instantiate a Roman numeral with
the value 678. Display the value as a Roman numeral.
Implementing the Equality Operator
C# Professional Skills Development 7-33
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Console.WriteLine("\n creating new Roman(567)");
Roman r1 = new Roman(678);
Console.WriteLine("r1: {0}", r1.ToString());
7. Instantiate a second Roman numeral with the value 345. Display its value:
Console.WriteLine("\n creating new Roman(1423)");
Roman r2 = new Roman(345);
Console.WriteLine("r2: {0}", r2.ToString());
8. Fill in the if statement to test for equality between the first and second
Roman object.
if ( r1 == r2 )
Console.WriteLine("r1 == r2");
9. Fill in the if statement to test for inequality between the first and second
Roman object.
if ( r1 != r2 )
Console.WriteLine("r1 != r2");
10. Examine the output from running this program as shown in Figure 9.
Notice that the output shows two calls to operator == and one to operator
!=. Why?
Figure 9. Running the program.
Lab 7:
Operator Overloading
7-34 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
11. Put a breakpoint in the program, as shown in Figure 10. Run the program
to the breakpoint and step into the overloaded operators.
Figure 10. Setting a breakpoint.
Overloading Conversion Operators
C# Professional Skills Development 7-35
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Overloading Conversion Operators
Objective
In this exercise, youll extend the previous example to add implicit and explicit
conversion operators and youll add an addition operator as well.
Things to Consider
Why would you choose implicit vs. explicit conversion?
Will data be lost converting a Roman to an int or vice versa?
Will data be lost converting a Roman to a float or vice versa?
What should be the type for the return value of the addition operator?
Step-by-Step Instructions
1. Open ConversionOperatorStarter.sln in the ConversionOperatorStarter
folder.
2. Scroll down past the existing operators and methods for the Roman class,
and add a new operator to support Addition. Think about which parameters
this method will need.
public static Roman operator+(Roman lhs, Roman rhs)
{
Console.WriteLine("In operator+");
return (lhs.val + rhs.val);
}
3. Implement the conversion operator from int to Roman. Should this be
implicit or explicit?
Lab 7:
Operator Overloading
7-36 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public static implicit operator Roman(int val)
{
Console.WriteLine("In implicit conversion from int");
return new Roman(val);
}
4. Implement the conversion operator from Roman to int. Should this be
implicit or explicit?
public static implicit operator int(Roman r)
{
Console.WriteLine("In implicit conversion to int");
return r.val;
}
5. Implement the conversion from Roman to float. Should this be implicit or
explicit?
public static implicit operator float(Roman r)
{
Console.WriteLine("In implicit conversion to float");
return (float) r.val;
}
6. Implement the conversion from float to Roman. Should this be implicit or
explicit?
public static explicit operator Roman(float val)
{
Console.WriteLine("In explict conversion from float");
return new Roman((int) val);
}
7. Add to the Run method. Instantiate a new Roman and initialize it with the
sum of the first two Roman objects. Display the results to the console.
Overloading Conversion Operators
C# Professional Skills Development 7-37
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Console.WriteLine("\n r3 = r1 + r2...");
Roman r3 = r1 + r2;
Console.WriteLine("r1 + r2 = r3: {0}", r3.ToString());
8. Create a new instance of Roman and initialize it by adding the integer
value 5 to the Roman object created in Step 7.
Console.WriteLine("\n r4 = r3 + 5...");
Roman r4 = r3 + 5;
Console.WriteLine("r3 + 5 = r4: {0}", r4.ToString());
9. Create a new Roman object and initialize it by adding the float value 7.5 to
the Roman object created in Step 8.
Console.WriteLine("\n r4 = r3 + 7.5f...");
Roman r5 = r3 + (Roman) 7.5f;
Console.WriteLine("r3 + 7.5f = r5: {0}", r5.ToString());
10. Compile and run the program. It should look like Figure 11.
Lab 7:
Operator Overloading
7-38 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 11. The final exercise results.
Structs and Interfaces
C# Professional Skills Development 8-1
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Structs and
Interfaces
Objectives
Create structs as lightweight value-type objects.
Create interfaces as contracts for classes to fulfill.
Instantiate interfaces.
Pass interfaces to methods.
Extend interfaces to provide specialization.
Use the keywords is and as to test the implementation of interfaces.
Structs and Interfaces
8-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Alternatives to Classes
The principal method for defining new types in C# is to create a class. Classes
offer the full array of support for creating types, and much of your work as a
C# developer will be in designing and implementing classes.
There are times, however, when you will want to create special types to meet
extraordinary requirements. This chapter focuses on two such occasions.
You may, from time to time, need to create a lightweight value type (rather
than a reference type). For this, you may choose to create a struct, rather than
a
class. There are limitations with structs (discussed in the next section) but if

what you need is a small value type, structs may be the answer.
More important, you will often want to define a contract that other classes
must fulfill. For example, you may want to define what methods an object
must have if it is to be stored in your database. To create such a contract,
define an interface.
This chapter provides examples of how to use each of these alternative types.
Structs
C# Professional Skills Development 8-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Structs
A struct is a lightweight user-defined value type. They are similar to classes
and may have properties, methods, constructors, fields, operators, nested types,

and indexers. They are different from classes in some very important ways,
including:
Structs do not support inheritance.
Structs are value types, not reference types.
Structs do not support user-defined default constructors.
There is no initialization.
TIP: Structs are sealed, meaning that you may not inherit from them, nor may a
struct inherit from any class except object.
Defining Structs
You define a struct much as you do a class. The syntax is as follows:
[attributes] [access-modifiers] struct identifier [
:interface-list] { struct members }
Attributes are discussed in the advanced portion of this course.
Access modifiers include public and private. Typically, structs are public.
The key word struct is followed by an identifierthe struct name is like a
class name.
The interface list shows which interfaces the struct implements. Interfaces are
described later in this chapter.
The struct members define the struct, much like the class members define the
class.
Structs and Interfaces
8-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
To see how to work with structs, youll create a simple application that defines
a struct and manipulates instances of the struct.
1. Create a new console application named structs.
2. Create a struct named Book and give it three member fields: bookName
and isbn, both of which are strings. Also give it a public member
salesRank of type int. It is not uncommon to have public fields in a struct,
though some might argue that it is not a good programming practice.
namespace structs
{
public struct Book
{
private string bookName;
private string isbn;
public int salesRank;
3. Provide a constructor that takes a name and an isbn and assigns them to the
member fields. Note that your constructor must specifically set a value for
the salesRank. There can be no initializer in a struct, so you must set this
value in the constructor.
public Book(string name, string isbn)
{
bookName = name;
this.isbn = isbn;
salesRank = -1;
}
4. Create properties for the two private fields.
See structs.sln
Structs
C# Professional Skills Development 8-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public string BookName
{
get { return bookName; }
set { bookName = value; }
}
public string ISBN
{
get { return isbn; }
}
5. Override the ToString() method. Note that while structs do not support
inheritance (they are sealed) they do still derive from object, and may
override methods of object.
public override string ToString()
{
return (String.Format("{0} ISBN: {1} Rank: {2}",
bookName, isbn, salesRank));
}
6. Create a test class to test your new struct. In the Run() method, instantiate

two struct objects.
Book progASP =
new Book("Programming ASP.NET","0596001711");
Book cSharp =
new Book("Programming C#", "0596001177");
7. Assign the following to the public field.
progASP.salesRank = 12;
cSharp.salesRank = 2;
8. Use the public field to access information about the book.
Structs and Interfaces
8-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Console.WriteLine ("{0} has a sales rank of {1}",
cSharp.BookName, cSharp.salesRank);
9. Use the overridden ToString method.
Console.WriteLine(progASP);
The output of this program is shown in Figure 1.
Figure 1. Testing the struct.
Structs Are Value Types
The Book struct youve created creates value objects. If you pass a Book to a
method, a copy is made. You can prove this to yourself by creating a new
method, PassByValue that takes a Book object as a parameter:
public void PassByValue(Book theBook)
{
theBook.salesRank = 20;
Console.WriteLine("In the book: {0}", theBook);
}
This method modifies the Book object passed in; it sets the salesRank to 20
and then displays the information about the book.
Call this method from your test program, displaying the book both before and
after the call:
Console.WriteLine(progASP);
Console.WriteLine("\nPassing progASP to PassByValue...");
PassByValue(progASP);
Console.WriteLine("Back from PassByValue: {0}", progASP);
Structs
C# Professional Skills Development 8-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The results are shown in Figure 2. You can see that the Rank was set within
the method, but when you return to Main the value is not changed. This is
consistent with pass by value semantics.
Figure 2. Passing by value.
If you do need to pass a struct by reference, you do so as you would with an
intrinsic type: using the ref keyword. Add another method, PassByRef that
takes a Book as a reference:
public void PassByRef(ref Book theBook)
{
theBook.salesRank = 20;
Console.WriteLine("In the book: {0}", theBook);
}
This method is identical to PassByValue except that the Book type is preceded
by the keyword ref. Remember to use the ref keyword when you invoke the
method as well:
Console.WriteLine("\nPassing progASP to PassByRef...");
PassByRef(ref progASP);
Console.WriteLine("Back from PassByRef: {0}", progASP);
The output is shown in Figure 3. You can see that using the keyword ref
avoided the copy and the original object is now modified.
Structs and Interfaces
8-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 3. Passing with the ref keyword.
Be Careful with Structs
Because structs are value objects they can degrade performance when you
store them in collections. Most .NET collections expect to store reference
objects. When you pass in a struct the struct must be boxed, and there is
overhead in boxing an object. This can degrade performance when you are
storing many structs. This lightweight object can actually hurt your programs
performance rather than helping it!
Interfaces
C# Professional Skills Development 8-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Interfaces
An interface is a contract. When a class implements the interface it must
support the methods, properties, and events of the interface. The interface
describes the contract, and the class implements that contract.
The designer of a class may choose not to implement any interfaces, or the
designer may choose to implement one or more interfaces. That is entirely a
design decision, but if an interface is implemented, then it must be
implemented in full.
The formal definition of an interface is as follows:
[attributes]
[access-modifier] interface identifier
[:base-list]
{interface body}
Attributes are an advanced topic, covered in future chapters.
The access modifier is typically public. An interface can be declared with any
visibility, but interface members must all be public.
The keyword interface is followed by the interface name. It is a convention to
have that name begin with the letter I, as in IStorable, ITransmittable, and so
forth.
The base list includes the list of interfaces that this interface extends. Youll
see more about extending interfaces later in this chapter.
The interface body defines the methods, properties, and events that must be
implemented by the class fulfilling the contract.
Try It Out!
To get started, youll create a simple interface: IInvestment.
1. Open a new console application and call it Interfaces.
2. Create a public interface named IInvestment and give it two methods as
shown here.
See Interfaces.sln
Structs and Interfaces
8-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public interface Iinvestment
{
void Invest(float amount);
void DisplayCurrentValue();
}
3. Notice that the Interface methods do not include implementation. The
return type and parameters are listed, but there is no body; the definition is
followed by a semicolon.
4. Also notice that the methods have no access modifiers. All interface
methods must be public.
5. You have now created a contract. To fulfill that contract, you need a class
that implements the interface. Create a Stock class and indicate that it
implements the interface using the colon operator:
public class Stock : IInvestment
{
6. The colon followed by the name of the interface indicates that the class
implements the interface. This looks very similar to derivation, and it is up
to the compiler to determine that Stock does not inherit from IInterface,
but rather fulfills the interface.
7. You are free to give the implementing class any fields and methods you
like, so long as you also include all the members required by the interface
definition. Start by adding three private members:
private string name;
private float price;
private float numShares;
8. Initialize these values in the constructor.
Interfaces
C# Professional Skills Development 8-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public Stock(
string name,
float price,
float numShares)
{
this.name = name;
this.price = price;
this.numShares = numShares;
}
9. Provide public read-only properties for the name and number of shares.
public string Name
{
get
{
return name;
}
}
public float NumShares
{
get
{
return numShares;
}
}
10. Finally, it is time to implement the two methods required by the interface.
The first is Invest, which must return void and take a float indicating the
amount to invest. In a real program you might implement this by actually
purchasing the stock and updating database records. For this simplified
example, youll just set a member variable:
public void Invest (float amt)
{
this.numShares += (amt/price);
}
Structs and Interfaces
8-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
11. Similarly, you must implement DisplayCurrentValue; a method taking no
parameters and returning void.
public void DisplayCurrentValue()
{
Console.WriteLine("{0}: totalValue: {1}",
name, price*numShares);
}
12. Test this program by creating an instance of Stock and setting its values.
You can then call methods defined in the interface to interact with the
stock as an investment.
public class Tester
{
public void Run()
{
Stock myStock = new Stock("Liberty", 5.7f, 100f);
myStock.Invest(5200f);
myStock.DisplayCurrentValue();
Console.WriteLine("{0} has {1} shares",
myStock.Name, myStock.NumShares);
The results are shown in Figure 4.
Figure 4. Stock class implements Iinvest.
13. To see the flexibility of the interface, create a second class, OilWell that

will also implement this interface. An OilWell has different private
member variables, and it can have different methods than Stock, but will
also implement the required methods. Of course, the details of how an
OilWell implements the Invest method may differ from how it is
implemented by Stock.
Interfaces
C# Professional Skills Development 8-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public class OilWell : Iinvestment
{
private string wellID;
private float investment = 0;
public OilWell(string wellID)
{
this.wellID = wellID;
}
public string WellID
{
get { return wellID; }
}
public void Invest(float amount)
{
this.investment = amount;
}
public void DisplayCurrentValue()
{
Console.WriteLine("{0}: has a current value of {1}",
wellID, investment);
}
}
14. Notice that in this case, the OilWell invests by setting its investment fiel
d
to the total value of the amount invested, while a stock implements Invest
by incrementing the number of shares, computed by dividing the amount
invested by the current price.
15. Add test code to your test method to instantiate an OilWell and invest in it
.
OilWell myWell = new OilWell("J3752");
myWell.Invest(10000f);
myWell.DisplayCurrentValue();
Structs and Interfaces
8-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The results are shown in Figure 5.
Figure 5. A second class implements Iinvest.
Instantiating the Interface
Once you have classes that have implemented the interface, you can create
instances of the interface itself. This allows you to interact with the
implementing classes polymorphically. That is, you can interact through the
interface and access all of the members of the interface, without regard to
which class has implemented it.
This ability to work with an interface generically allows you to create methods
that expect an interface object, and pass in an instance of any object that
implements that interface. You can see this by adding code to the test method
in the example just reviewed:
IInvestment myInvestment = myStock;
Console.WriteLine(
"\nCalling myInvestment.DisplayCurrentValue()");
myInvestment.DisplayCurrentValue();
You have instantiated myInvestment, an object of type IInvestment, which you
have initialized to refer to the object myStock. Since myStock implements
IInvestment, this is a reasonable thing to do. You are now free to call any
IInvestment method such as DisplayCurrentValue, and you will call that
method on the myStock instance, as shown in Figure 6.
Interfaces
C# Professional Skills Development 8-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 6. Instantiating the interface.
You can reassign that same reference to IInvestment to point to the OilWell
object:
myInvestment = myWell;
Console.WriteLine(
"\nCalling myInvestment.DisplayCurrentValue()");
myInvestment.DisplayCurrentValue();
The result of adding these lines is shown in Figure 7. You can see here that
myInvestment is first assigned to myStock, and then reassigned to myWell.
Figure 7. Reassigning the reference to Iinvestment.
Structs and Interfaces
8-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Implementing More than One Interface
While a class can only inherit from (at most) one other class, it can implement
any number of interfaces. This allows you to define small sets of functionality
as interfaces, and then implement classes that mix and match that functionality.

To see this, extend the previous example to add a second interface.
Try It Out!
In this next example you will add a second interface, ITaxable, and define
classes that implement more than one interface.
1. Reopen the previous example.
2. Add a second interface, ITaxable, to support investments on which you
will pay tax.
interface ITaxable
{
float ComputeTaxes();
}
3. Modify Stock to implement both interfaces.
public class Stock : IInvestment, Itaxable
4. Add a method to Stock to implement Itaxable.
public float ComputeTaxes()
{
return (price * numShares) * 0.3f;
}
5. Do not modify OilWell. Just because stocks are both Investments and
Taxable does not mean that all investments are taxable.
// no change
public class OilWell : IInvestment
See Interfaces2.sln
Interfaces
C# Professional Skills Development 8-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
6. Modify the test program to create a stock and test its implementation of the
interface.
public void Run()
{
Stock myStock = new Stock("Liberty", 5.7f, 100f);
myStock.Invest(5200f);
myStock.DisplayCurrentValue();
Console.WriteLine("{0} has {1} shares",
myStock.Name, myStock.NumShares);
Console.WriteLine("Taxes on my stock investment: {0}",
myStock.ComputeTaxes());
The results are shown in Figure 8.
Figure 8. Implementing two interfaces.
7. Try calling ComputeTaxes on the OilWell.
OilWell myWell = new OilWell("J3752");
myWell.Invest(10000f);
myWell.DisplayCurrentValue();
Console.WriteLine("Taxes on my stock investment: {0}",
myWell.ComputeTaxes());
When you try to compile this the compile fails with the following message:
Interfaces2.OilWell does not contain a definition for
ComputeTaxes
Oops; OilWell did not implement the ITaxable interface.
Structs and Interfaces
8-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Extending Interfaces
Just as you can derive a new class from an existing class, you can extend an
existing interface. This allows you to specialize the requirements of an
interface, and allow classes to choose the level of specialization they will
implement.
Try It Out!
To see how this works, reopen the previous example. This time youll add an
extension to one of the interfaces you created in the previous example.
1. Reopen the previous example.
2. Create a new interface, IShelterable that extends ITaxable. This supports
investments that are not only taxable, but eligible to be used as a tax
shelter.
interface IShelterable : ITaxable
{
float TaxableAmount { get; }
void ReduceTaxImpact();
}
Notice that extending the interface uses the same syntax as deriving from a
class. That is, you write the name of the new interface, followed by a colon,
followed by the name of the base interface.
This new interface, IShelterable, adds a property and a new method. Notice
also that the property is not implemented it is just defined.
3. Modify OilWell to implement Ishelterable.
public class OilWell : IInvestment, IShelterable
4. You will have to implement not only the methods of IShelterable, but also
the methods of ITaxable, the interface IShelterable extends. To accomplish
this, start by adding a member field taxableAmount to the OilWell:
See Interfaces3.sln
Interfaces
C# Professional Skills Development 8-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
private string wellID;
private float investment = 0;
private float taxableAmount;
5. Implement the method of Itaxable.
public float ComputeTaxes()
{
return taxableAmount * 0.3f;
}
6. Implement the method of Ishelterable.
public void ReduceTaxImpact()
{
taxableAmount -= taxableAmount * .97f;
}
7. Modify the test program to test the Shelterable interface.
public class Tester
{
public void Run()
{
Stock myStock = new Stock("Liberty",5,100);
myStock.Invest(5200);
myStock.DisplayCurrentValue();
Console.WriteLine(
"Taxes on my stock investment: {0}",
myStock.ComputeTaxes());
OilWell myWell = new OilWell("Well 15");
myWell.Invest(20000);
Console.WriteLine(
"Taxes on my well: {0}",myWell.ComputeTaxes());
myWell.ReduceTaxImpact();
Structs and Interfaces
8-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Console.WriteLine(
"Taxes on my well reduced to: {0}",
myWell.ComputeTaxes());
}
public static void Main()
{
Tester t = new Tester();
t.Run();
}
}
The result of running this code is shown in Figure 9. You can see that the
OilWell is able to reduce its taxes by implementing IShelterable!
Figure 9. Extending an interface.
Testing an Implementation
It is possible to create a method that takes a Taxable object (that is, an objec
t
that implements ITaxable) and pass to that method both objects that implement
ITaxable and also objects that implement IShelterable. That is, wherever you
can use an interface, you can use an extension to that interface.
Within such a method you may need to test whether the object you have
actually implements one or another extension. You can test an interface with
the is keyword. The keyword is will return true if the object implements the
interface you are testing and false if it does not.
Interfaces
C# Professional Skills Development 8-21
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
Modify the previous example by adding a new method, Shelter.
1. Reopen the previous example.
2. Add a new method, Shelter that takes an ITaxable object. This method
takes a second parameter, identifier as a string.
public void Shelter(
ITaxable investment, string identifier)
{
Console.WriteLine("Taxes on {0}: {1}",
identifier,
investment.ComputeTaxes());
3. Test the investment, and if it is shelterable then apply the shelter,
otherwise indicate that by writing to the console.
if (investment is IShelterable)
{
IShelterable shelterable = (IShelterable) investment;
shelterable.ReduceTaxImpact();
Console.WriteLine(
"Taxes reduced to: {0}",
shelterable.ComputeTaxes());
}
else
Console.WriteLine ("{0} is not shelterable!",
identifier);
4. Add test code to test this method.
See Interfaces3.sln
Structs and Interfaces
8-22 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
OilWell myWell = new
Shelter(myWell,myWell.WellID);
Stock myStock = new Stock("Liberty",5,100);
myStock.Invest(5200);
myStock.DisplayCurrentValue();
Shelter(myStock,myStock.Name);
When you run the program, the results are as shown in Figure 10. The stock is
not shelterable, but the OilWell is.
Figure 10. Passing interfaces to methods.
Using the as Keyword
While the is keyword works, it is not efficient to test the interface and then c
ast
it. You can combine these two steps by using the as keyword. The keyword as
casts the interface, and if the cast fails it returns null.
You can see the difference with a small change to the example just completed.
1. Reopen the project you just completed.
2. Modify the Shelterable method to use as rather than is.
See Interfaces5.sln
Interfaces
C# Professional Skills Development 8-23
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public void Shelter(
ITaxable investment,
string identifier)
{
Console.WriteLine("Taxes on {0}: {1}",
identifier,
investment.ComputeTaxes());
IShelterable shelterable =
investment as IShelterable;
if (shelterable != null)
{
shelterable.ReduceTaxImpact();
Console.WriteLine("Taxes reduced to: {0}",
shelterable.ComputeTaxes());
}
else
Console.WriteLine ("{0} is not shelterable!",
identifier);
}
3. Notice that in this example you cast the investment to a Shelterable using
the keyword as, and then test to see if the cast interface is null.
IShelterable shelterable =
investment as IShelterable;
if (shelterable != null)
{
This is more efficient than first testing with is and then casting. The results
are
identical, as shown in Figure 11.
Structs and Interfaces
8-24 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 11. Using as rather than is.
Interfaces
C# Professional Skills Development 8-25
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
Structs are lightweight user-defined value types.
Structs may have properties, methods, constructors, fields, operators,
nested types, and indices.
Structs are sealed and do not support user-defined default constructors
or member initialization.
Interfaces create a contract.
You may instantiate an interface, and you may make an interface be a
parameter to a method.
A class may implement zero, one, or more interfaces.
An interface may be extended much as a class is derived from.
You may test whether an object implements a particular interface
using either the is or the as keyword.
Structs and Interfaces
8-26 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
(Review questions and answers on the following pages.)
Interfaces
C# Professional Skills Development 8-27
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. What is the difference between a struct and a class?
2. What is an interface?
3. Can a class implement more than one interface?
4. How do you extend an interface?
5. How do you test if an instance of a class implements an interface?
6. What is the difference between the keywords is and as?
Structs and Interfaces
8-28 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. What is the difference between a struct and a class?
A struct is a value type. Structs are sealed and they do not
support user-defined constructors. Structs do not support
initialization.
2. What is an interface?
An interface defines a contract that will be fulfilled
(implemented) by a class. The interface defines the members
(methods, properties, events, etc.) that the implementing class
must provide.
3. Can a class implement more than one interface?
Yes, a class may implement no interfaces, a single interface, or
any other number of interfaces.
4. How do you extend an interface?
You extend an interface much like you derive from a base class,
using the colon operator (:). Classes implementing the extended
interface must fulfill the contract of the base interface as well.
5. How do you test if an instance of a class implements an interface?
You can use the is keyword that returns a Boolean value, or you
can cast using the as keyword, which returns null if the cast fails.
6. What is the difference between the keywords is and as?
The keyword is provides a test that returns a Boolean, but does
not actually make the cast. The keyword as performs the cast,
but if the cast fails, it returns null.
Interfaces
C# Professional Skills Development 8-29
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 8:
Structs and
Interfaces
Lab 8:
Structs and Interfaces
8-30 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 8 Overview
In this lab youll learn how to create structs and interfaces.
To complete this lab, youll need to work through two exercises:
Creating Structs
Implementing Interfaces
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-bystep

instructions.
Creating Structs
C# Professional Skills Development 8-31
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Creating Structs
Objective
In this exercise, youll create a struct to represent a simple CD object that
might be used to keep track of popular music CDs in your inventory. You will
explore whether CDs are value or reference objects, and see that CDs provide
much the same kind of interface as classes do.
Things to Consider
Is a struct a reference or a value type?
Why would you use structs rather than classes?
What are the limitations of structs?
Step-by-Step Instructions
1. Open the project named Structs_Starter.sln in the Structs_Starter folder.
2. Create a structure for a CD that might contain information about the CDs
name, artist, ISBN, and sales rank.
public struct CD
{
private string cdName;
private string artist;
private string isbn;
private int salesRank;
3. Create a constructor to set all the valuesset the sales rank to 1.
Lab 8:
Structs and Interfaces
8-32 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public CD(string name, string isbn, string artist)
{
cdName = name;
this.artist = artist;
this.isbn = isbn;
salesRank = -1;
}
4. Create properties for the four members.
public string CDName
{
get { return cdName; }
set { cdName = value; }
}
public string ISBN
{
get { return isbn; }
set { isbn = value; }
}
public string Artist
{
get { return artist; }
set { artist = value; }
}
public int SalesRank
{
get { return salesRank; }
set { salesRank = value; }
}
5. Override ToString to display the name, artist, ISBN, and rank.
Creating Structs
C# Professional Skills Development 8-33
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public override string ToString()
{
return (String.Format(
"{0} by {1}. ISBN: {2} Rank: {3}",
cdName, artist, isbn, salesRank));
}
}
6. Create a public method within Tester that takes a CD object by value and
sets the sales rank and artist. Display the value to the console:
public void PassByValue(CD theCD)
{
theCD.SalesRank = 20;
theCD.Artist = "D. Brubeck";
Console.WriteLine("In PassByValue with : {0}", theCD);
}
7. Create a public method that takes a CD object by reference and sets the
sales rank and the artist. Display the values to the console.
public void PassByRef(ref CD theCD)
{
theCD.SalesRank = 20;
theCD.Artist = "D. Brubeck";
Console.WriteLine("In PassByRef with: {0}", theCD);
}
8. Create a public method, Run that instantiates two CD objects.
public void Run()
{
CD TakeFive =
new CD("Take Five", "B00005J96U", "Dave Brubeck");
CD Rhapsody =
new CD("Rhapsody in Blue", "B0000026FG",
"Gershwin");
Lab 8:
Structs and Interfaces
8-34 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
9. Set the sales ranks for the two CDs and display one of them in detail.
TakeFive.SalesRank = 266;
Rhapsody.SalesRank = 712;
Console.WriteLine ("{0} has a sales rank of {1}",
TakeFive.CDName, TakeFive.SalesRank);
Console.WriteLine(TakeFive);
10. Pass one of the CDs to the method that takes a CD struct by value. Display
its contents when you return:
Console.WriteLine(
"\nPassing TakeFive to PassByValue...");
PassByValue(TakeFive);
Console.WriteLine("Back from PassByValue: {0}", TakeFive);
11. Pass one of the CDs to the method that takes a CD struct by reference.
Display its contents when you return:
Console.WriteLine("\nPassing TakeFive to PassByRef...");
PassByRef(ref TakeFive);
Console.WriteLine("Back from PassByRef: {0}", TakeFive);
12. Compile and run the program. It should look like Figure 12.
Figure 12. Final exercise results.
Implementing Interfaces
C# Professional Skills Development 8-35
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Implementing Interfaces
Objective
In this exercise, youll create and extend interfaces, and youll create classes
that implement the interfaces.
Things to Consider
What is the purpose of an interface?
Why might you extend an interface?
What must a class do to implement the interface?
Step-by-Step Instructions
1. Open the project named Interfaces_Starter.sln in the Interfaces_Starter
folder.
2. Create an interface named IShelfable. This will represent items that might
be on a shelf in your store. The interface requires that implementing
classes provide a method PutOnShelf that returns void and takes no
parameters, and a read-only property that provides the classification for the
shelfable object:
public interface IShelfable
{
void PutOnShelf();
string Classification { get; }
}
3. Create an interface ISellable for any item that might be sold. Provide a
property for the selling price:
public interface ISellable
{
float Price { get; set; }
}
Lab 8:
Structs and Interfaces
8-36 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
4. Create a new interface that extends ISellable, named IDiscountable. Add a
read-only property for the discount percentage. Also add a method that
takes no parameters and returns void, named ReduceSellingPrice.
public interface IDiscountable : ISellable
{
float DiscountPctg { get; }
void ReduceSellingPrice();
}
5. Create a Book class that implements both IShelfable and ISellable.
public class Book : IShelfable, ISellable
{
6. Add three members to the Book class: name, price, and classification. Use
a float for the price and a string for the others.
private string name;
private float price;
private string classification;
7. Write the constructor for the Book class.
public Book(
string name,
float price,
string classification)
{
this.name = name;
this.price = price;
this.classification = classification;
}
8. Implement the Name property (read-only):
Implementing Interfaces
C# Professional Skills Development 8-37
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public string Name
{
get { return name; }
}
9. Implement the method PutOnShelf. For the purposes of this lab, just
display the name of the book, its selling price, and its classification.
NOTE To display the selling price as a currency, use the C formatter. To
display the second parameter as a price rather than using the
substitution parameter {1}, you may instead use {1:C} (note the
colon between the 1 and the C).
public void PutOnShelf ()
{
Console.WriteLine(
"Displaying {0} selling for {1:C} Classify as: {2}",
name, price,classification);
}
10. Implement the Price property.
public float Price
{
get { return price; }
set { price = value; }
}
11. Implement the read-only Classification property.
public string Classification
{
get { return classification; }
}
12. Implement a class CD to represent a music CD that implements both
IShelfable and Idiscountable.
Lab 8:
Structs and Interfaces
8-38 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public class CD : IShelfable, IDiscountable
{
13. Create the four private fields for the CD class: cdName, price,
discountPctg, and classification. The members price and discountPctg will
be floats, the other two will be strings. Initialize the classification to
Popular Music.
private string cdName;
private float price;
private float discountPctg;
private string classification = "Popular Music";
14. Implement the read-only property DiscountPctg.
public float DiscountPctg
{
get { return discountPctg; }
}
15. Implement the Price property.
public float Price
{
get { return price; }
set { price = value; }
}
16. Implement the read-only property Classification.
public string Classification
{
get { return classification; }
}
17. Implement the constructor.
Implementing Interfaces
C# Professional Skills Development 8-39
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public CD(string name, float price, float discount)
{
this.cdName = name;
this.price = price;
| this.discountPctg = discount;
}
18. Implement the method PutOnShelf. For the purposes of this lab, just
display the name of the book, its selling price, and its classification.
NOTE To display the selling price as a currency, use the C formatter. To
display the second parameter as a price rather than using the
substitution parameter {1} you may instead use {1:C} (note the
colon between the 1 and the C).
public void PutOnShelf ()
{
Console.WriteLine("Displaying {0} selling for {1:C}",
cdName,price);
}
19. Implement ReduceSellingPrice by applying the available discount to the
current price:
public void ReduceSellingPrice()
{
price -= price * discountPctg;
}
20. In the Run method, create a CD object and put it on the shelf.
CD takeFive = new CD("Take Five", 15.75f, .10f);
takeFive.PutOnShelf();
21. Reduce the selling price of the CD and put it back on the shelf.
Lab 8:
Structs and Interfaces
8-40 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
takeFive.ReduceSellingPrice();
takeFive.PutOnShelf();
22. Create a new book object and put it on the shelf.
Book progCS = new Book(
"Programming C#",39.95f,"Programming");
progCS.PutOnShelf();
23. Set its price and reshelf it.
progCS.Price = 45.50f;
progCS.PutOnShelf();
24. Compile and run the program. It should look like Figure 13.
Figure 13. Final exercise results.
Arrays
C# Professional Skills Development 9-1
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Arrays
Objectives
Create and use arrays to store same-type items in a simple, safe,
ordered collection.
Iterate over arrays using the foreach loop.
Build rectangular multidimensional arrays, as well as jagged arrays of
arrays.
Pass in variable numbers of parameters using the params keyword.
Create indexers for your own collection classes.
Arrays
9-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Defining Arrays
An array is a collection that is built into the C# language. An array is a simpl
e,
sized collection composed entirely of the same type of objects. That is, if you
create an integer array you can hold a fixed number of integers and no objects
of other types. When you create the array you must tell the compiler how many
items are in the array.
Arrays are indexed, starting at 0. That means that you access each item in the
array using a specific integer index. If you create an integer array named
myIntArray, the first item in the array is myIntArray[0] and the second item in
the array is myIntArray[1].
C# considers the array to be part of the language, maintaining compatibility
with C and C++. That said, when you create an array in C++, what you
actually create is an instance of System.Array. This gives you the backward
compatibility of making the array part of the language, but the object-oriented
flexibility of interacting with an actual class.
Here is the formal syntax for creating an array:
type[] identifier;
The identifier is the name of the array. For example, you might create an array
of integers named myIntArray with the following declaration:
int[] myIntArray;
Instantiating an Array
You instantiate an array with the keyword new. At the time you instantiate the
array you must assign it as size:
int[] myIntArray;
myIntArray = new int[5]; // size to hold 5 integers
In the example, the five integers can be accessed as follows:
Defining Arrays
C# Professional Skills Development 9-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
int first = myIntArray[0];
int second = myIntArray[1];
int third = myIntArray[2];
int fourth = myIntArray[3];
int fifth = myIntArray[4];
Notice that the first element in the array is indexed at 0 and the fifth at inde
x 4.
Programmers say that an array of n elements is indexed 0 to n-1.
Reference and Value Types
Each element in the array is an object of the type defined for the array. An
array may hold objects of either type: intrinsic or user-defined. The elements
of the array are created based on their type, but the array itself is a referenc
e
type.
Initialized Values
When you add a value type to an array it is initialized to its default value:
Numeric values are initialized to 0
Boolean values are initialized to false
Character values are initialized to null (\0)
Enumerated constants are initialized to 0
Reference types are not initialized to their default values; they are initialize
d to
null. If you attempt to access an uninitialized reference type within an array,
an
exception will be thrown (exceptions are explained elsewhere in this course).
foreach Loops
You can iterate over an array (and many other collections) with the foreach
statement. Here is the formal declaration of foreach:
foreach(type-identifier in expression)
statement;
The type-identifier identifies the type of elements in the array. The expression
,
in this case, will be the name of the array. If you create an array of Employee
Arrays
9-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
objects named empArray, you can print the ID value from each with the
following foreach loop:
foreach(Employee emp in empArray)
Console.WriteLine(ID: {0}, emp.ID);
In this simple example, the local variable emp will be a reference to each
Employee object in the array empArray. Each time through the loop, emp
refers to the next Employee object in the array.
Try It Out!
The best way to see how to work with arrays is to create a simple array and
then to iterate over it.
1. Create a new console application and name it Arrays.
2. Create an Employee class to store in the array. Give the Employee two
member fields and a constructor.
public class Employee
{
private int age;
private string name;
public Employee(int age, string name)
{
this.age = age;
this.name = name;
}
3. In Employee, override ToString to return the Employees name.
public override string ToString()
{
return name;
}
See arrays.sln
Defining Arrays
C# Professional Skills Development 9-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
4. In the test method Run, create an array of four Employee objects.
public void Run()
{
Employee[] empArray = new Employee[4];
5. Instantiate an Employee object, passing in the value 20 as the first
parameter (age) and John Galt as the second parameter (name). You get
back a reference to the Employee; rather than assigning this to a local
variable, assign it directly to the first array element.
empArray[0] = new Employee(20, "John Galt");
6. Assign three more Employee objects to the subsequent array elements.
empArray[1] = new Employee(30, "Dagney Taggert");
empArray[2] = new Employee(40, "Hank Reardon");
empArray[3] = new Employee(50, "Ayn Rand");
7. Iterate through the array and display the contents. For the first iteration u
se
a simple for loop. To test the counter variable (i) use the length of the
array. The length property returns the number of elements in the array (4),
so you want to iterate from 0 through 3 (n-1).
for (int i = 0; i < empArray.Length; i++)
Console.WriteLine(
"empArray[" + i.ToString() + "]: {0}",
empArray[i].ToString());
8. Iterate through the loop again, this time using a foreach loop.
foreach (Employee instance in empArray)
Console.WriteLine("{0}", instance.ToString());
The results are shown in Figure 1. The advantage of the simple for loop is that
you have a counter that can be used to identify the members. The advantage of
the foreach loop is that it is simple and clean.
Arrays
9-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 1. Iterating over the array.
Initializing the Array
It is possible to initialize the contents of the array at the time it is instant
iated:
int myFirstIntArray = new int[5] { 2, 3, 4, 5, 6 }
If you are initializing all the members of the array, you are free to leave out
the
operator new, the type, and the size; the compiler will create the appropriately

sized array for you. So, the initialization above is identical to the following
initialization:
int myFirstIntArray = { 2, 3, 4, 5, 6 }
Multidimensional Arrays
C# Professional Skills Development 9-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Multidimensional Arrays
Until now, youve worked only with arrays that have a single index. It is
possible for arrays to have more than one index. This is known as adding
dimensions to the array. Multidimensional arrays come in two flavors:
Rectangular arrays
Jagged arrays (or arrays of arrays)
Rectangular Arrays
A simple array can be thought of as a series of boxes all in a row, as shown in
Figure 2. Here, myInt is a simple (one-dimensional) array with six elements.
Figure 2. A simple array.
The simplest rectangular array is an array of two dimensions. An array of two
dimensions can be thought of as an array with rows and columns, where each
row is like a simple one-dimensional array, as illustrated in Figure 3. Here,
myRectArray is an array of two dimensions. The first dimension describes the
number of rows (3) and the second describes the number of columns (6).
Figure 3. A rectangular array.
In a rectangular array, the number of columns is the same in each row.
Rectangular arrays are not limited to two dimensions. A three-dimensional
Arrays
9-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
array can be thought of as a cube. In C# you are not limited to three
dimensions; you may have four, five, or any number of dimensions that is
useful (though ones mind reels at creating nine-dimensional objects!)
Try It Out!
The best way to see how to work with a rectangular array is to create one.
1. Create a new console application named Arrays2.
2. Create constants for the two dimensions: number of values in a row and
number of values in a column.
const int rows = 4;
const int columns = 5;
3. Instantiate the array.
int[,] rectArray = new int[rows,columns];
Notice that the declaration of the array does not provide the size of the
dimensions; the comma simply indicates that there are two dimensions (for
three dimensions there would be two commas). When the array is instantiated
using the keyword new, however, the size of both dimensions (rows, columns)
must be supplied.
4. Fill the array by using a for loop.
for(int i = 0; i < rows; i++)
for(int j = 0; j < columns; j++)
rectArray[i,j] = i*j+j;
The logic here is to iterate through each row with the outer for loop. Within
each iteration of the row, you iterate through all the columns. The values
placed into the array are somewhat arbitrary (multiplying the current row times
the column and adding the column).
5. Iterate through the array again, this time displaying the contents of the
array.
See Array2.sln
Multidimensional Arrays
C# Professional Skills Development 9-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
for(int i = 0; i < rows; i++)
for(int j = 0; j < columns; j++)
Console.WriteLine("rectArray[{0},{1}] = {2}",
i,j, rectArray[i,j]);
The results of iterating through the array are shown in Figure 4. Notice that fo
r
any given row/column you can verify that the value is correct. For example,
for row = 2 column = 3 (rectArray[2,3]), the result is the row (2) times the
column (2*3 = 6) plus the column (6+3 = 9).
Figure 4. Iterating through an integer array.
6. Add a second array. This time, rather than dimensioning it explicitly,
initialize the array on creation and allow the compiler to size the array
implicitly.
int[,] secondRectArray =
{
{2,4,6,8,10},
{8,10,12,14,16},
{14,16,18,20,22},
{20,22,24,26,28}
};
Arrays
9-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
7. This is a 4x5 array, just as the previous one was. You can iterate through
this array using the same construct.
for(int i = 0; i < rows; i++)
for(int j = 0; j < columns; j++)
Console.WriteLine("secondRectArray[{0},{1}] = {2}",
i,j, secondRectArray[i,j]);
Note that this array must be treated as a 4x5 array. In other C-family languages

(C and C++) a 4x5 array is really a 20-element array that can just as easily be
viewed as a 5x4 array. That is not true in C#, and if you try to treat your arra
y
as a 5x4 array you will receive a compile error. For example, if you were to
write the following code, the program would compile but when you run it, it
would throw an exception:
for(int i = 0; i < columns; i++)
for(int j = 0; j < rows; j++)
Console.WriteLine("secondRectArray[{0},{1}] = {2}",
i,j, secondRectArray[i,j]);
Jagged Arrays
Think of jagged arrays as arrays of arrays. Each sub-array in the jagged array
need not be the same size as the other arrays in the jagged array, as shown in
Figure 5. Here jagged is a jagged array. The definition calls for the first
dimension to be three; so there will be three arrays within jagged. The size of
the internal arrays is not specified. You can see that one has three elements,
one has six, and one has four.
Figure 5. A jagged array.
Multidimensional Arrays
C# Professional Skills Development 9-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
To see how to create a jagged array, youll build a simple array of arrays of
integers.
1. Create a new console application called Array3.
2. In this case youll need only a single constant to indicate the number of
rows.
int[][] jaggedArray = new int[rows][];
3. Instantiate four arrays, each of a different size, for the four rows within
jaggedArray.
jaggedArray[0] = new int[3];
jaggedArray[1] = new int[2];
jaggedArray[2] = new int[4];
jaggedArray[3] = new int[2];
This is the essence of the jagged array. Each new array is assigned to a
member of the array of arrays: jaggedArray and the various arrays need not be
of the same length.
4. Assign values to some of the members of the jagged array.
jaggedArray[0][0] = 2;
jaggedArray[0][1] = 3;
jaggedArray[0][2] = 4;
jaggedArray[1][0] = 5;
jaggedArray[1][1] = 3;
jaggedArray[2][1] = 6;
jaggedArray[3][0] = 30;
jaggedArray[3][1] = 40;
Notice that you do not need to fill all the values. Values that are not set have
a
default value based on the type of the contents of the array (in this case, an
integer array, the values are set to zero).
5. Iterate through the various arrays, displaying their values.
See Arrays3.sln
Arrays
9-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
for (int i = 0; i < jaggedArray[0].Length; i++)
Console.WriteLine("JaggedArray[0][{0}] = {1}",
i,jaggedArray[0][i]);
for (int i = 0; i < jaggedArray[1].Length; i++)
Console.WriteLine("JaggedArray[1][{0}] = {1}",
i,jaggedArray[1][i]);
for (int i = 0; i < jaggedArray[2].Length; i++)
Console.WriteLine("JaggedArray[2][{0}] = {1}",
i,jaggedArray[2][i]);
for (int i = 0; i < jaggedArray[3].Length; i++)
Console.WriteLine("JaggedArray[3][{0}] = {1}",
i,jaggedArray[3][i]);
The results are shown in Figure 6. Notice that the values that were not set are
displayed as 0.
Figure 6. Iterating through the jagged array.
Using the Index Operator
When you access members of an array, you do so through the index operator
([]). With a rectangular array, you access members by using two values within
a single index operator:
Multidimensional Arrays
C# Professional Skills Development 9-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
int a = myRectArray[3,5];
When you access members of a jagged array, however, you put each index into
its own index operator. This is consistent with the idea of an array of arrays:
int a = myJaggedArray[3][5];
Arrays
9-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Params
Until now, all of the parameters to a method have been explicitly declared in
the method signature. There are times, however, when you do not know how
many objects youll want to pass to a method. You can use method
overloading, but C# offers an additional powerful option: the params keyword.
Params declares that your method will take an unknown (at compile time)
number of parameters. Within the method, the parameters are treated as if they
were submitted in an array. When you actually call the method, however, you
pass in any number of objects of the declared type, and the compiler assembles
them into the array.
The easiest way to see how this works is to write a simple test program.
1. Create a new console application named params.
2. Create a method that takes an unknown number of parameters. To do this,
name a single parameter values. You may use any name you want; values
is just an arbitrary name. Declare the type of the parameter to be an int
array, with the keyword params.
public void DisplayValues(params int[] values)
3. Within the body of the method, treat the values parameter as if it were an
array of ints.
public void DisplayValues(params int[] values)
{
foreach (int i in values)
Console.WriteLine(i);
}
4. Create a test class. In the Run method, create a number of int variables and
pass them, in various combinations, to DisplayValues.
See Params.sln
Params
C# Professional Skills Development 9-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public void Run()
{
int x = 5, y = 7, z = 9, a = 11, b = 13;
Console.WriteLine("Pass in all 5 integers...");
DisplayValues(x,y,z,a,b);
Console.WriteLine("\nPass in 3 integers...");
DisplayValues(a,b,x);
}
5. Notice that the first time DisplayValues is called, five integers are passed
in, but the second time, only three are passed in. The results are shown in
Figure 7.
Figure 7. Using params with variable arguments.
6. You can modify the program to take user-defined types rather than
integers. To do so, create a simple Employee class:
class Employee
{
7. Give the class a single private variable of type string, and a constructor to

initialize that variable.
Arrays
9-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
private string name;
public Employee(string name)
{
this.name = name;
}
8. Override the ToString() method to return the Employees name.
public override string ToString()
{
return name;
}
9. Modify the DisplayValues method to take an array of Employee objects.
public void DisplayValues(params Employee[] values)
{
foreach (Employee e in values)
Console.WriteLine(e);
}
10. Modify the test program. Comment out the integer test and create four
Employee objects. Pass them into DisplayValues and note that the same logic
applies to treating the parameter now as an array of Employee objects.
public void Run()
{
/*
int x = 5, y = 7, z = 9, a = 11, b = 13;
Console.WriteLine("Pass in all 5 integers...");
DisplayValues(x,y,z,a,b);
Console.WriteLine("\nPass in 3 integers...");
DisplayValues(a,b,x);
*/
Employee john = new Employee("John");
Params
C# Professional Skills Development 9-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Employee paul = new Employee("Paul");
Employee george = new Employee("George");
Employee ringo = new Employee("Ringo");
Console.WriteLine("\nNow pass in Employees...");
DisplayValues(john, paul, george, ringo);
}
The results are shown in Figure 8. The params keyword can just as easily be
used with user-defined types as it can with intrinsic types.
Figure 8. Params with user-defined types.
Mixing Types
You can create a method that takes a variable number of parameters of
different types. To do so, you take advantage of the fact that everything in C#
derives from object. Declare the params type to be object and it will be safe to

pass in objects of any type.
Reopen the project you just completed, and modify DisplayValues to take an
object rather than an Employee:
public void DisplayValues(params object[] values)
{
foreach (object o in values)
Console.WriteLine(o);
}
Uncomment the int test and test both int and Employee:
Arrays
9-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public void Run()
{
int x = 5, y = 7, z = 9, a = 11, b = 13;
Console.WriteLine("Pass in all 5 integers...");
DisplayValues(x,y,z,a,b);
Console.WriteLine("\nPass in 3 integers...");
DisplayValues(a,b,x);
Employee john = new Employee("John");
Employee paul = new Employee("Paul");
Employee george = new Employee("George");
Employee ringo = new Employee("Ringo");
Console.WriteLine("\nNow pass in Employees...");
DisplayValues(john, paul, george, ringo);
}
The result is shown in Figure 9. This works because you are calling only
methods common to the ToString()object (which is implicitly called by
Console.WriteLine()).
Figure 9. Mixing types with params.
Params
C# Professional Skills Development 9-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
If you want to take advantage of a method or property specific to Employee,
you will have to cast the parameter. Once again, you can use the keyword is to
test whether the object is of the expected type. To see this, reopen the example

and add a property to Employee:
public string Name
{
get { return name; }
}
You can now modify DisplayValues to test whether the object you are working
with is an Employee. If so, you can get the property:
if (o is Employee)
{
Employee e = (Employee)o;
Console.WriteLine("Got Employee: {0}",
e.Name);
}
If the object is an Employee, you can cast the object and then invoke the
property. The result is shown in Figure 10. Notice that for the employees the
cast is skipped (an int is not an Employee) but for the employees the test
returns true and the value is printed using the property.
Arrays
9-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 10. Casting the object.
The params keyword gives you a great deal of flexibility when defining
methods; you can decide at run time how many objects to pass in to the
method and treat the result as an array.
Indexers
C# Professional Skills Development 9-21
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Indexers
Arrays are built into C#, but it would be nice if you could add the ability to
access data within your own collection classes using indexing. For example,
assume you have a user-defined type office that acts as a collection of
employees. It would be convenient if you could index into the office to find the

fifth employee with the following code:
Employee employeeOfTheWeek = myOffice[4];
The square brackets operator is called the index operator (sometimes called the
indexer). You can overload this operator much as you overload other operators
in C#.
NOTE Index operators only really make sense with classes that act like a
collection.
Overloading the Index Operator
Indexers are implemented in a way very similar to properties. You use the this
keyword along with the index type:
public string this[int offset]
{ get{} set {} }
The code above defines an index. Offset is an integer value that will be used to

index into your collection. By providing both a get and a set, you allow the
client to read from and write to your collection using the indexer:
// read from the collection
Employee employeeOfTheWeek = myOffice[4];
// create a new Employee
Employee joe = new Employee(25,Joe);
// write to the collection
myOffice[12] = joe;
Arrays
9-22 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
You can index on values other than integers! For example, it might make sense
to allow the client to index on strings:
public string this[string name]
{ get{} set {} }
This allows you to write code like this:
Employee employeeOfTheWeek = myOffice[Joe];
The code shown above will index into the Office instance myOffice and find
and retrieve the Employee whose name is Joe.
Try It Out!
The best way to see how to work with indexers is to write a small test program.
1. Create a new console application named Indexer.
2. Create an Employee class to store within your collection. The Employee
class will have two fields, both strings. The field name will hold the
Employees name and the field empID will hold the Employees employee
ID.
3. Create a constructor to initialize the fields, and create properties to acces
s
the two fields.
public class Employee
{
string name;
string empID;
public Employee(string name, string ID)
{
this.name = name;
empID = ID;
}
public string EmpID
{
See Indexers.sln
Indexers
C# Professional Skills Development 9-23
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
get
{
return empID;
}
}
public string Name
{
get
{
return name;
}
}
}
4. Create a collection class named office to hold Employees. You could store
the employees in any number of collection types, or you could store the
employees in a database. To keep the example simple, youll store the
Employee objects in a simple array. In addition, youll keep an integer
field to keep track of the number of Employee objects already in the array.
public class Office
{
private Employee[] employees;
private int ctr = 0;
5. Initialize the array of Employee objects in the constructor. Allow enough
room for all the Employees your office will hold. The constructor will take
a variable number of Employee objects, by using the params keyword.
Initialize the array with the Employees passed in to the constructor.
Arrays
9-24 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public Office(params Employee[] employeeList)
{
// allocate space for the employees
this.employees = new Employee[256];
// copy the employees passed in to the constructor
foreach (Employee e in employeeList)
{
this.employees[ctr++] = e;
}
}
6. Add an indexer for the office, based on an integer value. The indexer will
take an int, and test to make sure the int is within the legal range. If not, it

will handle the error (possibly throwing an exception); otherwise it will set
or retrieve the appropriate value.
Indexers
C# Professional Skills Development 9-25
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public Employee this[int index]
{
get
{
if (index < 0 || index >= employees.Length)
{
// handle bad index
}
return employees[index];
}
set
{
if (index >= employees.Length)
{
// handle error
}
else
{
employees[index] = value;
if (ctr < index)
ctr = index;
}
}
}
Notice that the underlying array is now acting as a sparse array. That is, it is

legal to set an Employee at offset 25 even if you only have four Employee
objects in the array. If you prefer not to allow this, you handle that problem i
n
the set part of the indexer.
If you were storing your Employee objects in a database, then the get and set
part of the indexer would manage retrieving and storing the data in the
database.
7. Add a method, DisplayEmployees that iterates over the array of Employee
objects and displays them to the console.
Arrays
9-26 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public void DisplayEmployees()
{
foreach (Employee e in employees)
{
if (e != null)
{
Console.WriteLine("{0} Employee ID: {1}",
e.Name, e.EmpID);
}
}
}
8. Write a test class and method to create Employee objects and add them to
the office.
public void Run()
{
Employee[] empArray = new Employee[5];
empArray[0] = new Employee("John Galt","001");
empArray[1] = new Employee("Figaro Barbara", "002");
empArray[2] = new Employee("Wotan Warrior", "003");
empArray[3] = new Employee("George Washington", "004");
empArray[4] = new Employee("John Adams", "005");
Office office = new Office(empArray);
In the code shown above, a new office is created by passing an array of
Employee objects to the office constructor. It is legal to pass an array to a
params parameter, though you could have passed the five Employee objects
individually.
9. Add another Employee through the indexer.
office[10] = new Employee("Thomas Jefferson", "007");
10. Call DisplayEmployees to see the Employees youve added.
office.DisplayEmployees();
Indexers
C# Professional Skills Development 9-27
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
11. Access an Employee using the indexer, and assign that Employee to a
local object.
Employee empOfTheMonth = office[3];
12. Access the local Employee objects Name property, and display it.
Console.WriteLine("Employee of the month: {0}",
empOfTheMonth.Name);
The results of this code are shown in Figure 11.
Figure 11. Adding an indexer to a class.
The syntax of working with indexers in a user-defined collection is
comfortable and, once again, furthers the aim of making classes act just as
intrinsic types do.
Indexing on Strings
With this application working, you are ready to add an index based on the
Employees name
1. Add a new indexer to Office based on string.
See Indexer2.sln
Arrays
9-28 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public Employee this[string index]
{
get
{
if (index.Length == 0)
{
// handle bad index
}
return this[FindEmployee(index)];
}
set
{
employees[FindEmployee(index)] = value; }
}
2. Both the get and the set index functions require that you access the intbased

indexer, passing in the int value of the Employee record
corresponding to the string value index that was passed in as a parameter.
If the client passes in Joe as a string, that value will be passed to the method

FindEmployee (described in the next step). FindEmployee will return an int
representing the index for the record corresponding to the Employee whose
name is Joe. You then pass that int to the index that takes an int and you get
back the Employee record.
3. Write the findEmployee method. It takes a string representing the
Employees name, and iterates through its records until it finds the correct
record.
Indexers
C# Professional Skills Development 9-29
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
private int FindEmployee(string employeeName)
{
for (int i = 0;i<employees.Length;i++)
{
if (employees[i] != null &&
employees[i].Name == employeeName)
{
return i;
}
}
return -1;
}
Because Office supports a sparse array it is possible that the record at a given

offset might be null. If so, accessing the Name property would throw an
exception. The first half of the if statement tests that employees[i] is not nul
l.
If it is null, then the second half of the and statement within the if is never
tested. This is called short-circuit evaluation, and is covered elsewhere in thi
s
course.
The net effect is that the Name property is only accessed on non-null entries in

the underlying array. If the name in the record matches the parameter, then the
value of the offset is returned, and it is that value (i) that is used as an off
set
into the int-based index.
4. Add to the test code to retrieve the record using the string:
public void Run()
{
Employee[] empArray = new Employee[5];
empArray[0] = new Employee("John Galt","001");
empArray[1] = new Employee("Figaro Barbara", "002");
empArray[2] = new Employee("Wotan Warrior", "003");
empArray[3] = new Employee("George Washington", "004");
empArray[4] = new Employee("John Adams", "005");
Office office = new Office(empArray);
office[10] = new Employee("Thomas Jefferson", "007");
office[20] = new Employee("Sam Adams", "008");
Employee empOfTheMonth = office[3];
Arrays
9-30 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Employee bestBrewer = office["Sam Adams"];
Console.WriteLine("Employee of the month: {0}",
empOfTheMonth.Name);
Console.WriteLine("Best brewer in the office: {0}",
bestBrewer.Name);
Console.WriteLine("Office[10] = {0}",
office[10].Name);
}
The results of running this test program are shown in Figure 12.
Figure 12. Using the string-based indexer.
5. The use of the string-based indexer can be a bit confusing. To make it
explicit, put a breakpoint on line 150 where bestBrewer is initialized using
the string offset operator, as shown in Figure 13.
Indexers
C# Professional Skills Development 9-31
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 13. Setting a breakpoint on the string-based indexer.
6. Press F5 to run to the breakpoint.
7. Press F11 to step into the indexer. Press F11 again to get to the return
statement.
return this[findEmployee(index)];
The return statement calls FindEmployee, passing in the index, as shown in
Figure 14. You can see that the index passed in to this method is the string
SamAdams (see the circled, highlighted value in the Locals window).
Arrays
9-32 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 14. Invoking FindEmployee.
8. Press F11 to step into FindEmployees. As you step through, for each entry
in employees that is not null you will step into the Name property. The
value returned is then compared to employeeName (Sam Adams).
9. Set a breakpoint return i on line 106. This breakpoint will fire when the
Name property matches the employeeName parameter. Run to the
breakpoint.
10. When the breakpoint fires, check the Locals window. The value of i is the
index at which the matching record is found, as shown in Figure 15.
Indexers
C# Professional Skills Development 9-33
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 15. Returning the value of the offset for the target record.
You can see that i is set to the value 20. That is the value returned by
FindEmployee.
11. Press F11 to exit from FindEmployee, returning back to the string indexer.
Notice that the return statement invokes the integer-based indexer. The
value returned from FindEmployee is used as the index value.
12. Press F11 again to step into the int-based indexer. Here the value (20) is
used as an index into the underlying array, and the value within that array
(the Employee object at offset 20) is returned, as shown in Figure 16.
Arrays
9-34 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 16. Retrieving the Employee object.
What is returned is an Employee object, which is assigned to the reference
bestBrewer. You may then use that reference to refer to the object held in the
office at offset Sam Adams.
Indexers
C# Professional Skills Development 9-35
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
Arrays offer a simple sized collection of objects in which all objects
are of the same type.
Arrays are indexed, starting at 0.
When you instantiate an array you must size it, or you must provide
the initialization list from which the compiler can deduce the size.
Foreach loops can be used to iterate over the array, extracting each
value in turn.
Arrays may have more than one dimension.
Multidimensional arrays come in two flavors: rectangular and jagged.
Jagged arrays are best described as arrays of arrays.
You can add an index operator to your own classes.
Index operators only makes sense in classes that act as collections.
Index operators can index on any type, such as string, int, etc.
Arrays
9-36 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
(Review questions and answers on the following pages.)
Indexers
C# Professional Skills Development 9-37
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. How do you refer to the second item in an array?
2. How do you refer to the fifth item in the third row of a two-dimensional
rectangular array?
3. How do you refer to the fifth item in the third row of a two-dimensional
jagged array?
4. How do you declare a method that returns void and takes one or more
integer variables as parameters?
5. How do you declare an indexer for a collection class Jar that indexes on an
integer and returns a Marble object?
6. How do you declare an indexer for a collection class Jar that indexes on a
string and returns a Marble object?
Arrays
9-38 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. How do you refer to the second item in an array?
myArray[1]; // remember offsets start at 0
2. How do you refer to the fifth item in the third row of a two-dimensional
rectangular array?
myArray[2,4];
3. How do you refer to the fifth item in the third row of a two-dimensional
jagged array?
myArray[2][4];
4. How do you declare a method that returns void and takes one or more
integer variables as parameters?
public void myMethod(params int[] myParameter)
5. How do you declare an indexer for a collection class Jar that indexes on an
integer and returns a Marble object?
public Marble this[int index]{get{ // } set {//} };
6. How do you declare an indexer for a collection class Jar that indexes on a
string and returns a Marble object?
public Marble this[string index]{get{ // } set {//} };
Indexers
C# Professional Skills Development 9-39
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 9:
Arrays
Lab 9:
Arrays
9-40 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 9 Overview
In this lab youll learn to work with Arrays and the Params keyword, and
youll work with Indexers.
To complete this lab, youll need to work through two exercises:
Using the Keyword Params
Implementing an Indexer
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-bystep

instructions.
Using the Keyword Params
C# Professional Skills Development 9-41
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Using the Keyword Params
Objective
In this exercise, youll explore passing a variable number of parameters to a
method using the params keyword.
Things to Consider
The params keyword signals that the parameters passed should be
treated like an array.
You can pass in values one by one, or you can pass in an array.
You can iterate over an array using the foreach loop.
Step-by-Step Instructions
1. Open the project named ParamsStarter.sln in the ParamsStarter folder.
2. In the Cat class, create the constructor.
public Cat(string name)
{
this.name = name;
}
3. Override ToString to return the Cats name.
public override string ToString()
{
return name;
}
4. Provide a public, read-only property for the name.
Lab 9:
Arrays
9-42 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public string Name
{
get { return name; }
}
5. Within the Params class, create a method DisplayValues. This method
takes a variable number of parameters.
public void DisplayValues(params object[] values)
{
6. Within DisplayValues iterate over the objects provided. If the iterator has a

Cat object, display the Cats name.
foreach (object o in values)
{
Console.WriteLine(o);
if (o is Cat)
{
Cat c = (Cat)o;
Console.WriteLine("Got Cat: {0}",
c.Name);
}
}
7. Within the Run method, create five integers and pass them to
DisplayValues.
int x = 5, y = 7, z = 9, a = 11, b = 13;
Console.WriteLine("Pass in all 5 integers...");
DisplayValues(x,y,z,a,b);
8. Call DisplayValues again, and pass in only three integers.
Console.WriteLine("\nPass in 3 integers...");
DisplayValues(a,b,x);
Using the Keyword Params
C# Professional Skills Development 9-43
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
9. Create four cat objects and pass them to DisplayValues.
Cat frisky = new Cat("Frisky");
Cat boots = new Cat("Boots");
Cat puff = new Cat("Puff");
Cat poco = new Cat("Poco");
Console.WriteLine("\nNow pass in Cats...");
DisplayValues(frisky, boots, puff, poco);
10. Create an array of four Cat objects. Fill the Array with the four Cat object
s
you created in Step 9 and pass that array to DisplayValues.
Cat[] catArray = new Cat[4];
catArray[0] = frisky;
catArray[1] = boots;
catArray[2] = puff;
catArray[3] = poco;
Console.WriteLine("\nNow pass in an array of Cats...");
DisplayValues(catArray);
11. Compile and run the program, as shown in Figure 17.
Lab 9:
Arrays
9-44 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 17. Final exercise results.
Implementing an Indexer
C# Professional Skills Development 9-45
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Implementing an Indexer
Objective
In this exercise, youll create a Cat class and a Kennel class. The Kennel will
hold a collection of Cat objects and will provide array-like access using an
indexer.
Things to Consider
The Kennel class must implement its collection, either using an
underlying private member variable or interacting with a back-end
data store such as a database.
The indexer should check for out of bounds requests.
Step-by-Step Instructions
1. Open IndexerStarter.sln in the Indexer Starter folder.
2. Create a Cat class. Youll use this as an object to hold in your Kennel
class.
public class Cat
{
3. Cat gets two member variables: a name and an age.
string name;
int age;
4. Implement a constructor.
Lab 9:
Arrays
9-46 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public Cat(string name, int age)
{
this.name = name;
this.age = age;
}
5. Implement read-only properties for the age and the name.
public int Age
{
get
{
return age;
}
}
public string Name
{
get
{
return name;
}
}
6. Create a Kennel class to act as a collection for the Cat objects.
public class Kennel
{
7. Give Kennel a private array of Cat objects. This will act as the backingstore

for the underlying data.
private Cat[] cats;
8. Create the constructor. Pass in a variable number of Cat objects (using
Params).
Implementing an Indexer
C# Professional Skills Development 9-47
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public Kennel(params Cat[] catList)
{
// allocate space for the cats
this.cats = new Cat[256];
int ctr = 0;
// copy the cats passed in to the constructor
foreach (Cat c in catList)
{
this.cats[ctr++] = c;
}
}
9. Implement the indexer, allowing the user to index into the Kennel by
providing an integer (just as you would do for an array).
public Cat this[int index]
{
get
{
if (index < 0 || index >= cats.Length)
{
// handle bad index
}
return cats[index];
}
set
{
if (index >= cats.Length)
{
// handle error
}
else
{
cats[index] = value;
}
}
}
Lab 9:
Arrays
9-48 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
10. Implement a method DisplayCats that iterates over the collection and
displays the name and age.
public void DisplayCats()
{
foreach (Cat c in cats)
{
if (c != null)
{
Console.WriteLine("{0} is {1} years old",
c.Name, c.Age);
}
}
}
11. Within the Run method, create an array of five Cat objects and populate
the array.
Cat[] catArray = new Cat[5];
catArray[0] = new Cat("Frisky",5);
catArray[1] = new Cat("Figaro", 2);
catArray[2] = new Cat("Wotan", 3);
catArray[3] = new Cat("Washington",4);
catArray[4] = new Cat("Adam", 5);
12. Create an instance of Kennel and pass in the array.
Kennel kennel = new Kennel(catArray);
13. Assign a new Cat object to the eleventh offset into the array:
kennel[10] = new Cat("Jefferson", 3);
14. Display the array.
kennel.Displaycats();
Implementing an Indexer
C# Professional Skills Development 9-49
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
15. Get the fourth member and display it.
Cat prettyKitty = kennel[3];
Console.WriteLine("PrettyKitty: {0}",
prettyKitty.Name);
16. Compile and run the program, as shown in Figure 18.
Figure 18. Final exercise results.
Lab 9:
Arrays
9-50 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Collection Interfaces
C# Professional Skills Development 10-1
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Collection
Interfaces
Objectives
Understand the role and uses of the various collection interfaces.
Implement IEnumerable to enable custom classes to support the
foreach loops enumeration.
Use ArrayList to provide dynamically sized arrays.
Implement IComparer to enable sorting of user-defined classes.
Create a custom IComparer instance to enable dynamic sorting of
user-defined types.
Collection Interfaces
10-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Standard Collection Interfaces
The .NET Framework defines a number of standard interfaces to help you
build collection classes that provide the core functionality of the .NET
collection classes. If your collection classes implement these interfaces, they
will look and feel like fully functional collections to your clients.
The standard interfaces define what your classes must do to support
comparing, enumerating, and creating collections. This chapter examines a
couple of these interfaces in some detail.
IEnumerable
You must implement the IEnumerable interface if you want your application to
support foreach loops. When the compiler sees a foreach loop it invokes the
method described in the IEnumerable interface.
The IEnumerable interface requires only one method: GetEnumerator. The job
of GetEnumerator is to return an object of a class that implements
IEnumerator. This allows your class to return an IEnumerator specialized to
enumerate over your collection.
Implementing IEnumerable
Lets consider an example. Assume you have a class Employee that represents
an Employee of a firm, and you have a class Office that represents an office
location. You would like to have your Office class function as a collection, and

you might provide it with an indexer as shown in a previous chapter.
Because your Office class acts as a collection, youd like to allow clients of
your class to enumerate over the Office, retrieving each Employee object in
turn. To do so, you will designate your Office class to implement IEnumerable.
public class Office : IEnumerable
When your code says that your Office class implements IEnumerable, you
must provide a GetEnumerator method:
public IEnumerator GetEnumerator()
{
return (IEnumerator) new OfficeEnumerator(this);
}
Standard Collection Interfaces
C# Professional Skills Development 10-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
In this case, your GetEnumerator method returns an OfficeEnumerator object;
that is an object that specializes the implementation of IEnumerator for Office
objects.
OfficeEnumerator must implement IEnumerator, providing the required
methods and properties, which are listed in Table 1.
Method or Property Descriptions
MoveNext() Advance the enumerator
Reset() Set the enumerator to initial position
Current Get the current element
Table 1. The methods and properties of IEnumerator.
Implementing IEnumerator
The specialized IEnumerator may be implemented by the Office class itself, or
by a separate class, e.g., OfficeEnumerator. This allows Office to delegate
responsibility for enumeration to OfficeEnumerator, which better encapsulates
the details of enumerating over the collection.
In the common design pattern, OfficeEnumerator is implemented as a nested
class; that is, as a class defined within the definition of Office. Nesting the
private implementing class hides it from all external classes. Since
OfficeEnumerator is of interest only to Office, nesting OfficeEnumerator
within Office simplifies your larger program.
OfficeEnumerator must know which Office it is enumerating. You will pass in
a reference to the current Office object as a parameter to the OfficeEnumerator
constructor.
The relationship between Office and the OfficeEnumerator class is shown in
Figure 1. OfficeEnumerator derives from IEnumerator, and so implements the
methods MoveNext and Reset as well as the property Current. It also has two
private member fields: Officea reference to the current Office object, and
indexused for tracking the current Employee during enumeration.
Collection Interfaces
10-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 1. The Office and OfficeEnumerator classes.
The outer Office class has an array of Employees (though it could just as easily

store its Employees in another collection or in a database. It also has a method

GetEnumerator that returns an instance of the nested OfficeEnumerator class.
Try It Out!
The relationship between the outer Office class and the nested
OfficeEnumerator class is best understood from an example.
1. Create a new console application named Enumerator.
2. Create a simple Employee class. Provide two fields, name and empID, and
initialize them in the constructor. Provide public properties for these fields.
public class Employee
{
string name; // employees name
string empID; // employees ID
// constructor
public Employee(string name, string ID)
{
this.name = name;
See enumerator.sln
Standard Collection Interfaces
C# Professional Skills Development 10-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
empID = ID;
}
// public properties
public string EmpID
{
get {return empID; }
}
public string Name
{
get { return name; }
}
}
3. Add the Office class, and have it implement IEnumerable. Provide two
member fields: employees, an array of Employee objects, and ctr, which
keeps track of the number of Employees in the array.
public class Office : IEnumerable
{
private Employee[] employees;
private int ctr = 0;
4. Add a nested class OfficeEnumerator that implements IEnumerator. Give
that class two fields: office, a reference to the Office instance and index, to
keep track of which Employee is current.
public class Office : IEnumerable
{
private Employee[] employees;
private int ctr = 0;
private class OfficeEnumerator : IEnumerator
{
private Office office;
private int index = -1;
Collection Interfaces
10-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Notice that the definition of OfficeEnumerator is nested within the definition
of Office. Note also that index is initialized to -1, to designate that no recor
d is
current.
5. Add a constructor for OfficeEnumerator. Pass in the current instance of
Office and initialize the member field.
public OfficeEnumerator (Office office)
{
this.office = office;
}
The constructor is public within the private nested class and is private to othe
r
classes.
6. Implement the first of the two required methods, MoveNext. Increment the
index to point to the next object. Test whether index is greater than or
equal to the length of the array held in Office. If so, the index is past the
end of the array and the code must return false, otherwise return true.
public bool MoveNext()
{
index++;
if (index >= office.employees.Length)
return false;
else
return true;
}
Notice that OfficeEnumerator has intimate knowledge of the private members
of Office. This is a privilege of a nested class, and the semantics are that the

OfficeEnumerator knows the details of Office, but Office need not know the
details of OfficeEnumerator.
7. Implement the second required method: Reset. To implement this method,
just set index back to its initial value of -1.
Standard Collection Interfaces
C# Professional Skills Development 10-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public void Reset()
{
index = -1;
}
8. Add the required property: Current. The Current property returns the
current object. In this case, it uses the index field as an offset into Office,
allowing Offices index operator to return the correct Employee object:
public object Current
{
get
{
return (office[index]);
}
}
9. Close off the definition of the nested class and implement the methods of
Office. Start with the required GetEnumerator method. Note that
GetEnumerator must return an object of type IEnumerator. The code
returns an OfficeEnumerator.
public IEnumerator GetEnumerator()
{
return new OfficeEnumerator(this);
}
Notice that there is no need to explicitly cast the OfficeEnumerator to an
IEnumerator. Because OfficeEnumerator implements IEnumerator, the cast is
implicit. The value returned will be an object of type IEnumerator, because
that is declared in the definition of GetEnumerator.
10. Implement the Office constructor.
Collection Interfaces
10-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public Office(params Employee[] employeeList){
this.employees = new Employee[256];
foreach (Employee e in employeeList)
{
this.employees[ctr++] = e;
}
}
11. Implement the Office indexer.
public Employee this[int index]
{
get
{
if (index < 0 || index >= employees.Length)
{
// handle bad index
}
return employees[index];
}
set
{
if (index >= employees.Length)
{
// handle herror
}
else
{
employees[index] = value;
if (ctr < index)
ctr = index;
}
}
}
Standard Collection Interfaces
C# Professional Skills Development 10-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
12. You are ready to test the office. Create an instance of the office, and
populate it with a series of Employee objects.
Employee jg = new Employee("John Galt","001");
Employee fb = new Employee("Figaro Barbara", "002");
Employee ww = new Employee("Wotan Warrior", "003");
Employee gw = new Employee("George Washington", "004");
Employee ja = new Employee("John Adams", "005");
Office office = new Office(jg, fb, ww, gw, ja);
The Office constructor uses the params keyword to allow you to add as many
Employee objects as youd like.
13. Add a couple more Employees to the office, using the indexer.
office[10] = new Employee("Thomas Jefferson", "007");
office[20] = new Employee("Sam Adams", "008");
14. Use a foreach loop to iterate over the office.
foreach (Employee e in office)
{
if (e != null)
{
Console.WriteLine("{0} Employee ID: {1}",
e.Name, e.EmpID);
} // end if
} // end for each
Notice that you must test each member of the array to ensure it is not null. The

design of the office is that space is allocated for 256 employees, but those
spaces are initialized to null.
15. To prove that the foreach loop requires the implementation of
IEnumerator, put a breakpoint on line 135 at the start of the foreach loop,
as shown in Figure 2. Run to the breakpoint.
Collection Interfaces
10-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 2. A breakpoint at the foreach loop.
16. Step into the foreach with F8. Immediately move into MoveNext, as shown
in Figure 3. Notice that the index is set to -1, its initial value. It is about
to
be incremented to 0 to point to the first element in the collection.
Standard Collection Interfaces
C# Professional Skills Development 10-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 3. Move Next called by foreach.
17. Continue stepping; you return to the foreach header, and then, where in the
header it says Employee it is time to retrieve the Employee object. Step
into the Current property of the OfficeEnumerator, where the current
Employee object is retrieved from Office, using the current value of index
(0), as shown in Figure 4.
Collection Interfaces
10-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 4. Retrieving the current Employee.
18. Continue stepping through the program; youll see that you step into the
indexer to retrieve the current Employee object.
19. As you continue stepping through the program you will enter MoveNext
and then Current, for each iteration of the foreach loop.
ArrayList
For the next interface examples youll want to work with an ArrayList.
ArrayLists exist to overcome a significant limitation in normal arrays: arrays
are of a fixed size. When you declare an array you must tell the compiler how
many elements it will have. This is a problem, as youve seen with office.
If you underestimate the number of elements youll need, you may run out
while the program is running with no easy way to add more elements to the
array. If you allow more than you need, then you waste a great deal of
memory.
Standard Collection Interfaces
C# Professional Skills Development 10-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The ArrayList is much like an Array, except that it grows as you need it to,
dynamically, while the program is running. You add to an arrayList with the
Add method. The most important ArrayList methods are shown in Table 2.
Method/ Property Description
Count Get number of elements
Add() Add an object
Clear() Remove all objects
Reverse() Reverse order of elements
Sort() Sort the elements
Table 2. ArrayList methods.
IComparable
Notice that the ArrayList, like many .NET collections, offers a Sort() method.
To sort the ArrayList, the objects in the array must implement IComparable.
IComparable has only one method: CompareTo().
For Employees to be sortable within an ArrayList or other collection, the code
must implement IComparable. To do so, it must implement the CompareTo
method, which compares the current employee with a second employee and
returns a value indicating which is greater and which is lesser. How you define
greater and lesser is entirely up to the designer of the Employee class. You
might do so by comparing their employee ID, their years of service, their rank
in the corporation; or you might sort them alphabetically by last name.
All of the intrinsic types support IComparable, and offer a CompareTo
method. Strings return values for alphabetical sorts, ints, and other numeric
values return values for a value sort.
Try It Out!
To see how to implement IComparable, youll revise the Employee class from
the previous example to support sorting.
1. Reopen the previous example.
2. Mark Employee to implement Icomparable.
public class Employee : IComparable
See comparable.sln
Collection Interfaces
10-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
3. Implement the required method CompareTo. This method takes an object
as a parameter. In this case, the object will be another Employee object.
One convention is to name the parameter rhs, as it is on the right-hand side
of the comparison operator.
Within CompareTo, create a local instance of Employee, and assign to it the
object passed as a parameter, cast to an Employee. Call the CompareTo
method on the name field, passing in the name field of the rhs parameter:
public int CompareTo(Object rhs)
{
Employee right = (Employee) rhs;
return this.name.CompareTo(right.name);
}
The net effect is that the value returned by CompareTo is the value returned by
calling CompareTo on the name fields. That is, the Employee objects are
sorted alphabetically.
4. Modify the Office to use an ArrayList rather than an array. You no longer
need the ctr field.
public class Office : IEnumerable
{
private ArrayList employees;
5. Modify OfficeEnumerator.MoveNext to use the Count field of the
ArrayList.
public bool MoveNext()
{
index++;
// ** change to count
if (index >= office.employees.Count)
return false;
else
return true;
}
6. Modify the Office constructor to initialize and use the array list.
Standard Collection Interfaces
C# Professional Skills Development 10-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public Office(params Employee[] employeeList)
{
// *** change
this.employees = new ArrayList();
foreach (Employee e in employeeList)
{
// *** change
this.employees.Add(e);
}
}
7. Add a new Add method to the office. This method takes an Employee
object and adds it to the Array list.
public void Add(Employee e)
{
employees.Add(e);
}
8. Add a Sort method to the Office and delegate the sort to the ArrayList.
public void Sort()
{
employees.Sort();
}
9. Modify the indexer. Notice that you must now cast the result of indexing
into the ArrayList, because the ArrayList indexer returns an object, not an
Employee. Since you know that only Employee objects have been stored
in the ArrayList, the cast is safe. Notice also that ctr is now gone, and you
must use employees.Count.
Collection Interfaces
10-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public Employee this[int index]
{
get
{ // *** change ***
if (index < 0 || index >= employees.Count)
{
// handle bad index
}
// *** CAST ***
return (Employee)employees[index];
}
set
{
// ** change to count
if (index >= employees.Count)
{
// handle herror
}
else
{
employees[index] = value;
// ** change no ctr ***
}
}
}
10. Modify the test code. Add a call to office.Sort and then redisplay the
contents of the ArrayList.
public void Run()
{
Employee jg = new Employee("John Galt","001");
Employee fb = new Employee("Figaro Barbara", "002");
Employee ww = new Employee("Wotan Warrior", "003");
Employee gw = new Employee("George Washington", "004");
Employee ja = new Employee("John Adams", "005");
Office office = new Office(jg, fb, ww, gw, ja);
office.Add(new Employee("Thomas Jefferson", "007"));
Standard Collection Interfaces
C# Professional Skills Development 10-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
office.Add(new Employee("Sam Adams", "008"));
foreach (Employee e in office)
{
Console.WriteLine("{0}",e.ToString());
}
// *** Sort the array and display the results ***
office.Sort();
Console.WriteLine ("\n\nAfter sort: \n");
foreach (Employee e in office)
{
Console.WriteLine("{0}",e.ToString());
}
}
The results of these changes are shown in Figure 5. Note that the names are
sorted alphabetically by first name.
Figure 5. Sorting Employees by name.
Collection Interfaces
10-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Custom IComparer
The Sort method uses the default IComparer. You may choose to implement
your own IComparer. Doing so allows you to customize the comparison. For
example, right now Employees is sorted by name. It may be that under some
conditions, to be determined at run time, you will want to sort by ID rather
than by name. You can choose which sort to use by creating a custom
IComparer.
To create a custom IComparer you will create an EmployeeComparer class
that will implement IComparer. EmployeeComparer will be a private nested
class within Employee, as illustrated in Figure 6. The EmployeeComparer will
be responsible for using the correct comparison.
Figure 6. The EmployeeComparer class nested within Employee.
Within EmployeeComparer you will declare an enumeration
EmployeeComparison. If the client wishes to sort by name, the
whichComparision field of EmployeeComparer will be set to
Employee.EmployeeComparer.EmployeeComparison.name. In fact, the
definition for the whichComparision field is:
private Employee.EmployeeComparer.EmployeeComparison
whichComparison;
Standard Collection Interfaces
C# Professional Skills Development 10-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Note that the type of the whichComparison field is
Employee.EmployeeComparer.EmployeeComparison. That is, the
EmployeeComparison enumeration is scoped within the EmployeeComparer
class, which in turn is scoped within Employee. If you refer to this
enumeration from a method of another class, you must use the full name.
Try It Out!
To see how to write your own customized Icomparer, youll modify your
previous example to customize Employee comparisons.
1. Reopen the previous project.
2. Create a new nested class within Employee, EmployeeComparer that
implements IComparer.
public class Employee : IComparable
{
string name;
string empID;
public class EmployeeComparer : IComparer
{
3. Within EmployeeComparer, create an enumeration EmployeeComparison
to hold constant values for the two types of comparisons youll support.
public enum EmployeeComparison
{
name,
ID
}
In this case you did not set values for name and ID, so their values will be 0
and 1, respectively. You dont care about the actual constant value, however,
because youll be using these enumerations as flags to signal the type of
comparison to be done, as youll see shortly.
4. Add a member variable to hold the current comparison type. The client
will set this variable, and the comparison that is executed will be
determined by the current value of this variable.
See
Comparable2.sln
Collection Interfaces
10-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
private Employee.EmployeeComparer.EmployeeComparison
whichComparison;
5. Create a property to make this value accessible to clients. You want this to
be read/write because your clients will need to set this value when they
want to change the type of sort.
public
Employee.EmployeeComparer.EmployeeComparison
WhichComparison
{
get { return whichComparison; }
set { whichComparison = value; }
}
6. Implement the required Compare method. Compare must take two objects
and compare them. You know these objects are Employee objects, so you
can create local Employee references and cast the parameters accordingly.
public int Compare(object lhs, object rhs)
{
Employee left = (Employee) lhs;
Employee right = (Employee) rhs;
return left.CompareTo(right,WhichComparison);
}
Notice that the work of comparison is delegated to the Employee class. You
call the CompareTo method of Employee, passing in the second Employee
object and the value retrieved by the WhichComparison property.
7. You must now overload the CompareTo method of Employee to support
the call you just wrote within the nested class. The original CompareTo
method looks like this:
Standard Collection Interfaces
C# Professional Skills Development 10-21
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public int CompareTo(Object rhs)
{
Employee right = (Employee) rhs;
return this.empID.CompareTo(right.name);
}
You can leave this intact, though it wont be used in this exercise. Your new
CompareTo method takes two parameters: an Employee object to compare to,
and an enumerated constant indicating which comparison is requested:
public int CompareTo(
Employee rhs,
Employee.EmployeeComparer.EmployeeComparison
whichComparison )
{
switch (whichComparison)
{
case
Employee.EmployeeComparer.EmployeeComparison.ID:
return this.empID.CompareTo(rhs.empID);
case
Employee.EmployeeComparer.EmployeeComparison.name:
return this.name.CompareTo(rhs.name);
}
return 0;
}
In this method you switch on the value of the whichComparison parameter.
This parameter will match one of the two enumerated constants:
Employee.EmployeeComparer.EmployeeComparison.ID
or
Employee.EmployeeComparer.EmployeeComparison.name
Collection Interfaces
10-22 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
When the match is made, the responsibility for comparison is delegated to the
appropriate field of Employee. If you are comparing by ID, the value is
returned by calling CompareTo on the empID, passing in the second Employee
objects empID field:
return this.empID.CompareTo(rhs.empID);
Similarly, if you are comparing by name, you return the result of calling
CompareTo on the name field, passing in the name field of the other
Employee:
return this.name.CompareTo(rhs.name);
8. Overload the Employee Sort method. The original Sort method delegated
to the ArrayList.
public void Sort()
{
employees.Sort();
}
You can leave this intact and add a second Sort method that takes as a
parameter an instance of the specialized EmployeeComparer object. This
method will also delegate responsibility to the ArrayList, passing in the
comparer. The ArrayList Sort() method is overloaded, and the version youll
call expects an object of type IComparer.
public void Sort(
Employee.EmployeeComparer comparer)
{
employees.Sort(comparer);
}
9. Modify the Run() method in your test program. Start by creating the Office
and iterating over the members.
Standard Collection Interfaces
C# Professional Skills Development 10-23
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Employee jg = new Employee("John Galt","001");
Employee fb = new Employee("Figaro Barbara", "002");
Employee ww = new Employee("Wotan Warrior", "003");
Employee gw = new Employee("George Washington", "004");
Employee ja = new Employee("John Adams", "005");
Office office = new Office(jg, fb, ww, gw, ja);
office.Add(new Employee("Thomas Jefferson", "007"));
office.Add(new Employee("Sam Adams", "008"));
foreach (Employee e in office)
{
Console.WriteLine("{0}",e.ToString());
}
Add code to create an instance of the EmployeeComparer object.
Employee.EmployeeComparer comparer =
Employee.GetComparer();
10. Set the value of the WhichComparison property within the
EmployeeComparer instance to sort by name.
comparer.WhichComparison =
Employee.EmployeeComparer.EmployeeComparison.name;
11. Call the Sort method of office, passing in the EmployeeComparer object
and then iterate over Office.
office.Sort(comparer);
Console.WriteLine ("\n\nAfter sort by Name: \n");
foreach (Employee e in office)
{
Console.WriteLine("{0}",e.ToString());
}
Collection Interfaces
10-24 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
12. Change the value of the WhichComparison property to sort by ID and then
iterate over Office again.
comparer.WhichComparison =
Employee.EmployeeComparer.EmployeeComparison.ID;
office.Sort(comparer);
Console.WriteLine ("\n\nAfter sort by ID: \n");
foreach (Employee e in office)
{
Console.WriteLine("{0}",e.ToString());
}
The results are shown in Figure 7. You can see that the first time through, the
values display in the order they were added to Office. The second time through
they are sorted by name, and the third time through they are sorted by ID.
Standard Collection Interfaces
C# Professional Skills Development 10-25
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 7. Using a custom IComparer.
Encapsulation
It is important to focus on which class has what responsibility. In the example
you just wrote:
The Employee class has responsibility for knowing about Employee
information, such as Employee ID and name.
The nested EmployeeComparer class has responsibility for knowing
how Employees are sorted. As such it belongs to Employee, but
relieves the Employee class of responsibility for the details of sorting.
The EmployeeComparison enumeration is nested within
EmployeeComparer, because it exists only to support the
EmployeeComparer.
The Office class is responsible for knowing about offices. In this
example Office focuses only on the list of Employees, but an Office
class might also know about addresses, and so forth.
Collection Interfaces
10-26 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The nested OfficeEnumerator class is responsible for enumerating
over an office. As such it belongs to the Office, but relieves the Office
class of responsibility for the details of enumeration.
The Tester class is responsible for testing the Employee and Office
classes and as such is protected from the details of how these classes
work.
Each class is responsible for one discrete area of interest, and knows as little
as
possible about the internal state and mechanisms of any other class. This is the

essence of encapsulation and data hiding, and it is this approach that helps you

manage large complex programs.
Standard Collection Interfaces
C# Professional Skills Development 10-27
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
The .NET framework defines a number of standard collection
interfaces.
The IEnumerable interface supports enumeration and the use of the
foreach loop.
IEnumerable requires only one method: GetEnumerator.
GetEnumerator returns an instance of Ienumerator.
IEnumerator requires two methods: MoveNext and Reset and one
property: Current.
Typically an Enumerator is implemented as a nested class within the
collection class.
An ArrayList can act like a dynamically sizeable array.
To support sorting you implement Icomparable.
IComparable has only one method: CompareTo.
You can implement a custom Icomparer.
Typically custom IComparer objects are created as nested classes
within the class youll compare.
IComparer requires you implement Compare().
Collection Interfaces
10-28 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
(Review questions and answers on the following pages.)
Standard Collection Interfaces
C# Professional Skills Development 10-29
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. What is the interface you must implement to support foreach loops?
2. What are the methods and properties of the IEnumerator class?
3. What is the difference between an ArrayList and an Array?
4. What are the methods and properties of IComparable?
5. How do you refer to the ID enumerated constant within the
EmployeeComparison enumeration defined within the EmployeeComparer
class that is a nested class within Employee?
6. What are the advantages of nesting the EmployeeComparer within the
Employee class?
Collection Interfaces
10-30 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. What is the interface you must implement to support foreach loops?
The interface you must implement for your class to be iterated
with a foreach loop is IEnumerable. The IEnumerable interface
has only one method: GetEnumerator().
2. What are the methods and properties of the IEnumerator class?
The IEnumerator class has two methods, MoveNext and Reset as
well as one property, Current.
3. What is the difference between an ArrayList and an Array?
The principal difference is that an Array is of a fixed size
determined at compile time, while the ArrayList grows as needed.
4. What are the methods and properties of IComparable?
IComparable has only one method and no properties. The one
method is CompareTo(), but that method may be overloaded.
5. How do you refer to the ID enumerated constant within the
EmployeeComparison enumeration defined within the EmployeeComparer
class that is a nested class within Employee?
You refer to the ID constant as
Employee.EmployeeComparer.EmployeeComparison.ID.
6. What are the advantages of nesting the EmployeeComparer within the
Employee class?
Nesting within the Employee class establishes that the
EmployeeComparer is owned by the Employee and has special
knowledge of the Employee class (it has access to the private
members of Employee). It also scopes the EmployeeeComparer to
the Employee class so that external methods must refer to it
through the nesting class. This supports encapsulation.
Standard Collection Interfaces
C# Professional Skills Development 10-31
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 10:
Collection
Interfaces
Lab 10:
Collection Interfaces
10-32 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 10 Overview
In this lab youll learn to implement the enumeration, comparison, and related
standard interfaces for collections.
To complete this lab, youll need to work through two exercises:
Creating Enumerable Classes
Sorting Objects by Implementing IComparable
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-bystep

instructions.
Creating Enumerable Classes
C# Professional Skills Development 10-33
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Creating Enumerable Classes
Objective
In this exercise, youll implement a collection and the standard IEnumeration
interface.
Things to Consider
Enumerable collections can be iterated with a foreach loop.
You can specialize your enumerator to know about your specific
collection.
Typically the class implementing the enumeration is created as a
nested class within the collection itself.
Step-by-Step Instructions
1. Open Enumeration Starter.sln in the Enumeration Starter folder.
2. Create a class named Cat that you will hold in your collection.
public class Cat
{
3. Give the Cat class two member variables: name (a string) and age (an int).
string name;
int age;
4. Implement the constructor.
Lab 10:
Collection Interfaces
10-34 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public Cat(string name, int age)
{
this.name = name;
this.age = age;
}
5. Implement read-only properties for the age and name member variables.
public int Age
{
get
{
return age;
}
}
public string Name
{
get
{
return name;
}
}
6. End the Cat class and start a new class Kennel that implements
Ienumerable.
public class Kennel : IEnumerable
{
7. Declare a private member variable named cats, which is an array of Cat
objects.
private Cat[] cats;
8. Create a nested class called KennelEnumerator to implement the
IEnumerator interface.
Creating Enumerable Classes
C# Professional Skills Development 10-35
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
private class KennelEnumerator : IEnumerator
{
9. Give the nested class a reference to the outer class and an integer to
represent an index into the array of Cat objects. Initialize the index to 1.
private Kennel kennel;
private int index = -1;
10. Create the KennelEnumerator constructor. It takes one parameter: a
reference to the Kennel class.
public KennelEnumerator (Kennel kennel)
{
this.kennel = kennel;
}
11. Implement the MoveNext method to increment the index. If youve gone
past the end, return false, otherwise return true.
public bool MoveNext()
{
index++;
if (index >= kennel.cats.Length)
return false;
else
return true;
}
12. Implement the Reset method.
public void Reset()
{
index = -1;
}
13. Implement the Current property.
Lab 10:
Collection Interfaces
10-36 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public object Current
{
get
{
return (kennel[index]);
}
}
14. End the definition of the nested KennelEnumerator class and continue on
with the outer class.
} // end KennelEnumerator
15. Implement the GetEnumerator method, returning an IEnumerator.
public IEnumerator GetEnumerator()
{
return new KennelEnumerator(this);
}
16. Implement the Kennel constructor to take a variable number of arguments.
public Kennel(params Cat[] catList)
{
this.cats = new Cat[256];
int ctr = 0;
foreach (Cat c in catList)
{
this.cats[ctr++] = c;
}
}
17. Implement the indexer to take an int and return the indexed Cat object.
Creating Enumerable Classes
C# Professional Skills Development 10-37
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public Cat this[int index]
{
get
{
if (index < 0 || index >= cats.Length)
{
// handle bad index
}
return cats[index];
}
set
{
if (index >= cats.Length)
{
// handle error
}
else
{
cats[index] = value;
}
}
}
18. Close the Kennel class.
} // Close Kennel class
19. Create an array of five Cat objects and populate the array.
Cat[] catArray = new Cat[5];
catArray[0] = new Cat("Frisky",5);
catArray[1] = new Cat("Figaro", 2);
catArray[2] = new Cat("Wotan", 3);
catArray[3] = new Cat("Washington",4);
catArray[4] = new Cat("Adam", 5);
Lab 10:
Collection Interfaces
10-38 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
20. Create an instance of the collection class and pass the array to the
constructor.
Kennel kennel = new Kennel(catArray);
21. Assign a new Cat to the eleventh member of the array.
kennel[10] = new Cat("Jefferson", 3);
22. Iterate over the Kennel, displaying each Cat in turn.
foreach (Cat c in kennel)
{
if (c != null)
{
Console.WriteLine("{0} Cat ID: {1}",
c.Name, c.Age);
} // end if
} // end foreach
23. Compile and run the program as shown in Figure 8.
Figure 8. Final exercise results.
Sorting Objects by Implementing IComparable
C# Professional Skills Development 10-39
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Sorting Objects by Implementing
IComparable
Objective
In this exercise, youll see how to create sortable objects.
Things to Consider
For a class to be sortable you must implement Icomparable.
Typically the class implementing IComparer is created as a nested
class within the class being compared.
You can tightly control the comparison by overloading the CompareTo
method.
Step-by-Step Instructions
1. Create a Cat class that implements the IComparable interface. Give the Cat
two members: name (a string) and age (an int).
public class Cat : IComparable
{
string name;
int age;
2. Create a nested class CatComparer that implements the IComparer
interface.
public class CatComparer : IComparer
{
3. Within CatComparer create an enumeration, CatComparison, with two
values: name and age.
Lab 10:
Collection Interfaces
10-40 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public enum CatComparison
{
name,
age
}
4. Create a private enumerated constant of type CatComparison named
whichComparison.
private Cat.CatComparer.CatComparison whichComparison;
5. Create a property for the comparison named WhichComparison.
public Cat.CatComparer.CatComparison WhichComparison
{
get { return whichComparison; }
set { whichComparison = value; }
}
6. Implement the Compare method. Compare two Cat objects and invoke the
CompareTo method of the first one, passing in the second and the current
value of whichComparison.
public int Compare(object lhs, object rhs)
{
Cat left = (Cat) lhs;
Cat right = (Cat) rhs;
return left.CompareTo(right,WhichComparison);
}
7. End the nested class CatComparer.
} // End CatComparer
8. Within the outer class Cat, implement the GetComparer method. Return a
CatComparer object.
Sorting Objects by Implementing IComparable
C# Professional Skills Development 10-41
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public static CatComparer GetComparer()
{
return new Cat.CatComparer();
}
9. Implement the Cat constructor.
public Cat(string name, int age)
{
this.name = name;
this.age = age;
}
10. Override ToString to return the Cats name and age.
public override string ToString()
{
return name + " [Cat age: " + age + "]";
}
11. Implement the CompareTo method and compare the current Cat to the Cat
passed in as a parameter. Compare them based on the name (alphabetic).
public int CompareTo(Object rhs)
{
Cat right = (Cat) rhs;
return this.age.CompareTo(right.name);
}
12. Overload the CompareTo method. Compare the current Cat to the Cat
passed in as a parameter.
public int CompareTo(
Cat rhs,
Cat.CatComparer.CatComparison whichComparison )
{
Lab 10:
Collection Interfaces
10-42 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
13. Base the comparison on the CatComparison constant passed in as a
parameter.
switch (whichComparison)
{
case Cat.CatComparer.CatComparison.age:
return this.age.CompareTo(rhs.age);
case Cat.CatComparer.CatComparison.name:
return this.name.CompareTo(rhs.name);
}
return 0;
14. Implement properties for Age and Name.
public int Age
{
get
{
return age;
}
}
public string Name
{
get
{
return name;
}
}
15. Implement the collection (Kennel) that implements IEnumerable.
public class Kennel : IEnumerable
{
Sorting Objects by Implementing IComparable
C# Professional Skills Development 10-43
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
16. Provide a private ArrayList to hold the collection.
private ArrayList Cats;
17. Implement a nested class KennelEnumerator that implements IEnumerator.
private class KennelEnumerator : IEnumerator
{
18. Provide a member variable that is a reference to the outer Kennel object
and a second member that is an index, initialized to -1.
private Kennel kennel;
private int index = -1;
19. Provide the constructor. It takes one parameter, which is a Kennel
reference.
public KennelEnumerator (Kennel kennel)
{
this.kennel = kennel;
}
20. Implement the MoveNext and Reset methods and the Current property.
Lab 10:
Collection Interfaces
10-44 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public bool MoveNext()
{
index++;
if (index >= kennel.Cats.Count)
return false;
else
return true;
}
public void Reset()
{
index = -1;
}
public object Current
{
get
{
return (kennel[index]);
}
}
21. Implement GetEnumerator to return an IEnumerator object.
public IEnumerator GetEnumerator()
{
return (IEnumerator) new KennelEnumerator(this);
}
22. Implement a constructor for Kennel that takes a variable number of
parameters.
public Kennel(params Cat[] CatList)
{
this.Cats = new ArrayList();
Sorting Objects by Implementing IComparable
C# Professional Skills Development 10-45
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
foreach (Cat c in CatList)
{
this.Cats.Add(c);
}
}
23. Create an Add method that takes a Cat object and delegates adding the Cat
object to the ArrayList.
public void Add(Cat c)
{
Cats.Add(c);
}
24. Implement a Sort object that delegates sorting to the ArrayList.
public void Sort()
{
Cats.Sort();
}
25. Overload Sort to take an instance of CatComparer. Delegate sorting to the
ArrayList, but pass in the CatComparer object.
public void Sort(
Cat.CatComparer comparer)
{
Cats.Sort(comparer);
}
26. Implement an integer based index for Cat.
Lab 10:
Collection Interfaces
10-46 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public Cat this[int index]
{
get
{
if (index < 0 || index >= Cats.Count)
{
// handle bad index
}
return (Cat)Cats[index];
}
set
{
if (index >= Cats.Count)
{
// handle error
}
else
{
Cats[index] = value;
} // end else
} // end set
} // end indexer
27. Create an array of five Cat objects and populate the array.
Cat[] catArray = new Cat[5];
catArray[0] = new Cat("Frisky",6);
catArray[1] = new Cat("Figaro", 2);
catArray[2] = new Cat("Wotan", 3);
catArray[3] = new Cat("Washington",4);
catArray[4] = new Cat("Adam", 5);
28. Create an instance of the collection class and pass the array to the
constructor.
Kennel kennel = new Kennel(catArray);
Sorting Objects by Implementing IComparable
C# Professional Skills Development 10-47
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
29. Assign a new Cat to the eleventh member of the array.
kennel[10] = new Cat("Jefferson", 3);
30. Iterate over the Kennel. Display each Cat in turn.
foreach (Cat c in kennel)
{
if (c != null)
{
Console.WriteLine("{0} Cat ID: {1}",
c.Name, c.Age);
} // end if
} // end for each
31. Instantiate a CatComparer object.
Cat.CatComparer comparer = Cat.GetComparer();
32. Set the WhichComparison property of the CatComparer object to name
and invoke Sort on Kennel, passing in the CatComparer object you created
and set.
comparer.WhichComparison =
Cat.CatComparer.CatComparison.name;
kennel.Sort(comparer);
33. Iterate over the kennel, displaying the names and ages.
foreach (Cat c in kennel)
{
Console.WriteLine("{0} Cat ID: {1}",
c.Name, c.Age);
} // end foreach
Lab 10:
Collection Interfaces
10-48 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
34. Set the WhichComparison property of the CatComparer object to age and
invoke Sort on Kennel, passing in the CatComparer object.
comparer.WhichComparison =
Cat.CatComparer.CatComparison.age;
kennel.Sort(comparer);
35. Iterate over the kennel, displaying the names and ages.
foreach (Cat c in kennel)
{
Console.WriteLine("{0} Cat ID: {1}",
c.Name, c.Age);
}
36. Compile and run the program as shown in Figure 9.
Figure 9. Implementing Icomparable.
Collections and Strings
C# Professional Skills Development 11-1
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Collections and
Strings
Objectives
Create queues for first in, first out retrieval.
Create stacks for last in, first out retrieval.
Create dictionaries for matching keys with values.
Create and manipulate strings.
Compare strings and concatenate strings.
Parse strings.
Use regular expressions for advanced parsing.
Collections and Strings
11-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Collections
The .NET Framework provides a number of collection classes that are fully
debugged and ready for use. The .NET collections hold objects, and are not
type-safe. You are free to put any intrinsic or user-defined type into a
collection; since all derive from object, the collection is prepared to hold the
m
and treat them polymorphically. When you extract from the collection you will
need to cast them back to their original type.
In addition to Array and ArrayList, the principal .NET collections include:
Queues
Stacks
Dictionary
Queues
A queue offers first in, first out semantics. That is, the first item you add to
the
queue is the first item extracted. The second item added is the second item to
come out, and so forth.
The classic analogy for a queue is a line of people at a movie theater (in the
UK you dont line up, you queue up). You use queues when you are managing
access to a limited resource. As requests arrive for your resource, you add
them to the queue, and then you remove them in their order of arrival.
Imagine that you have 20 operators sitting at consoles in a large office. Phone
calls arrive (perhaps asking for customer support). The calls are routed to the
operators, but if there are more calls than there are operators, someone must
wait. You add the waiting calls to the queue and you inform your callers that
Your call is important to us, and will be handled in the order received.
Queues offer a number of methods and properties, the most important of which
are shown in Table 1.
Collections
C# Professional Skills Development 11-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Method/Property Description
Count Get the number of elements in the queue.
Clear() Remove all members of the queue.
Dequeue() Remove and return an object from the
queue.
Enqueue() Add an object to the end of the queue.
GetEnumerator() Return an enumerator for iterating over
the queue.
Peek() Return an object at the top of the queue
without removing it.
Table 1. Methods and properties of queues.
The Count property tells you how many objects are in your queue. Notice that
this is the same property you use with ArrayLists, and youll see that this is
common to all collections.
You can remove all the members of the queue by calling Clear(). Once again,
this idiom is common to all collections.
Another idiom common to many collections is the idea that you can either look
at (get a reference to) the next item but leave it in the collection (Peek) or y
ou
can take the next item out of the collection, again getting a reference to it
(Dequeue). There is also a method to add to the collection (Enqueue), and
finally a method for iterating over the collection (GetEnumerator).
Try It Out!
The best way to see how to work with a queue is to write a simple example
program.
1. Create a new console application and name it Queue.
2. For this incredibly simple example you do not need any classes except a
Tester class. As usual, give it a Run method.
3. Within Run(), create an instance of a Queue.
Queue myQueue = new Queue();
4. Add a string to the queue.
myQueue.Enqueue("Hello");
See Queue.sln
Collections and Strings
11-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
5. Now add five integer values.
for (int i = 1;i<6;i++)
{
myQueue.Enqueue(i*10);
}
6. You are ready to review the contents of the queue. Write a small method to
print all the values in the queue. Generalize this method to take any
enumerable container; youll use it again in later examples.
public static void PrintValues(
IEnumerable myCollection )
{
foreach (object i in myCollection)
{
Console.Write("{0} ", i.ToString());
}
Console.WriteLine();
}
The one parameter to the PrintValues method is an object of type IEnumerable.
All of the .NET collections support this interface (which is why Queue has a
method GetEnumerator).
7. Call PrintValues, passing in the queue.
Console.Write( "myQueue values:\t" );
PrintValues( myQueue );
The results are shown in Figure 1. You can see that the values are displayed in
the order in which they were added to the queue.
Figure 1. Printing the values in myQueue.
Collections
C# Professional Skills Development 11-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
8. Remove an element from the queue and display it. Remember that the
Dequeue() method both removes the element and returns it.
Console.WriteLine(
"\n(Dequeue)\t{0}", myQueue.Dequeue() );
9. Display the queue again to show that the element was removed.
Console.Write( "myQueue values:\t" );
PrintValues( myQueue );
The results of running the code to this point are shown in Figure 2. You can
see that Hello is removed from the queue and displayed. When the queue is
redisplayed, the first element, Hello, is now missing.
Figure 2. Dequeing an item.
10. Remove the next element and display the queue again.
Console.WriteLine(
"\n(Dequeue)\t{0}", myQueue.Dequeue() );
Console.Write( "myQueue values:\t" );
PrintValues( myQueue );
11. Finally, display the first element in what remains of the queue, but do not
remove it. To accomplish this, use the Peek method. Like Dequeue, Peek
returns the first element in the queue, but unlike Dequeue it does not
remove it. You can prove this to yourself by displaying the queue before
and after calling Peek.
Collections and Strings
11-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Console.WriteLine(
"\n(Peek) \t{0}", myQueue.Peek() );
Console.Write( "myQueue values:\t" );
PrintValues( myQueue );
The result of running all of this code is shown in Figure 3. You can see that
Peek displays the first element in the queue, but does not remove the element;
the queue is unchanged by calling Peek.
Figure 3. Peeking at the next element in the queue.
Stacks
A stack offers last in, first out semantics. That is, the last item you add to t
he
Stack will be the first item extracted. The penultimate item added will be the
second item to come out, and so forth.
The classic analogy for a stack is a stack of dishes at a buffet table. The last

dish placed on top of the stack is the first dish to pop off the stack.
The operating system uses a stack (referred to as the stack) to hold program
instructions and parameters to methods, as well as local variables. As you
work with the stack, items are pushed onto the stack. As the items are
destroyed, they are popped off the stack.
Stacks offer a number of methods and properties, the most important of which
are shown in Table 2.
Collections
C# Professional Skills Development 11-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Method/Property Description
Count Get the number of elements in the stack.
Clear() Remove all objects from the stack.
GetEnumerator() Return an enumerator for iterating over
thesStack.
Peek() Return an object at the top of the stack
without removing it.
Pop() Return and remove top element of the
stack.
Push() Add an element to the stack.
Table 2. Methods and properties of stacks.
As with Queue, the Count property tells you how many objects are in your
stack. As with Queue, you can call Clear() to remove all the elements of the
stack. And once again you use the GetEnumerator() method to iterate over the
stack.
Peek works as it does with Queue; that is, you retrieve the top element in the
stack but you do not remove it. The equivalent to Dequeue is Popretrieve
and remove the top element. The equivalent to Enqueue is Push.
Try It Out!
The best way to see how to work with a stack is to write another simple
example program.
1. Create a new console application and name it Stack.
2. Once again youll use a very simple program, putting all the work in the
Run() method of the Tester class.
3. Within Run(), create an instance of a stack.
Stack myStack = new Stack();
4. Populate the stack with seven integer values.
for (int i = 1;i<8;i++)
{
myStack.Push(i*10);
}
See Stack.sln
Collections and Strings
11-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
5. Display the stack using the same PrintValues method you used with
queues. Again, Stack implements IEnumerable, so this is safe to do.
Console.Write( "stack values:\t" );
PrintValues( myStack );
6. To demonstrate how Pop removes an element much as Dequeue did for
Queue, call Pop, display the results, and then show the stack. Do this
twice, removing two elements from the stack.
// Remove an element from the stack.
Console.WriteLine( "\n(Pop)\t{0}",
myStack.Pop() );
// Display the stack.
Console.Write( "myStack values:\t" );
PrintValues( myStack );
// Remove another element from the stack.
Console.WriteLine( "\n(Pop)\t{0}",
myStack.Pop() );
// Display the stack.
Console.Write( "myStack values:\t" );
PrintValues( myStack );
7. Call Peek() to look at the top element in the stack, but dont remove it.
Print the stack again to show that it was unchanged by the call to Peek():
Collections
C# Professional Skills Development 11-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
a// Display the stack.
Console.Write( "myStack values:\t" );
PrintValues( myStack );
// View the first element in the stack
Console.WriteLine( "\n(Peek) \t{0}",
myStack.Peek() );
// Display the stack.
Console.Write( "myStack
The result of this work is shown in Figure 4.
Figure 4. Peeking at the stack.
Stacks and Arrays
You can copy the entire contents of a stack into an array at an arbitrary index
into the array. For example, you can copy the stack from the previous example
into an array of integers.
1. To see this at work, first create an array of integers.
int[] targetArray;
targetArray = new int[15];
Interestingly, you are free to use collection semantics with an array as well.
That is, rather than using the declaration of the array, you can declare an
Collections and Strings
11-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
instance of the Array class by calling the static CreateInstance method of
Array, passing in the type of Array to create and the size:
Array targetArray=Array.CreateInstance(
typeof(int), 15 );
This line is identical in effect to the lines above; it declares targetArray to
be
an instance of an array of integers of size 15.
2. Populate the array with values. Again, you can do this in one of two ways,
using traditional array semantics, or using methods of the Array class. To
use traditional semantics write:
for (int i = 0; i < 15; i++)
targetArray[i] = i * 100 + 100;
This assigns values to each of the first 15 elements in the array. The first
element will be 100 (0 * 100 + 100), the second will be 200 (1 *100 + 100),
and so forth.
As an alternative, you can use the SetValue method of the Array class rather
than the traditional array indexing:
for (int i = 0; i < 15; i++)
targetArray.SetValue(i*100+100,i);
The two for loops accomplish exactly the same work.
3. Pass the array to the PrintValues method. Because Array supports
IEnumerable, PrintValues will display the contents of the array.
Console.WriteLine( "\nTarget array: ");
PrintValues( targetArray );
4. Copy the stack to the target array, starting at index 6. This overwrites the
array with the contents of the stack.
myStack.CopyTo( targetArray, 6 );
5. Display the results using PrintValues.
Collections
C# Professional Skills Development 11-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Console.WriteLine( "\nTarget array after copy: ");
PrintValues( targetArray );
The results are shown in Figure 5. You can see that the stack values are 50, 40,

30, 20, 10. The array values are 100 through 1500. After you copy the stack
over the array at index 6, the seventh through eleventh members of the array
have been overwritten with the values from the stack.
Figure 5. Copying a stack over an array.
6. You can also copy the entire stack to a new array by calling ToArray().
This returns a new array of objects that you can again pass to PrintValues.
// Copy the entire source Stack
// to a new standard array.
Object[] myArray = myStack.ToArray();
// Display the values of the new standard array.
Console.WriteLine( "\nThe new array:" );
PrintValues( myArray );
The results are shown in Figure 6. This time a new array is created with the
five elements of the stack.
Collections and Strings
11-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 6. Creating a new array from the stack.
Dictionary
Perhaps the most useful collection in the .NET Framework is the Dictionary. A
Dictionary is a collection that creates key/value pairs. A classic dictionary
collection, of course, is Websters Unabridged Dictionary of the English
Language, in which each word is a key, and the value returned by the key is
the definition of the word.
In .NET, the most common Dictionary collection is the HashTable. A
HashTable is a highly efficient dictionary. When you think of a HashTable,
think of a series of numbered buckets. Each entry in the Dictionary is stored in

a numbered bucket based on a hashvalue returned by the key.
The advantage of HashTables is that they are highly efficient. When
programmers talk about efficiency, they mean either that the approach takes
little memory or that it is very fast. In this case, a HashTable is very fast;
objects can be retrieved from a HashTable based on their key, very quickly.
Object.GetHashCode
HashTables work by asking the objects they store for a hashcode. This
hashcode allows quick retrieval of the object. Because GetHashCode is a
method of object, every object (both intrinsic and user-defined) implements the
method. You are free to override the method in your own classes, providing a
hashcode by whatever mechanism you think best.
Collections
C# Professional Skills Development 11-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
When two or more objects return the same hashcode, it is known as a collision.
In .NET HashTables, collisions are handled by having each bucket hold its
own contents in an ordered list. Once the hashcode identifies a bucket, a binary

search is conducted of the ordered list within that bucket. Binary searches are
very fast.
HashTables as Dictionaries
HashTables are dictionaries because they implement the IDictionary interface.
The IDictionary interface dictates that a HashTable must have a public
indexer:
object this[object key]
{ get; set; }
The index value is an object, known as the key. What is returned is also an
object. The IDictionary interface also dictates two additional properties: keys
and values. The keys property returns a collection of all of the keys. The value
s
property returns a collection of all the values.
The IDictionaryEnumerator interface allows you to iterate over a dictionary as
you might iterate over another collection (i.e., using foreach).
HashTables offer a number of methods and properties, the most important of
which are shown in Table 3.
Method/Property Description
Add() Add an entry with a key/value pair.
Count Get the number of elements in the
HashTable.
Clear() Remove all objects from the HashTable.
Contains Is the element in the HashTable?
ContainsKey Is the key in the HashTable?
GetEnumerator() Return an enumerator for iterating over
the HashTable.
Item() Indexer for the HashTable.
Table 3. Methods and properties of HashTables.
The Add method is used to add items to the HashTable, just as Add is used by
ArrayList.
As with Queue and Stack, the Count property tells you how many objects are
in your HashTable. Once again, you can call Clear() to remove all the elements
of the HashTable. And, as with the other collections, the GetEnumerator()
Collections and Strings
11-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
method iterates over the HashTable; however this time what is returned is an
item of type IDictionaryEnumerator.
Item is the indexer for HashTable; this is the underlying method for indexing
supported by other programming languages that do not allow you to overload
operators.
Try It Out!
To see how to work with HashTables, youll create a new console application.
1. Create a new console application named HashTable.
2. Create a HashTable object using the keyword new.
Hashtable hashTable = new Hashtable();
3. Populate the HashTable by calling the Add method. The arguments to Add
are the key and the value. In this case youll be saving names (as the value)
keyed to the persons social security number.
NOTE To avoid using a valid social security number, weve used
intentionally invalid social security numbers here.
For each value you add youll provide two strings: the key and the value itself.
hashTable.Add("000-12-3456","Ayn Rand");
hashTable.Add("000-13-3938","John Galt");
hashTable.Add("000-14-3837","Hank Reardon");
hashTable.Add("000-15-4837","John Adams");
hashTable.Add("000-16-4738","Sam Adams");
4. You can retrieve any value just by providing the key to the HashTable.
Console.WriteLine("hashTable[\"000-14-3837\"]: {0}",
hashTable["000-14-3837"]);
This line of code prints the string hashTable[000-14-3837] followed by
whatever value is returned by passing the string 000-14-3837 to the
HashTable.
See HashTable.sln
Collections
C# Professional Skills Development 11-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
NOTE Notice that you must escape the double quotes in the WriteLine
statement (\) so that the quote marks will actually be printed.
The results are shown in Figure 7. You can see that the value (Hank Reardon)
has been retrieved using the key 000-14-3837.
Figure 7. Retrieving a value from the HashTable.
5. Retrieve the collection of keys by calling hashTable.Keys. What is
returned is a collection: an object implementing the ICollection interface.
You may instantiate an object of type ICollection and pass that to a
foreach loop (all ICollection objects support IEnumerator).
Console.WriteLine("\nKeys:");
ICollection keys = hashTable.Keys;
foreach (string key in keys)
Console.WriteLine("{0}", key);
The results are shown in Figure 8. The entire collection of keys is displayed.
Figure 8. Iterating over the Keys collection.
6. Iterate over the Values collection just as youve iterated over the Keys
collection.
Collections and Strings
11-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Console.WriteLine("\nValues:");
ICollection values = hashTable.Values;
foreach (string value in values)
Console.WriteLine("{0}", value);
Again, you are passing the ICollection object to the foreach loop, displaying
all the values as shown in Figure 9. The Keys and Values collections give you
access to the entire set of data within the HashTable.
Figure 9. Iterating over the Values collection.
7. You are free to extract the iterator yourself, using GetEnumerator. You can
then use that iterator to iterate over the two collections, explicitly
extracting the records by calling the Key and Value properties of the
iterator.
Collections
C# Professional Skills Development 11-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Console.WriteLine(
"\nUsing the Dictionary enumerator...");
IDictionaryEnumerator enumerator =
hashTable.GetEnumerator();
while(enumerator.MoveNext())
Console.WriteLine("\t{0}:\t{1}",
enumerator.Key, enumerator.Value);
The reason that IEnumerator was extended to IDictionaryEnumerator was
explicitly to add the Key and Value properties. The result of iterating explicit
ly
is shown in Figure 10. By using the dictionary enumerator you are able to
iterate over all the records, displaying the relationship between keys and
values.
Figure 10. Using the IDictionaryEnumerator.
Collections and Strings
11-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Strings
C# inherits many of its characteristics from the C family of languages (C and
C++). One striking limitation in these languages is the limited support they
provide for strings. A string is a set of characters, typically used to hold nam
es,
identifiers, and other text for display. C# corrects this limitation, making
strings first-class members of the language.
In C#, strings are immutable.
Key Term
Immutable Immutable objects cannot be changed. When you appear to
modify a string what you get back is not a changed string, but a
new instance of string!
When you modify a string you actually create a new one. The original string
remains unchanged. In most cases this has little impact, except that repeatedly
modifying strings can be somewhat inefficient (lots of copies must be made)
and has the potential to slow down your program. You can solve this by using
the StringBuilder class (discussed later).
Strings are also sealed.
Key Term
Sealed Classes that are sealed may not be derived from.
It is not legal to derive a class from String. There are no sub-classes of Strin
g.
The formal definition of string is:
public sealed class String : IComparable, ICloneable,
IConvertible
You can see that the string class supports three interfaces. IComparable allows
strings to be sorted, as discussed in a previous chapter. ICloneable allows
strings to be duplicated. IConvertible provides for easy type conversion
between strings and other types.
Strings
C# Professional Skills Development 11-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
It turns out that the C# string (lowercase s) class is just a synonym for the
.NET Framework String class (uppercase s). You are free to use either
designation; there is no functional difference. Most C# programmers will use
the (lowercase) string class, recognizing that it maps directly to the .NET
String class.
String Manipulation
The .NET String class provides extensive support for manipulating strings,
concatenating strings, and comparing strings. Because String is a class, these
are implemented as class methods (either static or instance) of the String class
.
Try It Out!
By far the best way to learn how to manipulate strings is by writing a small tes
t
program.
1. Create a new console application named Strings.
2. Create two strings, as shown below.
string s1 = "hello";
string s2 = "HELLO";
3. Notice that the two strings are identical except for case. You can compare
two strings by matching case (in which case these strings will not evaluate
as equal to one another) or you can evaluate them by ignoring case (in
which case these two strings are equal).
The code calls the static method compare, and gets back an integer value that
indicates which string is greater or lesser. The value 0 indicates they are the
same.
See String.sln
Collections and Strings
11-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
int result;
// compare two strings, case sensitive
result = string.Compare(s1, s2);
Console.WriteLine("Compare s1: {0}, s2: {1}, result: {2}",
s1, s2, result);
//overloaded version (true = ignore case
result = string.Compare(s1,s2, true);
Console.WriteLine(
"Compare insensitive s1: {0}, s2: {1}, result: {2}\n",
s1, s2, result);
The result of running this code is shown in Figure 11. The first comparison is
case sensitive, and the lowercase string is evaluated as being smaller than the
second string (as indicated by the return value 1). The second comparison is
case insensitive, and the return value of 0 indicates that the strings are equal
.
Table 4 shows how to interpret the result of a comparison operation.
Figure 11. Comparing two strings.
Value Interpretation
Any value less than 0 The first string is less than the second.
Zero The strings are equal.
Any value greater than 0 The first string is greater than the second.
Table 4. Interpreting the results of a string comparison.
Strings
C# Professional Skills Development 11-21
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Concatenation
You can concatenate two strings either by using the static method Concat or by
using the overloaded + operator.
1. Concatenate the two strings s1 and s2, using the Concat operator, and
display the results.
// concatenation method
string s3 = string.Concat(s1,s2);
Console.WriteLine(
"s3 concatenated from s1 and s2: {0}", s3);
2. Concatenate the same two strings using the overloaded + operator.
// use the overloaded operator
string s4 = s1 + s2;
Console.WriteLine(
"s4 = s1 + s2: {0}", s4);
The result of running this code is shown in Figure 12. There is no difference in

the result whether you use the static method or the overloaded + operator.
Figure 12. Concatenating strings.
Copying Strings
Just as there are two ways to concatenate strings, there are two ways to
compare strings. You may use the Copy instance method, or you may choose
to use the overloaded assignment operator (=).
1. Copy string s4 to a new string s5 with the Copy method.
Collections and Strings
11-22 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
// the string copy method
string s5 = string.Copy(s4);
Console.WriteLine(
"\ns5 = string.Copy(s4): {0}", s5);
NOTE The special escape sequence \n at the beginning of the string
passed to WriteLine adds an extra new line (white space) to make
the output easier to read.
2. Copy the resulting string s5 to a new string s6, using the overloaded
assignment operator.
// use the overloaded operator
string s6 = s5;
Console.WriteLine("s6 = s5: {0}", s6);
The output from running this code is shown in Figure 13. The result is
identical whether you use the Copy method or the assignment operator.
Figure 13. Using Copy or the assignment operator to compare strings.
String Equality
There are three ways to determine whether two strings are identical. All three
return a Boolean value of true if the two strings are equal. The first uses the
static Equals method, the second uses an instance equals method, and the third
uses the overloaded equality operator (==).
1. Test whether string s6 is equal to string s5 by using the static equals
method.
Strings
C# Professional Skills Development 11-23
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
// static equals method
Console.WriteLine(
"\nTest static: Equals(s6,s5)?: {0}",
string.Equals(s6,s5));
2. Test again using the instance method.
// instance equals method
Console.WriteLine(
"Test instance: s6.Equals(s5)?: {0}",
s6.Equals(s5));
3. Test one more time using the overloaded operator.
// overloaded operator
Console.WriteLine(
"Test operator: s6==s5?: {0}", s6 == s5);
The results are shown in Figure 14.
Figure 14. Using three methods to test equality.
String Properties
Strings provide a number of properties. Among the more useful of the string
properties are the length property and the indexer.
Collections and Strings
11-24 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
1. Display the length of a string.
Console.WriteLine(
"\nString s6 is {0} characters long. ", s6.Length);
2. Find the character at offset 4 (the fifth character).
Console.WriteLine(
"The 5th character is {0}\n", s6[4]);
The results are shown in Figure 15. The length property returns the length of
the entire string. The index property returns the character at the specified
index.
Figure 15. Testing the length and using an indexer.
3. Create a for loop to display all the characters in turn.
for (int i = 0; i < s6.Length; i ++)
Console.WriteLine("String[" + i + "]: {0}", s6[i]);
The result is shown in Figure 16. You iterate through the string, using the
index operator to pick out each of the characters in turn.
Strings
C# Professional Skills Development 11-25
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 16. Iterating through the characters in a string.
Verbatim Strings
C# supports the concept of a verbatim string. This is a string in which all the
characters are accepted without interpretation by the compiler. If a quote
symbol or other special character is within the string it is accepted as is. Thi
s
can greatly simplify working with complicated strings.
1. Create a verbatim string.
string s7 = @"Liberty Associates, Inc.
provides custom .NET development,
and Consulting";
2. Display the verbatim string to the console.
Console.WriteLine("Verbatim string: {0}", s7);
Collections and Strings
11-26 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The results are shown in Figure 17. The verbatim string is displayed exactly as
it was entered; even the new implicit newline characters were preserved.
Figure 17. The compiler accepts verbatim strings without interpretation.
Finding Substrings
C# provides a number of methods for finding substrings within strings. For
example, you can test whether a string ends with a sequence of characters,
using the EndsWith method. You can also obtain the index of the first instance
of a substring within another string, using the IndexOf method.
1. Add code to test whether the string s7 ends with the string Consulting.
Console.WriteLine("s7:{0}\nEnds with Consulting?: {1}\n",
s7,
s7.EndsWith("Consulting") );
Strings
C# Professional Skills Development 11-27
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
2. Find the index of the first instance of the substring within the outer string
.
// return the index of the substring
Console.WriteLine(
"\nThe first occurrence of Consulting ");
Console.WriteLine ("in s3 is {0}\n",
s3.IndexOf("Consulting"));
The result of running this code and scrolling to just the new output is shown in

Figure 18. First test whether the verbatim string (s7) ends with the characters
Consulting. This returns a Boolean value, in this case True. Then return the
offset into the string where Consulting appears (99).
Figure 18. Finding the first occurrence of a substring.
Inserting New Strings
You can use that offset to insert a new substring into the outer string, using t
he
Insert method of string.
1. Start by instantiating an integer variable to hold the offset of Consulting
within the string s7.
int indexOfConsulting = s7.IndexOf("Consulting");
2. Use that index to insert the new value, the string excellent into string s7 a
t
the offset held by indexofConsulting.
string s8 = s7.Insert(indexOfConsulting,"excellent ");
Collections and Strings
11-28 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
3. Display the result to the console.
Console.WriteLine("s8: {0}\n",s8);
The result is shown in Figure 19. You have inserted the substring excellent into

string s7 at the offset of the string Consulting. The rest of the outer string (
the
word Consulting) is pushed right to make room for the new substring.
Figure 19. Inserting a substring into a string.
Note that you can combine the three steps of
Getting the index
Using the index in the Insert statement
Displaying the results
all into a single C# statement:
Console.WriteLine(s7.Insert(s7.IndexOf("Consulting"),
"excellent "));
You have to examine this statement pretty carefully to be certain what is going
on. The best way to read a statement like this is inside out. The innermost
statement is:
s7.IndexOf("Consulting")
This returns the index of the substring Consulting (99), and this value is passe
d
to the outer statement:
Strings
C# Professional Skills Development 11-29
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
s7.Insert(99, excellent)
This results in a returned string, and that string is passed to the outermost
Console.WriteLine statement.
Splitting Strings
Often you will have a string that consists of tokens that you want to parse. For

example, you may want to parse an English sentence into words, or you may
want to pick out an IP address from a log entry.
The C# string class provides some support for splitting a string into its
constituent parts. The Split method takes an array of delimiters and returns an
array of the substrings. If you call Split on a string with six words separated
by
spaces and you pass in an array that contains a space character, youll get back
an array of six strings, one for each of the words in the original string.
1. Create a string filled with words to parse and display it.
string s9 =
"One Two Three Four Five Liberty Associates";
Console.WriteLine("\n\nString s9: {0}", s9);
2. Create two constants for the delimited characters and store the constants in
an array.
// constants for the space and comma characters
const char Space = ' ';
const char Comma = ',';
// array of delimiters to split the sentence with
char[] delimiters = new char[]
{
Space,
Comma
};
3. Create a string to assemble the results and a local counter variable to keep
track of how many words were found.
Collections and Strings
11-30 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
string output = "";
int ctr = 1;
4. Split the string s9 with the delimiter array. The result is an array of the
substrings; iterate over that array with a foreach loop.
foreach (string subString in s9.Split(delimiters))
{
5. Within the foreach loop, assemble the output string with the counter
(indicating the number of words found), followed by the substring, and
then a new line.
foreach (string subString in s9.Split(delimiters))
{
output += ctr++;
output += ": ";
output += subString;
output += "\n";
}
6. Display the results, as shown in Figure 20. Each of the seven words is
individually separated from the original string.
Figure 20. Parsing the words in a sentence.
Strings
C# Professional Skills Development 11-31
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The StringBuilder Class
Strings are immutable. When you append to a string, you actually create a new
string:
output += ctr++;
output += ": ";
output += subString;
output += "\n";
The result was not that the string output was incremented, but rather a new
string object was created as the result of each statement. This is terribly
inefficient. You can rewrite the previous block of code using a StringBuilder.
A StringBuilder is a class designed to encapsulate a strings constructor.
StringBuilders are used with Strings to accomplish highly efficient
modification of strings.
1. Add a new string to parse, and reset the ctr variable.
string s10 = "One Two Three Four Five Liberty Associates";
Console.WriteLine("\n\nString s10: {0}", s10);
ctr = 1;
2. Create a new output string; this time use StringBuilder.
StringBuilder sbOut = new StringBuilder();
3. Recreate the foreach loop, this time using the StringBuilder object.
foreach (string subString in s10.Split(delimiters))
{
sbOut.Append(ctr++);
sbOut.Append(": ");
sbOut.Append(subString);
sbOut.Append("\n");
}
4. Display the string youve built in the StringBuilder by calling ToString.
Collections and Strings
11-32 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Console.WriteLine(sbOut.ToString());
The results are identical, as shown in Figure 21. The StringBuilder is
somewhat more cumbersome to use, but the code runs faster. You wont notice
the difference in this tiny program, but when you make many modifications in
a tight loop, using the StringBuilder can improve the performance of your
application and use less memory.
Figure 21. Using the StringBuilder.
You can modify the way you assemble the output string by using the
AppendFormat instance method of StringBuilder. So, rather than writing
foreach (string subString in s10.Split(delimiters))
{
sbOut.Append(ctr++);
sbOut.Append(": ");
sbOut.Append(subString);
sbOut.Append("\n");
}
you can accomplish the same output by writing:
Strings
C# Professional Skills Development 11-33
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
foreach (string subString in s10.Split(delimiters))
{
sbOut.AppendFormat(
"{0}: {1}\n", ctr++, subString);
}
Notice that you use the same formatting options in AppendFormat as you
would with Console.WriteLine. The output is identical.
Regular Expressions
You didnt know it, but you cheated a bit in the previous example, by lopping
off the end of the name of the company, Liberty Associates, Inc. If you try
splitting on the entire name of the company, the results are slightly different.

1. Add a new string with the full name of the company. Display it and reset
the ctr variable.
string s11 =
"One Two Three Four Five Liberty Associates, Inc.";
Console.WriteLine("\n\nString s11: {0}", s11);
ctr = 1;
2. Point the variable sbOut to a new StringBuilder and parse the new string.
Display the results.
sbOut = new StringBuilder();
// split the string and then iterate over the
// resulting array of strings
foreach (string subString in s11.Split(delimiters))
{
sbOut.AppendFormat(
"{0}: {1}\n", ctr++, subString);
}
Console.WriteLine(sbOut.ToString());
Collections and Strings
11-34 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The results are shown in Figure 22. This time the parse did not go quite as
expected.
Figure 22. Splitting the full name of the company.
This almost worked. The first seven words were parsed appropriately, but why
is word 8 listed as blank? The answer is that the comma after Liberty
Associates was seen as a delimiter, as was the space after the comma!
You need to program the Split method to match either a space or a comma.
Unfortunately, there is no way to convey that with a normal string. For that
kind of advanced string manipulation you need a Regular Expression.
Regular Expressions Defined
Regular Expressions are a language for describing and manipulating text. You
apply a Regular Expression to a string, and you get back a new string. Often
that new string is a substring of the original or a modification of the original
.
Strings
C# Professional Skills Development 11-35
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Regex: Implementing Regular Expressions
In C#, Regular Expressions are encapsulated in the Regex class. This class
implements a more powerful form of Split that returns an array of substrings
based on a Regular Expression. In addition, you can extract the Match
collection, which contains all the matches to the Regular Expression.
Try It Out!
To see how this works, youll rewrite the previous example using a Regular
Expression.
1. Create a new console application named RegularExpressions.
2. In the Run method, add a definition of a string to parse.
string s1 =
"One Two Three Four Five Liberty Associates, Inc.";
3. Recreate the delimiters from the previous example.
// constants for the space and comma characters
const char Space = ' ';
const char Comma = ',';
// array of delimiters to split the sentence with
char[] delimiters = new char[]
{
Space,
Comma
};
4. Display the string, set up the counter, create the StringBuilder, and parse
using the String.Split method to show the problem again.
See Regular
Expressions.sln
Collections and Strings
11-36 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Console.WriteLine("\n\nString s1: {0}", s1);
int ctr = 1;
StringBuilder sbOut = new StringBuilder();
// split the string and then iterate over the
// resulting array of strings
foreach (string subString in s1.Split(delimiters))
{
sbOut.AppendFormat(
"{0}: {1}\n", ctr++, subString);
}
Console.WriteLine(sbOut.ToString())
5. Create a Regex object to hold the regular expression.
Regex theRegex = new Regex(" |, ");
In Regular Expressions, the | character represents or. This Regular Expression
will match on a space or a comma followed by a space. (For a complete
treatment of Regular Expressions, see Mastering Regular Expressions by
Friedel, OReilly 1997. ISBN: 1-56592-257-3).
6. Reset the StringBuilder and the counter
sbOut = new StringBuilder();
ctr = 1;
7. Call the instance method Split on the Regex object and iterate over the
array of strings returned. Print their contents.
foreach (string subString in theRegex.Split(s1))
{
sbOut.AppendFormat(
"{0}: {1}\n", ctr++, subString);
}
Console.WriteLine(sbOut);
Strings
C# Professional Skills Development 11-37
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
This time the output is what you would hope, as shown in Figure 23. Because
youve split the string using a regular expression, the comma followed by a
space after Associates is seen as a single delimiter, and the string is tokenize
d
appropriately.
Figure 23. Using a regular expression to split the string.
Regular Expressions offer a powerful mechanism for string manipulation. You
can use Regular Expressions to pick out tokens from within large text files,
and to produce new strings for storage or display.
Collections and Strings
11-38 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
The .NET Framework provides a number of fully debugged and tested
collection classes that you can use in your applications.
The Queue class provides first in, first out collection semantics.
The Stack class provides last in, first out collection semantics.
The HashTable provides a fast and efficient Dictionary class to match
keys to values.
Collection classes implement IEnumerable to support iteration.
Collection classes often provide a Count property to get the number of
items, as well as methods to add and remove (and possibly peek at)
their contents.
Strings are immutable and sealed.
Strings provide methods for comparison and manipulation.
The StringBuilder class encapsulates a string constructor.
Regular Expressions are encapsulated in the Regex class.
Strings
C# Professional Skills Development 11-39
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. If you have a limited resource and want to provide access to that resource
from a collection, retrieving from the collection on a first come, first
served basis, which collection class should you use?
2. How can you retrieve all the contents of a HashTable, matching each key
to its value?
3. What are the implications of the fact that strings are immutable?
4. If you have a variable numDogs and a second variable numCats, how can
you use StringBuilder to create a string that looks like We have 9 dogs and
5 cats?
5. What is a verbatim string?
6. What are Regular Expressions?
Collections and Strings
11-40 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. If you have a limited resource and want to provide access to that resource
from a collection, retrieving from the collection on a first come, first
served basis, which collection class should you use?
To support first come, first served, you should use a Queue.
2. How can you retrieve all the contents of a HashTable, matching each key
to its value?
Ask the HashTable for its enumerator (GetEnumerator()) and
then iterate with a while loop: while(enumerator.MoveNext().
For each entry retrieved, access enumerator.Key and
enumerator.Value.
3. What are the implications of the fact that strings are immutable?
Every time you modify a string you actually create a new one.
This calls the strings constructor and has an impact on
performance.
4. If you have a variable numDogs and a second variable numCats, how can
you use StringBuilder to create a string that looks like We have 9 dogs and
5 cats?
You can use the StringBuilders instance method AppendFormat,
passing in the substitution parameters as you would for
Console.WriteLine: myStringBuilder.AppendFormat(We have
{0} dogs and {1} cats, numDogs, numCats);
5. What is a verbatim string?
A verbatim string begins with the at symbol (@) and what follows
is taken as a literal string, character for character, including
characters that would otherwise have to be escaped.
6. What are Regular Expressions?
Regular Expressions are a language for describing and
manipulating text.
Strings
C# Professional Skills Development 11-41
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 11:
Collections and
Strings
Lab 11:
Collections and Strings
11-42 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 11 Overview
In this lab youll learn how to work with standard .NET collections: stacks and
queues.
To complete this lab, youll need to work through two exercises:
Storing Data in a Queue
Storing Data in a Stack
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-bystep

instructions.
Storing Data in a Queue
C# Professional Skills Development 11-43
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Storing Data in a Queue
Objective
In this exercise, youll create and manipulate a Queue class.
Things to Consider
Queues are used for FIFO (First In, First Out) storage of data.
A Queue class holds an object. You may pass in any kind of object and
mix and match the objects held in the queue.
When you remove an object from the Queue a reference to that object
is returned to you.
Step-by-Step Instructions
1. Open the file Queue Starter.sln in the Queue Starter folder.
2. Instantiate a Queue object.
Queue queue = new Queue();
3. Add a string ("Hello") to the queue.
queue.Enqueue("Hello");
4. Add ten integers to the queue.
queue.Enqueue(i*10);
5. Output the progress to the console and then display the queue.
Console.Write( "queue values:\t" );
PrintValues( queue );
Lab 11:
Collections and Strings
11-44 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
6. Remove an element from the queue.
Console.WriteLine(
"\n(Dequeue)\t{0}", queue.Dequeue() );
7. Output the progress to the console and then display the queue.
Console.Write( "queue values:\t" );
PrintValues( queue );
8. Remove another element from the queue.
Console.WriteLine(
"\n(Dequeue)\t{0}", queue.Dequeue() );
9. Output the progress to the console and then display the queue.
Console.Write( "queue values:\t" );
PrintValues( queue );
10. Display the first element in the queue but do not remove it.
Console.WriteLine(
"\n(Peek) \t{0}", queue.Peek() );
11. Output the progress to the console and then display the queue.
Console.Write( "queue values:\t" );
PrintValues( queue );
12. Create the PrintValues method to iterate a collection. Given an object that
implements IEnumerable, iterate over the collection and call ToString on
each member.
Storing Data in a Queue
C# Professional Skills Development 11-45
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public static void PrintValues(
IEnumerable myCollection )
{
foreach (object i in myCollection)
{
Console.Write("{0} ", i.ToString());
}
Console.WriteLine();
}
13. Compile and run the program as shown in Figure 24.
Figure 24. Final exercise results.
Lab 11:
Collections and Strings
11-46 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Storing Data in a Stack
Objective
In this exercise, youll create and manipulate a Stack class.
Things to Consider
Stacks are used for LIFO (Last In, First Out) storage of data.
A Stack class holds an object, you may pass in any kind of object and
mix and match the objects held in the Stack.
When you remove an object from the Stack a reference to that object is
returned to you.
Step-by-Step Instructions
1. Open Stack Starter.sln in the Stack Starter folder.
2. Instantiate a Stack object.
Stack myStack = new Stack();
3. Populate the stack.
myStack.Push(i*10);
4. Output the progress to the console and then display the Stack.
Console.Write( "stack values:\t" );
PrintValues( myStack );
5. Remove an element from the stack.
Console.WriteLine( "\n(Pop)\t{0}",
myStack.Pop() );
Storing Data in a Stack
C# Professional Skills Development 11-47
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
6. Output the progress to the console and then display the stack.
Console.Write( "myStack values:\t" );
PrintValues( myStack );
7. Remove another element from the stack.
Console.WriteLine( "\n(Pop)\t{0}",
myStack.Pop() );
8. Output the progress to the console and then display the stack.
Console.Write( "myStack values:\t" );
PrintValues( myStack );
9. Display the first element in the stack.
Console.WriteLine( "\n(Peek) \t{0}",
myStack.Peek() );
10. Output the progress to the console and then display the stack.
Console.Write( "myStack values:\t" );
PrintValues( myStack );
11. Declare an array of 15 integers.
int[] targetArray = new int[15];
12. Populate the array.
for (int i = 0; i < 15; i++)
targetArray[i] = i * 100 + 100;
Lab 11:
Collections and Strings
11-48 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
13. Output the progress to the console and then display the values of the target

Array instance.
Console.WriteLine( "\nTarget array: ");
PrintValues( targetArray );
14. Copy the entire source Stack to the target Array instance, starting at
index 6.
myStack.CopyTo( targetArray, 6 );
15. Output the progress to the console and then display the values of the target

Array instance.
Console.WriteLine( "\nTarget array after copy: ");
PrintValues( targetArray );
16. Copy the entire source Stack to a new standard array.
Object[] myArray = myStack.ToArray();
17. Output the progress to the console and then display the values of the new
standard array.
Console.WriteLine( "\nThe new array:" );
PrintValues( myArray );
18. Compile and run the program as shown in Figure 25.
Storing Data in a Stack
C# Professional Skills Development 11-49
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 25. Final exercise results.
Lab 11:
Collections and Strings
11-50 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Exceptions
C# Professional Skills Development 12-1
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Exceptions
Objectives
Use exceptions to handle unexpected but predictable undesirable
situations while your program runs.
Create exception handlers.
Manage the unwinding of the stack in the presence of exceptions.
Set the Message and Help properties of exceptions.
Create custom exceptions.
Manage nested exceptions.
Exceptions
12-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Throwing and Catching Exceptions
C# developers distinguish among three things that might go wrong while their
code is running:
Bugs
User errors
Exceptions
A bug is a mistake in your code. Bugs should be found through careful testing,
and repaired as quickly as possible. While few modern complex programs can
claim to be bug-free, the only strategy for dealing with bugs is to squish them
as quickly as possible.
User errors must be anticipated and coped with. When you ask the user to enter
his name, he may well enter his birthdate. When you ask the user to click a
button, he may right-click, click the wrong button, or pull the plug out of his
computer. Users are like that, and you have to be ready and you must recover
from their errors as well as you can.
Exceptions, however, are neither bugs nor a result of user errors. Exceptions
are situations that are predictable, undesirable, and unpreventable. For
example, when you try to read a file from the operating system, the file may
not exist. Or the file may be in use by another application. Or you may not
have enough memory to open the file. Each of these is an exceptional, but
predictable circumstance.
In C#, exceptions encapsulate this concept of an exceptional circumstance, and
they offer you an object-oriented way to handle the exception and to recover
gracefully.
Exception Terminology
When an exceptional circumstance arises, an exception object is thrown (some
programmers talk about raising an exception, but the common C# term is to
throw the exception). When an exception is thrown, an exception handler
catches the exception and takes corrective action.
When the exception is thrown, all execution stops. If there is an exception
handler in the same method, then processing switches to the exception handler.
If not, then the stack is unwound until a handler is found.
When the stack is unwound, control passes to the method that called the
method that threw the exception. If that calling method has no handler, the
stack is unwound further, to the method that called that method, and so forth.
Throwing and Catching Exceptions
C# Professional Skills Development 12-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
This process of unwinding is illustrated in Figure 1. The illustration starts in

the upper left corner. A method SomeDangerousThing is called (see 1). Within
SomeDangerousThing, a second method, SomeOtherFunc is invoked (see 2).
Within SomeOtherFunc an exception is thrown. There is no handler in
SomeOtherFunc, so the stack is unwound and the exception is passed to the
calling method, SomeDangerousThing (see 3). SomeDangerousThing does not
have a handler, so the stack is further unwound, and the exception is passed to
the first method (see 4). This first method does have a handler (see the catch
block). The exception is handled, and program flow continues in this
outermost method just below the catch block.
Figure 1. Unwinding the stack.
Exceptions
12-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
System.Exception
When you catch an exception, you may catch it by type. This allows you to
narrow your exception handler down to a specific type of exception. For
example, you might create an exception handler to catch all arithmetic
exceptions with this code:
catch(System.ArithmeticException)
{ // }
System.ArithemeticException derives from System.Exception and is supplied
by the .NET Framework. Put the type of the exception in parentheses after the
catch statement, and put the handler inside braces, as shown in the previous
code.
The .NET Framework provides System.Exception, and a number of derived
types. You are free to derive your own types from System.Exception as well.
In any case, all exceptions must be of type System.Exception or a class derived
from System.Exception.
If you do not provide a type for the catch statement, it will catch exceptions o
f
any type at all:
catch
{ // }
The code above is identical in its effect to writing:
catch(System.Exception)
{ // }
Either of these statements will catch any exception that comes their way.
Exception Handling Order
Because exception handlers are evaluated in the order they appear in the
method, you will want to put your more specialized exception handlers before
your more general exception handlers.
Since System.DivideByZeroException derives from
System.ArithmeticException, you will want to place the
Throwing and Catching Exceptions
C# Professional Skills Development 12-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
DivideByZeroException handler before the more general ArithmeticException
handler:
catch (System.DivideByZeroException) { // }
catch (System.ArithmeticException) { // }
catch { // }
If the order were reversed, the DivideByZeroException would never be called.
catch (System.ArithmeticException) { // }
catch (System.DivideByZeroException) { // }
catch { // }
In the order shown here, if a DivideByZero exception were to be raised, the
exception would match the more general ArithmeticException and would be
handled in that catch block. The DivideByZero handler would never run.
If you put these back into the correct order,
catch (System.DivideByZeroException) { // }
catch (System.ArithmeticException) { // }
catch { // }
the first handler catches a DivideByZeroException. If another arithmetic
exception is thrown, it will be ignored by DivideByZero and handled by the
second handler. All other exceptions will be ignored by the first and second
handler, but caught by the third.
Closing Resources
C# has garbage collection, so you dont have to worry that objects once
created might hang around in memory if an exception takes you out of the
normal program flow. With that said, there are other resources that are not
managed by the garbage collector and can be left in an unacceptable state if an
exception is thrown.
Imagine, for example, that in a method you open a file, take action, and then
close the file. It is imperative that you close the file so that other code can
get
to the file. If, however, that action throws an exception, there is a danger tha
t
the file will not be closed:
Exceptions
12-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
SomeMethod()
{
OpenFile();
TakeDangerousAction();
CloseFile(); // will you get here??
}
If the method TakeDangerousAction throws an exception (or calls a method
that throws an unhandled exception) the call to CloseFile may never take place
and the file may be left open.
You could try to fix this by adding a catch block, and closing the file in the
catch block, but this method is prone to error.
SomeMethod()
{
try
{
OpenFile();
TakeDangerousAction();
CloseFile(); // will you get here??
}
catch
{
CloseFile(); // error prone
}
}
The problem with this approach is that changes made in the try block must be
duplicated in the catch block. You might add additional catch blocks (for
specialized errors) and each will need the call to CloseFile. Sooner or later
youll forget to update one or the other and your program will stop working.
Or worse, it will work for a long time and then fail unexpectedly.
What you need is a way to ensure that the CloseFile() method will be called
whether or not an exception is thrown. That is what the finally block is for. Th
e
code in a finally block is guaranteed to be called whether or not the catch bloc
k
is called.
Throwing and Catching Exceptions
C# Professional Skills Development 12-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
SomeMethod()
{
try
{
OpenFile();
TakeDangerousAction();
}
catch
{
CloseFile(); // guaranteed to get here
}
}
Rules for finally Blocks
Finally blocks can be created without a catch block, but you must have a try
block.
SomeMethod()
{
try
{
OpenFile();
TakeDangerousAction();
}
finally
{
CloseFile(); // guaranteed to get here
}
}
Finally must not exit with break, continue, return, or goto. These unconditional

branching statements are covered elsewhere in this course.
Exceptions
12-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
To see how to use exceptions, youll create a simple test application and add
exception handling.
1. Open a new console application and name it Exceptions.
2. Create a simple Tester class with a Run() method.
3. Within Run() display where you are and call Function1(). When you return
from Function1() display that you are ready to exit from Run().
public class Tester
{
public void Run()
{
Console.WriteLine("In Run()");
Function1();
Console.WriteLine("Exiting Run...");
}
4. Add Function1. Have it invoke Function2. Before and after invoking
Function1, display your progress.
public void Function1()
{
Console.WriteLine("In function 1");
Function2();
Console.WriteLine("Exiting function 1");
}
5. Write Function2. Within Function2, display where you are, but throw an
exception.
See Exceptions.sln
Throwing and Catching Exceptions
C# Professional Skills Development 12-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public void Function2()
{
Console.WriteLine("In
Console.WriteLine("Exiting function 2");
}
Notice that you throw an exception by creating one on the heap with the new
keyword. In this case, youll throw an instance of System.Exception, passing
no parameters to the constructor.
6. Run the application. The results are shown in Figure 2. You can see that
the Just In Time debugger has caught the exception. Looking at the output,
you see the beginning of Run, Function1, and Function2, but none of the
functions complete.
Exceptions
12-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 2. Throwing the exception.
7. Click Yes and proceed into the debugger. You can get to the same place by
running the code in the debugger (press F5). In either case, you find
yourself in the development environment. The current line of execution is
the line in which the exception is thrown. The Debugging dialog box
offers you the option to break there or to continue, as shown in Figure 3.
Notice in the lower right corner that the call stack shows that the static
method Main called the instance method Run, which called Function1
which in turn called the current method: function2.
Throwing and Catching Exceptions
C# Professional Skills Development 12-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 3. Catching the exception.
8. Click on Break, and press SHIFT+F5 to stop debugging.
To avoid crashing, your program needs a catch block to handle the exception.
9. Rewrite Function2: surround the exception with a try block. Add a catch
block to handle the exception; in this case youll just display a message
indicating that you caught the exception.
See Exceptions2.sln
Exceptions
12-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public void Function2(){
Console.WriteLine("In function 2");
try
{
Console.WriteLine("Entering Try...");
throw new System.Exception();
Console.WriteLine("Exiting Try...");
}
catch
{
Console.WriteLine("Caught exception...");
}
Console.WriteLine("Exiting function 2");
}
10. Run the application. The results are shown in Figure 4. The catch block
allows your program to recover. You now see Function2 return, Function1
returns, and Run returns. All is right with the world.
Figure 4. Catching the exception.
Unwinding the Stack
Often you will not have a catch block (or even a try block) in the particular
method that throws an exception. Unhandled exceptions are passed back to the
calling method, and you may have an exception handler there.
Throwing and Catching Exceptions
C# Professional Skills Development 12-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
To see how a calling function might handle an exception, youll rewrite the
previous example.
1. Reopen the previous example.
2. Take the try/catch block out of function2.
public void Function2()
{
Console.WriteLine("In function 2");
throw new System.Exception();
Console.WriteLine("Exiting function 2");
}
3. Add a try/catch block to Function1. Function1 will now call Function2
from within its try block. If an unhandled exception is thrown in
Function2, it will be caught in the try block of Function1.
public void Function1()
{
Console.WriteLine("In function 1");
try
{
Console.WriteLine("Entering Try...");
Function2();
Console.WriteLine("Exiting Try...");
}
catch
{
Console.WriteLine("Caught exception...");
}
Console.WriteLine("Exiting function 1");
}
4. Run the application. The results are shown in Figure 5. Examine Figure 5
carefully and compare it with Figure 4. They are different in a very
important way. In Figure 4 you see that Function2 exits cleanly and the
See Exceptions3.sln
Exceptions
12-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
message Exiting Function2 is displayed. That message does not appear in
Figure 5.
Figure 5. Catching a calling function.
In this example, the method Function2 did not exit cleanly; an exception was
thrown and not handled. The stack was unwound and the exception was
handled in Function1, but the final line of Function2 never executes. This is a
critical distinction.
Catching Specific Exception Types
Until now youve been using the generic System.Exception. The .NET
Framework provides a number of specialized exception types to help you
manage various exceptional circumstances.
Try It Out!
In the next example, you will create a method that under one circumstance
throws a DivideByZero exception, and under a different circumstance throws
an ArithmeticException. Your code must differentiate between these two
exception types.
It is important to understand that DivideByZeroException derives from
ArithmeticException, which in turn derives from System.Exception. Thus,
DivideByZeroException is an ArithmeticException.
1. Reopen the previous example.
2. Create a new method, DoDivide. This method will take two doubles and
return the quotient as a double. If the divisor is zero you will throw the
specialized DivideByZeroException because it is illegal to divide by zero.
See Exceptions4.sln
Throwing and Catching Exceptions
C# Professional Skills Development 12-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Normally, it is just fine to divide zero by another number, but in this case,
youre not going to allow that, and youre going to throw the more general
ArithmeticException. This is a bit contrived (since dividing zero by another
number is mathematically just fine) but it will illustrate how exceptions are
handled.
public double DoDivide (double dividend, double divisor)
{
if (divisor == 0)
throw new System.DivideByZeroException();
if (dividend == 0)
throw new System.ArithmeticException();
return dividend/divisor;
}
3. You will call this method from Function2, The first time you call youll
divide 12 by 15.
public void Function2()
{
Console.WriteLine("In function 2");
double x = 12;
double y = 15;
Console.WriteLine("Trying {0} / {1}...", x,y);
Console.WriteLine ("{0} / {1} = {2}",
x, y, DoDivide(x,y));
4. Assign the value zero to the local variable y and call DoDivide again.
Console.WriteLine("Trying {0} / {1}...", x,y);
Console.WriteLine ("{0} / {1} = {2}",
x, y, DoDivide(x,y));
5. When you are done with Function2, display that and exit the method.
Console.WriteLine("Exiting function 2");
}
Exceptions
12-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Notice that there is no try or catch block in Function2. Youll catch any
exceptions in Function1.
6. Rewrite Function1 to call Function2 within a try block, but specialize your
exception handling. Youll need three catch blocks. The first will catch a
DivideByZeroException, the second will catch the more general
ArithmeticException, and the third will catch any exception at all.
public void Function1()
{
Console.WriteLine("In function 1");
try
{
Console.WriteLine("Entering Try...");
Function2();
Console.WriteLine("Exiting Try...");
}
catch (System.DivideByZeroException)
{
Console.WriteLine(
"Divide by zero exception caught");
}
catch (System.ArithmeticException)
{
Console.WriteLine(
"ArithmeticException exception caught");
}
catch
{
Console.WriteLine(
"Caught unknown exception...");
}
Console.WriteLine("Exiting function 1");
}
7. Run the program. The results are shown in Figure 6. The first time
through, the DoDivide method works fine and no exception is thrown;
12/15 = 0.8. The second time through, you try to divide 12 by 0 and that
Throwing and Catching Exceptions
C# Professional Skills Development 12-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
does throw an exception. The exception is caught and the correct specific
message is displayed.
Figure 6. Catching specialized exceptions.
8. Try reversing the catch blocks in Function1.
public void Function1()
{
Console.WriteLine("In function 1");
try
{
Console.WriteLine("Entering Try...");
Function2();
Console.WriteLine("Exiting Try...");
}
catch (System.ArithmeticException)
{
Console.WriteLine(
"ArithmeticException exception caught");
}
catch (System.DivideByZeroException)
{
Console.WriteLine(
"Divide by zero exception caught");
}
Exceptions
12-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
catch
{
Console.WriteLine("Caught unknown exception...");
}
Console.WriteLine("Exiting function 1");
}
9. Run the application again. The program wont compile. The error
message, on the line for the DivideByZero exception is: A previous catch
clause already catches all exceptions of this or a super type
(System.ArithmeticException). The compiler recognizes that it is
impossible to catch a DivideByZero exception if you place the
ArithmeticExcpetion first!
Finally
In the previous example, Function1 calls Function2 and Funtion2 throws an
exception. The catch block in Fuction1 catches the exception and does not
crash, but the line after the call to Function2, Exiting Try is never invoked.
This line doesnt do anything important, so no real harm is done, but there are
circumstances where not invoking the lines following the call to a function
might be disastrous. For example, if Function1 were to open a file and then
call Function2 and then close the file, the exception in Function2 might
prevent the file from ever being closed.
You can solve that problem with a finally block. The keyword finally creates a
block of code that will run whether or not an exception is handled. The finally
block can only follow a try block: you cant have finally without try, though
you can have finally without a catch block.
Try It Out!
In this next exercise, youll rewrite the previous example to illustrate the use o
f
the finally block.
1. Reopen the previous example.
2. In the try block within Function1 open a file (youll mimic this by
displaying the words Opening a file).
See Exceptions5.sln
Throwing and Catching Exceptions
C# Professional Skills Development 12-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public void Function1()
{
Console.WriteLine("In function 1");
try
{
Console.WriteLine("Entering Try...");
Console.WriteLine("Opening a file...");
Function2();
Console.WriteLine("Exiting Try...");
}
catch (System.DivideByZeroException)
{
Console.WriteLine("Divide by zero exception caught");
}
catch (System.ArithmeticException)
{
Console.WriteLine("ArithmeticException exception
caught");
}
catch
Console.WriteLine("Caught unknown exception...");
}
3. You must close the file. You cant put it in the try block because
Function2 might throw an exception. Instead, youll put it in the finally
block where it is guaranteed to run whether or not there is an exception.
finally
{
Console.WriteLine("Closing the file...");
}
5. Run the application. The results are shown in Figure 7. Notice that the
finally block has executed even though an exception was caught.
Exceptions
12-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 7. Executing the finally block.
6. Comment out the call to set y to 0. This removes the need to throw an
exception.
7. Run the application again. The results are shown in Figure 8. This time no
exception is thrown, and you see the display of Exiting Try. Notice,
however, that the message Closing the file is still displayed; the finally
block is invoked whether or not an exception is thrown.
Figure 8. No exception was thrown.
The Exception Object
Until now youve been dealing with exceptions as sentinel objects; their very
presence signals a problem. Exceptions are classes, however, and as such they
Throwing and Catching Exceptions
C# Professional Skills Development 12-21
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
have methods and properties. System.Exception, for example, has a few very
useful properties.
The Message Property stores a message describing the exception. Each .NET
Framework exception type has a default message, but you are free to set the
message as well. One overloaded version of the constructor for
System.Exception takes a message parameter; whatever string you pass in is
stored as the message for that exception.
A second useful property is the HelpLink. This is a URL that you can store
with the exception to guide the user to more documentation about the
exception itself.
A third very helpful property is the StackTrace. By displaying the StackTrace
the developer can examine the exact location in the code where the exception
was thrown. You can take control of this information to write it to a log file o
r
to display it to the user.
Try It Out!
To see how these properties are used, youll rewrite the previous example to
create and manipulate instances of .NET exceptions.
1. Reopen the previous example.
2. Rewrite DoDivide. If the divisor is 0, instantiate a DivideByZeroException
object.
public double DoDivide (double dividend, double divisor)
{
if (divisor == 0)
{
DivideByZeroException e =
new DivideByZeroException();
3. Add a help link for this exception. Typically youd provide a URL to a
specific document. In this case, just provide a general URL to a Web site.
e.HelpLink = "http://www.LibertyAssociates.com";
4. Now that the exception has been set the way you want it, throw the
exception.
See Exceptions6.sln
Exceptions
12-22 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
throw e;
5. Modify the code that catches the DivideByZero exception to display the
default message, along with the HelpLink you created and the StackTrace
created by the CLR.
catch (System.DivideByZeroException e)
{
Console.WriteLine("Divide by zero exception: {0}",
e.Message );
Console.WriteLine("\nHelp link: {0}", e.HelpLink);
Console.WriteLine("\nStack trace: {0}", e.StackTrace);
}
Notice that this time you provide an identifier for the DivideByZeroException.
Here you use the letter e as the identifier; this is a common idiom. You can
display the Message property by writing e.Message, just as you would with
any other object.
6. Run the application; the results are shown in Figure 9. You can see the
exception thrown, and the default message is displayed: Attempted to
divide by zero. The HelpLink property is used to display a URL for more
information, and the stack trace tells you the exact line on which the
exception occurs. Notice that the stack trace also shows the line on which
Function1 calls Function2, and on which Run calls Function1. This is a
complete stack trace to help you track down exactly what was happening
in your code.
Throwing and Catching Exceptions
C# Professional Skills Development 12-23
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 9. Exception properties.
7. Modify the creation of the DivideByZero exception. This time, pass a
custom message to the constructor.
public double DoDivide (double dividend, double divisor)
{
if (divisor == 0)
{
DivideByZeroException e =
new DivideByZeroException(
"No quick road to infinity!");
8. Comment out the line with the stack trace (to simplify the output).
Exceptions
12-24 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
catch (System.DivideByZeroException e)
{
Console.WriteLine("Divide by zero exception: {0}",
e.Message );
Console.WriteLine("\nHelp link: {0}", e.HelpLink);
// Console.WriteLine("\nStack trace: {0}",
// e.StackTrace);
}
9. Rerun the application. The results are shown in Figure 10. Your custom
message is displayed. When you instantiate the
DivideByZeroExceptionObject the string you pass as a parameter is sent
up to the base class System.Exception and assigned to the Message
property.
Figure 10. A custom exception message.
Creating Custom Exception Types
The .NET Framework provides a number of specialized exception types, but if
you cant find what you want, you are free to create your own. You may then
throw these exceptions and catch them just as you might throw and catch any
of the built-in types.
Throwing and Catching Exceptions
C# Professional Skills Development 12-25
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The only restriction on your built-in types is that they must ultimately derive
from System.Exception. You are free to derive from an existing exception type
or from System.Exception directly.
Rethrowing Exceptions
So far, whenever youve thrown an exception, youve caught it in a catch
block, handled it, and moved on. There are times, however, when you will take
some action in the exception handler and then throw the exception again. You
can rethrow the same exception just by writing.
throw;
You might decide to throw a new exception, however. You do that by creating
the new exception and throwing it from within the exception handler for the
first exception.
Inner Exceptions
Throwing a new exception is fine, but perhaps you dont want to lose the
information stored in the original exception. You can wrap the old exception
inside the new exception! Every exception has a reference to another
exception, known as the inner exception. When your exception is created the
InnerException property is null, but you are free to assign an exception to it.
For example, you might catch a DivideByZero exception and take some action.
You might then create a new System.Exception object to throw, but youll nest
the original DivideByZero inside it to preserve the original message, as shown
in Figure 11. The original DivideByZero exception is housed within the new
System.Exception object.
Exceptions
12-26 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 11. Nesting an exception.
When you catch the System.Exception object you might decide to throw a new
exception, nesting the original exception within the new, custom exception. Of
course, the Exception object you are nesting is itself nesting the DivideByZero
object, as shown in Figure 12.
Figure 12. Cascading nested exceptions.
Throwing and Catching Exceptions
C# Professional Skills Development 12-27
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
To see how custom exceptions work, youll create a simple console application
and add a custom exception type. Youll also nest exceptions to see the
advantage of keeping an exception history.
1. Create a new console application named CustomExceptions.
2. Create a custom exception type, BozoException. The constructor will take
two parameters: a string message and an object of type System.Exception
inner.
public class BozoException : System.Exception
{
public BozoException(
string message,
Exception inner):
base(message, inner)
{ }
}
BozoException derives from System.Exception, and it chains up to the
Exception constructor, passing along the two parameters to its base class
constructor. System.Exceptions constructor is overloaded to take these two
objects. The first, message is assigned to the Message property. The second,
inner, is assigned to the InnerException property. This is how inner exceptions
are stored.
3. Create a Tester class with seven methods, each of which uses
Console.WriteLine to display its progress. The first method is the static
method Main, which instantiates a Tester and calls Run.
public static void Main()
{
Console.WriteLine("In Main()");
Tester t = new Tester();
t.Run();
Console.WriteLine("Exiting Main...");
}
See Custom
Exceptions.sln
Exceptions
12-28 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
4. Create the Run method. Other than display its progress, Runs only job is
to invoke Method1.
public void Run()
{
Console.WriteLine("In Run()");
Method1();
Console.WriteLine("Exiting Run...");
}
5. Method1 opens a file and then invokes Method 2.
Console.WriteLine("In Method 1");
try
{
Console.WriteLine("Entering Try...");
Console.WriteLine("Opening a file...");
Method2();
Console.WriteLine("Exiting Try...");
}
6. Method2 invokes Method3, which invokes Method4, which in turn
invokes Method5. Method5 throws a DivideByZeroException, passing in a
custom message.
public void Method5()
{
throw new DivideByZeroException(
"Method5 Divided by zero exception");
}
7. The stack is unwound and the exception is caught by Method4 (the method
that called Method5). Give Method4 a catch block to catch
ArithmeticException objects.
Method4 will take partial action based on this exception. In this case it just
prints a message to the console, but in a real application it might take other
actions based on registering the exception. It then rethrows the exception,
exactly as is, making no modifications to it.
Throwing and Catching Exceptions
C# Professional Skills Development 12-29
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public void Method4()
{
try
{
Method5();
}
catch (System.ArithmeticException e)
{
Console.WriteLine(
"Method4 caught an arithmetic
exception. Rethrowing...");
throw; // just toss it back out there
}
}
Notice that the catch block is looking for a base class of
DivideByZeroException. Since DivideByZeroException derives from
ArithmeticException it is-an ArithmeticException and so the catch block
matches. Note also that in order to rethrow the exception you use the throw
keyword.
8. The stack is unwound and the exception is offered to Method3. Method3
has a handler for DivideByZero exception. In the handler Method3 creates
a new exception object (in this case, of type System.Exception). It creates
the new exception object and passes in two parameters: a string and the
original exception. The original exception is assigned to the
InnerException property of the new exception, then the new exception is
thrown.
Exceptions
12-30 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public void Method3()
{
try
{
Method4();
}
catch (System.DivideByZeroException e)
{
Exception ex =
new Exception(
"Method3 Caught divide by zero", e);
throw ex;
}
}
9. Once again the stack is unwound, this time to Method2. Method2 has a
catch block for System.Exception, and so catches the thrown exception. In
the exception handler, it creates a new instance of the custom exception
type BozoException, passing in a new message and the old exception.
Notice that the exception it passes in is the System.Exception object created in

Method3s catch block, which in turn has as a nested exception, the original
DivideByZeroException object. This System.Exception object is now the
InnerException property in the new BozoException.
public void Method2()
{
try
{
Method3();
}
catch (System.Exception e)
{
BozoException bozo =
new BozoException(
"Method2 Custom Exception Situation!",e);
throw bozo;
}
}
Throwing and Catching Exceptions
C# Professional Skills Development 12-31
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
10. The catch block then throws this BozoException, again unwinding the
stack. Control returns to the catch block for Method1. In that catch block,
you will print out the message from the exception and all its nested
exceptions.
public void Method1()
{
Console.WriteLine("In Method 1");
try
{
Console.WriteLine("Entering Try...");
Console.WriteLine("Opening a file...");
Method2();
Console.WriteLine("Exiting Try...");
}
catch (BozoException e)
{
Console.WriteLine("\n{0}",e.Message);
Console.WriteLine("Exception history...");
Exception inner = e.InnerException;
while (inner != null)
{
Console.WriteLine("{0}", inner.Message);
inner = inner.InnerException;
}
}
finally
{
Console.WriteLine("Closing the file...");
}
Console.WriteLine("Exiting Method 1");
}
You start by printing the message from the exception:
Exceptions
12-32 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
catch (BozoException e)
{
Console.WriteLine("\n{0}",e.Message);
To print the inner exception messages, you start by creating a local object of
type Exception and assigning to it the InnerException property of the
exception you caught.
Exception inner = e.InnerException;
You then create a while loop to test if inner is null. You print the message, an
d
then you assign to inner its own InnerException property, thus reassigning the
nested exception object to inner.
while (inner != null)
{
Console.WriteLine("{0}", inner.Message);
inner = inner.InnerException;
}
This is a tricky bit of code, so make sure you understand how it works before
going forward. At the start of this catch block you have the following
structure:
BozoException
System.Exception
DivideByZeroException
BozoException has an InnerException property of type System.Exception,
which in turn has an InnerException property of type DivideByZeroException.
When you assign inner to e.InnerException, e is the BozoException and inner
is now a reference to System.Exception. When you write
inner = inner.InnerException
inner is the System.Exception so InnerException is the
DivideByZeroException. At the end of the assignment, inner refers to the
DivideByZeroException!
Throwing and Catching Exceptions
C# Professional Skills Development 12-33
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
11. Run the application. The results are shown in Figure 13. You see the
exception history printed, much as you might expect.
Figure 13. Displaying inner exceptions.
To really understand how this works, you need to put this code into the
debugger.
12. Place a breakpoint in Method5 where the exception will be thrown, as
shown in Figure 14. Run to the breakpoint by pressing F5.
Exceptions
12-34 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 14. Setting the breakpoint.
13. Press F11 to step into the code. Control transfers to the Catch block in the

calling method, Method4. Step in; youll see the message displayed and
the exception is rethrown.
14. The stack is unwound when the exception is rethrown and control transfers
to the catch block in the calling method, Method3. This time you create a
new exception and throw that.
15. Continue to step in; youll find control passes to the catch block in
Method2 where a new exception of type BozoException is created.
Continue to press F11; youll step into the constructor for your custom
exception, and youll see the parameters passed to the base class
constructor.
16. Continue stepping back into Method 2; the new BozoException is thrown.
17. The stack continues to unwind and you find yourself back in the catch
block of Method1. Before you step further, examine the Locals window.
You should see the exception, e.
18. Undock the Locals window to make it nice and big, and expand e, as
shown in Figure 15. When you expand e, youll find System.Exception,
the base class. Expand that as well. Inside the base class youll find many
Throwing and Catching Exceptions
C# Professional Skills Development 12-35
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
fields, including _innerException. This is the underlying field supporting
the InnerException property.
Figure 15. Viewing the nested objects.
19. Expand innerException and youll find the properties of the inner
exception object. One of the properties will be _innerException. This is the
innerException field of the nested exception.
20. Expand the inner exception object and youll find a
System.DivideByZeroException. Inside youll find its base class
Sytem.ArithemeticException. In that youll find System.Exception and
inside that will be the _innerException field. That innermost
innerException field is null, just as youd expect.
21. Continue stepping through the code. You can watch as inner is assigned to
the object referred to by the InnerException property.
22. Each message is displayed in turn until the object inner is null. The loop
then finishes, and youll step into the finally block where the file is closed,
and Method 1 unwinds to the Run method, which in turn unwinds to Main,
and the program ends.
Exceptions
12-36 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
Exceptions are thrown to flag undesirable, but predictable problems in
the running of your program.
Exceptions should not be used to manage bugs or user errors.
Exceptions are thrown and caught. The catch block handles the
exception and allows your program to continue.
When an exception is thrown, control moves to the catch block. If
there is no appropriate catch block, the stack is unwound until a
suitable handler is found.
A finally block is guaranteed to run whether or not there is an
exception.
A finally block does not require a catch block, but it does require a try
block.
You may derive new exceptions from System.Exception or any other
existing exception class.
Exceptions are presented to their handlers in the order the handlers are
written. You must put more specialized handlers before more general
handlers.
The exception type has a number of useful properties, including
Message, HelpLink, and StackTrace.
The Message property allows you to create a custom message. You
may pass the message to the constructor of the exception.
The HelpLink property allows you to provide a URL for help files.
The StackTrace exception is supported by the CLR and allows you to
display or log the line on which the exception is thrown, along with a
complete stack trace of the method calls.
An exception can be rethrown with the keyword throw.
You may nest one exception within another using the InnerException
property.
Throwing and Catching Exceptions
C# Professional Skills Development 12-37
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. Name three valid circumstances for throwing exceptions and two invalid
circumstances.
2. How do you ensure that a resource will be closed (or otherwise disposed
of), in the presence of exceptions?
3. How do you pass a custom message to an exception?
4. How do you pass a URL to provide a Help file when an exception is
thrown?
5. Can a custom exception derive from Object?
Exceptions
12-38 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. Name three valid circumstances for throwing exceptions and two invalid
circumstances.
A program might throw an exception for any number of reasons,
typically when a resource is not available. Three good examples
include (1) running out of memory, (2) attempting to open a file
and the file is missing or not valid to open, and (3) attempting to
contact another machine on the network and your program times
out. You do not want to use an exception for (1) a bug in your
code or (2) invalid user data entry.
2. How do you ensure that a resource will be closed or otherwise disposed, in
the presence of exceptions.
Be sure to create a try block around any potentially dangerous
code, and put the close/dispose code inside a finally block.
3. How do you pass a custom message to an exception?
You may pass a message to the constructor; it will be assigned to
the Message property. The property is read-only, so you must set
the message when you construct the object.
4. How do you pass a URL to provide a Help file when an exception is
thrown?
To set the URL of a Help file, use the read/write HelpLink
property of the exception.
5. Can a custom exception derive from Object?
All exceptions must derive from System.Exception, which of
course, ultimately derives from Object.
Throwing and Catching Exceptions
C# Professional Skills Development 12-39
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 12:
Exceptions
Lab 12:
Exceptions
12-40 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 12 Overview
In this lab youll learn how to work with exceptions.
To complete this lab, youll need to work through two exercises:
Throwing and Catching Exceptions
Creating Custom Exceptions
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-bystep

instructions.
Throwing and Catching Exceptions
C# Professional Skills Development 12-41
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Throwing and Catching Exceptions
Objective
In this exercise, youll throw and catch exceptions and work with finally
blocks.
Things to Consider
Exceptions are caught in the order most-derived to least derived.
The finally block is executed whether or not an exception is thrown.
The .NET Framework provides a number of standard exception
classes.
All Exception classes must ultimately derive from System.Exception.
Step-by-Step Instructions
1. Open Exceptions Starter.sln in the Exceptions Starter folder.
2. Create a try block and display it to the console.
try
{
Console.WriteLine("Opening file...");
Console.WriteLine("Calling SecondFunction");
3. Call the second function, display it to the console, and end the try block.
SecondFunction();
Console.WriteLine("Exiting Try...");
}
4. Create a catch block to catch DivideByZeroException. Display to the
console that DivideByZeroException was caught.
Lab 12:
Exceptions
12-42 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
catch (System.DivideByZeroException)
{
Console.WriteLine("Divide by zero exception caught");
}
5. Create a generic catch block. Display to the console that an exception was
caught.
catch
{
Console.WriteLine("Caught unknown exception...");
}
6. Create a finally block. Display to the console that a finally block was
caught.
finally
{
Console.WriteLine("Closing the file...");
}
7. Implement the SecondFunction. Display the progress to the console.
public void SecondFunction()
{
Console.WriteLine("In SecondFunction");
8. Create two double variables and initialize them to small values.
double x = 10;
double y = 5;
9. Call DoDivide and display the results.
Console.WriteLine ("{0} / {1} = {2}", x, y,
DoDivide(x,y));
Throwing and Catching Exceptions
C# Professional Skills Development 12-43
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
10. Set the second variable to zero.
y = 0;
11. Call DoDivide and display the results.
Console.WriteLine ("{0} / {1} = {2}", x, y,
DoDivide(x,y));
12. Implement DoDivide. Test for divide by zero and if found, throw
DivideByZeroException.
public double DoDivide (double a, double b)
{
if (b == 0)
throw new System.DivideByZeroException();
return a/b;
}
13. Compile and run the application as shown in Figure 16.
Figure 16. Final exercise results.
Lab 12:
Exceptions
12-44 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Creating Custom Exceptions
Objective
In this exercise, youll see how to create and work with your own custom
exception class, and how to nest exceptions in multiple throw statements.
Things to Consider
When you catch an exception and then throw a new exception, you
may house the original exception as an inner exception within the new
one.
Exceptions stack one within the other, so that you may retrieve the
entire history of inner exceptions.
Custom exceptions may have methods and members, but it is not
uncommon to use an exception object as a signal; its very existence
signals the special kind of exception.
Step-by-Step Instructions
1. Open Custom Exceptions Starter.sln in the Custom Exceptions Starter
folder.
2. Create a custom exception class derived from System.Exception.
public class CustomException : System.Exception
{
3. Create a constructor that takes two parameters. The first is a string for the

exception message. The second is of type Exception and represents the
inner exception. Pass both to the base class constructor.
public CustomException(
string message,
Exception inner):
base(message, inner)
{ }
Creating Custom Exceptions
C# Professional Skills Development 12-45
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
4. Create a try block and output the progress to the console.
try
{
Console.WriteLine("Opening a file...");
5. Call SecondMethod. Output the progress to the console and end the try
block.
SecondMethod();
Console.WriteLine("Exiting Try...");
}
6. Catch the CustomException.
catch (CustomException e)
{
7. Display that youve caught the custom exception and display the
exceptions message.
Console.WriteLine("First Method Caught custom exception");
Console.WriteLine("{0}\n",e.Message);
8. Display that you are going to show the exception history.
Console.WriteLine("Displaying exception history...");
9. Display all the inner exceptions.
Lab 12:
Exceptions
12-46 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Exception inner = e.InnerException;
while (inner != null)
{
Console.WriteLine("{0}", inner.Message);
inner = inner.InnerException;
}
10. Create a finally block. Within the finally block, display that you are
closing the file.
finally
{
Console.WriteLine("Closing the file...");
}
11. Create a SecondMethod to call the first method.
public void SecondMethod()
{
12. Make the method call within a try block.
try
{
ThirdMethod();
}
13. Catch any Exception object and create a new instance of
CustomException, passing in this text as the exception text: Ave, Caeser,
morituri te salutant. Pass the exception you caught as a second parameter.
Creating Custom Exceptions
C# Professional Skills Development 12-47
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
catch (System.Exception e)
{
CustomException Custom =
new CustomException(
"Ave, Caeser, morituri te salutant.",e);
throw Custom;
}
14. Create the ThirdMethod. This method creates a try block and within it,
calls FourthMethod.
public void ThirdMethod()
{
try
{
FourthMethod();
}
15. Create a catch block and catch DivideByZeroException. Create a new
generic exception and pass in the string ThirdMethod caught divide by
zero as well as the original exception.
catch (System.DivideByZeroException e)
{
Exception ex =
new Exception(
"ThirdMethod caught divide by zero", e);
throw ex;
}
16. Create a FourthMethod.
public int FourthMethod()
{
17. Create two integer variables. Initialize them to five and zero. Divide the
first by the second.
Lab 12:
Exceptions
12-48 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
int x = 5;
int y = 0;
return x / y;
18. Compile and run the application as shown in Figure 17.
Figure 17. Final exercise results.
Delegates
C# Professional Skills Development 13-1
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Delegates
Objectives
Understand what delegates are.
See the use of delegates for the indirect invocation of methods.
Explore delegates as static members.
Define delegates as static properties.
Delegates
13-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Introduction to Delegates
There are times when you need to invoke a method indirectly; when you need
to call a method but you cant know at compile time which method youll call.
Delegates are a way to identify a method at run time and to invoke that method
indirectly (through the delegate rather than by writing the name of the delegate

into your code).
Technically, delegates are reference types that encapsulate a method with a
specific signature and return type. The equivalent to delegates in C++ is a
pointer to a member function. In C#, however, delegates are first class typesafe

members of the language.
A delegate is said to encapsulate a method. The methods a given delegate may
encapsulate are described by the delegate definition. Each delegate defines the
return type and parameter list of the methods it can encapsulate. For example,
a given delegate might be defined as:
void myDelegate(string s1, int i2);
This defines myDelegate as a delegate that can encapsulate any method that
returns void and that takes two parameters; the first must be a string and the
second must be an int. You can use this delegate to represent any method with
this return type and signature.
Why Use Delegates?
Delegates are used whenever you cant know at compile time, which method
out of a set of similar methods will be invoked.
You can also use delegates to support the publish/subscribe idiom and handle
events, as will be described later in this course.
Delegates are the mechanism through which C# supports callback methods. A
callback method allows you to invoke a method and say to that method When
you are done with your work, call this method. To accomplish this, you must
pass in a method to callback, and you do so using a delegate.
Encapsulating a Method
C# Professional Skills Development 13-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Encapsulating a Method
Objective
The objective of this example is to create a class with a delegate that
encapsulates a method that will be invoked at run time. This demonstrates the
definition of a delegate and invokes a method through that delegate.
The Details
Start by creating the Couple class to hold an ordered pair of objects. Couple
stores these objects in a private array and initializes the array in its constru
ctor:
public class Couple
{
object[] theCouple = new object[2];
public Couple(object first, object second)
{
theCouple[0] = first;
theCouple[1] = second;
}
The Couple class contains a display method that shows which object is first
and which is second.
public void Display()
{
Console.WriteLine("theCouple[0]:{0}",
theCouple[0].ToString());
Console.WriteLine("theCouple[1]:{0}",
theCouple[1].ToString());
}
See Delegates1.sln
Delegates
13-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Couple also has a SetOrder method that sets the order of the objects. To do
this, Couple calls a method, determined at run time, which decides whether the
objects should be re-sorted so the second object comes first.
The job of the method passed in to SetOrder is to take the two objects as
arguments, and return a Boolean: true if the objects are currently in the wrong
order and should be reversed, or false if they are already in the right order an
d
should not be reversed.
Couple defines the signature and returns the type of method it needs using a
delegate:
public delegate bool Reverse(object lhs, object rhs);
Reverse is a public delegate for a method that returns bool and takes two
parameters, both of type object.
The SetOrder method takes an instance of such a delegate as a parameter and
then calls the delegated method to see if it should reverse the order of the
elements in its internal array:
public void SetOrder(Reverse theDelegatedFunction)
{
if (theDelegatedFunction(theCouple[0],theCouple[1]))
{
object temp = theCouple[0];
theCouple[0] = theCouple[1];
theCouple[1] = temp;
}
}
If the delegated function returns true, the order of the internal objects is
reversed.
Creating an Employee Class
You now need to create two classes, instances of which may be stored in a
Couple. The first class will be a simple Employee class. An employee will
have two private members, an int representing the employee id and a string
representing the employees name. These will be initialized in the constructor.
Encapsulating a Method
C# Professional Skills Development 13-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public class Employee
{
private int empID;
private string name;
public Employee(string name, int ID)
{
this.name = name;
empID = ID;
}
To be stored in a Couple, the Employee must support a method that can be
encapsulated by the Reverse delegate. This method must return a bool
indicating whether the employees are out of order. You can imagine setting
that order either by the ID or by the name. Employee supports methods for
both.
public static bool SecondIDLower(
object firstEmployee, object secondEmployee)
{
Employee first = (Employee) firstEmployee;
Employee second = (Employee) secondEmployee;
return first.empID > second.empID;
}
public static bool SecondAlphaLower(
object firstEmployee, object secondEmployee)
{
Employee first = (Employee) firstEmployee;
Employee second = (Employee) secondEmployee;
return (string.Compare(first.name, second.name))>0;
}
Notice that neither of these methods is named Reverse. The Reverse delegate
encapsulates these methods, but they can be named whatever the class designer
decides is appropriate.
Finally, the Employee class overrides the ToString method to print the
employees name and employee id:
Delegates
13-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public override string ToString()
{
return name + ": " + empID.ToString();
}
Creating a Game Class
Lets use a second class to store in the Couple to demonstrate that the Couple
is flexible enough to work with different types of objects. The GamePiece
class has two member variables: an int named value and a string for the name
of the piece. It offers only one delegatable method, LeftIsStronger, and it
overrides ToString to print the name and value of the piece.
public class GamePiece
{
private int value;
private string name;
public GamePiece(string name, int value)
{
this.name = name;
this.value = value;
}
public static bool LeftIsStronger(
object left, object right)
{
GamePiece lhs = (GamePiece) left;
GamePiece rhs = (GamePiece) right;
return lhs.value > rhs.value;
}
public override string ToString()
{
return name + ": " + value.ToString();
}
}
Encapsulating a Method
C# Professional Skills Development 13-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Testing the Application
To test the functionality of the Couple object, youll instantiate two Employee
objects and then put them into a Couple. You can then ask that couple to
display the Employees.
Employee joe = new Employee("joe",25);
Employee fred = new Employee("fred",35);
Couple employees = new Couple(joe, fred);
employees.Display();
You are now ready to instantiate the delegate, passing in the method youll use
to sort the Employee objects.
Couple.Reverse employeeByID =
new Couple.Reverse(Employee.SecondIDLower);
This defines employeeByID as an instance of the Reverse delegate defined
within the Couple class. It encapsulates the method SecondIDLower of the
Employee class within the newly created employeeByID delegate.
You can pass that delegate to the SetOrder method of the Couple object:
employees.SetOrder(employeeByID);
You can then display the results of the sort:
Console.WriteLine("after sort by id...");
employees.Display();
Having sorted by id, you are ready to create a second instance of the delegate,
this time encapsulating the method that sorts alphabetically. Rather than
creating an instance of the delegate in a temporary variable, youll instantiate
the delegate within the call to SetOrder:
employees.SetOrder(new
Couple.Reverse(Employee.SecondAlphaLower));
Delegates
13-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The logic here is that the new statement creates a new Reverse delegate,
encapsulating the method SecondAlphaLower and that delegate is passed as a
paramter to SetOrder. You can display the results by calling Display:
Console.WriteLine("After sort by alpha...");
employees.Display();
You are now ready to prove that the delegate can encapsulate not only
different methods from the same class, but also methods from other classes. To
demonstrate this, youll instantiate a pair of Game pieces, and store them in a
Couple object and display them. Youll then create a new instance of the
Reverse delegate and pass that to the SetOrder method of the Couple youve
created, displaying the results:
GamePiece scout = new GamePiece("Scout",2);
GamePiece general = new GamePiece("Marshall",10);
Couple gamePieces = new Couple(general,scout);
gamePieces.Display();
Couple.Reverse theGameDelegate =
new Couple.Reverse(GamePiece.LeftIsStronger);
gamePieces.SetOrder(theGameDelegate);
Console.WriteLine("after sort pieces by values...");
gamePieces.Display();
Running the Program
The results of running the program are shown in Figure 1.
Encapsulating a Method
C# Professional Skills Development 13-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 1. Invoking methods with delegates.
The first two lines show the output after adding joe and fred, and you can see
the result of sorting by id. Since the ids were already in the correct order, th
ere
was no change. Sorting by alpha, however, reversed the order of the objects in
the array.
The next part of the output indicates the creation of the Marshall and the Scout

and the initial display shows that the Marshall is listed first. After sorting b
y
value the game pieces are reversed.
Delegates
13-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Delegates as Static Methods
While the previous example works well, there is a problem with the design as
presented so far. The test class knows a bit too much about the internals of the

Employee and Game class. To create the delegate, the Test class must
instantiate a delegate, passing in the Employee method. Youd like the
responsibility for creating the delegate, and knowing which method to use, to
be encapsulated within the Employee (or Game) class itself. That is, a client of

a Couple should not need to know which Employee or Game method to
invoke.
You can solve this by providing static instances of the delegates within the
Employee or Game class. For example, you can modify the Employee class to
add two new declarations:
public static readonly Couple.Reverse EmployeeByID =
new Couple.Reverse(SecondIDLower);
public static readonly Couple.Reverse EmployeeByAlpha =
new Couple.Reverse(SecondAlphaLower);
This declares EmployeeByID to be a static read-only (not modifiable) instance
of a Reverse Delegate, and instantiates the static instance with the method
SecondIDLower. Similarly, EmployeeByAlpha is a static instance of the
delegate, encapsulating SecondAlphaLower.
The test class no longer needs to create an instance of the delegate. It can jus
t
invoke SetOrder, passing in the static delegate instance:
employees.SetOrder(Employee.EmployeeByID);
After displaying the results, it can call SetOrder again, ordering the couple
alphabetically:
employees.SetOrder(Employee.EmployeeByAlpha);
Similarly, the Game class can create a static instance of the delegate:
public static readonly Couple.Reverse GamePieceByStronger
= new Couple.Reverse(LeftIsStronger);
See Delegates2.sln
Delegates as Static Methods
C# Professional Skills Development 13-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Nothing else in the Game class changes; the static delegate allows the test
program to call SetOrder on the Couple housing two game pieces just by
passing in the correct static delegate:
gamePieces.SetOrder(GamePiece.GamePieceByStronger);
The Static Declaration in Detail
The static declaration is somewhat complicated. Lets take it apart:
public static readonly Couple.Reverse GamePieceByStronger
= new Couple.Reverse(LeftIsStronger);
The first keyword is public. This indicates that the static delegate will
be visible to methods of other classes (in this case, the test class).
The static keyword indicates that the delegate is accessed through the
class, rather than through an instance of the class.
The readonly keyword indicates that once instantiated, the static
delegate GamePieceByStronger cannot be modified.
Couple.Reverse is the type. The object GamePieceByStronger is
defined to be a Reverse delegate as defined within the Couple class.
GamePieceByStronger is the identifer: the name of the delegate.
The keyword new allocates memory for the instance of the delegate.
Couple.Reverse following new is the type of the object being
instantiated.
LeftIsStronger is passed in to the delegate as the method to
encapsulate. Notice that since this occurs within the definition of the
Employee class, there is no need to specify the class for the method
LeftIsStronger.
The output of the program using static delegates is shown in Figure 2. You can
see that it is unchanged from Figure 1. Using static delegates gives you a
cleaner design, but does not change the output in any way.
Delegates
13-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 2. Static delegates.
Delegates as Properties
C# Professional Skills Development 13-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Delegates as Properties
While creating the delegates as static methods works, it has the disadvantage
that the static delegates are instantiated whether or not they are ever used.
Static members are created when the application begins, and there is no test to
see if they are ever used in the running of the program. With just three such
delegates, there is little harm in creating them at the start of the program. On

the other hand, if you had 500 or 50,000 such delegates, and you were going to
invoke only a handful, this would be a tremendous waste of memory.
You really want the ability to use the delegates as if they were members of the
class held in the Couple, but only by having the delegate instantiated on
demand. This is a perfect use for properties.
To implement the delegates as static properties, modify the declaration of the
delegates in both Employee and GamePiece. For example, within Employee,
the two delegate declarations become:
public static Couple.Reverse EmployeeByID
{
get
{
return new Couple.Reverse(SecondIDLower);
}
}
public static Couple.Reverse EmployeeByAlpha
{
get
{
return new Couple.Reverse(SecondAlphaLower);
}
}
Each of these properties is read-only; there is a get, but no set accessor. The
get returns a new instance of the delegate with the appropriate method
encapsulated. Unlike static members, the static property does not create the
delegate until the get accessor is invoked.
By making the property static, the test class does not need an instance of
Employee to invoke the delegate, it can refer to the delegate through the class:

See Delegates3.sln
Delegates
13-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
employees.SetOrder(Employee.EmployeeByID);
This provides the same syntax as the static member, with the appropriate
encapsulation within Employee. This time, however, rather than instantiating
the delegate when the Employee class is created, the delegate is created only
when it is requested.
The GamePiece can also be modified to use properties, rather than a static
member:
public static Couple.Reverse GamePieceByStronger
{
get
{
return new Couple.Reverse(LeftIsStronger);
}
}
The test program is unchanged from the example using static members:
public class Test
{
public void Run()
{
Employee joe = new Employee("joe",25);
Employee fred = new Employee("fred",35);
Couple employees = new Couple(joe, fred);
employees.Display();
employees.SetOrder(Employee.EmployeeByID);
Console.WriteLine("after sort by id...");
employees.Display();
employees.SetOrder(Employee.EmployeeByAlpha);
Console.WriteLine("After sort by alpha...");
employees.Display();
GamePiece general = new GamePiece("Marshall",10);
GamePiece scout = new GamePiece("Scout",2);
Delegates as Properties
C# Professional Skills Development 13-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Couple gamePieces = new Couple(scout,general);
gamePieces.Display();
gamePieces.SetOrder(GamePiece.GamePieceByStronger);
Console.WriteLine("after sort pieces by values...");
gamePieces.Display();
}
public static void Main()
{
Test t = new Test();
t.Run();
}
}
Most important, the output is unchanged, as shown in Figure 3.
Figure 3. Using static properties.
Using Non-Static Properties
You can modify the delegates to use non-static properties. For example, you
can rewrite EmployeeByID to be non-static just by removing the static
keyword.
See Delegates4.sln
Delegates
13-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public Couple.Reverse EmployeeByID
{
get
{
return new Couple.Reverse(SecondIDLower);
}
}
This will continue to work, but youll need to refer to the delegate through an
instance rather than through the class:
employees.SetOrder(joe.EmployeeByID);
While this code will compile and run, it is not a good design. You are calling a

delegate on an instance, but semantically, the instance should not own the
delegate, the class should. The delegate was made a static property to allow the

client to refer to the delegate through the class, rather than through an instan
ce:
employees.SetOrder(Employee.EmployeeByID);
MultiCast Delegates
C# Professional Skills Development 13-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
MultiCast Delegates
One usage of delegates is to support the Publish and Subscribe design
pattern. The idea is that one class in your code may publish an action or event
and other classes may subscribe to that event. For example, a clock might
publish a second change event, saying I will notify anyone who is interested
that a second (or minute or hour) has passed. Other classes may subscribe to
that event so that they can be notified each time the interval has passed.
Multicast delegates can be used to call a series of methods all at the same time
.
You encapsulate all the methods in the one multicast delegate and when you
invoke the methods through the delegate each participating method is called.
Whenever you create a delegate that returns void, C# actually creates an object
of type Multicast delegate for you. All multicast delegates must return void. If

you think about this for a while, it makes a lot of sense. Since you will be
invoking multiple methods, there would be no way for each of the invoked
methods to return a value; the values would be lost.
In the next example, you will create a Delegate class whose entire purpose is to

publish a MultiCast delegate, StringDelegate and to provide a method,
Display that invokes that delegate.
You will treat StringDelegate as a multicast delegate and you will encapsulate
a number of matching methods with that one delegate.
To get started, create a new console application and crate the following class:
public class DelegateClass
{
public delegate void StringDelegate(string s);
public void Display(StringDelegate d, string s)
{
d(s);
}
}
The first line of code in this class creates the StringDelegate that encapsulate
s
a method that returns void and takes a single string parameter. The Display
method takes two parameters: an instance of the delegate and a string to
display. It then invokes the delegated method, passing in its own second
parameter (s) as the argument to the invoked method.
See Delegates5.sln
Delegates
13-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
To see how this delegate can work as a multicast delegate, youll need to
create a class with multiple methods that can be encapsulated by this delegate.
Youll create a simple Employee class, giving that class two member
variables. The first will hold the employees ID, the second will hold the
employees name,
public class Employee
{
private int empID;
private string name;
The constructor for the Employee class will initialize these values:
public Employee(string name, int ID)
{
this.name = name;
empID = ID;
}
Youll override ToString() so that the Employee class can display the
Employees name and ID:
public override string ToString()
{
return name + ", employee ID: " + empID;
}
With the preliminaries taken care of, you are ready to give the Employee class
methods that can be encapsulated by the delegate. The first method is
WriteString. This simple method does nothing but write the string provided to
the console:
public static void WriteString(string s)
{
Console.WriteLine("Writing string {0}", s);
}
MultiCast Delegates
C# Professional Skills Development 13-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The next method that can be encapsulated by the delegate is StoreString. In a
real application this would write the string to the database. To simplify this
example, youll just have it write a message to the console:
public static void StoreString(string s)
{
Console.WriteLine("Storing string {0}", s);
}
Finally, youll add a method named LogString. This method writes the string
to a special log file, but again youll simplify the program by having it write to

the console:
public static void LogString(string s)
{
Console.WriteLine("Logging string {0}", s);
}
That completes the Employee class. You are ready now to test the multicast
delegate, which you will do in the test class. Following the idiom used
throughout this course, the Test class will have a Static Main() method that
will instantiate an instance of Test and call the Run method.
In the Run method, you will create a new Employee object, joe, passing in a
string for his name and an int to represent his employeeID:
public class Test
{
public void Run()
{
Employee joe = new Employee("joe",25);
You will then declare three variables of the type StringDelegate:
DelegateClass.StringDelegate Writer, Logger, Storer;
Notice that Writer, Logger, and Storer are all declared to be of type
DelegateClass.StringDelegatethat is to be of the type of delegate declared
within the DelegateClass.
Delegates
13-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Once declared, each of the delegate objects must be instantiated, passing in the

appropriate method. The method passed in must meet the signature of the
delegate; that is it must return void and take a single string as a parameter.
Writer = new DelegateClass.StringDelegate(
Employee.WriteString);
Logger = new DelegateClass.StringDelegate(
Employee.LogString);
Storer = new DelegateClass.StringDelegate(
Employee.StoreString);
You are now ready to invoke the methods through the delegate. To do so, you
must instantiate an object of type DelegateClass, so that you can call the
instance method Display.
DelegateClass theDelegateClass = new DelegateClass();
With that instance, you can call the Display method, passing in each of the
delegates in turn:
Console.WriteLine("Calling Writer delegate...");
theDelegateClass.Display(Writer,joe.ToString());
Console.WriteLine("Calling Logger delegate...");
theDelegateClass.Display(Logger,joe.ToString());
Console.WriteLine("Calling Storer delegate...");
theDelegateClass.Display(Storer,joe.ToString());
Notice that until now you have used only simple delegates, each call to
Display invokes only a single method. You will now create a new instance of
StringDelegate, named multiCast.
NOTE There is nothing magical in this name, youll call it multiCast
simply to distinguish it; it will work just as well if you name it
Fred.
DelegateClass.StringDelegate multiCast;
MultiCast Delegates
C# Professional Skills Development 13-21
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The new delegate, multiCast is an instance of StringDelegate just like all the
others. What makes it special is how youll use it. Youll begin by assigning to
it the Writer and the Logger delegates. In C# you create multicast delegates by
using the overloaded + operator.
multiCast = Writer+Logger;
You can add additional delegates using the += operator:
multiCast += Storer;
When you invoke the Display method, passing in the new delegate and a
string, each of the delegated methods are invoked. That is, the methods
encapsulated by Writer, Logger, and Storer will each be invoked in turn.
Console.WriteLine(
"\nCalling all three through multicast...");
theDelegateClass.Display(multiCast,joe.ToString());
You can then remove one delegate from the multicast delegate using the -=
operator, and call again, this time invoking only the remaining two methods:
Console.WriteLine(
"\nRemoving Logger and invoking again...");
multiCast -= Logger;
theDelegateClass.Display(
multiCast,joe.ToString());
The results of running this program are shown in Figure 4. You can see that
the first invocation of each of the delegates prints the expected message. When
the multicast delegate is invoked the first time, all three delegated methods ar
e
called. You then removed one delegate and called the multicast delegate again,
causing just two methods to be called.
Delegates
13-22 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 4. MultiCast delegates.
Study the output carefully, you want to be sure that it matches your
expectations based on the code shown earlier.
MultiCast Delegates
C# Professional Skills Development 13-23
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
Delegates are reference types that encapsulate a method with a specific
signature and return type.
You will use delegates when you have a number of similar methods
that can be invoked at run time, but you cant know at compile time
which method to invoke.
Delegates are instrumental in callbacks.
Multicast delegates can invoke multiple methods with a single call.
Multicast delegates support publish/subscribe.
Multicast delegates must return void.
Delegates
13-24 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
(Review questions and answers on the following pages.)
MultiCast Delegates
C# Professional Skills Development 13-25
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. Name two places you might use a delegate?
2. What is the advantage of making a delegate a static member of the class
rather than creating the delegate in the invoking class?
3. What is the advantage of making a delegate a property rather than a static
member of a class?
4. Why make the property static? Why not use an instance property?
5. Can multicast delegates have any signature and return type?
6. What is the advantage of using a multicast delegate?
Delegates
13-26 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. Name two places you might use a delegate?
Delegates are useful in callback methods and whenever you need
to invoke a method at run time, but you cant know at compile
time which method youll invoke. Methods are also useful with
events.
2. What is the advantage of making a delegate a static member of the class
rather than creating the delegate in the invoking class?
The goal is to encapsulate knowledge of which method will be
invoked within the appropriate class. The invoking test class
should not need to know which method within the called class
will handle the delegated functionality.
3. What is the advantage of making a delegate a property rather than a static
member of a class?
Static delegates are instantiated whether or not they are
invoked. By making the delegate a property, you instantiate the
delegate only when and if you invoke it.
4. Why make the property static? Why not use an instance property?
The semantics of the delegate are that they belong to the class
rather than to the instance. The delegated functionality is
understood to be a capability of the entire class, like any method
is, rather than of a specific instance of that class.
5. Can multicast delegates have any signature and return type?
Multicast delegates must return void.
6. What is the advantage of using a multicast delegate?
Multicast delegates allow you to invoke multiple methods with a
single method call. Youll see the power of multicast methods
when events are considered.
MultiCast Delegates
C# Professional Skills Development 13-27
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 13:
Delegates
Lab 13:
Delegates
13-28 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 13 Overview
In this lab youll learn to work with delegates and to implement delegates as
properties. Youll also work with multicast delegates.
To complete this lab, youll need to work through two exercises:
Invoking Methods with Delegates
Creating Multicast Delegates
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-bystep

instructions.
Invoking Methods with Delegates
C# Professional Skills Development 13-29
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Invoking Methods with Delegates
Objective
In this exercise, youll create a class called SetOfTwo that holds any two
objects. This class declares a delegate named Reverse to reverse the order of
the objects in the collection.
You will implement two classes whose objects may be held in a SetOfTwo.
The first is a Cat class. You may sort cats by name or by age. The second is a
Book class. You may sort books by ISBN.
You will delegate to the sortable classes (i.e., Cat and Book) the responsibilit
y
for implementing the delegated methods. You will also delegate to the class
the responsibility for instantiating the delegate, which they will implement as
a
property.
Things to Consider
The class defining the collection defines the signature of the delegate.
The class implementing the delegate defines the semantics of the
delegated method. So, the Cat class defines what it means to sort Cat
objects.
The implementing class can provide the delegate as a property, so that
the client class does not need to know the semantics of sorting (e.g., a
Cat).
Step-by-Step Instructions
1. Open Delegates Started.sln in the Delegates Started folder.
2. Implement the collection class SetOfTwo.
public class SetOfTwo
{
3. Define a delegate, Reverse that takes two objects as parameters and returns
a Boolean value.
Lab 13:
Delegates
13-30 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public delegate bool Reverse(object lhs, object rhs);
4. Implement the underlying data store as an array of two objects.
object[] theSet = new object[2];
5. Implement a constructor.
public SetOfTwo(object first, object second)
{
theSet[0] = first;
theSet[1] = second;
}
6. Define a method, SetOrder, that takes the Reverse delegate as a parameter.
public void SetOrder(Reverse theDelegatedFunction)
{
7. Within SetOrder invoke the delegated function, passing in the first and
second member of the underlying array. If you get back a true value,
reverse the order of the members of the array.
if (theDelegatedFunction(theSet[0],theSet[1]))
{
object temp = theSet[0];
theSet[0] = theSet[1];
theSet[1] = temp;
}
8. Define a Display method that displays the two members of the array by
calling their ToString methods.
Invoking Methods with Delegates
C# Professional Skills Development 13-31
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public void Display()
{
Console.WriteLine(
"theSet[0]:{0}",theSet[0].ToString());
Console.WriteLine(
"theSet[1]:{0}",theSet[1].ToString());
}
9. Define a Cat class to store in a SetOfTwo collection.
public class Cat
{
10. Give the Cat class an age (int) and a name (string).
private int age;
private string name;
11. Implement the constructor.
public Cat(string name, int age)
{
this.name = name;
this.age = age;
}
12. Define a property to return an instance of the delegate that will invoke a
method called SecondYounger (implemented below). Call the property
CatByAge.
public static SetOfTwo.Reverse CatByAge
{
get
{
return new SetOfTwo.Reverse(SecondYounger);
}
}
Lab 13:
Delegates
13-32 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
13. Define a property to return an instance of the delegate that will invoke a
method called SecondNameFirst (implemented below). Call the property
CatByName.
public static SetOfTwo.Reverse CatByName
{
get
{
return new SetOfTwo.Reverse(SecondNameFirst);
}
}
14. Implement the SecondYounger method to match the delegate. Return true
if the second cat is younger than the first.
public static bool SecondYounger(object firstCat,
object secondCat)
{
Cat first = (Cat) firstCat;
Cat second = (Cat) secondCat;
return first.age > second.age;
}
15. Implement SecondNameFirst to match the delegate. Return true if the
second name comes first alphabetically.
public static bool SecondNameFirst(object firstCat,
object secondCat)
{
Cat first = (Cat) firstCat;
Cat second = (Cat) secondCat;
return (string.Compare(first.name, second.name))>0;
}
16. Override ToString to return the cats name and age.
Invoking Methods with Delegates
C# Professional Skills Development 13-33
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public override string ToString()
{
return name + ": " + age.ToString();
}
17. Implement a Book class to store in the SetOfTwo.
public class Book
{
18. Give the Book two members that are both strings: an ISBN and a name.
private string isbn;
private string name;
19. Implement the Book constructor.
public Book(string name, string isbn)
{
this.name = name;
this.isbn = isbn;
}
20. Define a property to return an instance of the delegate that will invoke a
method called RightComesFirst (implemented below). Call the property
BookByISBN.
public static SetOfTwo.Reverse BookByISBN
{
get
{
return new SetOfTwo.Reverse(RightComesFirst);
}
}
Lab 13:
Delegates
13-34 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
21. Implement the RightComesFirst method to match the delegate. Return true
if the second books ISBN comes before the first books ISBN
alphabetically.
public static bool RightComesFirst(object left, object
right)
{
Book lhs = (Book) left;
Book rhs = (Book) right;
return (string.Compare(rhs.isbn, lhs.isbn))>0;
}
22. Instantiate two Cats, frisky and boots with different ages.
Cat frisky = new Cat("frisky",2);
Cat boots = new Cat("boots",3);
23. Create an instance of SetOfTwo named kitties. Pass in your two objects.
SetOfTwo kitties = new SetOfTwo(frisky, boots);
24. Display the two objects.
kitties.Display();
25. Invoke SetOrder on the collection, passing in the delegate to sort the cats
by age.
kitties.SetOrder(Cat.CatByAge);
26. Display the sorted collection.
kitties.Display();
27. Invoke SetOrder on the collection, passing in the delegate to sort the cats
by Name.
Invoking Methods with Delegates
C# Professional Skills Development 13-35
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
kitties.SetOrder(Cat.CatByName);
28. Display the sorted collection.
kitties.Display();
29. Create a SetOfTwo collection with the two books.
SetOfTwo books = new SetOfTwo(progCS,progASPNET);\
30. Display the collection.
books.Display();
31. Call SetOrder, passing in the delegate to sort the books by ISBN.
books.SetOrder(Book.BookByISBN);
32. Display the sorted books.
books.Display();
33. Compile and run the application as shown in Figure 5.
Lab 13:
Delegates
13-36 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 5. Final exercise results.
Creating Multicast Delegates
C# Professional Skills Development 13-37
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Creating Multicast Delegates
Objective
In this exercise, youll create a multicast delegate and add and subtract
delegated methods from the multicast.
Things to Consider
Multicast delegates can call multiple delegated methods in sequence.
You may add to and subtract methods from the multicast delegate.
Any delegate that is defined to return void may be a multicast
delegate.
Step-by-Step Instructions
1. Open Multicast Started.sln in the Multicast Started folder.
2. Create a class named ClassWithDelegate.
public class ClassWithDelegate
{
3. Define a Delegate that takes a string and returns void.
public delegate void StringDelegate(string s);
4. Define a method, Display, that takes a delegate and a string and invokes
the delegate, passing in the string.
public void Display(StringDelegate d, string s)
{
d(s);
}
Lab 13:
Delegates
13-38 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
5. Implement a Cat class.
public class Cat
{
private int age;
private string name;
6. Implement the constructor.
public Cat(string name, int age)
{
this.name = name;
this.age = age;
}
7. Override ToString to show the name and age.
public override string ToString()
{
return name + ", Cat age: " + age;
}
8. Implement a method, StoreString, which can be used with the delegate.
public static void StoreString(string s)
{
Console.WriteLine("Storing string {0}", s);
}
9. Implement a method, LogString, which can be used with the delegate.
public static void LogString(string s)
{
Console.WriteLine("Logging string {0}", s);
}
Creating Multicast Delegates
C# Professional Skills Development 13-39
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
10. Create an instance of Cat frisky.
Cat frisky = new Cat("frisky",5);
11. Create three instances of the delegate: Writer, Logger, and Storer.
ClassWithDelegate.StringDelegate Writer, Logger, Storer;
12. Instantiate the three Delegates with the three implementing methods.
Writer = new ClassWithDelegate.StringDelegate(
Cat.WriteString);
Logger = new ClassWithDelegate.StringDelegate(
Cat.LogString);
Storer = new ClassWithDelegate.StringDelegate(
Cat.StoreString);
13. Create a new instance of ClassWithDelegate called theDelegateClass.
ClassWithDelegate theDelegateClass = new
ClassWithDelegate();
14. Output the progress to the console and then invoke the Display method
with Writer.
Console.WriteLine("Calling Writer delegate...");
theDelegateClass.Display(Writer,frisky.ToString());
15. Output the progress to the console and then invoke the Display method
with Logger.
Console.WriteLine("Calling Logger delegate...");
theDelegateClass.Display(Logger,frisky.ToString());
16. Output the progress to the console and then invoke the Display method
with Storer.
Lab 13:
Delegates
13-40 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Console.WriteLine("Calling Storer delegate...");
theDelegateClass.Display(Storer,frisky.ToString());
17. Create another instance of the Delegate named multiCast.
ClassWithDelegate.StringDelegate multiCast;
18. Initialize multiCast with Writer and Logger.
multiCast = Writer+Logger;
19. Output the progress to the console and then invoke the multicast delegate.
Console.WriteLine("\nCalling multicast...");
theDelegateClass.Display(multiCast,frisky.ToString());
20. Add Storer to multiCast.
multiCast += Storer;
21. Output the progress to the console and then invoke the multicast delegate.
Console.WriteLine("\nCalling multicast...");
theDelegateClass.Display(multiCast,frisky.ToString());
22. Output the progress to the console and then remove the Logger delegate.
Console.WriteLine(
"\nRemoving Logger and invoking again...");
multiCast -= Logger;
theDelegateClass.Display(
multiCast,frisky.ToString());
23. Compile and run the application as shown in Figure 6.
Creating Multicast Delegates
C# Professional Skills Development 13-41
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 6. Final exercise results.
Lab 13:
Delegates
13-42 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Events
C# Professional Skills Development 14-1
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Events
Objectives
Understand what events are.
See how events are declared.
Discover how events are raised.
See how events are handled.
Events
14-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Defining Events
An event is your classs way of signaling that something has happened.
Typically, an event represents a change in state for your class. Events may be
raised when buttons are pressed, text box contents change, items in lists are
selected and so forth.
The designer of each class decides what events, if any, that class will raise, a
nd
under what conditions. The purpose of an event is to notify other interested
classes that your class has changed in some specific way. For example, the
page may want to know when the Submit button is clicked, a list box may
want to know when a text box has been modified, or a display class may want
to know when a network read completes.
In C# you define an event with the keyword event. The event keyword allows
you to specify a delegate that will be used to invoke one or more methods
when the event is fired.
Most events are UI actions (button clicks, list changes), but there are other
system events that you may want to respond to as well. For example, a clock
object might raise an event each time the second changes. A stream object
might raise an event when the data has been received from the disk or across
the network.
Events work together with delegates to support the publish and subscribe
design pattern. For example, a button might publish the Click event as
shown in Figure 1.
Figure 1. A button publishes the Click event.
Other windows might subscribe to that event as shown in Figure 2.
Defining Events
C# Professional Skills Development 14-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 2. Windows subscribe to the event.
When the button is clicked, all the subscribers are notified as shown in
Figure 3.
Figure 3. Button notifies the subscribers.
Events
14-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Advantages of Event-Driven
Programming
In the early days of computing the user interacted with the computer in a very
proscribed way. The user worked his way through each screen, filling in data
and working through a series of menus and forms. Todays Graphical User
Interface (GUI) based programs are far more interactive. The user may, at any
time, press a button, interact with a list or other complicated control, or choo
se
from a variety of menus. This interactive programming is known as eventdriven.
The user performs actions that raise events that the program responds
to.
The support for events and delegates in C# allows the programmer to manage
the complexities that arise out of event-driven programming in a highly objector
iented
and component-oriented way. Each individual object publishes the
events it raises, and any other class may choose to subscribe to those events or

to ignore them.
Implementing Events
C# Professional Skills Development 14-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Implementing Events
In C#, events are implemented using multicast delegates. You will remember
that multicast delegates must return void. By convention, events in .NET are
encapsulated by multicast delegates that take two parameters. The first, of type

object represents the object raising the event. The second, of type EventArgs
(or a class deriving from EventArgs) contains data that may be useful to the
class handling the event.
Programming Events: An Example
To understand how to program an event, you will create an office class that
fires an event each time its list of Employees changes. An office will contain
information about a business office, including a list of their Employees. The
office will also, of course, contain other information, such as the address,
phone number, and so forth.
Objective
The office will publish an event each time an employee is hired or fired; that
is, each time the collection of employees changes. Your office class will
publish an event OfficeChanged, as shown in Figure 4.
Figure 4. Publishing OfficeChanged.
Other classes may subscribe to the event. In this program, youll create a class
Tester that subscribes to the OfficeChanged event as shown in Figure 5.
Events
14-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 5. Tester subscribes to the OfficeChanged event.
When the event fires, Tester is, as shown in Figure 6. On notification, the
Tester prints the number of employees currently in the office.
Figure 6. Tester is notified of the event.
Implementing Events
C# Professional Skills Development 14-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The Code in Detail
The office class holds a collection of all the Employees. Youll begin by
creating a simple class to represent the Employee:
// Employee class to hold in office collection
public class Employee
{
string name;
string empID; // employees id
// constructor
public Employee(string name, string ID)
{
this.name = name;
empID = ID;
}
// properties (get only)
public string EmpID
{
get
{
return empID;
}
}
public string Name
{
get
{
return name;
}
}
}
This simple class has two private member variables, name and empID, both of
which can be accessed through Properties (Name and EmpID, respectively).
This class exists only to allow objects to be stored in the Office.
See Events.sln
Events
14-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The Office class itself could have private members representing the address,
city, phone, and so forth. Youll simplify the class into two members. The first
will be an array of Employee objects. Again, in a commercial application you
might store this information in a database, but to keep things simple here
youll use an Array:
public class Office
{
// backing store for list of employees
private Employee[] employees;
The only other private member is count, an integer member variable that you
will initialize to zero. This counter will be incremented when new members are
added to the array.
// highest index in employees array
private int count = 0;
Declaring the Delegate
The Office class declares a multicast delegate, OfficeChangedHandler. Like
all multicast delegates it must return void. The convention is for delegates tha
t
will be used by events to take two parameters. The first is of type object, and
the second is of type EventArgs or a class that derives from OfficeEventArgs.
// delegate to create event with
public delegate void OfficeChangedHandler(
object sender, OfficeEventArgs e);
Declaring the EventArgs Class
The class OfficeEventArgs is designed by you, specifically to handle the
OfficeChanged event. This little helper class is defined in the same file as the

Office class:
Implementing Events
C# Professional Skills Development 14-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
// Event arguments for office events
public class OfficeEventArgs : EventArgs
{
public int count;
public OfficeEventArgs(int count)
{
this.count = count;
}
}
The constructor to the OfficeEventArgs class takes a single int parameter,
count, and uses that parameter to initialize its only member variable, count.
When you construct an instance of OfficeEventArgs you pass in the current
count of the number of employees in the office, and this value is made
available to any interested classes (e.g., any classes that subscribe to the
event).
Declaring the Event
Returning to the Office class, the next line of code after the declaration of th
e
delegate is the declaration of the event itself:
// event officeChanged raised when an employee
// is added or removed
public event OfficeChangedHandler OfficeChanged;
The event name is OfficeChanged. The delegate for the OfficeChanged event
is the OfficeChangedHandler delegate previously defined and described above.
When the OfficeChanged event is fired, the methods encapsulated by the
OfficeChangedHandler multicast delegate are invoked.
Firing the Event
When the class detects that the event should be raised (e.g., when an Employee
is added or modified), the method adding the Employee calls the private helper
method NotifyOfficeChanged, passing in an instance of OfficeEventArgs.
For example, the indexer has both a get and a set part. The set part is changing

the office, and so must raise the event. It does so by instantiating an object o
f
type OfficeEventArgs, passing in the new count to the constructor. It then uses
that object as a parameter to the NotifyOfficeChanged method, which is
responsible for raising the event.
Events
14-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public Employee this[int index]
{
get
{
if (index < 0 || index >= employees.Length)
{
// handle bad index
}
return employees[index];
}
set // modify the office
{
if (index >= employees.Length)
{
// handle error
}
else
{
employees[index] = value;
if (count < index)
count = index;
// the set method has been called
// the office is changing
// create an officeEventArgs object
// and call the method to raise
// the event
OfficeEventArgs e = new OfficeEventArgs(count);
NotifyOfficeChanged(e);
}
}
}
The method NotifyOfficeChanged checks to see if any classes have registered
delegates of type OfficeChangeHandler with the event. It does this by testing
whether the event is null. The event will return null if there are no registered

delegates, and it will return non-null if anyone wants to be notified.
Implementing Events
C# Professional Skills Development 14-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
If the event is not null, the methods are invoked by raising the event and
passing in the two parameters: the sender (in this case the office itself) and t
he
OfficeEventArgs object it received as a parameter. It sends a reference to the
office itself by using the this keyword:
// raise the event
protected virtual void NotifyOfficeChanged(
OfficeEventArgs e)
{
if (OfficeChanged != null)
OfficeChanged(this,e);
}
Handling the Event
To see the event at work, you need a class that wishes to be notified when the
office has changed. Youll create a very simple class, OfficeWatcher that will
register an event handler in its constructor:
// class to watch for office
// change events
public class OfficeWatcher
{
// constructor: register an event handler
public OfficeWatcher(Office office)
{
// add a delegate to the office's event
// pass in a reference to my method to
// call when the event fires
office.OfficeChanged +=
new Office.OfficeChangedHandler(OnOfficeChanged);
}
The event is registered using the += operator, which you will remember is used
by multicast delegates to add a new delegate to its notification list. The
argument to the delegate constructor created with the keyword new is
OnOfficeChanged, which is a method of OfficeWatcher:
Events
14-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public void OnOfficeChanged(
object sender, OfficeEventArgs e)
{
Console.WriteLine(
"Office changed. Count now {0}",e.count);
}
This simple test method just updates the UI with the current count of the
employees in the office, but you can imagine that a real application might take
other action based on the event.
Testing the Event
To test the event handler, youll create a Tester class, and in its instance
method, Run, youll create an array of employees, and use these to instantiate
an office object.
public class Tester
{
static void Main()
{
// Make an instance and call Run
Tester t = new Tester();
t.Run();
}
// instance method for testing
public void Run()
{
// create a temporary
// array of employees
Employee[] empArray =
new Employee[5];
Implementing Events
C# Professional Skills Development 14-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
// fill the array with
// Employee objects
empArray[0] =
new Employee("John Galt","001");
empArray[1] =
new Employee("Figaro Barbara", "002");
empArray[2] =
new Employee("Wotan Warrior", "003");
empArray[3] =
new Employee("George Washington", "004");
empArray[4] =
new Employee("John Adams", "005");
// create the office object
// passing in the array of Employees
Office office =
new Office(empArray);
You are ready to create an OfficeWatcher to watch that office. Pass in a
reference to the office you just created as a parameter to the constructor for
OfficeWatcher:
// create the officeWatcher,
// pass in a reference to the office
// you just created
OfficeWatcher watcher =
new OfficeWatcher(office);
The OfficeWatchers constructor will use that reference to register an event
handler with the office it is watching. To test the notification, add two
employees. The first is added by calling the Add method:
Console.WriteLine("Adding Sam Adams...");
office.Add(new Employee("Sam Adams", "008"));
The Add method of Office delegates responsibility for adding the new
Employee to the indexer. The indexer raises the event.
Events
14-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
// add a single Employee to the office
public void Add(Employee employee)
{
// no notification, will be done by this[].
this[count] = employee;
}
This addition of Sam Adams should raise the event. The test program then
goes on to call the indexer directly:
Console.WriteLine("Adding Thomas Jefferson...");
office[6] = new Employee("Thomas Jefferson", "007");
Finally, the test program displays the current contents of the array:
Console.WriteLine("\nDisplaying office employees...");
office.DisplayEmployees();
The results of running this are displayed in the console window, as shown in
Figure 7. You can see that after each Employee is added, the
OnOfficeChanged method is called.
Figure 7. Testing the Event Handler.
Implementing Events
C# Professional Skills Development 14-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Stepping Through the Program
To see how the event is raised, run the program in the debugger. Put a
breakpoint at the point in the program where the OfficeWatcher class is
created, as shown in Figure 8.
Figure 8. A breakpoint at the instantiation of OfficeWatcher.
You can create the breakpoint by clicking on the line and pressing CTRL+B.
This brings up the New Breakpoint dialog box as shown in Figure 9. This
window allows you to set the conditions on which this breakpoint will fire, or
the number of times the line must be reached before the breakpoint fires (Hit
Count).
A simpler way to set the breakpoint is to put the cursor in the margin to the fa
r
left, and click. This adds an unconditional breakpoint. You can remove the
breakpoint by clicking on the red dot.
Events
14-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 9. The New Breakpoint dialog box.
Run the program to the Breakpoint. You can do that by pressing F5 or by
choosing Debug->Start from the menu. The program will run to the
breakpoint, and then stop. The current line is indicated by a yellow arrow,
which will be superimposed over the red dot, as shown in Figure 10.
Figure 10. Hitting the breakpoint.
You can step-in to the constructor, by pressing F11. In the constructor you can
see the registration of the event, as you expect. Continuing to step will bring
you back to the test program where you print the string Adding Sam
Adams to the console, and then you step into the Add method of office.
Stepping in brings you into the constructor for the Employee you are adding
(resulting from calling new Employee). You then step into the Add method of
the Office class, which calls the indexer.
Implementing Events
C# Professional Skills Development 14-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
As you continue to step through, youll see the construction of the
OfficeEventArgs object, passing in the current count as a parameter, as shown
in Figure 11.
Figure 11. Creating the OfficeEventArgs object.
This invokes the constructor where the local member count is set to the value
in Office.Count (5), as shown in Figure 12.
Figure 12. The OfficeEventArgs constructor.
Continue to step through the office indexer. On the final line you call the
helper method NotifyOfficeChanged, and you pass in the OfficeEventArgs
object just created.
If you look at the Locals window you will find e (the OfficeEventArgs object),
which you can open (click on the + sign). Youll see that it consists of two
parts: the parent object (of type System.EventArgs) and the count member,
whose value is 5. This is circled and highlighted in Figure 13 and expanded in
Figure 14.
Events
14-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 13. Examining the OfficeEventArgs object.
Figure 14. Examining the OfficeEventArgs object (close up).
Step into the NotifyOfficeChanged method. Here you test whether the event
is not null. Since the OfficeWatcher has registered a delegate, this test will
succeed, and the method will be invoked. To see this, set a breakpoint on the
OfficeWatcher.OnOfficeChanged method. Stepping through the notification
will bring you to your new breakpoint; the method has been called as a result
of the event firing. This is where the count is displayed to the console, using
the value placed into the OfficeEventArgs object by the office.
Implementing Events
C# Professional Skills Development 14-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
Events allow your objects to notify interested other classes about
changes in their state.
Events are implemented using multicast delegates.
An event evaluates to null if no delegates are registered.
When the event fires, all the registered delegates are called.
By convention delegates for events take two parameters: an object of
type object, and an object of type EventArgs (or a class derived from
EventArgs).
The classes that register with the event provide an event handler to
respond to the event.
Events
14-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
(Review questions and answers on the following pages.)
Implementing Events
C# Professional Skills Development 14-21
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. What are events used for?
2. What is the relationship between events and delegates?
3. What is the customary signature of event delegates?
4. Can more than one method be invoked for a given event?
5. Can a class have more than one event?
6. Why would you derive from EventArgs?
Events
14-22 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. What are events used for?
Events signal a change in the state of a class. They are typically
used to inform other classes that something has happened,
e.g., a button click or a selection in a list box.
2. What is the relationship between events and delegates?
Events are implemented using multicast delegates. The delegate
manages the notification of the interested methods when the
event fires.
3. What is the customary signature of event delegates?
In .NET, events return void (as they must since they use
multicast delegates) and by convention they take two
arguments: an object and an EventArgs object.
4. Can more than one method be invoked for a given event?
Yes, it is common for methods of many different classes to
register with a given event.
5. Can a class have more than one event?
Yes, many objects have more than one event. For example, a
User Interface control might have events for adding data,
removing data, resizing the control, drawing the control, and so
forth.
6. Why would you derive from EventArgs?
You provide a class derived from EventArgs when you need to
offer specialized data or methods that are not supplied by the
base class. In the example shown in this chapter, you derive from
EventArgs to provide the current count of the offices array of
Employee objects.
Implementing Events
C# Professional Skills Development 14-23
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 14:
Responding to
Events
Lab 14:
Responding to Events
14-24 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 14 Overview
In this lab youll learn to register and respond to events.
To complete this lab, youll need to work through two exercises:
Responding to Events
Extending Custom Event Handlers
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-bystep

instructions.
Responding to Events
C# Professional Skills Development 14-25
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Responding to Events
Objective
In this exercise, youll create and register events and respond to events.
Things to Consider
Classes publish events so that interested observers can respond.
The class responding to the event uses a delegate to determine which
method is invoked when the event is raised.
You can specialize the information provided when the event is raised
by deriving from System.EventArgs.
Step-by-Step Instructions
1. Open Events Starter.sln in the Events Starter folder.
2. Create a Cat class. Give it two member fields: name (a string) and age (an
int).
public class Cat
{
string name;
int age;
3. Implement the Cat constructor.
public Cat(string name, int age)
{
this.name = name;
this.age = age;
}
4. Implement properties for Age and Name.
Lab 14:
Responding to Events
14-26 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public int Age
{
get
{
return age;
}
}
public string Name
{
get
{
return name;
}
}
5. Create class Kennel to hold a collection of Cat objects.
public class Kennel
{
6. Store the Cat objects in an Array called kitties.
private Cat[] kitties;
7. Declare a variable to hold the count of objects.
private int count = 0;
8. Declare a delegate for an event handler method (returns void, takes two
arguments: an object and a KennelEventsArgs object).
public delegate void KennelChangedHandler(
object sender, KennelEventArgs e);
9. Declare an event based on the delegate.
Responding to Events
C# Professional Skills Development 14-27
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public event KennelChangedHandler kennelChanged;
10. Define a method that raises the event if a handler is registered.
protected virtual void NotifyKennelChanged(
KennelEventArgs e)
{
if (kennelChanged != null)
kennelChanged(this,e);
}
11. Define a Kennel constructor that takes a variable number of Cat objects.
public Kennel(params Cat[] catList)
{
12. Allocate space for the Cats in the array.
this.kitties = new Cat[256];
13. Copy the Cats passed in to the constructor into the array.
foreach (Cat c in catList)
{
this.kitties[count++] = c;
}
14. Define a method to add a single Cat to the Kennel. No notification, will be
done by this[].
public void Add(Cat kitty)
{
this[count] = kitty;
}
15. Define an int based indexer if an item is added to the array to raise the
event.
Lab 14:
Responding to Events
14-28 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public Cat this[int index]
{
get
{
if (index < 0 || index >= kitties.Length)
{
// handle bad index
}
return kitties[index];
}
set
{
if (index >= kitties.Length)
{
// handle error
}
else
{
kitties[index] = value;
if (count < index)
count = index;
KennelEventArgs e = new KennelEventArgs(count);
NotifyKennelChanged(e);
}
}
}
16. Define a method that iterates through the collection, displaying each Cat in

turn.
Responding to Events
C# Professional Skills Development 14-29
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public void DisplayKitties()
{
foreach (Cat c in kitties)
{
if (c != null)
{
Console.WriteLine("{0} is {1} years old",
c.Name, c.Age);
}
}
}
17. Create the KennelEventArgs class (what class must it derive from?). The
class has a single member variable: count (an int).
public class KennelEventArgs : EventArgs
{
public int count;
18. Implement the KennelEventArgs constructor.
public KennelEventArgs(int count)
{
this.count = count;
}
19. Create the KennelWatcher class to watch for the Kennel change event.
public class KennelWatcher
{
20. Implement the KennelWatcher constructor. It takes one parameter: an
object of type Kennel.
public KennelWatcher(Kennel kennel)
{
Lab 14:
Responding to Events
14-30 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
21. Add a delegate to the Kennels event. Pass in a reference to the
KennelWatcher method to call when the event fires.
kennel.kennelChanged +=
new Kennel.KennelChangedHandler(OnKennelChanged);
22. Implement the method to wrap in the delegate.
public void OnKennelChanged(
object sender, KennelEventArgs e)
{
Console.WriteLine(
"Kennel changed. Count now {0}",e.count);
}
23. Create a temporary array of Cat objects called kittyArray.
Cat[] kittyArray =
new Cat[5];
24. Fill the array with Cat objects.
kittyArray[0] =
new Cat("Frisky",3);
kittyArray[1] =
new Cat("Figaro", 2);
kittyArray[2] =
new Cat("Wotan", 4);
kittyArray[3] =
new Cat("Washington", 6);
kittyArray[4] =
new Cat("Boots", 5);
25. Create the Kennel object passing in the array of Cats.
Responding to Events
C# Professional Skills Development 14-31
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Kennel kitties =
new Kennel(kittyArray);
26. Create the KennelWatcher object, passing in a reference to the Kennel you
just created.
KennelWatcher watcher =
new KennelWatcher(kitties);
27. Add Cats to the Kennel object, writing the progress to the console.
Console.WriteLine("Adding Boots...");
kitties.Add(new Cat("Boots", 8));
Console.WriteLine("Adding Kant...");
kitties[6] = new Cat("Kant", 7);
Console.WriteLine("\nDisplaying kitties...");
kitties.DisplayKitties();
28. Compile and run the program as shown in Figure 15.
Figure 15. Final exercise results.
Lab 14:
Responding to Events
14-32 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Extending Custom Event Handlers
Objective
In this exercise, youll create a second observer of the KennelChanged event.
Things to Consider
More than one class may be interested in the event.
Classes that respond to an event should not know about one another.
The class raising the event does not know which classes are interested.
Step-by-Step Instructions
1. Open Events Extended Starter.sln in the Events Extended Starter folder.
2. Add a second class, KennelAuditer, to subscribe to the event.
public class KennelAuditer
{
public KennelAuditer(Kennel kennel)
{
3. Create a constructor for KennelAuditor that takes a Kennel object.
public KennelAuditer(Kennel kennel)
{
4. Register to receive the event.
kennel.kennelChanged +=
new Kennel.KennelChangedHandler(OnAuditableEvent);
5. Implement the method to wrap in the delegate.
Extending Custom Event Handlers
C# Professional Skills Development 14-33
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public void OnAuditableEvent(
object sender, KennelEventArgs e)
{
Console.WriteLine(
"Audit note: Kennel changed. Count now
{0}",e.count);
}
6. Add an instance of KennelAuditer.
KennelAuditer auditer =
new KennelAuditer(kitties);
7. Compile and run the program as shown in Figure 16.
Figure 16. Final exercise results.
Lab 14:
Responding to Events
14-34 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Windows Forms
C# Professional Skills Development 15-1
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Windows Forms
Objectives
Create Windows applications using the command line compiler.
Create Windows applications using Visual Studio .NET.
Explore Windows controls and Windows Forms.
Set properties on forms and controls.
Create event handlers for Windows Forms.
Use XML comments to generate documentation.
Windows Forms
15-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Creating Windows Applications
The entire reason to learn C# is to build applications. Otherwise, this is all a
n
academic exercise in computer science. While C# may one day be a cross
platform language, for the immediate future its principal (if not only) reason t
o
exist is to support development on the .NET platform.
.NET applications come in three flavors: Windows Applications, Web
Applications and Web Services. This chapter will focus on Windows
Applications.
There was a time, not very long ago, when the distinction between a desktop
application and a Web application was clear and unmistakable. Today, and
increasingly, these distinctions are blurring. Many desktop applications
integrate with the Web. For example, Microsofts Streets and Tips program
uses the Web to update its list of construction delays, and Nortons Antivirus
program is fully integrated with the Web to keep itself up to date with the
latest virus cures.
Web applications, on the other hand, are doing more and more work on the
client, further reducing the distinction between Web and Windows
applications. In the final analysis, the real distinction is this: who is respon
sible
for the User Interface? If the UI is a browser showing HTML sent from a
server, its called a Web application; otherwise its called a desktop
application.
In .NET, Windows desktop applications are built using Windows Forms. The
goal of Windows Forms is to bring the Rapid Application Development
environment made famous in Visual Basic 6 to C# and other .NET application
languages.
The model is quite simple: you create a form object that derives from
System.Windows.Forms.Form. You then create controls, such as
System.Windows.Forms.Label or System.Windows.Forms.Button. You set the
text, location, and size of your controls and then set up event handlers. The
event handlers are compiled in what is known as a code behind page. Finally,
you add the controls youve created to your forms controls collection and you
run the application, passing in the form to the applications Run method.
Writing Windows Apps by Hand
As a rule, you will write your Windows Applications in Visual Studio .NET.
This integrated Development Environment (IDE) offers tremendous support
for building windows applications quickly and painlessly. The forms builder
alone will save you many hours of hand-coding your application.
Creating Windows Applications
C# Professional Skills Development 15-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
That said, there is nothing magical about the IDE and you are certainly free to
write your applications by hand, using nothing more than a simple text editor
and the command line compiler.
Try It Out!
To prove this to yourself, write your first Windows application in Notepad.
1. Open Notepad and create a new file. Save it as HelloWorld.cs.
2. Add two using statements to the top of the file to inform the compiler
which namespaces youll be using in this application.
using System;
using System.Windows.Forms;
These designations simply save you time. Rather than having to declare your
Label object as
System.Windows.Forms.Label output
you can add the using System.Windows.Forms designation to the top of the file
and then just write:
Label output
3. Create your form class (myForm), and derive from the System Form class:
public class myForm: Form
{
4. Add two controls as private member fields. One is a Label, while the other
is a Button.
See
HelloWorldText.cs
Windows Forms
15-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
// a label to display text
private Label output;
// a cancel button
private Button cancel;
5. Create the class constructor. In the constructor youll start by initializing
your controls.
public myForm()
{
// create the objects
output = new Label ();
cancel = new Button ();
6. The title on the form is held as a member field Text; you can set that to
something appropriate.
// set the form's title
Text = "Hello World From Windows";
7. You are now ready to set the location, size, and text for the output label.
// set up the output label
output.Location = new System.Drawing.Point (20, 30);
output.Text = "Hello World From Windows!";
output.Size = new System.Drawing.Size (200, 25);
Because you are doing this by hand, you must set the Location by hand. You
do that by creating a Point object, passing in the coordinates (20,30) for the
object, and passing that Point object to the Location property of the control.
Set the size of the control by creating a System.Drawing.Size object, passing in

the size to the constructor. This size struct is then assigned to the Size prope
rty
of the control.
8. Set the Location, Size, and Text of the button as well.
Creating Windows Applications
C# Professional Skills Development 15-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
// set up the cancel button
cancel.Location = new System.Drawing.Point (150,200);
cancel.Text = "&Cancel";
cancel.Size = new System.Drawing.Size (110, 30);
9. You must assign an event handler to the Click event of the button. This
uses events and delegates as explained elsewhere in this course.
// set up the event handler
cancel.Click +=
new System.EventHandler (this.OnCancelClick);
Youve told the compiler that Click events for the button will be handled by
the OnCancelClick method, which you will write in just a moment.
10. Add the controls to the forms Controls collection.
// Add the controls
Controls.Add (cancel);
Controls.Add (output);
11. That completes the constructor. You now need only to write the
OnCancelClick method that youve wired as the event handler for the
button. When the user clicks the Cancel button you want to exit the
application. You do this with the static Exit method of the Application
class.
// handle the cancel event
protected void OnCancelClick(
object sender, System.EventArgs e)
{
Application.Exit();
}
By convention, all Windows events take two parameters. The first, of type
object, represents the control sending the event (in this case the button). The
second is an object of type EventArgs that provides additional information
about the event. In the case shown here, neither parameter is used in the event
handler.
Windows Forms
15-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
12. All that is left is to start the application. Like all C# applications, this
one
begins with a static method Main. In this static method youll call the static
Run method of the Application, passing in a new instance of the form
youve just created.
// Run the app
public static void Main()
{
Application.Run(new myForm());
}
Build the Application:
13. Save the file as HelloWorld.cs and close the file in Notepad.
14. To build the application navigate to the Visual Studio .NET program group
and choose Visual Studio Tools. Within the Tools group choose Visual
Studio .NET Command Prompt. This opens a Command window with
the environment set properly for building .NET applications.
15. Navigate to the directory in which youve saved your file. Take a
directory; you should see just HelloWorld.cs.
16. Enter csc HelloWorld.cs. The compiler prints a message and returns a
command prompt.
17. Type dir to get a directory listing; you should now see HelloWorld.exe.
18. Type HelloWorld and the application opens, as shown in Figure 1. The
application is quite simple; the label displays your text and the button is
drawn waiting for you to click it.
19. Click the Cancel button and the application exits.
Creating Windows Applications
C# Professional Skills Development 15-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 1. Running HelloWorld.
Writing Windows Apps with
Visual Studio .NET
While that wasnt very difficult, large applications are quite tedious to craft by

hand. Visual Studio .NET makes the task much easier, as youll see in the next
exercise.
Try It Out!
To see how much easier it is to work with Visual Studio, youll write the same
application again, this time using the IDE to drag the controls onto a form.
1. Open Visual Studio .NET and click on New Project.
2. Under Project Types, choose Visual C# Projects and under Templates
choose Windows Application. Name the project HelloWorldVS, as
shown in Figure 2. Click OK to create the project.
See
HelloWorldVS.sln
Windows Forms
15-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 2. Creating a new project.
3. A new project is created. Depending on how your system is configured,
you should see the Toolbox open on the left, a form designer in the center,
and the Solution Explorer and Properties window on the right, with a
number of windows available at the bottom, as shown in Figure 3. If any
of these windows are not open, you can open them from the View menu or
the Debug window. If the Toolbox does not appear, but a small rectangle
with the word Toolbox is visible in the upper left, click on the rectangle,
the Toolbox should open. You can then pin it in place, using the Pushpin
icon.
Creating Windows Applications
C# Professional Skills Development 15-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 3. Visual Studio .NET windows.
4. Drag a Label to the form. Place it where youd like it.
5. Use the Properties window to set the text property of the label to Hello
World From Windows!
6. Drag the label to size it big enough to hold the text.
7. Drag a button onto the form. Set its text property to Cancel.
8. Drag the button to where you want it on the form, then click on the form
and resize it to the size you want.
9. Double-click on the button to open its Click event handler. Visual Studio
brings you to the code-behind page in the button1_Click event handler. It
has already wired the handler for you; no need to do that by hand.
10. Add the code for the event handler, just as you did previously.
Windows Forms
15-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
private void button1_Click(
object sender, System.EventArgs e)
{
Application.Exit();
}
11. Press CTRL+F5 to run the application. The application starts up and the
window opens, as shown in Figure 4. You can press Cancel to close the
application and return to the development environment.
Figure 4. Running the application from Visual Studio .NET
Enhancing Your Application
You can see that even with the worlds simplest Windows application, Visual
Studio .NET has made your life a lot easier. You are ready now to enhance this
application a bit. There is a tremendous amount you can do; this chapter will
only highlight a few of the tools available.
Try It Out!
To get started, just modify the existing application to see how you can add
features with very little effort.
1. Reopen the project from the previous example.
2. Click on the form itself, and go to the Properties window. Set the name of
the form to HelloWorld and set the Text to Hello World From
See HelloWorld
WindowsVSNET.
sln
Creating Windows Applications
C# Professional Skills Development 15-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Windows!. When you leave the Text property, you should see the title of
the Form change.
3. Click on the BackColor attribute in the Properties window. A dropdown
opens. Click on Custom to open a color palette. Choose a pleasing color
and youll see the background color for the form change to your choice.
4. Click on the label and change the text to HelloWorld. Change the name to
lblHello. Click on the font attribute and set the font to Comic Sans MS.
Set the size to 14.
5. Change the text on the Cancel button to Bye and add a second button with
the text ChangeText. Name the new button btnChange. Move both
buttons to the upper left of the form.
6. Click on the first button, then shift click on the second to select them both
.
Use Format|Make Same Size to make the buttons the same size. Use
Format|Align to align the two buttons on their left sides, then use
Format|Vertical spacing to space the buttons.
7. Click on the form to show its sizing handles, and resize the form, so that it

looks like Figure 5. Feel free to adjust the form to fit your own aesthetic
sensibilities.
Figure 5. Resizing and formatting the form.
8. You are ready to wire up the event handlers. Double-click the Bye button
and verify that the code has not changed in the event handler.
9. Return to the designer and double-click on btnChange. You are now in
the event handler for the btnChange click event. Add the following text:
lblHello.Text = "Goodbye world!";
When the user clicks on the btnChange button the text in the label will
change.
10. Navigate in the page behind to find the Main method. Change the
argument to run to new HelloWorld.
Windows Forms
15-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
static void Main()
{
Application.Run(new HelloWorld());
}
You renamed the form, and you must use the new name of the form when you
create it. Scroll up to see that the name of the class was changed as well, and
that it is an instance of this newly-named class that you are creating.
public class HelloWorld : System.Windows.Forms.Form
11. Build the application and run it. When it opens, click on btnChange to see
the effect, as shown in Figure 6. When you click on the btnChange
button, the text in the label is altered. Click Bye to exit the program.
Figure 6. After clicking Change Text.
Controls
The key to Windows Forms applications is the plethora of controls available in
the Toolbox. Writing a good Windows application is more than just dragging
controls onto a form, but that is a good starting point. Put the right controls
on
the form and wire up useful event handlers, and you are well on your way to
building powerful Windows applications.
This is a course on C# and not on Windows application development, so this
chapter just scratches the surface on what is available. Nonetheless, looking at

how to build Windows applications provides powerful insight into the utility
of the techniques taught in the chapters on language fundamentals.
Creating Windows Applications
C# Professional Skills Development 15-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
To get started, youll build a simple form-based application with various
controls.
The final product is shown in Figure 7. This is a form that might be used by a
salesperson to take an order for a new computer. Choose various options, and
when you click Order, the order is summarized in the space below the Order
button.
Figure 7. A complex order form.
See WinForm
Controls.sln
Windows Forms
15-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
1. Create a new Windows Forms application and name it
WinFormControls.
2. Drag a single check box control onto the form and place it where you want
it. Name the box chkService and set the text to Full service.
3. Run the application. Your check box is drawn and you can check and
uncheck it (though nothing interesting will happen when you do). The
result is shown in Figure 8. You dont get much functionality, but its not
bad for 30 seconds work.
Figure 8. Testing the check box.
4. Add a group box to the form and make it bigger then youll need.
5. Add three radio buttons within the group box (just drag them onto the
group box). Align them more or less one below the other.
6. Name the three radio buttons btnLaptop, btnDesktop, and
btnWorkStation.
7. Set their text to Laptop, Desktop, and Work Station respectively.
8. Shift click through the three buttons to select them all.
9. Choose Format|Align|Lefts to align their left-hand edges.
10. Choose Format|VerticalSpacing|MakeEqual to set equal spacing
between all three controls.
Creating Windows Applications
C# Professional Skills Development 15-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
11. Choose Format|VerticalSpacing|Increase (or decrease) if you need to
change the amount of space between the controls. Keep the vertical
spacing fairly tight.
12. Move the three buttons to the upper left of the group box and resize the
group box to fit.
13. Click on the group box and change its text to Computer Type as shown in
Figure 9.
Figure 9. Setting the group box.
14. Click on the rbDesktop control and set its checked property to true. When
you run the application, this button will default to checked. Run the
application to test it, as shown in Figure 10. You are free to click on other
buttons, but this way the default choice is Desktop.
Windows Forms
15-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 10. Testing the radio button checked property.
15. Drag a button onto the form, and name it btnOrder. Set its text to Order.
16. Lengthen the form to make room, and drag a label under the order button.
Name the label lblOutput. Widen the label and lengthen it to hold a good
bit of text. Delete the text in its text property (so that it is blank).
17. Double-click on btnOrder to set the event handler. You want to assemble
the output string. Start by creating a string named output. Add code to test
whether the chkService check box is checked. If so, modify the output
string:
string output = "Your order...\n";
if (chkService.Checked == true)
output += "You ordered the full in-house service\n";
18. Add code to the event handler to check which radio button is checked, and
to modify the output accordingly.
if (rbLapTop.Checked)
output += "Laptop computer\n";
else if (rbDeskTop.Checked)
output += "Desktop computer\n";
else
output += "Workstation computer";
Creating Windows Applications
C# Professional Skills Development 15-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
19. Add code to display the results in the label.
lblOutput.Text = output;
20. Run the program to test it. Click on choices and click Order to see the
results displayed in the label, as shown in Figure 11. If the output is
wrapping where you dont expect, or is cut off, you may need to expand
the label field.
Figure 11. Testing the button event handler.
Get It Working/Keep It Working
The approach you are taking with this application is a very powerful design
approach: get something working and keep it working. Rather than adding all
the controls, setting all the properties and then trying the program, you are
adding small changes and repeatedly testing. This is a very effective way to
write complex applications.
Events
When you double-clicked the Order button you went to the event handler for
the OnClick event. This is the default event for the button, but of course butto
n
supports many other events. To see how to wire up other events, click on the
button and then click on the lightning bolt button in the Properties window. A
list of events is shown, as illustrated in Figure 12. You are free to fill in th
e
Windows Forms
15-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
name of event handlers for any of the events. For example, type in the name
OnDragDrop next to the DragDrop event and press ENTER. You are taken to
the OnDragDrop event handler.
Figure 12. Events for the button.
If you made a mistake and did not want to create this event, just go back to the

form and delete it from the events list.
Do not delete the event handler in the code-behind or youll leave behind the
code that adds the method to the event. You can remove that by hand, but it is
cleaner to let the IDE do it for you.
Adding More Controls
To see how some of the other controls work, youll add a few more controls to
the application you created in the previous Try It Out.
Try It Out!
1. Add a checked list box and name it cblFeatures. Youll probably have to
lengthen the form and move the list box and Order button out of the way.
See WinForm
Controls.sln
Creating Windows Applications
C# Professional Skills Development 15-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
2. You will want to populate the list box when the form is created. To do this,
double-click on the form. This opens the default event handler for the
form, Form1_Load. This is the event that fires when the form is loaded.
3. Add code to the Form Load event handler to populate the list box. The list
box has a member Items, which is a collection of the items in the list. You
can add to that collection using the AddRange method that takes an array
of objects. Youll pass in an array of eight strings:
cblFeatures.Items.AddRange(
new object[8] {
"Monitor", "Tape backup",
"Zip drive", "Modem", "Extra Ram",
"Speakers", "CD", "CDRW"
}
);
4. Modify the order_click event handler to pick up the choices made by the
user. The check box list contains a list of items that are checked,
CheckedItems. You can iterate over that list with a foreach loop, adding
each string to the output. Modify the event handler to look like the
following:
Windows Forms
15-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
private void btnOrder_Click(
object sender, System.EventArgs e)
{
string output = "Your order...\n";
if (chkService.Checked == true)
output +=
"You ordered the full in-house service\n";
if (rbLapTop.Checked)
output += "Laptop computer\n";
else if (rbDeskTop.Checked)
output += "Desktop computer\n";
else
output += "Workstation computer";
foreach (string s in cblFeatures.CheckedItems)
output += "Feature: " + s + "\n";
lblOutput.Text = output;
}
5. Build and run the application. Click on various features and then click
Order. The results should look something like Figure 13.
Creating Windows Applications
C# Professional Skills Development 15-21
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 13. Testing the check box list box.
6. You can add a regular (not checked) list box in much the same way. Drag
a list box onto the form and name it lbModel. Once again, populate this in
the form load event:
for (int i = 0; i<20; i++)
{
lbModel.Items.Add("Model X" + i);
}
This creates model numbers such as ModelX0, ModelX1, and so forth.
7. Adjust the event handler for the button to display the model.
Windows Forms
15-22 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
if (lbModel.SelectedIndex != -1)
output += "Model: " +
lbModel.Items[lbModel.SelectedIndex] + "\n";
If no item is checked, the SelectedIndex property of the list box will be 1,
otherwise the SelectedIndex property can be used as an index into the Items
collection of the list box.
8. Youll add one more control: a combo box to list the salesperson taking
the order. Drag a combo box onto the form, and name it cbSalesPerson.
9. Initialize the combo box in the form load event handler.
cbSalesperson.Items.AddRange(
new object[4]
{ "Jesse", "Mr. Galt", "Milo", "JW" }
);
Once again you are filling the list from an array of Strings. This time, however
,
you want to set the text for the drop-down list box to a prompt that is not in i
ts
list of choices.
10. Set the Text property of the combo box to Choose Sales Person.
cbSalesperson.Text = "Choose sales person";
11. Add code in the button event handler to pick up the choice of salesperson.
if (cbSalesperson.Text != "Choose sales person")
output += "Sold by: " + cbSalesperson.Text + "\n";
12. Test the application. Click on the various features and then click Order, as

shown in Figure 14. You should see a summary of all your choices in the
label below the Order button.
Creating Windows Applications
C# Professional Skills Development 15-23
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 14. Testing the controls.
Color and Font Properties
While the form works, it doesnt look great. The job of laying out a truly
professional-looking form is beyond the scope of this book (and beyond my
capabilities!) but there are some properties you can use to add color to your
form. For example, you can set the background color of the list boxes, change
the text color of the text itself, and change the font as shown in Figure 15.
Windows Forms
15-24 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 15. Adding a splash of color.
Date/Time Picker
This chapter has barely scratched the surface on the various features and
attributes of these controls and these are only some of the standard controls
available to you. There is also a suite of new sophisticated advanced controls
provided by the Frameworks. In this section youll take a look at just one: the
DateTimePicker control.
The purpose of the DateTimePicker control is to provide easy access to dates
and times (as you might guess). Youll use it to add a control for setting the
delivery date of the order you are building.
Creating Windows Applications
C# Professional Skills Development 15-25
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
Youll make one minor modification to the form you just built, adding the new
control and using it to set the delivery date.
1. Reopen the project you were just working on.
2. Expand the form to make room for the new control.
3. Drag a dateTimePicker control onto the form. Name the dateTimePicker
object dpDelivery.
4. The dateTimePicker has a number of powerful properties. Youll take
control over the format of the date by setting the Format property to
Custom.
5. Set the CustomFormat field to MMMM dd, yyyy dddd. This causes the
date to be displayed as, for example, July 10, 2005 Sunday.
6. In the Order buttons on click method, add the following line.
output += "Delivered on " +
dpDelivery.Value.ToString("MM/dd/yy (dddd)")
+ "\n";
This formats the delivery date to the date and day, as shown in Figure 16.
See Controls.sln
Windows Forms
15-26 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 16. Formatting the DateTime
Using the Documentation
All of this is great, but how would you possibly figure out how to do this with
the dateTimePicker if this book didnt tell you? Visual Studio .NET comes
with documentation. Look up the dateTimePicker class in the Help index and
click on All Members. Scroll down to the value property and click on that.
You see that the Value property returns a DateTime object. Click on DateTime
in the Help file and scroll down to the bottom. One of the links is DateTime
Members. Click on that link. One of the instance methods is ToString. Click on
that link and youll see that it is overloaded. One of the overloaded versions
Creating Windows Applications
C# Professional Skills Development 15-27
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
takes a string. Click on that and youll see that the string is named format. The
documentation then provides all the formatting characters and what they mean.
Getting Started
You dragged a number of controls onto the form and added event handlers as
required. You set properties to change the look and feel of the form. There is
much more you can do to make this form useful, but that is beyond the scope
of a course on C#. Before going on, however, you may want to play with the
form, add new controls, and get a sense of what power lies in this development
environment.
Windows Forms
15-28 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
XML Documentation
Youve seen in previous chapters that C# offers both the traditional C-style
comments,
/* this is a comment */
and the more modern C++ style comments:
// this is a comment
C-style comments can span lines and can be used to comment-out entire
sections of code.
There is a third type of comment in C#the XML comment:
/// this is an xml comment
XML comments begin with three slashes and run to the end of the line. XML
comments are used to document your code, and with the right tools, these can
be used to generate extensive documentation pages.
You can use compile times switches to generate an XML document based on
your XML comments and you can use XSL to transform these comments into
any kind of document youd like. Visual Studio .NET also provides built-in
support for XML comment documentation.
Try It Out!
To see how XML comments work, return to the previous example and add
XML comments to document the file.
1. Reopen the previous example.
2. Scroll to the top of the file and modify the documents above the
declaration of the form.
See WinForm
Controls.sln
XML Documentation
C# Professional Skills Development 15-29
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
/// <summary>
/// Form for accepting computer orders
/// </summary>
public class Form1 : System.Windows.Forms.Form
3. Scroll down to the Form1_Load event handler and type three slashes
before the method header. A summary comment springs open. Add a
summary within the summary tags and add a description of the parameters
within the param tags:
/// <summary>
/// Event handler for Form loading
/// </summary>
/// <param name="sender">The form itself</param>
/// <param name="e">EventArgs structure</param>
4. Add a comment block above the event handler for the button click.
/// <summary>
/// Button is clicked event
/// </summary>
/// <param name="sender">The button</param>
/// <param name="e">Struct with info about the
event</param>
private void btnOrder_Click(
object sender, System.EventArgs e)
{
5. When you are done commenting, choose the menu items Tools|Build
CommentWebPages. A dialog box opens as shown in Figure 17. Leave
the defaults and click OK.
Windows Forms
15-30 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 17. Building Comment Web pages.
6. A page is created for each project. In this case there is only one project,
WinFormControls, as shown in Figure 18.
Figure 18. A report is created for each project.
XML Documentation
C# Professional Skills Development 15-31
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
7. Click on the report for WinFormControls. Click on the + sign next to
WinFormControls and the forms are displayed. Click on the Form1 link to
show the report about Form1, as shown in Figure 19. Each of the controls
have links to documentation, as do all the methods you commented or that
were commented automatically by Visual Studio .NET.
Figure 19. The report for Form1.
8. Click on btnOrder_Click. You are taken to a page with information about
this method, including its parameters. The descriptions you added in the
params element is shown here, as shown in Figure 20.
Windows Forms
15-32 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 20. The btnOrder_Click Function.
Its Just HTML
The reports themselves are just HTML. To see this, open an Explorer window
and navigate to the directory for your project. Youll find a
CodeCommentReport directory with a number of images in it. Within that
directory youll find a ControlForm directory filled with htm files that
represent the various reports you are viewing.
Really, Its XML
The HTML pages were generated from the XML file you created based on the
comments. You can also see the XML file itself.
1. Open the project in Visual Studio .NET and click on the project in the
Solution Explorer.
2. Choose View/Property Pages.
3. Click on Configuration Properties.
4. Set the XML documentation file property as shown in Figure 21. You may
name the XML Documentation File anything you wish.
XML Documentation
C# Professional Skills Development 15-33
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 21. Setting the XML Documentation File.
5. Rebuild the application.
6. Navigate to the directory for your project. You should find a file
XMLComments.xml (or whatever it is you named the file in Step 4).
7. Double-click the file. A browser opens with the XML for the comments
you added, as shown in Figure 22. It is this file that is used to generate the
HTML report. Typcially the file is not saved after the report is generated,
unless you set the file name as shown in Step 4.
Windows Forms
15-34 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 22. The XML comments file.
XML Documentation
C# Professional Skills Development 15-35
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
There are three types of applications you can build using C# and
.NET: Windows applications, Web applications, and Web Services.
Windows applications are built using Windows forms.
Windows Forms provide a Rapid Application Development (RAD)
environment in which you can drag controls onto a form.
You create event handlers for the controls on your form. The Visual
Studio .NET Integrated Development Environment (IDE) will help
with wiring the event handlers.
If you double-click on a control the IDE creates an event handler for
the default event.
You can access alternative events using the lightning bolt button on
the properties window.
List box controls have an Items collection to manage the items listed
in the list box.
The Help documentation can be a powerful resource for understanding
the properties and methods of controls.
You can document your files with XML document comments (///) and
then use Visual Studio to generate XML report files.
Windows Forms
15-36 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
(Review questions and answers on the following pages.)
XML Documentation
C# Professional Skills Development 15-37
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. What is the difference between a Windows application and a Web Forms
application?
2. From which class must your Form derive?
3. What is the purpose of the statement Using System.Windows.Forms?
4. How do you add controls to a form?
5. How do you add entries to a list box?
6. What is the method to call to add an array to a list box?
7. How do you generate a report based on your XML comments?
8. How do you generate the XML file itself?
Windows Forms
15-38 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. What is the difference between a Windows application and a Web Forms
application?
A Windows application uses Windows Forms and is intended to
be run on the desktop. A Web Forms application is served by a
Web server and uses a browser for its User Interface.
2. From which class must your Form derive?
You must derive all forms from System.Windows.Forms.Form
3. What is the purpose of the statement Using System.Windows.Forms?
The using statement allows you to refer to objects in the
System.Windows.Forms namespace without qualifying the
namespace; thus you can write Label rather than
System.Windows.Forms.Label.
4. How do you add controls to a form?
You add controls to the forms Controls collection.
5. How do you add entries to a list box?
You add entries to a list box by modifying the List boxs Items
collection.
6. What is the method to call to add an array to a list box?
The List Boxs Items collection has an AddRange method that will
take an array of objects.
7. How do you generate a report based on your XML comments?
To generate the report from within Visual Studio .NET you use the
menu choice Tools/Build Comment Web Pages.
8. How do you generate the XML file itself?
You generate the XML file by navigating to the PropertyPages for
the project and setting the XML Documentation File property
under Configuration Properties.
XML Documentation
C# Professional Skills Development 15-39
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 15:
Windows Forms
Lab 15:
Windows Forms
15-40 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 15 Overview
In this lab youll learn to create Windows Applications using WinForms.
To complete this lab, youll need to work through three exercises:
Building a Windows Form without Visual Studio .NET
Creating Hello World Using Visual Studio
Building Applications with Windows Controls
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-bystep

instructions.
Building a Windows Form without Visual Studio .NET
C# Professional Skills Development 15-41
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Building a Windows Form without
Visual Studio .NET
Objective
In this exercise, youll see that building a Windows Forms application by hand
is possible, but not necessarily easy.
Things to Consider
Your form must inherit from System.Windows.Forms.Form.
You must place each control at a specific location (using
System.Drawing.Point).
You must size each control (using System.Drawing.Size).
Once you create your controls you must add them to the Forms
control collection.
Step-by-Step Instructions
1. Open your favorite text editor, such as Notepad or WordPad, and start a
new text file. Call it HelloWorld.cs.
2. Add using statements for the namespaces System and
System.Windows.Forms.
using System;
using System.Windows.Forms;
3. Create a namespace (optional). Dont forget the closing brace at the end of
the file.
namespace HelloWorld_Completed
{
4. Create a class derived from Form.
Lab 15:
Windows Forms
15-42 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public class myForm: Form
{
5. Create a label to display text.
private System.Windows.Forms.Label output;
6. Create a button to cancel (exit).
private System.Windows.Forms.Button cancel;
7. Create the constructor for the myForm class.
public myForm()
{
8. Create the objects: a label and a button.
output = new System.Windows.Forms.Label ();
cancel = new System.Windows.Forms.Button ();
9. Set the forms title.
Text = "Hello World From Windows";
10. Set the labels location to 20,30 (use System.Drawing.Point).
output.Location = new System.Drawing.Point (20, 30);
11. Set the labels text to Hello World From Windows!
output.Text = "Hello World From Windows!";
12. Set the labels size to 200,25 (use System.Drawing.Size).
Building a Windows Form without Visual Studio .NET
C# Professional Skills Development 15-43
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
output.Size = new System.Drawing.Size (200, 25);
13. Set the buttons location to 150,200 (use System.Drawing.Point).
cancel.Location = new System.Drawing.Point (150,200);
14. Set the buttons text to &Cancel.
cancel.Text = "&Cancel";
15. Set the buttons size to 110,30 (use System.Drawing.Size).
cancel.Size = new System.Drawing.Size (110, 30);
16. Register the click event handler.
cancel.Click += new System.EventHandler
(this.OnCancelClick);
17. Add the controls to the form.
Controls.Add (cancel);
Controls.Add (output);
18. End the myForm constructor.
}
19. Implement the click event handler. The only action is to call the static
method Application.Exit.
Lab 15:
Windows Forms
15-44 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
protected void OnCancelClick(
object sender, System.EventArgs e)
{
Application.Exit();
}
20. Run the app from Main by calling Application.Run and passing in an
instance of the form.
public static void Main()
{
Application.Run(new myForm());
}
21. End the class and namespace.
} // end the class myForm
} // end the namespace
22. Open the command window for compiling your class. To do so, open the
Visual Studio .NET Command Prompt from the Start|Programs|Visual
Studio.NET Tools menu as shown in Figure 23.
Figure 23. Choosing the command prompt.
23. Navigate to the directory with your .cs file.
24. Compile the program with the following command:
csc HelloWorld.cs
25. List the directory; you should now find helloWorld.exe as well as
helloWorld.cs. Enter the name helloWorld and press ENTER. Your code
should run as shown in Figure 24.
Building a Windows Form without Visual Studio .NET
C# Professional Skills Development 15-45
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 24. Running the hand-crafted window.
Lab 15:
Windows Forms
15-46 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Creating Hello World Using Visual
Studio
Objective
In this exercise, youll recreate the Hello World application using Visual
Studio.
Things to Consider
Visual Studio writes much of the infrastructure for you, but the
fundamentals are unchanged.
Your form will be separated from your code-behind file. The codebehind
will house the event handlers.
When you place objects on the form, they will be located and sized for
you, but you may take explicit control either in the code or through the
properties.
Step-by-Step Instructions
1. Create a new Visual C# Windows Application in Visual Studio named
WindowsHelloWorld.
2. Drag a label onto the form and set the text to Hello World From
Windows!
3. Stretch the label to fit the words.
4. Drag a button onto the form. Name it btnCancel.
5. Set the buttons text to Cancel.
6. Double-click on the Cancel button to open the event handler.
7. Enter the code to close the application in the event handler.
Application.Exit();
8. Press CTRL+F5 to start the application, as shown in Figure 25.
Creating Hello World Using Visual Studio
C# Professional Skills Development 15-47
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 25. The Hello World application.
9. Click the Close button (X) to close the application.
Lab 15:
Windows Forms
15-48 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Building Applications with Windows
Controls
Objective
In this exercise, youll create a Windows form for booking a reservation at a
hotel.
Things to Consider
Radio buttons are used to select a single choice among many.
Check boxes are used to select multiple choices. When you have many
check boxes, consider using a check box list.
Drop-down list controls are good for a single selection among many
choices.
Double-click on a control to set its default event handler.
Step-by-Step Instructions
1. Create a new Windows application named Hotel Reservation.
2. Drag a label onto the form and set its text to Hotel Reservation Form.
3. Set the font size to 14.
4. Set the font to bold.
5. Set the TextAlign property to MiddleCenter.
6. Drag a check box onto the form and name it chkVIP.
7. Set its background color to Green and its text to VIP Customer.
8. Drag a group box onto the form, and set its text to Room Type.
9. Drag a radio button onto the group form, set its name to rbSingle, and its
text to Single.
10. Drag a second radio button onto the group form, set its name to rbDouble,
and its text to Double.
Building Applications with Windows Controls
C# Professional Skills Development 15-49
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
11. Drag a third radio button onto the group form, set its name to rbSuite, and
set its text to Suite.
12. Use the format menu choice to make all three radio buttons the same size,
align their left borders, and set their vertical spacing to equal.
13. Resize the group to fit the radio buttons and set the background color of
the group to Cornflower blue.
14. Drag a checkedListBox onto the form and set its name to clbFeatures.
15. Click on the Items property and add the following features:
Whirlpool
Color TV
VCR
DVD
On-Demand Movies
In-Room Wet Bar
Free Breakfast
Free Lunch
Free Meals
Late checkout
Early check in
16. Set the Sorted property to true and watch the list sort itself.
17. Set the background color to a pleasing pastel.
18. Add a button named btnBook and set its text to Book Reservations.
Stretch the button to fit.
19. Set the buttons background color to red, set its forecolor to yellow, and
set its font to bold.
20. Drag a label onto the form. Set its name to lblOutput and its text to blank.

Your form should now look more or less like Figure 26.
Lab 15:
Windows Forms
15-50 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 26. The hotel reservation form.
21. Double-click on the Book Reservations button to open the event handler.
Create a string to hold the output:
string output = "Your reservation...\n";
22. Check if the VIP Customer check box is checked, if so modify the text for
the output string.
if (chkVIP.Checked == true)
output +=
"VIP Service!\n";
23. Check which radio button is checked and modify the output string.
Building Applications with Windows Controls
C# Professional Skills Development 15-51
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
if (rbSingle.Checked)
output += "Single room\n";
else if (rbDouble.Checked)
output += "Double room\n";
else
output += "Suite";
24. Add output for each feature that is selected and checked in the list.
foreach (string s in clbFeatures.CheckedItems)
output += "Feature: " + s + "\n";
25. Display the output in the label.
lblOutput.Text = output;
26. Build and run the application as shown in Figure 27.
Lab 15:
Windows Forms
15-52 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 27. Final exercise results.
ADO.NET
C# Professional Skills Development 16-1
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
ADO.NET
Objectives
Understand how relational databases are organized.
Explore Declarative Referential Integrity, foreign key constraints,
database normalization and Structured Query Language.
Understand how to join tables to create declarative Select statements.
Explore the ADO Object Model.
Fill controls from data in a database.
Use both the SQL and ADODB Data Providers.
Bind data to Windows Forms controls.
Model data relations in a DataSet.
Display master/child relationships in a DataGrid control.
ADO.NET
16-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Relational Databases
Just about any non-trivial application will need to interact with data. Most
often, that data will be held in a relational database (RDB). ADO.NET is the
newest technology from Microsoft to interact with underlying databases.
The goal of ADO.NET is to provide an object-oriented interface to your data.
One of the problems with building object-oriented programs, historically, has
been in interacting with databases, which are typically not object oriented.
ADO.NET provides a bridge between your program and the underlying data; it
presents as a series of objects with which your program can interact, but it
manages the transition to the relational structure of your data.
ADO.NET also offers a disconnected model of your data, which allows you to
build large, scalable applications without a bottleneck at the connection to the

database. While ADO.NET does offer a series of classes that are optimized for
interacting with SQL Server, you can also use ADO.NET with any ODBC
database, whether or not your data is in a relational database.
That said, the principal use of ADO.NET will be with one of the major
relational databases: typically SQL Server, Access, or Oracle. Before
investigating the ADO.NET object model, this chapter provides a primer on
using relational databases.
RDB Structure
A relational database is nothing more than a repository of data. Typically a
relational data base is organized into tables. Each table is in turn organized i
nto
records (rows) and fields (columns).
Every row in a relational database has the same fields as all the other rows in
that table. Each column in each table holds a specific type of information.
The tables in a relational database are related to one another by common fields.

To see how this works, youll examine the Northwind relational database that
ships with both SQL Server and Access.
The Northwind Database describes a fictional food products company. The
tables in the database encapsulate all the companys information about its
products, customers, suppliers, and employees, as well as the details of every
order the company has filled.
For example, the Orders table has a variety of columns, as shown in Figure 1.
You can see that each row in the order table will record the CustomerID,
EmployeeID, OrderDate, and so forth.
Relational Databases
C# Professional Skills Development 16-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 1. The Orders table.
Normalization
When you examine an order, youll want the customers name and address.
Rather than sorting the name and address with each order, you store only the
CustomerID. You can then use the CustomerID to look up the customers
information in the Customer table, which in turn is organized by CustomerID.
This relationship is illustrated in Figure 2. The CustomerID field in Orders
establishes a relationship with the CustomerID field in Customers.
Figure 2. Orders to Customer.
This is much more efficient than storing the name and address with every
order, and it is much safer. If you later change the customers address, you
ADO.NET
16-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
need only update one record in Customers, rather than all the records for that
customer in Orders.
Keys
The CustomerID is a primary key in the Customers table (indicated by the key
symbol in Figure 2).
Key Terms
Primary key A primary key uniquely identifies a record in a database table.
Foreign key A primary key from a different table.
Foreign key constraint A constraint on the table that enforces that you create v
alid
records and disallows invalid deletions or updates based on a
foreign key relationship.
The CustomerID is a foreign key in the Orders Table.
In this case, CustomerID is used in Orders to relate the order to the correct
record in the Customers table. You can (of course) have many orders with the
same CustomerID, but you can have only one record in Customers with that
CustomerID.
The use of a foreign key in the Orders table creates a foreign key constraint.
This foreign key constraint ensures that you may not add a record in Orders
without a valid CustomerID. That is, the CustomerID must be a Primary key to
a row in the Customers table.
In addition, you may not delete a record in Customers whose CustomerID is
used in a record in Orders. This prevents you from having Orders with a (now)
invalid CustomerID.
Declarative Referential Integrity (DRI)
This process of establishing constraints on your database to ensure that your
data remains valid is called Declarative Referential Integrity. DRI helps you
avoid database corruption and reduces programming errors. You are enlisting
the database in rejecting operations that might otherwise leave your data in an
inconsistent (corrupt) state.
Relational Databases
C# Professional Skills Development 16-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Using SQL Server to Examine the Relationships
The diagrams shown previously were created in SQL Server. You can open the
Enterprise Manager and examine the structure of the tables. For example, you
can examine the structure and contents of the Orders table in a variety of ways.

First, you can look at the design of the table itself, using the Design view, as

shown in Figure 3. You can see that each field has a data type. The OrderID
(highlighted) is of type int. Int fields have a length of 4 bytes. This particul
ar
field is marked as an Identity column (see circled area on the bottom). The
OrderID is also marked as the primary key for this table.
The second field is the CustomerID, marked as being of type nchar with a
length of 5. This is a 5-character field. The third field is the EmployeeID, an
int. The OrderDate, RequiredDate, and ShippedDate are all datetime objects; a
datetime object is a SQL representation of a Julian date that maps well to .NET
dates.
ADO.NET
16-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 3. The Orders Table Design dialog box.
A second way to look at this same data is to examine the contents of the
database, again using a table view in SQL Server Enterprise Manager, as
shown in Figure 4. The table is too wide to show all of it, but you can see here

that there are numerous rows, each of which is divided into the columns
previously discussed.
Relational Databases
C# Professional Skills Development 16-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 4. Examining the contents of the table.
The very first record (OrderID 10248) is from a customer whose ID is VINET.
You can use that ID to look up that customer in the Customers table as shown
in Figure 5.
Figure 5. Looking up a customer in the Customers table.
You found the particular record you were looking for by issuing a select
statement:
SELECT * FROM Customers WHERE (CustomerID = VINET)
This statement is written in Structured Query Language (SQL). SQL is often
pronounced see-quill. SQL is a declarative language (rather than a procedural
or object-oriented language). That is, the goal in SQL is to create a statement
that works all at once, rather than in a series of steps. The heart of SQL is th
e
query. The select statement shown above is a very simple example of a query.
You can experiment with SQL queries using the Query Analyzer. For example,
you can issue a Select query in Query Analyzer to find all the records in
Customers where the city field is London. You might extract only a couple of
columns, such as the CustomerID and CompanyName, to keep the results
simple. This query is illustrated in Figure 6. The results are shown in the
ADO.NET
16-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
bottom pane; six companies were found. Notice that you do not need to
include the search field (city) in the results.
Figure 6. Issuing a select statement.
Joining Tables
To find more complex information you may have to join two or more tables
together. For example, suppose you are in charge of the Northwind database,
and you get a call from one of your customers: the president of Rancho Grande
restaurant. She wants to know what products she bought from you in 1998.
How do you answer the question?
Youll need to examine your Orders table to find all the orders with an
OrderDate in the year 1998. You also need to examine the OrderDetails table
to get the products ordered. The OrderDetails table records only the ProductID
of the product; to get the product name youll need the ProductTable (indexed
by ProductID). The relationship among these tables is illustrated in Figure 7.
Relational Databases
C# Professional Skills Development 16-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 7. Orders to Customers and Products.
You can see that the OrderID and ProductID together make a primary key in
Order Details. That makes sense, since any given order detail record will
record one product purchased on a given order. The OrderID itself is a primary
key in the Orders table. Just as the Orders table has a foreign key relationship

through the CustomerID to the Customers table, the OrderDetails table has a
foreign key relationship through the ProductID to the Products table.
To get started, you might first find all the customerIDs from the customer table

where the Company name is Rancho Grande, as shown in Figure 8. The
CompanyID for this company is RANCH.
Figure 8. Finding the Company ID.
ADO.NET
16-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
To find the orders by this company, youll join the Orders table to the
Customers table, finding only those records where the CustomerID matches, as
shown in Figure 9. There are a few interesting things to note about this search.

Figure 9. Searching for all orders from a given company.
First, notice that you can alias a table name. For example, in the SQL
statement shown in Figure 9, the table name customers is aliased to c; you can
refer to the CustomerID field of Customers with c.CustomerID.
Second, in this example the customers table is joined to the Orders table with
the keyword join. The on keyword sets the condition of the join; not every
record is joined to every other record; just those records where the
CustomerIDs are the same.
The result is as if you had created one very wide table of all the records in
Customers joined with all the matching records in Order. Within that, you find
only those records where the CompanyName field is equal to Rancho Grande.
This narrows the records to five matching orders.
You now need to narrow the search to those that occurred in 1998, as shown in
Figure 10. This query is just like the previous one, except that the where claus
e
is narrowed to find the matching order dates.
Relational Databases
C# Professional Skills Development 16-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 10. Selecting only matching dates.
With these three order records found, you are ready to find all the matching
records in the Order Details table. Youll join the Order Details table to the
Orders table on the orderID field, as shown in Figure 11.
Figure 11. Adding the OrderID table.
Notice that the Order Details table has a space in its name and so must be
enclosed in square brackets. The list of matching records is larger than the
previous search; this makes sense because now you are widening the table to
ADO.NET
16-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
include all the order details, and there are multiple details records for each
order.
Until now, youve been getting back all the fields. You really only care about
the OrderID, ProductName and OrderDate. With these, you can tell your
customer what she ordered and when. Youll need to add in the products table
(joined on the Product ID) to get the product name. This is illustrated in
Figure 12.
Figure 12. Getting the Product Name and order info.
SQL Is a Declarative Language
The important thing to realize about this last exercise is this: you built this
query in successive approximation, but that was just a teaching tool. If you
were a SQL expert, you could have written the entire SQL statement all at
once. The results are presented in totality.
This is very different from how you would handle a procedural language. In a
procedural language youd get all the order records for your customer. Youd
then use the OrderDate to get all the details. Youd then use the ProductIDs to
get all the product names. Here you simply declare it all, with a single select
statement:
Relational Databases
C# Professional Skills Development 16-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
select o.OrderID, productName, OrderDate
from [order Details] od
join orders o on o.orderID = od.OrderID
join products p on p.productID = od.ProductID
join customers c on o.CustomerID = c.CustomerID
where CompanyName = 'Rancho Grande'
and OrderDate >= '1/1/1998' and OrderDate <= '12/31/1998'
The SQL statement has no loops, no real procedures; it just selects the records
it needs as a set. It is the difference between saying go find all the orders thi
s
customer placed, now get the details, now find the products and give this
customer a list of them on the one hand, and saying give me all the product
names for all the orders I placed during 1998 on the other. SQL enables you
to make the latter declaration; which can be much faster and more efficient,
but it does take some getting used to.
ADO.NET
16-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The ADO.NET Object Model
The ADO.NET Object model starts with your data; typically stored in a
database. You can read that data using a simple ADO.NET object, DataReader,
as shown in Figure 13. The DataReader provides a connected, forward-only
firehose cursor on your data.
Figure 13. The DataReader.
More common than using a DataReader is to use the powerful DataAdapter,
and the associated Connection and Command objects. These three objects
work together to produce a DataSet, which is a disconnected object
representing a subset of your data and the relationships among the tables
housing your data, as shown in Figure 14.
Figure 14. The DataSet and associated objects.
The ADO.NET Object Model
C# Professional Skills Development 16-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
The DataSet Object
The DataSet is the heart of the ADO.NET model. It is a disconnected subset of
your database not a single table or a recordset, but rather a microcosm of the
database itself, complete with tables and, most important, the relationship
among the tables.
The DataSet, illustrated in Figure 15 has a member variable,
DataTableCollection, which contains a set of DataTable objects.
Figure 15. The DataSet object.
Each DataTable object has a DataRowCollection, which in turn has DataRow
objects. The DataTable also has a DataColumnCollection object, with, (you
guessed it) DataColumn objects. A third collection in the DataTable is the
ConstraintCollection, which encapsulates the constraints on the table.
Finally, the DataSet also contains a DataRelationsCollection, which contains
DataRelation objects that encapsulate the relationships among tables.
DataAdapter
The job of the DataAdapter, illustrated in Figure 16, is to decouple the DataSet

from the underlying database. It does this by providing a Fill() method, which
retrieves data from the database and fills tables in the dataset. To accomplish
ADO.NET
16-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
this decoupling, the dataAdapter encapsulates the various SQL commands
used to manipulate the database (select, insert, delete, and update).
Figure 16. The DataAdapter object.
Command and Connection
The DBCommand object serves to encapsulate the SQL commands you will
use to interact with the database. Using the DBCommand class allows you to
reuse commands, and to store them and otherwise treat them as objects with
which you can interact.
The DBConnection object encapsulates your connection to a particular
database. DBConnection is instrumental in .NET support for transaction, as
youll see in another chapter.
DataView
The DataView class, illustrated in Figure 17, is used for data binding, as you
will see later in this chapter. The job of a DataView object is to provide a
subset of the data available in a DataTable object.
The ADO.NET Object Model
C# Professional Skills Development 16-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 17. The DataView class.
Working with Managed Providers
ADO.NET currently provides support for two managed providers:
SQL Server
OLE DB
The idea here is straightforward; if you are working with SQL Server, then you
use the SQL Server Manager Provider classes. These are optimized for SQL
Server and provide the highest performance.
If you are working with virtually any other database, you use the OLE DB
Managed Provider, which provides a more generic approach to interacting with
the database.
The SQL Managed Provider is supported by classes in the
System.Data.SQLClient namespace, while the OLE DB managed provider is
supported by classes in the System.Data.OLEDB namespace. Whenever there
is a specialized class in one, there will be a shadow class in the other.
ADO.NET
16-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
To get started using ADO.NET youll populate a list box with the contents of
the Products table from the Northwind database. This first example assumes
you are running SQL Server.
1. Create a new Windows Project, named ADOSQL.
2. Drag a list box onto the form, and name it lbProducts.
3. Double-click on the form to open the Form1_Load event handler. This
event handler will be called when the form loads.
4. Go to the top of the page and add the Using statement for the SQL Data
Provider.
using System.Data.SqlClient;
5. Return to the event handler and create the connection string to connect to
your server.
string strConnection =
"server=YourServer; uid=sa;
pwd=YourPW; database=northwind";
6. Create the command string for a simple select statement.
string strCommand =
"Select productName, unitPrice from Products";
7. Create a dataAdapter using the SQL data provider version. Pass the
Connection string and command string as parameters to the DataAdapters
constructor.
SqlDataAdapter dataAdapter =
new SqlDataAdapter(strCommand, strConnection);
8. Create the DataSet. Notice that this is independent of the data provider.
DataSet dataSet = new DataSet();
See ADOSQL.sln
The ADO.NET Object Model
C# Professional Skills Development 16-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
9. Fill the DataSet by calling the Fill method on the DataAdapter, passing in
the DataSet and the name for the table you are creating within the DataSet.
dataAdapter.Fill(dataSet,"Products");
10. Extract the first table from the DataSet and assign it to an instance of
DataTable.
DataTable dataTable = dataSet.Tables[0];
11. Iterate over the DataTables Rows collection, adding the ProductName
column to the List box.
foreach (DataRow dataRow in dataTable.Rows)
{
lbProducts.Items.Add(dataRow["ProductName"] +
" (at $" + dataRow["UnitPrice"] + ")");
}
There is a lot to observe in this foreach loop. For each row, you are accessing
the column you want through the indexer, which has been overloaded to take a
string (the column name):
dataRow["ProductName"]
This returns a string with the value of the ProductName column within the
row. You add this to the Items collection in the list box. You then add a space
and the string (at $, followed by the value of the UnitPrice column and then
the closing parenthesis).
12. Build and run the application: the results are shown in Figure 18. This is
pretty painless for extracting fields from a database and filling a list box.
ADO.NET
16-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 18. Filling a list box from SQL Server.
Using the Generic Provider
Before you go on to add new functionality to this application, youll rebuild it
using the OLE DB provider, to show that this is equally easy. This example
assumes you have the Northwind Access database .mdb file copied to your C:
drive in the root directory.
13. Modify the using statement to say:
using System.Data.OleDb;
14. Modify the connection string to connect to the Access database. If your
copy of nwind.mdb is not in the root directory of C, be sure to set the
appropriate dataSource string.
string strConnection =
"provider=Microsoft.JET.OLEDB.4.0;
data source=c:\\nwind.mdb";
15. Modify the creation of the dataAdapter to use the OleDbDataAdapter.
OleDbDataAdapter dataAdapter =
new OleDbDataAdapter(strCommand, strConnection);
16. Build the application with no other changes. Your application runs,
extracting the data from the Access database, as shown in Figure 19. The
display is identical; the only change is that now you are accessing the data
from Access rather than from SQL Server.
See
ADOOLEDB.sln
The ADO.NET Object Model
C# Professional Skills Development 16-21
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 19. Filling a list box from the Access database.
Data Binding
In the previous examples, you created a DataSet and filled the list box by
explicitly extracting a table from the DataSet and iterating over the rows in th
at
table, pulling out column information and adding it to the list box.
Windows Forms (and Web Forms) controls offer a more powerful idiom for
filling data-bound controls. Rather than hand filling the control, you can set t
he
controls DataSource to the appropriate tables DataView. The control is then
filled for you automagically.
Try It Out!
To see how data binding works, youll create a new project using a more
powerful data bound control: the DataGrid.
1. Create a new Windows application and name it DataGrid1.
2. Drag a DataGrid onto the form and resize it as shown in Figure 20. The
DataGrid has no detail in it just yet.
See DataGrid1.sln
ADO.NET
16-22 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 20. The DataGrid in the Design view.
3. Rename the dataGrid to ProductDataGrid.
4. Right-click on the form and choose View Code. Set the using statement
for SQLClient.
5. Click on the Design tab to return to the designer. Double-click on the form
to go to the event handler for the Form1_Load event. Create the
connection string and command string.
string strConnection =
"server=YourServer; uid=sa;
pwd=YourPW; database=northwind";
string strCommand =
"Select productName, unitPrice, quantityperunit from
Products";
6. Create the DataAdapter and the DataSet and fill the DataAdapter.
SqlDataAdapter dataAdapter =
new SqlDataAdapter(strCommand, strConnection);
DataSet dataSet = new DataSet();
dataAdapter.Fill(dataSet,"Products");
7. This time, instead of extracting a table and iterating over the rows, assign
the DefaultView to the DataSource property of the DataGrid.
The ADO.NET Object Model
C# Professional Skills Development 16-23
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
productDataGrid.DataSource =
dataSet.Tables["Products"].DefaultView;
The DefaultView property returns a DataView object. By assigning this
DataView to the DataSource property of the DataGrid, the grid has all it needs
to display the data in the DataSet, as shown in Figure 21.
Figure 21. Filling the DataGrid.
Modeling Table Relationships
It was stated earlier in this chapter that the DataSet can capture the
relationships among tables. One common relationship between two tables is
the Master/Detail relationship, in which one table (e.g., Orders) holds a master

record, and a second table (e.g., Order Details) holds the detail records.
The DataGrid is uniquely useful for displaying the Master/Detail relationship,
and works together with the DataSet very cleanly.
Try It Out!
To see modeling table relationships at work, youll create a new Windows
application that reveals the relationship between the Orders table and the Order

Details table.
1. Create a new Windows application and name it DataGridRelations.
2. Drag a DataGrid onto the form and resize it. Set its Size property to 865,
255. Set the forms size to 888,300. (Feel free to modify these to fit your
own aesthetic judgment.) Name the grid dgOrders.
See DataGrid
Relations.sln
ADO.NET
16-24 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
3. Go to the code behind page and add the using statement.
Creating the SQLCommand and SQL Connection
Objects
4. In previous examples you passed the command string and connection
string to the DataAdapter object. In this example, youll explicitly create
these objects. Go to the code-behind page and add the following members
to the class.
public class Form1 : System.Windows.Forms.Form
{
private SqlConnection connection;
private DataSet dataSet;
private SqlCommand command;
private SqlDataAdapter dataAdapter;
Notice that you do not have to explicitly write
System.Data.SqlClient.SqlDataAdapter; the using statement allows you to just
write SqlDataAdapter.
5. Return to the form and double-click to open the form1_Load event
handler.
6. To create the connection object, you first create the connection string, just

as you did previously.
string strConnection =
"server=YourServer; uid=sa;
pwd=YourPW; database=northwind";
7. Instantiate the connection object, passing in the connection string.
connection = new
System.Data.SqlClient.SqlConnection(connectionString);
8. Open the connection by calling the instance method Open on the
connection object.
connection.Open();
The ADO.NET Object Model
C# Professional Skills Development 16-25
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
9. Create the DataSet object.
dataSet = new System.Data.DataSet();
10. Create the DataCommand object and set its Connection property to the
connection you created in Step 8.
command = new System.Data.SqlClient.SqlCommand();
command.Connection=connection;
11. Set the CommandText property of the command to the Select command
youll use to obtain all the rows in Orders.
command.CommandText = "Select * from Orders";
12. Create the DataAdapter object and set its SelectCommand property to the
command object you created in Step 10.
dataAdapter =
new System.Data.SqlClient.SqlDataAdapter();
dataAdapter.SelectCommand= command;
13. Add a Table Mapping to the DataAdapter.
dataAdapter.TableMappings.Add("Table","Orders");
14. You are ready to fill the DataSet with the DataAdapter.
dataAdapter.Fill(dataSet);
15. Bind the default view of the Orders table to the data grid.
dgOrders.DataSource =
dataSet.Tables["Orders"].DefaultView;
ADO.NET
16-26 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
16. Run the application. Youll see the contents of the Orders table displayed
in the data grid, as shown in Figure 22.
Figure 22. Displaying the Orders table in a DataGrid.
Now it is time to add the relationship between the Orders table and the Order
Details table. As noted earlier, relational databases are built on the idea of o
ne
table relating to another. The Orders table relates to the Order Details table i
n a
one-to-many, parent/child relationship; this is often called a master/detail
relationship.
The relationship is built on the fact that the OrderID is a primary key in Order
s
and a foreign key in Order Details. Youll expand the data grid to represent
both the Orders and the Order Details tables.
Note that you only need one dataSet. The idea of a dataSet is to encapsulate a
subset of the database, including multiple tables and their relationships to one

another.
To accomplish this youll need a second command object and a second data
adapter. The command object encapsulates the command to the database, and
the data adapter is the bridge between the data set and the back-end database.
17. Add two new data members.
private SqlCommand command2;
private SqlDataAdapter dataAdapter2;
18. Modify the form1Load event handler to add the new command object. Set
the Connection property to the original connection object, and set the
CommandText property to a string that will select all the records from
Order Details.
Place the new code immediately after the call to Fill and before the binding to
the DataSource property of dgOrders.
The ADO.NET Object Model
C# Professional Skills Development 16-27
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
command2 = new System.Data.SqlClient.SqlCommand();
command2.Connection=connection;
command2.CommandText = "Select * from [Order details]";
19. Create the new DataAdapter object and set its SelectCommand property to
the new Command object you just created. Map the table under the name
Details. Then fill the new DataAdapter.
dataAdapter2 = new System.Data.SqlClient.SqlDataAdapter();
dataAdapter2.SelectCommand= command2;
dataAdapter2.TableMappings.Add("Table","Details");
dataAdapter2.Fill(dataSet);
You have filled the DataSet with two tables. Each table is mediated by its own
DataAdapter, each of which maps a table in the database to a table in the
DataSet.
Each DataAddapter uses its own command object to encapsulate the select
command, but the command objects share a common connection to the
database.
You are now ready to create the DataRelation object to encapsulate the
relationship between the two tables.
20. Create two instances of type System.Data.DataColumn.
DataColumn dataColumn1;
DataColumn dataColumn2;
The two data columns will be used to represent the columns that manage the
relationship between the two tables: the OrderID columns.
21. You want to assign to the first DataColumn object the OrderID column
from the Orders table. To do so, index into the Tables collection of the
dataSet, using the string Orders as the index value. The indexer will return
a DataTable object that you can assign to a temporary value.
DataTable orderTable = dataSet.Tables["Orders"];
ADO.NET
16-28 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
22. Index into the Columns collection of the DataTable to retrieve the OrderID
column, and assign that to a temporary variable.
DataColumn orderIDColumn = orderTable.Columns["OrderID"];
23. Assign the DataColumn object to dataColumn1.
dataColumn1 = orderIDColumn;
Note that you could have combined Steps 21, 22, and 23 into a single
assignment statement.
dataColumn1 = dataSet.Tables["Orders"].Columns["OrderID"];
24. Assign the OrderID column of the Details table to dataColumn2.
dataColumn2 =
dataSet.Tables["Details"].Columns["OrderID"];
At this point, dataColumn1 represents the OrderID column from the Orders
table, and dataColumn2 represents the OrderID column from the Order Details
table. They are the key components of a relationship between the two tables,
and you will use them to create a DataRelation object.
25. Create a new DataRelation object and initialize it with the two data
columns. The first parameter to the constructor is the name of the relation,
passed as a string. You may name the relationship anything you like; for
this exercise name the relationship OrdersToDetails.
DataRelation dataRelation =
new System.Data.DataRelation(
"OrdersToDetails",
dataColumn1,
26. Add the Data Relation to the Relations table held by the DataSet.
dataSet.Relations.Add(dataRelation);
The ADO.NET Object Model
C# Professional Skills Development 16-29
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Note that this is a collection of DataRelation objects. The DataSet can model
many relationships among its various tables.
27. Bind the DefaultViewManager of the DataSet to the DataSource of the
DataGrid. You cant use the Default View of the table, because now you
have two tables.
dgOrders.DataSource = dataSet.DefaultViewManager;
28. The DataGrid now has a view on two tables, and it needs to know which is
the master table. You tell it by setting its DataMember property.
dgOrders.DataMember = "Orders"; // parent table
29. Run the application. The DataGrid opens as previously shown, but now
there is a + symbol next to each order, as shown in Figure 23.
Figure 23. The DataGrid representing the master table.
30. Click on the + symbol next to the fourth record. Quick. A row opens below
with a link to the Order Details table, as shown in Figure 24. The link is
labeled with the name of the relationship you created in Step 25.
ADO.NET
16-30 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 24. The DataGrid with a link to the details records.
31. Click on the link. The DataGrid is redrawn with the contents of the details
record, as shown in Figure 25. Look closely at the output. The row whose
detail you are viewing is shown at the top: OrderID 10285. The fields are
summarized and you can scroll through them with the arrow at the end of
the list. You can return to the first display by clicking the white arrow in
the upper right corner.
Figure 25. The details record.
The ADO.NET Object Model
C# Professional Skills Development 16-31
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
The goal of many real-world programs is to retrieve, display, and
update data from a relational database.
Relational Databases are organized into tables; each table consists of
records (rows) and fields (columns). Relationships among tables are
managed through common fields (foreign keys).
Normalizing tables removes redundant data and reduces the danger of
data corruption.
A Primary key uniquely identifies a record in a database table; a
Foreign key is a primary key from a different table and is used to
establish a relationship between the two tables.
A Foreign Key Constraint enforces that you create valid records and
disallows invalid deletions and updates based on a foreign-key
relationship. Declarative Referential Integrity uses constraints on the
database to avoid data corruption.
You interact with and manipulate your database using Structured
Query Language (SQL). SQL is a declarative language.
The principal classes in the ADO.NET object model are the
Connection and Command objects and the DataAdapter. These three
work together to interact with the DataSet.
The job of the Connection object is to encapsulate a connection to the
back-end database, while the job of the Command object is to
encapsulate a SQL command.
The job of the DataAdapter is to decouple the DataSet from the details
of interacting with the database.
The DataSet represents a subset of the database, complete with
multiple tables, and the relationship among the tables. DataSets are
disconnected from the database.
A DataSet consists of a DataRelation collection and a DataTable
collection. The DataTable collection holds DataTable objects, which
in turn consist of a DataRow collection, a DataColumn collection, and
a Constraint collection.
ADO.NET currently offers two managed providers: SQL and
OLE DB.
You can bind to a data control using the DataSource property.
The DataGrid enables you to display the DataRelation encapsulated in
a DataRelation object.
ADO.NET
16-32 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
(Review questions and answers on the following pages.)
The ADO.NET Object Model
C# Professional Skills Development 16-33
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. What does it mean to say that ADO.NET offers a disconnected data set?
2. What is a foreign key constraint?
3. What is normalization?
4. What are DataAdapter objects for?
5. What is the difference between the OLE DB provider and the SQL
provider?
6. Why do you pass two DataColumn objects to the constructor of the
DataRelation object?
ADO.NET
16-34 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. What does it mean to say that ADO.NET offers a disconnected data set?
The use model with DataSets is that they connect to the
Database to be filled and to update the database, but they are
otherwise disconnected. Most of the time that you are interacting
with the DataSet it is disconnected from the backing database.
2. What is a Foreign Key Constraint?
A foreign key constraint states that there is a relationship
between a primary key in one table and that same column, acting
as a foreign key, in another, and that the two tables are
constrained in the way they add, update or delete records based
on that relationship.
3. What is normalization?
Normalization is the process of removing redundant data from
tables. The Orders table does not hold the phone number for each
customer for each order; rather it holds a customerID and the
phone number and other customer-specific data is factored out
into the appropriate (customer) table.
4. What are DataAdapter objects for?
DataAdapter objects mediate between a DataSet (which knows
about the structure of the data) and the underlying database.
5. What is the difference between the OLE DB provider and the SQL
provider?
The SQL Data Provider is optimized for Microsoft SQL Server. To
interact with other data sources, use the OLE DB data provider.
6. Why do you pass two DataColumn objects to the constructor of the
DataRelation object?
The relationship between two tables will be created by a foreign
key relationship. This typically involves matching a column in
each of the two tables.
The ADO.NET Object Model
C# Professional Skills Development 16-35
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 16:
ADO.NET
Lab 16:
ADO.NET
16-36 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 16 Overview
In this lab youll learn to manipulate databases using the ADO.NET object
model.
To complete this lab, youll need to work through two exercises:
Displaying Customers
Displaying Relationships
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-bystep

instructions.
Displaying Customers
C# Professional Skills Development 16-37
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Displaying Customers
Objective
In this exercise, access the database to retrieve the list of customers, and youl
l
display that list in a list box within a Windows application.
Things to Consider
You cannot display any fields you dont retrieve from the database.
There is no need to retrieve fields you wont display.
The list box will display a simple string; you must assemble that string
from the data you retrieve.
Youll need to know the server name, userID, and password for the
SQL Server account youre using.
Step-by-Step Instructions
1. Create a new Windows application named Displaying Customers.
2. Drag a list box onto the form and name it lbCustomers.
3. Size the list box and form as shown in Figure 26.
Figure 26. Sizing the list box and form.
4. Double-click on the form to open the Form1_Load method.
5. Add a connection string in it. Be sure to use the correct server name,
userID, and password for your SQL Server account.
Lab 16:
ADO.NET
16-38 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
string strConnection =
"server=YourServer; uid=sa; pwd=YourPassword;
database=northwind";
6. Create the command string.
string strCommand =
"Select CompanyName, ContactName, ContactTitle from
Customers";
7. Create the data adapter.
SqlDataAdapter dataAdapter =
new SqlDataAdapter(strCommand, strConnection);
8. Create the data set.
DataSet dataSet = new DataSet();
9. Fill the DataSet object.
dataAdapter.Fill(dataSet,"Customers");
10. Get the first (and only) table from the DataSet.
DataTable dataTable = dataSet.Tables[0];
11. For each row in the table, fill the list.
Displaying Customers
C# Professional Skills Development 16-39
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
foreach (DataRow dataRow in dataTable.Rows)
{
lbCustomers.Items.Add(dataRow["CompanyName"] +
", Contact: " + dataRow["ContactName"] + " (" +
dataRow["ContactTitle"] + ")" );
}
12. Compile and run the application as shown in Figure 27.
Figure 27. Displaying customers.
Lab 16:
ADO.NET
16-40 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Displaying Relationships
Objective
In this exercise, youll see how table relationships can be reflected within
DataSet object, and how you can use a DataGrid object to display these
relationships.
Step-by-Step Instructions
1. Create a new Windows Forms project named Data Relations.
2. Drag a data grid onto the form and size it as shown in Figure 28.
Figure 28. Sizing the list box and form.
3. Name the data grid dgRelations.
4. Right-click on the form and select View Code.
5. Add a using statement for System.Data.SqlClient.
using System.Data.SqlClient;
6. Add private member variables for the connection, as well as the command
object, the data set, and the data adapter.
Displaying Relationships
C# Professional Skills Development 16-41
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
private SqlConnection connection;
private DataSet dataSet;
private SqlCommand command;
private SqlDataAdapter dataAdapter;
7. Add a second command object and a second dataAdapter.
private SqlCommand command2;
private SqlDataAdapter dataAdapter2;
8. Locate the Form1_Load method. In it, create the connection object and
open it. Be sure to use the correct server name, userID, and password for
your SQL Server account.
string strConnection =
"server=YourServer; uid=sa; pwd=YourPassword;
database=northwind";
9. Create a new connection object using the connection string.
connection = new
System.Data.SqlClient.SqlConnection(connectionString);
10. Open the connection.
connection.Open();
11. Create the DataSet and set the CaseSensitive property.
dataSet = new System.Data.DataSet();
dataSet.CaseSensitive=true;
12. Create the SqlCommand object and assign the connection.
command = new System.Data.SqlClient.SqlCommand();
command.Connection=connection;
Lab 16:
ADO.NET
16-42 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
13. Set the command objects command text to select all the fields from the
orders table.
command.CommandText = "Select * from Orders";
14. Create the DataAdapter object and pass in the SQL Command object and
establish the table mapping to the orders table.
dataAdapter =
new System.Data.SqlClient.SqlDataAdapter();
dataAdapter.SelectCommand= command;
dataAdapter.TableMappings.Add("Table","Orders");
15. Tell the DataAdapter object to fill the DataSet.
dataAdapter.Fill(dataSet);
16. Create a second command object, reusing the connection.
command2 = new System.Data.SqlClient.SqlCommand();
command2.Connection=connection;
17. Set the command text to get all fields from the Customers table.
command2.CommandText = "Select * from Customers";
18. Instantiate the second data adapter.
dataAdapter2 = new System.Data.SqlClient.SqlDataAdapter();
19. Set the second data adapters Select command to the second command
object.
dataAdapter2.SelectCommand= command2;
20. Map the new table to the Customers table.
Displaying Relationships
C# Professional Skills Development 16-43
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
dataAdapter2.TableMappings.Add("Table","Customers");
21. Fill the dataset with the second dataAdapter.
dataAdapter2.Fill(dataSet);
22. Declare two data column objects.
DataColumn dataColumn1;
DataColumn dataColumn2;
23. Create a customerTable DataTable object and initialize it with the
Customers table from the DataSet.
DataTable customerTable = dataSet.Tables["Customers"];
24. Assign the CustomerID column in the customerTable DataTable object to
dataColumn1.
dataColumn1 = customerTable.Columns["CustomerID"];
25. Assign the customerID column to dataColumn2 in the Orders table.
dataColumn2 =
dataSet.Tables["Orders"].Columns["CustomerID"];
26. Instantiate a DataRelation object. Name it CustomerToOrders and pass in
the two dataColumn objects.
DataRelation dataRelation =
new System.Data.DataRelation(
"CustomersToOrders",
dataColumn1,
dataColumn2);
27. Add the DataRelation object to the Relations collection of the DataSet.
Lab 16:
ADO.NET
16-44 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
dataSet.Relations.Add(dataRelation);
28. Set the DataSource for the DataGrid to the Default View Manager of the
DataSet.
dgRelations.DataSource = dataSet.DefaultViewManager;
29. Set the DataMember property of the DataGrid to the Customers table.
dgRelations.DataMember = "Customers";
30. Compile and run the application as shown in Figure 29.
Figure 29. Displaying relationships.
Updating Databases with ADO.NET
C# Professional Skills Development 17-1
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Updating
Databases with
ADO.NET
Objectives
Update the database from changes made in the user interface.
Update multiple tables in the database based on user input.
Use transactions to support database integrity.
Implement transactions with stored procedures.
Implement transactions with the Connection object.
Use the ACID test to understand transactions.
Updating Databases with ADO.NET
17-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Updating Data
Until now, youve displayed data in various controls but have not modified the
data in the database. In many real-world applications, of course, you will want
to allow your user to make a purchase, change inventory, update records, or
otherwise manipulate your data.
ADO.NET provides extensive support for updating data. This can be done
quite simply if your application only allows one person at a time to access your

data, or it can be quite complex if you need to allow many different people
(perhaps in different locations) to update the data at the same time.
The steps for a simple update are straightforward:
1. Fill the DataSet from the database.
2. Display the data from the DataSet.
3. Allow the user to indicate the changes to be made.
4. Update the data in the DataSet.
5. Update the data in the database.
Try It Out!
The easiest way to demonstrate how to update data in the database is to do so
in a simple example program. This demonstration program involves a fairly
complex user interface, as shown in Figure 1. To make it easier for you to
focus on the ADO.NET features rather than on the UI features, weve supplied
a starting program, but you are free to write this from scratch if you prefer.
See DataUpdate
Working1.sln
Updating Data
C# Professional Skills Development 17-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 1. The user interface.
1. Open the project DataUpdateStarter.
NOTE Weve provided a starter project (DataUpdateStarter) as well as a
completed version (DataUpdateWorking1). As you progress
through the chapter youll continue to modify your program.
Weve provided files showing the completed interim steps and the
names of these files will be shown in the left-hand column.
2. Notice that the starter program includes the using statement:
using System.Data.SqlClient;
3. Notice that the starter program declares three private member variables: a
DataAdapter, DataSet, and DataTable.
private SqlDataAdapter dataAdapter;
private DataSet dataSet;
private DataTable dataTable;
In this exercise youll add event handlers for the form loading and for
changing the selection in the list box, as well as for clicking the Update butto
n.
Updating Databases with ADO.NET
17-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
4. Double-click on the form to open the event handler Form1_Load. Youll
set up your form in two steps.
Step 1 is to fill the list box, which youll delegate to a helper method, FillLB()
.
Factor this out to a separate method, because youll also want to fill the list
box after you process the Update buttons click event.
Step 2 in the Form1_Load event is to add True and False to the drop-down list
for the discontinued field (cbDiscontinued).
private void Form1_Load(object sender, System.EventArgs e)
{
// call method to fill list box
FillLB();
// add true/false to drop down
cbDiscontinued.Items.AddRange(
new string[2] {"True", "False"});
}
5. In FillLB youll create connection and command strings and use them to
create a DataAdapter.
private void FillLB()
{
// create the connection string
string strConnection = "server=YourServer; uid=sa;
pwd=YourPW; database=northwind";
// create the command string
string strCommand = "Select * from Products";
// create the data set command object and dataset
dataAdapter =
new SqlDataAdapter(strCommand, strConnection);
6. Complete the FillLB method by creating a new DataSet and filling it from
the DataAdapter. Extract the DataTable and use it to fill the list box.
Updating Data
C# Professional Skills Development 17-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
dataSet = new DataSet();
// fill the dataset
dataAdapter.Fill(dataSet,"Products");
// get the table
dataTable = dataSet.Tables[0];
// first, empty the list box
lbProducts.Items.Clear();
// for each row in the table, fill the list
foreach (DataRow dataRow in dataTable.Rows)
{
lbProducts.Items.Add(dataRow["ProductName"]);
}
7. Run the application. The list box should be filled and the Discontinued
drop-down list should be filled with the values True and False. Clicking on
a value in the list box has no effect, nor does clicking on Update (see
Figure 2).
Updating Databases with ADO.NET
17-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 2. Testing the Form Load event.
8. When the user clicks on an entry in the list box (e.g., Teatime Chocolate
Biscuits), youd like to fill the text fields with the data that corresponds to
that record. Return to the designer and double-click on the list box. This
creates an event handler for the list box. The default event handler is
SelectedIndexChanged, which happens to be just what you want.
9. Get the DataRow from the DataTable that corresponds to the selected
index.
DataRow selectedRow =
dataTable.Rows[lbProducts.SelectedIndex];
The SelectedIndex property of the list box is the 0-based offset of the row the
user clicks on. You use that as an offset into the Rows collection of the table,

getting back the corresponding row in the data and assigning that DataRow to
the temporary variable selectedRow.
10. The column name can be used as an offset into the selected row to extract
the value for that column. You can, assign the value of the ProductName
column to the text property of the txtProductName text box.
txtProductName.Text =
selectedRow["ProductName"].ToString();
Updating Data
C# Professional Skills Development 17-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
11. Make this kind of assignment for all the remaining text fields.
txtProductID.Text =
selectedRow["ProductID"].ToString();
txtSupplierID.Text =
selectedRow["SupplierID"].ToString();
txtCategoryID.Text =
selectedRow["CategoryID"].ToString();
txtQuantityPerUnit.Text =
selectedRow["QuantityPerUnit"].ToString();
txtUnitPrice.Text =
selectedRow["UnitPrice"].ToString();
txtUnitsInStock.Text =
selectedRow["UnitsInStock"].ToString();
txtUnitsOnOrder.Text =
selectedRow["UnitsOnOrder"].ToString();
txtReorderLevel.Text =
selectedRow["ReorderLevel"].ToString();
12. You now need to set the value in the cbDiscontinued drop-down list. This
one is tricky. You want to set the SelectedIndex property of the drop-down
to 0 if the value is true and to 1 if the value is false. Unfortunately, the
value in the column is 0 for false and 1 for true, so, you must flip these.
Fortunately, the ToString value of 0 is false, so you can use the ternary
operator to test it.
cbDiscontinued.SelectedIndex =
selectedRow["Discontinued"].ToString() == "False" ?
1 : 0;
The logic of this string is this: assess whether the ToString value of the
Discontinued column within the selected row is equal to False; if so, return 1,
otherwise return 0. Whatever value is returned, assign that value to the
SelectedIndex property of the cbDiscontinued control.
13. Run the program. You should now be able to click on a row and see the
corresponding data in the text fields, as shown in Figure 3. In this figure,
you can also see the row in the database from which the data is drawn to
fill the text boxes. You can see that the 0 in the Discontinued field has
appropriately been translated to False in the cbDiscontinued field.
Updating Databases with ADO.NET
17-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 3. Filling the text fields.
14. The next task is to handle the Update button click event. Youll want to
formulate an update statement based on the values in the text boxes when
the button is clicked. Return to the designer and double-click on the
Update button to open the btnUpdate_Click event handler.
15. The first step is to write the message Updating Teatime Chocolate
Biscuits to the lblMsg label. To do so, you need the product name of the
currently selected row. To get that, youll extract the target row, just as
you did in Step 9.
DataRow targetRow =
dataTable.Rows[lbProducts.SelectedIndex];
16. Update the text field of the message and call the static method
Application.DoEvents. This method ensures that the UI is updated even if
the program is busy processing the next steps in the method.
lbMsg.Text =
"Updating " +
targetRow["ProductName"];
Application.DoEvents();
Updating Data
C# Professional Skills Development 17-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
17. Having updated the user interface, youre ready to update the database.
You will need the value for each of the text fields.
To simplify working with the value for the cbDiscontinued drop-down list,
first extract that value as an integer, setting the int to 0 if the value is fal
se (the
selected index is 1), otherwise setting it to 1.
int dc = cbDiscontinued.SelectedIndex == 1 ? 0 : 1;
18. Create the update statement, extracting values from the various text fields.

string cmd = "update Products set productName = '" +
txtProductName.Text.Trim() + "', supplierID = " +
txtSupplierID.Text + ", CategoryID = " +
txtCategoryID.Text + ", QuantityPerUnit = '" +
txtQuantityPerUnit.Text.Trim() + "', UnitPrice = " +
txtUnitPrice.Text + ", UnitsInStock = " +
txtUnitsInStock.Text + ", UnitsOnOrder = " +
txtUnitsOnOrder.Text + ", ReorderLevel = " +
txtReorderLevel.Text + ", Discontinued = " +
dc.ToString() +
" where productID = " + txtProductID.Text.Trim();
19. Update the database. To do so, pass your command string to a helper
method youll write in Step 22.
UpdateDB(cmd);
20. With the database updated, update the user interface.
lbMsg.Text = "Updated.";
Application.DoEvents();
21. Call FillLB to refill the list box with the newly updated data and close the

method.
FillLB();
}
Updating Databases with ADO.NET
17-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
22. Implement the UpdateDB method that you called in Step 19. This method
creates a connection to the database and then invokes the command string
passed in as a parameter. To begin, create the connection string and
instantiate a Connection object with the connection string.
private void UpdateDB(string cmd)
{
string connectionString =
"server=YourServer; uid=sa; pwd=YourPW;
database=Northwind";
SqlConnection connection =
new SqlConnection(connectionString);
23. Open the connection.
connection.Open();
24. Create a SQLCommand object to encapsulate the update command. Set the
commands Connection property to the Connection object you created in
Step 22 and set its CommandText property to the command text passed in
as a parameter.
SqlCommand command = new SqlCommand();
command.Connection=connection;
command.CommandText=cmd;
25. Execute the ExecuteNonQuery method of the command. This is a method
specifically designed to be used when your command will not return any
records. That is, you are not selecting records, you are updating the
database. This method is very efficient.
command.ExecuteNonQuery();
} // end UpdateDB
26. Run the application and scroll to Teatime Chocolate Biscuits. Set the Units
In Stock to 24 and click Update. Open the SQL Manager and check the
record; youll find it has been updated. The list box is updated as well.
Updating Data
C# Professional Skills Development 17-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Scroll back to Teatime Chocolate Biscuits and click on it; youll see the
new Units In Stock field has been updated, as shown in Figure 4.
Figure 4. Updating the database.
Updating Multiple Tables
In the previous example the CategoryID displayed as an integer. For example,
the CategoryID for Teatime Chocolate Biscuits is 3. The value 3, which is
retrieved from the Products table, is actually a foreign key into the category.
You can see that relationship in a diagram of the two classes. A relationship is

drawn between the two classes, as shown in Figure 5. If you hover over the
line between the two boxes, the relationship is identified as
FK_Products_Categoriesthat is, the foreign key relationship is established
between the two tables on the CategoryID field.
Updating Databases with ADO.NET
17-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 5. The relationship between the Products and Categories tables.
Open the Categories table in SQL Enterprise Manager, as shown in Figure 6.
You can see that Category 3 is Confections. The second column provides a
description, and presumably the Picture column offers a digital picture to be
displayed in your user interface.
Figure 6. The Categories table.
A second lookup table like this presents special challenges when updating the
records.
Updating Data
C# Professional Skills Development 17-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
To see how the second table affects the update of the database, youll return to
the previous example but this time youll display the CategoryName in the
Category text field, rather than the CategoryID.
NOTE This is somewhat artificial; in a real project youd probably have a
drop-down list of types to choose among, and then a second UI
interface (perhaps a dialog box) to allow the user to modify the
categories; but here youll simplify and make the UI a text box on
your Details page.
1. Reopen the previous example.
2. Change the CategoryID prompt to say Category and change the name of
the text field from txtCategoryID to txtCategory, as shown in Figure 7.
See DataUpdate
Working2.sln
Updating Databases with ADO.NET
17-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 7. Change the txtCategory field.
3. Modify the FillLB method. Change the command string to join the
Categories table to the Products table. This will get the Categories fields as
well as the Products fields.
string strCommand =
"Select * from Products p
join Categories c on p.CategoryID = c.CategoryID";
4. Modify the code in lbProducts_SelectedIndexChanged. Set the text for
txtCategory to the column Category Name. You get the CategoryName
field from the Categories table.
Updating Data
C# Professional Skills Development 17-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
txtCategory.Text =
selectedRow["CategoryName"].ToString();
5. Modify the update statement in btnUpdate_Click to remove the update to
the CategoryID field.
string cmd = "update Products set productName = '" +
txtProductName.Text.Trim() + "', supplierID = " +
txtSupplierID.Text + ", QuantityPerUnit = '" +
txtQuantityPerUnit.Text.Trim() + "', UnitPrice = " +
txtUnitPrice.Text + ", UnitsInStock = " +
txtUnitsInStock.Text + ", UnitsOnOrder = " +
txtUnitsOnOrder.Text + ", ReorderLevel = " +
txtReorderLevel.Text + ", Discontinued = " +
dc.ToString() +
" where productID = " + txtProductID.Text.Trim();
UpdateDB(cmd);
6. Add a second update statement, this time to update the Categories table.
cmd = "Update Categories set CategoryName = '" +
txtCategory.Text.Trim() +
"' where CategoryID = " + catID;
UpdateDB(cmd);
7. Run the application. Navigate to Teatime Chocolate Biscuits. Look at the
Category; it is set to Confections (which is consistent with the CategoryID
of 3 in the previous version).
8. You can now modify the category. Change it from Confections to Yummies
and click Update, as shown in Figure 8.
Updating Databases with ADO.NET
17-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 8. Changing the category.
9. Open the Categories table in SQL Server Enterprise Manager. Notice that
the CategoryName for CategoryID 3 has been changed to Yummies, as
shown in Figure 9.
Figure 9. The underlying database is updated.
Updating Data
C# Professional Skills Development 17-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Danger Will Robinson!
While the change to the code worked well, you have exposed your program to
a serious problem. What happens if the first update cannot be performed for
some reason? The way the code is written, the second update (to Categories)
may well occur even if the first update (to the other fields) does not. That may

be highly undesirable.
This brings us to the issue of transactions.
Updating Databases with ADO.NET
17-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Transactions
The idea behind a transaction is simple: you want all of your changes to take
effect (updating the Products table and updating the Categories table) or you
want none of them to take effect.
In the previous example the danger is small; it really doesnt matter that much
if the Categories table is updated, even if the Products table is not. The
canonical example of where transactions are critical, however, is this: suppose
you are writing a banking application. One of the actions youll permit is a
transfer of funds. To transfer money from my checking to my savings account,
you will take these steps:
1. Issue a database command to add the money into my savings account.
2. Issue a second database command to remove the money from my checking
account.
You cant do them simultaneously; there are two different tables to update
(Checking and Savings). If there is a problem between Steps 1 and 2, someone
will be very unhappy. If you add the money to my savings, but never add the
money to my checking, the bank loses (Bank error in your favor, collect
$50). If you reverse the steps, you run the risk of removing the money from
my savings account but never adding it to my checking account; youve just
committed a felony.
To solve this, you need a way to say that these two steps are really one
transaction, and that is where transaction support comes in.
The ACID Test
Database engineers talk about the ACID test for transactions. Transactions
must be atomic, consistent, isolated, and durable.
Atomic: It is not legal to implement only part of a transaction; the
transaction itself is the atomic unit and is indivisible.
Consistent: The database must be in a consistent state after the
transaction completes. It is legal for the database to be in an
inconsistent state during the transaction (e.g., your savings account has
diminished but your checking account has not yet received the funds)
but when the transaction is complete, the database must be in a
consistent state (the accounts balance).
Isolated: It must be possible to have simultaneous transactions that do
not interfere with one another. One transaction cannot depend on the
completion of another; if so, they are really a single transaction.
Transactions
C# Professional Skills Development 17-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Durable: Once the transaction is committed, the effect must be
permanent. It is legal to roll back a transaction before it is committed,
but once committed the transaction cannot be undone without a second
action (usually, a second transaction). This allows for the creation of
audit trails.
Two Flavors
Transaction support in .NET comes in two flavors.
SQL Server (and other relational databases) offers transaction support
within stored procedures (sprocs).
The .NET Connection object offers support for transactions. To use
transactions with the Connection object, you call the instance method
BeginTransaction, which returns an object of type SqlTransaction (or
OLEDBTransaction).
Stored Procedure Transaction Support
A stored procedure is the equivalent of a function that runs SQL statements.
Stored procedures can have parameters, and those parameters act as local
values within the stored procedure. You write your stored procedure within the
database itself.
To call a stored procedure in C#, follow these steps:
1. Create the stored procedure in SQL Server (or whatever database you are
using).
2. Create a Command object.
3. Set the Command objects text to the name of the stored procedure.
4. Set the Command objects type to the enumerated value
Commandtype.StoredProcedure.
5. Call the method Add method on the Parameters collection of the Command
object. This returns an object of type Parameter.
6. Set the direction property of the Parameter object to one of the enumerated
values Input, Output, or InputOutput.
7. Set the value property of the Parameter to the value you want to pass as a
parameter to the stored procedure.
Updating Databases with ADO.NET
17-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
To see how to use stored procedures and SQL supported transactions, youll
modify the previous example to use a stored procedure with transactions to
update the Products and Categories tables.
1. Create a stored procedure for updating the Product and Categories
tables. Open the SQL Enterprise Manager and navigate to the
Northwind database. Right-click on the Stored Procedure and choose
New Stored Procedure as shown in Figure 10.
Figure 10. Creating a new stored procedure.
2. Call your procedure spUpdateProductsAndCategories.
See DataUpdate
Working3.sln
Transactions
C# Professional Skills Development 17-21
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
CREATE PROCEDURE spUpdateProductsAndCategories
3. Give your procedure parameters for the values you want to set.
@ProductID int,
@ProductName varChar(40),
@SupplierID int,
@QuantityPerUnit varChar(20),
@UnitPrice money,
@UnitsInStock int,
@UnitsOnOrder int,
@ReorderLevel int,
@Discontinued int,
@CategoryName varChar(5000),
@CategoryID int
4. Begin the stored procedure by opening a transaction.
as
begin transaction
5. Update the products table.
Update products set
productName = @ProductName,
SupplierID = @supplierID,
QuantityPerUnit = @QuantityPerUnit,
UnitPrice = @UnitPrice,
UnitsInStock = @UnitsInStock,
UnitsOnOrder = @UnitsOnOrder,
ReorderLevel = @ReorderLevel,
Discontinued = @Discontinued
where productID = @productID
6. Check for an error. If there is an error, go to an error handler that will
roll back the transaction.
Updating Databases with ADO.NET
17-22 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
if @@error <> 0 goto ErrorHandler
7. If there is no error, update the Categories table.
Update Categories set CategoryName = @CategoryName where
CategoryID = @CategoryID
8. Check for an error. If there is an error, go to an error handler that will
roll back the transaction. If there is no error, commit the transaction
and exit the stored procedure.
if @@Error <> 0 goto ErrorHandler
commit transaction
return
9. Write the error handler that will roll back the transaction.
ErrorHandler:
rollback transaction
return
10. Test the syntax of the stored procedure by clicking the Check Syntax
button. A dialog box showing that the syntax check was successful
should appear, as shown in Figure 11. If not, correct the error and try
again.
Transactions
C# Professional Skills Development 17-23
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 11. Checking the syntax.
11. Click OK to close the syntax check dialog box and then click OK to
close the New Stored Procedure dialog box.
With your stored procedure in place, you are ready to modify your code.
Absolutely nothing in your code changes, except for how you update the
database.
12. Delete the UpdateDB method.
13. Modify the btnUpdate_Click method. Leave the code that creates the
connection string and Connection object and the Command object.
Leave the code that assigns the connection to the Command object, but
change the command text.
command.CommandText=
"spUpdateProductsAndCategories";
You have set the command text to the name of the stored procedure.
Updating Databases with ADO.NET
17-24 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
14. Previously you did not have to set the CommandType, because the
default is text, and that is what you wanted. Now, however, you must
set this property to indicate that you are not passing in a command but
invoking a stored procedure.
command.CommandType =
CommandType.StoredProcedure;
15. Create an instance of SqlParameter.
System.Data.SqlClient.SqlParameter param;
16. Assign to that param instance the parameter returned by calling the
Add method on the Parameters collection of the Command object.
Pass two arguments into the Add method: the name of the first
parameter (@ProductID) and the enumerated type of that parameter
(SqlDbType.Int).
param =
command.Parameters.Add("@ProductID",SqlDbType.Int);
17. Set the Direction and Value properties of the param object.
param.Direction =
ParameterDirection.Input;
param.Value =
txtProductID.Text.Trim();
18. Create a second parameter by calling command.Parameters.Add, and
pass in the name and type of the second parameter. Assign the
reference returned to the local param instance. Assign the Direction
and Value properties.
param =
command.Parameters.Add(
"@ProductName",SqlDbType.VarChar,40);
param.Direction =
ParameterDirection.Input;
param.Value =
txtProductName.Text.Trim();
Transactions
C# Professional Skills Development 17-25
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
NOTE You may be wondering why it is okay to assign this second
parameter to the same instance (params). Was the first parameter
lost? Remember that params is just a reference to the parameter
that was added to the Parameters collection. The original is still in
the collection; you are just using this local variable to refer to the
new one so that you can set properties on it.
19. Do the same for the remaining parameters.
param =
command.Parameters.Add(
"@SupplierID",SqlDbType.Int);
param.Direction =
ParameterDirection.Input;
param.Value =
txtSupplierID.Text.Trim();
param =
command.Parameters.Add(
"@QuantityPerUnit",SqlDbType.VarChar,20);
param.Direction =
ParameterDirection.Input;
param.Value =
txtQuantityPerUnit.Text.Trim();
param =
command.Parameters.Add(
"@UnitPrice",SqlDbType.Money);
param.Direction =
ParameterDirection.Input;
param.Value =
txtUnitPrice.Text.Trim();
param =
command.Parameters.Add(
"@UnitsInStock",SqlDbType.Int);
param.Direction =
ParameterDirection.Input;
param.Value =
Updating Databases with ADO.NET
17-26 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
txtUnitsInStock.Text.Trim();
param =
command.Parameters.Add(
"@UnitsOnOrder",SqlDbType.Int);
param.Direction =
ParameterDirection.Input;
param.Value =
txtUnitsOnOrder.Text.Trim();
param =
command.Parameters.Add(
"@ReorderLevel",SqlDbType.Int);
param.Direction =
ParameterDirection.Input;
param.Value =
txtReorderLevel.Text.Trim();
param =
command.Parameters.Add(
"@Discontinued",SqlDbType.Int);
param.Direction =
ParameterDirection.Input;
param.Value =
cbDiscontinued.SelectedIndex == 1 ? 0 : 1;
param =
command.Parameters.Add(
"@CategoryName",SqlDbType.VarChar,15);
param.Direction =
ParameterDirection.Input;
param.Value =
txtCategory.Text.Trim();
20. You need to pass the category ID to the stored procedure. You do that
by getting the selected row and extracting the category ID. You can
then use that to create your final parameter.
Transactions
C# Professional Skills Development 17-27
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
DataRow targetRow =
dataTable.Rows[lbProducts.SelectedIndex];
int catID =
(int) targetRow["CategoryID"];
param =
command.Parameters.Add("@CategoryID",SqlDbType.Int);
param.Direction =
ParameterDirection.Input;
param.Value = catID;
21. With all the parameters in place, use ExecuteNonQuery to invoke the
stored procedure. Then update the user interface and refill the list box
with the newly updated data.
command.ExecuteNonQuery();
// inform the user
lbMsg.Text = "Updated.";
Application.DoEvents();
FillLB();
} // end btnUpdate_click
22. Run the application. You have delegated responsibility for the
transaction support to the database, and the stored procedure will
ensure that either both tables are updated or neither table is updated.
Using Connection Transactions
The Connection object provides an alternative to database transaction support.
This frees you from the limitations and specifics of the particular database you

are using, which makes it easier to change back-end databases later.
To convert to connection-based transactions, youll invoke the
BeginTransaction method of the Connection object. This returns a reference to
a SqlTransaction object. You can then assign that object to the Command
objects Transaction property.
You will then create a try block, and within that block youll call the various
stored procedures to update the records. If the calls to ExecuteNonQuery throw
an exception, youll roll back the transaction. Otherwise, once all your calls to
Updating Databases with ADO.NET
17-28 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
stored procedures complete without an exception, you will commit the
transaction.
This approach has great advantages. If nothing else, you can catch the
exceptions thrown and display information about what went wrong.
Try It Out!
To see how to use connection-based transactions, youll recreate the previous
example using stored procedures without transaction support, and youll add
transaction support from the Connection object.
1. Create a new stored procedure named spUpdateProducts.
CREATE PROCEDURE spUpdateProducts
@ProductID int,
@ProductName varChar(40),
@SupplierID int,
@QuantityPerUnit varChar(20),
@UnitPrice money,
@UnitsInStock int,
@UnitsOnOrder int,
@ReorderLevel int,
@Discontinued int
as
Update products set
productName = @ProductName,
SupplierID = @supplierID,
QuantityPerUnit = @QuantityPerUnit,
UnitPrice = @UnitPrice,
UnitsInStock = @UnitsInStock,
UnitsOnOrder = @UnitsOnOrder,
ReorderLevel = @ReorderLevel,
Discontinued = @Discontinued
where productID = @productID
Notice that this stored procedure does not have transaction support and updates
only one table. Youll use this sproc to update the Products table and youll use
a simple SQL command to update the Categories table.
See DataUpdate
Working4.sln
Transactions
C# Professional Skills Development 17-29
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
2. Reopen the project used in the previous example.
3. Once again, create the connection string and Connection object, and open
the Connection object.
string connectionString =
"server=YourServer; uid=sa; pwd=YourPW;
database=Northwind";
SqlConnection connection =
new SqlConnection(connectionString);
connection.Open();
4. Create the Command object.
System.Data.SqlClient.SqlCommand command =
new System.Data.SqlClient.SqlCommand();
5. Call BeginTransaction on the Connection object. This begins the
transaction and returns an instance of SqlClient.SqlTransaction that
encapsulates the transaction as an object.
SqlTransaction transaction =
connection.BeginTransaction();
6. Set the Transaction property of the Command object to the Transaction
object returned by BeginTransaction.
command.Transaction = transaction;
7. Set the Connection property of the Command object to the connection
youre using.
command.Connection = connection;
8. The Command object is now ready to participate in the transaction that is
being mediated by the Connection object. Create a try block in which you
will do all the updating. Block out the try block and create the Catch block
to roll back the transaction if any errors are reported.
Updating Databases with ADO.NET
17-30 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
try
{
}
catch (Exception ex)
{
MessageBox.Show(
"Exception thrown when updating db: {0}",
ex.Message);
transaction.Rollback();
}
9. Fill in the Try block by setting the CommandText property of the
Command object to the name of the stored procedure you want to invoke.
command.CommandText= "spUpdateProducts";
10. Set the CommandType property to StoredProcedure.
command.CommandType = CommandType.StoredProcedure;
11. You are ready to create the parameters, much as you did in the previous
example.
param =
command.Parameters.Add("@ProductID",SqlDbType.Int);
param.Direction =
ParameterDirection.Input;
param.Value =
txtProductID.Text.Trim();
param =
command.Parameters.Add(
"@ProductName",SqlDbType.VarChar,40);
param.Direction =
ParameterDirection.Input;
param.Value =
txtProductName.Text.Trim();
Transactions
C# Professional Skills Development 17-31
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
param =
command.Parameters.Add(
"@SupplierID",SqlDbType.Int);
param.Direction =
ParameterDirection.Input;
param.Value =
txtSupplierID.Text.Trim();
param =
command.Parameters.Add(
"@QuantityPerUnit",SqlDbType.VarChar,20);
param.Direction =
ParameterDirection.Input;
param.Value =
txtQuantityPerUnit.Text.Trim();
param =
command.Parameters.Add(
"@UnitPrice",SqlDbType.Money);
param.Direction =
ParameterDirection.Input;
param.Value =
txtUnitPrice.Text.Trim();
param =
command.Parameters.Add(
"@UnitsInStock",SqlDbType.Int);
param.Direction =
ParameterDirection.Input;
param.Value =
txtUnitsInStock.Text.Trim();
param =
command.Parameters.Add(
"@UnitsOnOrder",SqlDbType.Int);
param.Direction =
ParameterDirection.Input;
Updating Databases with ADO.NET
17-32 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
param.Value =
txtUnitsOnOrder.Text.Trim();
param =
command.Parameters.Add(
"@ReorderLevel",SqlDbType.Int);
param.Direction =
ParameterDirection.Input;
param.Value =
txtReorderLevel.Text.Trim();
param =
command.Parameters.Add(
"@Discontinued",SqlDbType.Int);
param.Direction =
ParameterDirection.Input;
param.Value =
cbDiscontinued.SelectedIndex == 1 ? 0 : 1;
Notice that you do not include the CategoryName or CategoryID fields. These
are not in the stored procedure, because this stored procedure only updates the
Products table and not the Categories table.
12. Execute the command (run the stored procedure).
command.ExecuteNonQuery();
If the procedure is unable to complete properly, an exception will be thrown.
The exception will be caught in the catch block and the transaction will roll
back. Otherwise, you are ready to go on to update the Categories table.
13. Get the target row from the DataTable by indexing into the Rows
collection, using the SelectedIndex property of the list box. Use the target
row to set a local variable to hold the CategoryID value.
DataRow targetRow =
dataTable.Rows[lbProducts.SelectedIndex];
nt catID =
(int) targetRow["CategoryID"];
Transactions
C# Professional Skills Development 17-33
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
14. Create a command string and set that string to the CommandText property
of the Command object. Remember to set the CommandType property to
the enumerated value CommandType.Text because this time you are
executing the query, not passing in the name of a stored procedure.
string cmd = "Update Categories set CategoryName = '" +
txtCategory.Text.Trim() + "' where CategoryID = " +
catID;
command.CommandText=cmd;
command.CommandType = CommandType.Text;
15. Execute the Query.
command.ExecuteNonQuery();
16. If the procedure is unable to complete properly, an exception will be
thrown. The exception will be caught in the catch block, and the
transaction will roll back. Otherwise, you are ready to commit the
transaction.
transaction.Commit();
17. Update the User interface and close the try block. You are finished
modifying the btnUpdate_Click method.
lbMsg.Text = "Updated.";
Application.DoEvents();
} // end try block
18. Execute the application. There is no apparent change to the performance or
behavior, but now you have the security of database-independent
transaction support.
Updating Databases with ADO.NET
17-34 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
You can update the database by using a simple SQL statement, using
the ExecuteNonQuery method.
You can use multiple statements to update multiple tables, but this
raises problems with data corruption.
You use transactions to solve the problem of data corruption when
updating multiple tables.
Transactions can be supported by the database (using stored
procedures) or by the Connection object.
Transactions must be atomic, consistent, isolated, and durable
(ACID).
When you support transactions using the Connection object, you catch
exceptions thrown when updating the data and you roll back the
transaction.
Transactions
C# Professional Skills Development 17-35
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. What is the purpose of the ExecuteNonQuery method?
2. What is the danger in updating two tables without transactions?
3. What is the advantage of connection-based transactions over database
transactions?
4. What do the letters ACID stand for?
5. Why do you place connection-based transactions in a try block?
Updating Databases with ADO.NET
17-36 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. What is the purpose of the ExecuteNonQuery method?
ExecuteNonQuery is used when you need to send an SQL
statement to the database, but you do not expect to get a
recordset back.
2. What is the danger in updating two tables without transactions?
If the update of one table fails, the other may succeed, and the
result may be that the database is corrupted.
3. What is the advantage of connection-based transactions over database
transactions?
Connection-based transactions free you from the specific syntax
of the database, allowing you to change databases more easily if
you need to. Connection-based transactions are guaranteed to
work no matter what database technology is used to store your
data.
4. What do the letters ACID stand for?
The ACID acronym stands for atomic, consistent, independent,
and durable. Each transaction must work or fail as a whole
(atomic); it must leave the database in a consistent state when it
completes; it must be independent of all the other transactions;
and the results of the transaction must be permanent (durable).
5. Why do you place connection-based transactions in a try block?
If any part of the update fails, an exception will be thrown and
the transaction will be rolled back.
Transactions
C# Professional Skills Development 17-37
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 17:
Updating
Databases with
ADO.NET
Lab 17:
Updating Databases with ADO.NET
17-38 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 17 Overview
In this lab youll learn to provide transaction support for your database
updates.
To complete this lab, youll need to work through two exercises:
Providing Transaction Support with SQL
How the Connection Object Provides Transaction Support
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-bystep

instructions.
Providing Transaction Support with SQL
C# Professional Skills Development 17-39
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Providing Transaction Support with
SQL
Objective
In this exercise, youll use a stored procedure (sproc) with transaction support
to update a database.
Things to Consider
Transaction support in the sproc means that you do not have to provide
transaction support in your code.
If you are updating more than one table, you want either both updates
to occur or neither to occur.
Each parameter must be marked with a direction (e.g.,
ParameterDirection.Input) and a value.
Step-by-Step Instructions
1. Create a stored procedure called
spUpdateProductsAndCategoriesTransactions using SQL Server
Enterprise Manager. To do this perform the following steps:
a. Open SQL Server Enterprise Manager.
b. Drill down the hierarchical tree structure in the left pane by
clicking on the plus signs until the Northwind database is visible.
c. Drill down from the Northwind database one more level to Stored
Procedures.
d. Click on Stored Procedures. There will be a list of stored
procedures visible in the right pane, as shown in Figure 12.
Lab 17:
Updating Databases with ADO.NET
17-40 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 12. Stored procedures in Enterprise Manager.
e. Right-click on Stored Procedure in the left pane and select New
Stored Procedure.
f. A Stored Procedure Properties dialog box will appear with a large
text editing window containing the following boilerplate code:
CREATE PROCEDURE [OWNER].[PROCEDURE NAME] AS
g. Replace that boilerplate code with the following code:
CREATE PROCEDURE spUpdateProductsAndCategoriesTransactions
@ProductID int,
@ProductName varChar(40),
@UnitPrice money,
@CategoryName varChar(5000),
@CategoryID int
as
Providing Transaction Support with SQL
C# Professional Skills Development 17-41
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
begin transaction
Update products set
productName = @ProductName,
UnitPrice = @UnitPrice
where productID = @productID
if @@error <> 0 goto ErrorHandler
Update Categories set CategoryName = @CategoryName where
CategoryID = @CategoryID
if @@Error <> 0 goto ErrorHandler
commit transaction
return
ErrorHandler:
rollback transaction
return
NOTE To save you from typing, this code is provided for you in the file
spUpdateProductsAndCategoriesTransactions.sql.
h. Click on the Check Syntax button to verify that there are no
syntax errors.
i. Click the OK button to save the new sproc. The new sproc will
now be visible in the list of sprocs in the right pane.
2. Open the file named TransactionSupportStarter.sln in the
TransactionSupportStarter folder.
3. Add member variables for a SQLDataAdapter, a DataSet, and a
DataTable.
private SqlDataAdapter dataAdapter;
private DataSet dataSet;
private DataTable dataTable;
4. Read through the FillLB method that has been created for you.
Lab 17:
Updating Databases with ADO.NET
17-42 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
5. Implement the SelectedIndexChanged method for the list box.
private void lbProducts_SelectedIndexChanged(
object sender, System.EventArgs e)
{
6. Create a DataRow object and initialize it with the DataRow that
corresponds to the selected index in the list box.
DataRow selectedRow =
dataTable.Rows[lbProducts.SelectedIndex];
7. Fill the text fields from the row.
txtProductName.Text =
selectedRow["ProductName"].ToString();
txtProductID.Text = selectedRow["ProductID"].ToString();
txtCategory.Text = selectedRow["CategoryName"].ToString();
txtUnitPrice.Text = selectedRow["UnitPrice"].ToString();
8. Implement the event handler for clicking on the Update button.
private void btnUpdate_Click(
object sender, System.EventArgs e)
{
9. Create a connection string to connect to the Northwind Database. Be sure
to use the correct server name, userID, and password for your SQL Server
account.
string connectionString =
"server=YourServer; uid=sa; pwd=YourPassword;
database=Northwind";
10. Create a connection object and initialize it with a connection string. Then
open it.
Providing Transaction Support with SQL
C# Professional Skills Development 17-43
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
System.Data.SqlClient.SqlConnection connection =
new System.Data.SqlClient.SqlConnection(
connectionString);
connection.Open();
11. Create the command object.
System.Data.SqlClient.SqlCommand command =
new System.Data.SqlClient.SqlCommand();
12. Set the command connection property to the connection.
command.Connection = connection;
13. Set the command objects command text to the name of the sproc, which is
spUpdateProductsAndCategoriesTransactions.
command.CommandText=
"spUpdateProductsAndCategoriesTransactions";
14. Set the command type property of the command object to
CommandType.StoredProcedure.
command.CommandType =
CommandType.StoredProcedure;
15. Instantiate a parameter object.
System.Data.SqlClient.SqlParameter param;
16. Review how the parameters are set in the code already provided in the
starter file.
17. Extract the target row into a local DataRow object.
DataRow targetRow =
dataTable.Rows[lbProducts.SelectedIndex];
Lab 17:
Updating Databases with ADO.NET
17-44 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
18. Extract the CategoryID from the row you just obtained.
int catID =
(int) targetRow["CategoryID"];
19. Create the parameter for CategoryID, set the direction, and assign the
value you just obtained.
param =
command.Parameters.Add("@CategoryID",SqlDbType.Int);
param.Direction =
ParameterDirection.Input;
param.Value = catID;
20. Execute the command with ExecuteNonQuery.
command.ExecuteNonQuery();
21. Update the label. Remember to call DoEvents.
lbMsg.Text = "Updated.";
Application.DoEvents();
22. Call FillLB to refill the list box.
FillLB();
23. Compile and run the application as shown in Figure 13.
Providing Transaction Support with SQL
C# Professional Skills Development 17-45
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 13. The final results from using transaction support with SQL.
Lab 17:
Updating Databases with ADO.NET
17-46 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
How the Connection Object Provides
Transaction Support
Objective
In this exercise, youll provide transaction support using the .NET
Frameworks Connection object.
Things to Consider
If the connection object provides transaction support you do not need
to depend on transaction support from the database.
If you do not encounter an exception, you are safe to commit the
transaction after all the steps are completed.
If you do encounter an exception at any time after you start updating
the database, youll want to roll back the transaction.
You can make direct updates to the database without using a stored
procedure at all.
Step-by-Step Instructions
1. First create a stored procedure called
spUpdateProductsAndCategoriesWithoutTransactions using SQL
Server Enterprise Manager. To do this perform the following steps:
a. Open SQL Server Enterprise Manager.
b. Drill down the hierarchical tree structure in the left pane by
clicking on the plus signs until the Northwind database is visible.
c. Drill down from the Northwind database one more level to Stored
Procedures.
d. Click on Stored Procedures. There will be a list of stored
procedures visible in the right pane, as shown in Figure 14.
How the Connection Object Provides Transaction Support
C# Professional Skills Development 17-47
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 14. The list of stored procedures.
e. Right-click on Stored Procedure in the left pane and select New
Stored Procedure.
f. A Stored Procedure Properties dialog box will appear with a large
text editing window containing the following boilerplate code:
CREATE PROCEDURE [OWNER].[PROCEDURE NAME] AS
g. Replace that boilerplate code with the following code:
Lab 17:
Updating Databases with ADO.NET
17-48 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
CREATE PROCEDURE
spUpdateProductsAndCategoriesWithoutTransactions
@ProductID int,
@ProductName varChar(40),
@UnitPrice money
as
Update products set
productName = @ProductName,
UnitPrice = @UnitPrice
where productID = @productID
h. When you are done, click on the Check Syntax button to verify
that there are no syntax errors.
i. Click the OK button to save the new sproc. The new sproc will
now be visible in the list of sprocs in the right pane.
NOTE To save you from typing, this stored procedure is available in the
file spUpdateProductsAndCategoriesWithoutTransactions.slq.
2. Open the file \ConnectionTransactionSupportStarter\
ConnectionTransactionSupportStarter.sln.
3. Implement the Update button event handler.
private void btnUpdate_Click(object sender,
System.EventArgs e)
{
4. Create the connection string. Be sure to use the correct server name,
userID, and password for your SQL Server account.
string connectionString =
"server=YourServer; uid=sa; pwd=YourPassword;
database=Northwind";
5. Create the connection object and pass in the connection string.
How the Connection Object Provides Transaction Support
C# Professional Skills Development 17-49
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
System.Data.SqlClient.SqlConnection connection =
new
System.Data.SqlClient.SqlConnection(connectionString);
6. Open the connection object.
connection.Open();
7. Create a command object.
System.Data.SqlClient.SqlCommand command =
new System.Data.SqlClient.SqlCommand();
8. Create an SQLTransaction object.
SqlTransaction transaction =
connection.BeginTransaction();
9. Attach the transaction to the command.
command.Transaction = transaction;
10. Set the commands connection property to the connection.
command.Connection = connection;
11. Set the command text of the command object to
spUpdateProductsAndCategoriesWithoutTransactions.
command.CommandText=
"spUpdateProductsAndCategoriesWithoutTransactions";
12. Set the command type of the command object to stored procedure.
command.CommandType = CommandType.StoredProcedure;
Lab 17:
Updating Databases with ADO.NET
17-50 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
13. Instantiate an SQLParameter object.
System.Data.SqlClient.SqlParameter param;
14. The parameters have been created for you in the starter file. Review these
before going to the next step.
15. Execute the update using ExecuteNonQuery.
command.ExecuteNonQuery();
16. Get the selected row.
DataRow targetRow =
dataTable.Rows[lbProducts.SelectedIndex];
17. Extract the CategoryID.
int catID =
(int) targetRow["CategoryID"];
18. Create a command string with the category name and id to update the
Categories table.
string cmd = "Update Categories set CategoryName = '" +
txtCategory.Text.Trim() + "' where CategoryID = " +
catID;
19. Set the command objects command text to the string you just created.
command.CommandText=cmd;
20. Set the CommandType to CommandType.Text.
command.CommandType = CommandType.Text;
How the Connection Object Provides Transaction Support
C# Professional Skills Development 17-51
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
21. Execute the update by calling ExecuteNonQuery.
command.ExecuteNonQuery();
22. Commit the transaction.
transaction.Commit();
23. Update the lblMsg label and call DoEvents.
lbMsg.Text = "Updated.";
Application.DoEvents();
24. Catch exceptions and display the message in a MessageBox. Also roll back
the transaction.
catch (Exception ex)
{
MessageBox.Show(
"Exception thrown when updating db: {0}",
ex.Message);
transaction.Rollback();
}
25. Call FillLB to fill the list box.
FillLB();
26. Compile and run the application as shown in Figure 15.
Lab 17:
Updating Databases with ADO.NET
17-52 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 15. The final result of using transaction support with SQL.
Advanced ADO Data Update
C# Professional Skills Development 18-1
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Advanced ADO
Data Update
Objectives
Update DataSets independently of the database.
Update the database with changed records from a DataSet.
Handle concurrency issues in data updating.
Use the Command Builder to simplify concurrency checking.
Advanced ADO Data Update
18-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
DataSet Updates
Until now, youve updated the database every time you updated any data at all.
This works, but it does not take advantage of your disconnected DataSet
object. The goal with a disconnected representation of the database is to allow
you to work with the data for a long time before going back to the database to
resynchronize the data.
An alternative model to updating the database each time the user updates a
record is to store all the changes in the DataSet, and then, at a specific time,
or
in response to a specific user request, you can update the database with all the

changes at once.
Try It Out!
To see how to update the DataSet with multiple changes and then update the
DataBase from the modified DataSet, you will modify the previous example.
1. Reopen the previous example.
2. Remove the Update and Delete buttons and replace them with new
buttons, btnUpdateDataSet, with the text Update DataSet, and
btnUpdateDB with the text Update DB, as shown in Figure 1.
Figure 1. Modified update buttons.
See DataUpdate
Working5.sln
DataSet Updates
C# Professional Skills Development 18-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
3. Break up the FillLB method, separating the code to get and fill the
DataTable from the code to display the list box.
private void FillLB()
{
// create the connection string
string strConnection = "server=YourServer;
uid=sa; pwd=YourPW; database=northwind";
// create the command string
string strCommand = "Select * from Products";
// create the data set command object and dataset
dataAdapter =
new SqlDataAdapter(strCommand, strConnection);
// extract the DataTable
dataTable = dataSet.Tables[0];
// Call the new ShowLB method
ShowLB();
4. Create the ShowLB method to iterate over the DataTable (which you will
remember is a member variable of your form) and fill the list box, row by
row. This code is unchanged from how it worked previously in FillLB.
private void ShowLB()
{
lbProducts.Items.Clear();
foreach (DataRow dataRow in dataTable.Rows)
{
lbProducts.Items.Add(dataRow["ProductName"]);
}
}
5. Return to the designer, and double-click on btnUpdateDataSet to create
the btnUpdateDataSet_Click event handler. The job of this event handler
Advanced ADO Data Update
18-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
is to record the users changes to the current record in the dataset, but not
to update the database itself. The code is straightforward: you get the
selected row and then set its columns based on the values in the text boxes.
You then redisplay the list box (without getting the data from the
database!).
private void btnUpdateDataSet_Click(
object sender, System.EventArgs e)
{
// Get the selected row
DataRow selectedRow =
dataTable.Rows[lbProducts.SelectedIndex];
// Fill the columns in the DataRow from the
// text boxes in the dialog box
selectedRow["ProductName"] = txtProductName.Text;
selectedRow["ProductID"] = txtProductID.Text;
selectedRow["SupplierID"] = txtSupplierID.Text;
selectedRow["CategoryName"] = txtCategory.Text;
selectedRow["QuantityPerUnit"] =
txtQuantityPerUnit.Text;
selectedRow["UnitPrice"] = txtUnitPrice.Text;
selectedRow["UnitsInStock"] = txtUnitsInStock.Text;
selectedRow["UnitsOnOrder"] = txtUnitsOnOrder.Text;
selectedRow["ReorderLevel"] = txtReorderLevel.Text;
selectedRow["Discontinued"] =
cbDiscontinued.SelectedIndex == 0 ? 1 : 0;
// show the list box from the DataSet
ShowLB();
}
The DataRow object you create (selectedRow) is a reference to the DataRow
held in the Rows collection of the DataTable, which in turn is a reference to th
e
DataTable in the DataSet. So, the changes that you make to that row (by
setting its columns) are changed in the DataSet. When you call ShowLB the
list box is filled from the now modified DataSet.
6. Return to the Designer and double-click on btnUpdateDB to create the
btnUpdateDB_Click event handler. The job of this method is to update the
database with all the changes made in the DataSet.
DataSet Updates
C# Professional Skills Development 18-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
This is a non-trivial exercise and youll examine this method step-by-step. The
first step is to create the connection string and connection object and to open
the connection, as you did previously:
string connectionString =
"server=YourServer; uid=sa; pwd=YourPW;
database=Northwind";
SqlConnection connection =
new SqlConnection(connectionString);
connection.Open();
7. Create a Command object, call BeginTransaction, and set the Commands
Connection property as you did previously.
System.Data.SqlClient.SqlCommand command =
new System.Data.SqlClient.SqlCommand();
SqlTransaction transaction =
connection.BeginTransaction();
command.Transaction = transaction;
command.Connection = connection;
8. Set the CommandText to the name of yet another stored procedure
spUpdateProductsAndCategoriesNoTransactions,and set the
CommandType property of the Command object to
CommandType.StoredProcedure.
command.CommandText=
"spUpdateProductsAndCategoriesNoTransactions";
command.CommandType = CommandType.StoredProcedure;
9. Use SQL Server Enterprise Manager to create the new stored procedure.
Advanced ADO Data Update
18-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
CREATE PROCEDURE
spUpdateProductsAndCategoriesNoTransactions
@ProductID int,
@ProductName varChar(40),
@SupplierID int,
@QuantityPerUnit varChar(20),
@UnitPrice money,
@UnitsInStock int,
@UnitsOnOrder int,
@ReorderLevel int,
@Discontinued int,
@CategoryName varChar(5000),
@CategoryID int
as
Update products set
productName = @ProductName,
SupplierID = @supplierID,
QuantityPerUnit = @QuantityPerUnit,
UnitPrice = @UnitPrice,
UnitsInStock = @UnitsInStock,
UnitsOnOrder = @UnitsOnOrder,
ReorderLevel = @ReorderLevel,
Discontinued = @Discontinued
where productID = @productID
Update Categories set CategoryName = @CategoryName where
CategoryID = @CategoryID
You can see this time that you are updating both tables in a single stored
procedure. The transaction support is provided by the connection, not the
datatabase.
10. Create the parameters. This is somewhat different this time, because you
want to update all the rows that changed in the DataSet. Fortunately, the
DataAdapter object provides extensive support for this. Heres how youll
do it:
a. Create a stored procedure for the update (you did this in Step 9).
DataSet Updates
C# Professional Skills Development 18-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
b. Fill in the parameters.
c. Set the Parameter objects source column to the appropriate
column.
d. Set the Parameters SourceVersion to the enumerated value
DataRowVersion.Current. Youll see more about this parameter
later in this chapter.
e. Set the DataAdapters UpdateCommand property to the Command
object youve created.
f. Set the DataAdapters Transaction property to the transaction
object youve created.
g. Call Update on the DataAdapter. Youll get back the number of
rows that are updated. They are all updated at once; you do not
have to create a loop to manually iterate over the changes. Only
the rows that have changed are updated!
h. Commit the transaction and update the User Interface.
Youve already created the stored procedure, you now need to fill in the
parameters. Start with the first, ProductID. As youve done before, create this
by calling the Add method on the Parameters collection of the Command
object. Set the direction to the enumerated value Input.
param =
command.Parameters.Add("@ProductID",SqlDbType.Int);
param.Direction = ParameterDirection.Input;
Rather than setting the Value property, now you set the SourceColumn value.
This allows the DataAdapter to know where to get the value for each record
that has changed.
The DataSet keeps various values for each column, including the current value
(set when you updated the DataSet) and the original value (set when you
originally got the values from the database). Youll see how to use the original
value later in this chapter, for now you want the current value; you set that
with the enumerated value DataRowVersion.Current.
param.SourceColumn = "ProductID";
param.SourceVersion=DataRowVersion.Current;
Set the remaining parameters accordingly:
Advanced ADO Data Update
18-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
param =
command.Parameters.Add(
"@ProductName",SqlDbType.VarChar,40);
param.Direction = ParameterDirection.Input;
param.SourceColumn = "ProductName";
param.SourceVersion=DataRowVersion.Current;
param = command.Parameters.Add(
"@SupplierID",SqlDbType.Int);
param.Direction = ParameterDirection.Input;
param.SourceColumn = "SupplierID";
param.SourceVersion=DataRowVersion.Current;
param = command.Parameters.Add(
"@QuantityPerUnit",SqlDbType.VarChar,20);
param.Direction = ParameterDirection.Input;
param.SourceColumn = "QuantityPerUnit";
param.SourceVersion=DataRowVersion.Current;
param = command.Parameters.Add(
"@UnitPrice",SqlDbType.Money);
param.Direction = ParameterDirection.Input;
param.SourceColumn = "UnitPrice";
param.SourceVersion=DataRowVersion.Current;
param = command.Parameters.Add(
"@UnitsInStock",SqlDbType.Int);
param.Direction = ParameterDirection.Input;
param.SourceColumn = "UnitsInStock";
param.SourceVersion=DataRowVersion.Current;
param = command.Parameters.Add(
"@UnitsOnOrder",SqlDbType.Int);
param.Direction = ParameterDirection.Input;
param.SourceColumn = "UnitsOnOrder";
param.SourceVersion=DataRowVersion.Current;
DataSet Updates
C# Professional Skills Development 18-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
param = command.Parameters.Add(
"@ReorderLevel",SqlDbType.Int);
param.Direction = ParameterDirection.Input;
param.SourceColumn = "ReorderLevel";
param.SourceVersion=DataRowVersion.Current;
param = command.Parameters.Add(
"@Discontinued",SqlDbType.Int);
param.Direction = ParameterDirection.Input;
param.SourceColumn = "Discontinued";
param.SourceVersion=DataRowVersion.Current;
param = command.Parameters.Add(
"@CategoryName",SqlDbType.VarChar,15);
param.Direction = ParameterDirection.Input;
param.SourceColumn = "CategoryName";
param.SourceVersion=DataRowVersion.Current;
param = command.Parameters.Add(
@CategoryID",SqlDbType.Int);
param.Direction = ParameterDirection.Input;
param.SourceColumn = "CategoryID";
param.SourceVersion=DataRowVersion.Current;
11. Set the UpdateCommand of the DataAdapter to the Command object,
created in Step 7.
dataAdapter.UpdateCommand = command;
12. Set the Transaction property of the DataAdapters UpdateCommand to the
Transaction object created in Step 7.
dataAdapter.UpdateCommand.Transaction =
transaction;
Advanced ADO Data Update
18-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
13. You are ready to request that the DataAdapter update the DataSet. If all the

modified data rows are successfully updated, the number of rows updated
is returned and you can commit the transaction and update the user
interface.
try
{
int rowsUpdated =
dataAdapter.Update(dataSet,"Products");
transaction.Commit();
MessageBox.Show(rowsUpdated + " rows updated!" );
}
14. If there is a problem updating the database, an exception is thrown and
your catch block will roll back the transaction.
catch
{
transaction.Rollback();
}
15. Run the application. Scroll down to Teatime Chocolate Biscuits. Change
the Units in Stock to 25 and click Update Dataset. Scroll back to the
Teatime Chocolate Biscuits and click on it. Notice that the Units In Stock
is set to 25. Open the SQL Server table and you can see that the
UnitsInStock has not been changed, as shown in Figure 2.
DataSet Updates
C# Professional Skills Development 18-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 2. Updating the DataSet without updating the database.
16. Go on to the next record, Sir Rodneys Marmalade. Change the Units In
Stock to 39 and click UpdateDataset. Again, scroll to Sir Rodneys
Marmalade and click on it. You see that the Units In Stock is reported as
39, but if you check the database, the value has not been decremented, as
shown in Figure 3.
Advanced ADO Data Update
18-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 3. Updating a second record in the DataSet.
17. Update the database by clicking UpdateDB. The program reports that it
was able to update the two modified rows, as shown in Figure 4.
Figure 4. Reporting the update.
18. Scroll back to Teatime ChocolateBiscuits, and compare the values in the
DataSet with the values in the database. You should see that the DataSet
now agrees with the database values, as shown in Figure 5.
DataSet Updates
C# Professional Skills Development 18-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 5. Updating the database.
This is a far more efficient way to work with the database. Update the dataset
locally, disconnected from the database, and then when youve finished
modifying your records update the backing database.
Advanced ADO Data Update
18-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Concurrency
Until now youve written these example programs as if there would be only
one user updating the database at a time. In most real-world applications,
however, the potential exists for many users to update the database
simultaneously.
Overwriting Data
The risk of overwriting data creates a significant challenge for you as the
programmer. It is possible that one users updates to the database could
overwrite changes made by a second user. For example, assume that the
database is a column in a table whose current value is 20. Two users
(Workstation1 and Workstation2) read that value, as shown in Figure 6.
Figure 6. Two users read a value.
Once theyve read the data, they both modify the data locally (in their own
datasets or in-memory representation) as shown in Figure 7.
Figure 7. Values modified in each computer.
WorkStation 1 writes the modified data back to the database. The value in the
database is now 25, as shown in Figure 8.
Concurrency
C# Professional Skills Development 18-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 8. Modifying the data from Workstation1.
Unfortunately, when Workstation2 writes its modified data (30), it will
overwrite the data written by Workstation1.
Concurrency Checking
To prevent this kind of overwriting, you want Workstation2 to notice that the
value in the database has been changed by Workstation1. The easiest way to
do this is to check to make sure the value has not changed, just before writing
the update. For example, to update the UnitsInStock field for a given row
(where ProductID = 1) you might create a SQL statement along the lines of:
Update Products set UnitsInStock = 30 where ProductID = 1
and UnitsInStock = 20
That is, you want to update the UnitsInStock column with the new value (30)
when the productID matches (the ID of the row youre updating) and when the
original value is still in place (e.g., UnitsInStock = 20).
If someone else changed the UnitsInStock since you read it, the where
statement will fail and the row will not be updated. That is just what you want;

you will then notice that the row was not updated and ask the user how to
handle the conflict.
Try It Out!
You can see how to implement this kind of concurrency testing by modifying
the stored procedure and code from the previous example.
1. The code you will modify is in the btnUpdateDB_Click method. By the
time this code runs, youll have read the data into the DataSet. Your
update will depend on the fact that these values have not changed since
you read them from the database. To test this, youll modify the where
clause in your stored procedure. Create a new stored procedure called
spUpdateProdcutsAndCategoriesConcurrent.
See DataUpdate
Working6.sln
Advanced ADO Data Update
18-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
CREATE PROCEDURE spUpdateProductsAndCategoriesConcurrent
@ProductID int,
@ProductName varChar(40),
@SupplierID int,
@QuantityPerUnit varChar(20),
@UnitPrice money,
@UnitsInStock int,
@UnitsOnOrder int,
@ReorderLevel int,
@Discontinued int,
@CategoryName varChar(5000),
@CategoryID int,
@OldProductName varChar(40),
@OldSupplierID int,
@OldQuantityPerUnit varChar(20),
@OldUnitPrice money,
@OldUnitsInStock int,
@OldUnitsOnOrder int,
@OldReorderLevel int,
@OldDiscontinued int,
@OldCategoryName varChar(5000)
as
Update products set
productName = @ProductName,
SupplierID = @supplierID,
QuantityPerUnit = @QuantityPerUnit,
UnitPrice = @UnitPrice,
UnitsInStock = @UnitsInStock,
UnitsOnOrder = @UnitsOnOrder,
ReorderLevel = @ReorderLevel,
Discontinued = @Discontinued
where productID = @productID
and
productName = @OldProductName and
SupplierID = @OldsupplierID and
QuantityPerUnit = @OldQuantityPerUnit and
Concurrency
C# Professional Skills Development 18-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
UnitPrice = @OldUnitPrice and
UnitsInStock = @OldUnitsInStock and
UnitsOnOrder = @OldUnitsOnOrder and
ReorderLevel = @OldReorderLevel and
Discontinued = @OldDiscontinued
if @@RowCount > 0
begin
Update Categories set CategoryName = @CategoryName
where CategoryID = @CategoryID and
CategoryName = @OldCategoryName
end
The key difference in this new stored procedure is that you add parameters for
the old (original) values for each field, and you check in the where clause that

these values have not been modified.
2. Modify the code from the previous example to call the new stored
procedure.
command.CommandText=
"spUpdateProductsAndCategoriesConcurrent";
The parameters are the same as in the previous example, except that this time
you must also pass in the original values for the parameters whose names
begin with Old. Fortunately, the DataRow object supports this; you can get the
original value by setting the SourceVersion property of the Parameter to
DataRowVersion.Original.
Consider the parameter for the ProductName column. The code is:
param = command.Parameters.Add(
"@ProductName",SqlDbType.VarChar,40);
param.Direction = ParameterDirection.Input;
param.SourceColumn = "ProductName";
param.SourceVersion=DataRowVersion.Current;
This is identical to what you had in the previous example. For the
OldProductName parameter you will use the same code (setting the parameter
name appropriately) but rather than using the DataRowVersion.Current you
Advanced ADO Data Update
18-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
will instead use DataRowVersion.Original. That is, you are telling the
Parameter object to get is value from the ProductName column, but to use the
original value rather than the updated value:
param = command.Parameters.Add(
"@OldProductName",SqlDbType.VarChar,40);
param.Direction = ParameterDirection.Input;
param.SourceColumn = "ProductName";
param.SourceVersion=DataRowVersion.Original;
You will do this for all the Old parameters:
param = command.Parameters.Add(
"@OldSupplierID",SqlDbType.Int);
param.Direction = ParameterDirection.Input;
param.SourceColumn = "SupplierID";
param.SourceVersion=DataRowVersion.Original;
param = command.Parameters.Add(
"@OldQuantityPerUnit",SqlDbType.VarChar,20);
param.Direction = ParameterDirection.Input;
param.SourceColumn = "QuantityPerUnit";
param.SourceVersion=DataRowVersion.Original;
param = command.Parameters.Add(
"@OldUnitPrice",SqlDbType.Money);
param.Direction = ParameterDirection.Input;
param.SourceColumn = "UnitPrice";
param.SourceVersion=DataRowVersion.Original;
param = command.Parameters.Add(
"@OldUnitsInStock",SqlDbType.Int);
param.Direction = ParameterDirection.Input;
param.SourceColumn = "UnitsInStock";
param.SourceVersion=DataRowVersion.Original;
param = command.Parameters.Add(
"@OldUnitsOnOrder",SqlDbType.Int);
Concurrency
C# Professional Skills Development 18-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
param.Direction = ParameterDirection.Input;
param.SourceColumn = "UnitsOnOrder";
param.SourceVersion=DataRowVersion.Original;
param = command.Parameters.Add(
"@OldReorderLevel",SqlDbType.Int);
param.Direction = ParameterDirection.Input;
param.SourceColumn = "ReorderLevel";
param.SourceVersion=DataRowVersion.Original;
param = command.Parameters.Add(
"@OldDiscontinued",SqlDbType.Int);
param.Direction = ParameterDirection.Input;
param.SourceColumn = "Discontinued";
param.SourceVersion=DataRowVersion.Original;
param = command.Parameters.Add(
"@OldCategoryName",SqlDbType.VarChar,15);
param.Direction = ParameterDirection.Input;
param.SourceColumn = "CategoryName";
param.SourceVersion=DataRowVersion.Original;
3. These are the only changes, which means you are ready to test the
application. To do so, open the File Explorer and navigate to the directory
in which you saved your project. Double-click on the bin directory and
then the debug directory. You should find the .exe file for your program.
Double click on it, your application opens. Double-click on it again to
open a second version, as shown in Figure 9. The two versions can point to
the same data.
Advanced ADO Data Update
18-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 9. Open two instances of the program.
4. Click on Teatime Chocolate Biscuits in both versions and set the Units In
Stock to 30 in one and to 40 in the second. Click Update Dataset in both
to update their respective DataSets as shown in Figure 10.
Figure 10. Updating the DataSet with different values.
5. Update the DataBase from the first (left-hand) instance. The UnitsInStock
value is updated to 30, and the program reports that one row was updated.
6. Update the Database from the second (right-hand) instance. The program
reports a concurrency error as shown in Figure 11. This concurrency error
is displayed because the where clause has failed and an exception was
raised because the data could not be updated.
Concurrency
C# Professional Skills Development 18-21
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 11. Reporting the concurrency error.
Resolving the Concurrency Error
In this example, the program simply reports the error, but you can certainly
imagine extending the program to offer the user more useful choices, such as
being able to overwrite with the new data or reload the data currently in the
database.
Command Builder
In the previous example, you went to a great deal of work to build the
parameters for the stored procedure, hand filling each parameter with both the
new version and the original version of the values.
The .NET Frameworks provides astonishing support for this kind of
concurrency checking, but only if you meet certain stringent conditions.
All the rows you are updating must come from a single table.
The table must have a unique key.
The unique key must be returned by the query used to fill the table.
The name of the table must have no spaces, periods, quotes, or other
special characters.
The one constraint that is truly limiting is the first: you can only use the bui
ltin
concurrency support if you are working with rows from a single table. That
said, if you do meet this criterion, the support is incredible.
The Base Class Library offers a class SqlCommandBuilder that takes a
DataAdpater in its constructor. The CommandBuilder has methods that can
automatically generate the Command objects needed for Updating, Deleting,
and Inserting new records into the database. This saves you from creating
stored procedures and all the myriad parameters; you can just generate the
necessary command objects and let the DataAdapter take care of managing the
update!
Advanced ADO Data Update
18-22 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
To see the extent of this support youll modify the previous example to fill the
dialog box from a single table: products.
1. Modify the User Interface to display the CategoryID rather than the
Category Name. Change the label and rename the text box txtCategoryID.
2. Modify lbProducts_SelectedIndexChanged to use the new text box.
txtCategoryID.Text =
selectedRow["CategoryID"].ToString();
3. Modify the event handler btnUpdateDataSet_Click to use the modified text
field.
selectedRow["CategoryID"] = txtCategoryID.Text;
4. Modify the FillLB method to select every column from the Products table
and fill the DataSet with a single table: Products.
string strConnection =
"server=YourServer; uid=sa; pwd=YourPW;
database=northwind";
// select every column from Products
string strCommand = "Select * from Products";
dataAdapter =
new SqlDataAdapter(strCommand, strConnection);
dataSet = new DataSet();
dataAdapter.Fill(dataSet,"Products");
5. Create an instance of SqlCommandBuilder and pass in the DataAdapter.
See DataUpdate
Working7.sln
Concurrency
C# Professional Skills Development 18-23
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
SqlCommandBuilder bldr =
new SqlCommandBuilder(dataAdapter);
6. Use the CommandBuilder to generate the Update, Delete, and Insert
commands, and assign these Command objects to the appropriate
properties of the DataAdapter.
dataAdapter.UpdateCommand = bldr.GetUpdateCommand();
dataAdapter.DeleteCommand = bldr.GetDeleteCommand();
dataAdapter.InsertCommand = bldr.GetInsertCommand();
7. Complete the FillLB method to initialize the DataTable and call ShowLB.
dataTable = dataSet.Tables[0];
ShowLB();
} // end FillLB
8. Modify the btnUpdateDB_Click method. Rather than invoking a stored
procedure, just call Update on the DataAdapter from within a try block.
The DataAdapter will take care of the work for you!
private void btnUpdateDB_Click(
object sender, System.EventArgs e)
{
try
{
int rowsUpdated =
dataAdapter.Update(dataSet,"Products");
MessageBox.Show(rowsUpdated + " rows updated!" );
}
catch
{
MessageBox.Show("Concurrency error!" );
}
FillLB();
}
Advanced ADO Data Update
18-24 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
9. Start up two instances of the program by navigating to the executable file
in the bin/debug subdirectories of the project directory, as shown in
Figure 12.
Figure 12. Side-by-side instances of the modified program.
10. Change the Units In Stock number to 40 in the first and to 50 in the
second. Click Update Dataset in both to update their respective DataSet
objects.
11. Click Update.DB on the first and receive the message that one row was
updated.
12. Click Update DB on the second and receive the message that there was a
concurrency error.
The new version is far simpler than the version in which you filled in the
parameters for the update by hand. The Command Builder is doing all the
work for you.
You can actually see that the Command Builder is taking the same approach as
you did in the previous example, by examining the UpdateCommand that is
being executed on your behalf.
Add the following line to the beginning of the try block in
btnUpdateDB_Click:
MessageBox.Show(dataAdapter.UpdateCommand.CommandText );
Concurrency
C# Professional Skills Development 18-25
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
This opens a MessageBox to display the text of the UpdateCommand. Update
another record, and note the text that is displayed. The MessageBox shows that
the Update Command is using 19 parameters, the first nine are used to set the
new values, and the remaining ten are used to test that no original values were
changed. This is shown in Figure 13.
Figure 13. The update generated by the command builder.
Advanced ADO Data Update
18-26 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
Rather than updating the database each time the user makes a change,
you can update the disconnected DataSet.
You may update the database from your disconnected DataSet at any
time.
If more than one user is interacting with the database, you must take
precautions to ensure that changes are not overwritten due to
concurrency conflicts.
The best way to prevent a concurrency problem is to set a where
clause that checks that the data is unchanged from when it was first
read.
You can simplify creating the parameters for the where clause by
setting the SourceVersion of the Parameter to
DataRowVersion.Original.
Rather than writing the concurrency checking stored procedure and
parameters yourself, you can simplify your program by using a
Command Builder.
There are restrictions on using a Command Builder; the most
important of which is that you must update only a single table and that
table must have a unique key that was used to fill the table.
Concurrency
C# Professional Skills Development 18-27
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. What is the advantage of updating the DataSet rather than the database?
2. How do you loop through all the rows that have changed, to ensure they
are all updated on the database?
3. Why is having more than one user at a time problematic?
4. How do you structure a query to ensure that the data has not changed from
when you originally read the data?
5. How do you tell the Parameter object which version of data you want in a
DataRow?
6. What is the job of the CommandBuilder?
7. What are the principal restrictions when using CommandBuilder?
Advanced ADO Data Update
18-28 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. What is the advantage of updating the DataSet rather than the database?
Updating the DataSet allows you to minimize the traffic to the
Database. You can update a number of records in the DataSet
and then update the database all at once.
2. How do you loop through all the rows that have changed, to ensure they
are all updated on the database?
The DataAdapter will manage this for you. You provide an Update
command and set the SourceColumn and SourceVersion
properties of the Parameter object.
3. Why is having more than one user at a time problematic?
It is possible for two users to update the same data, with one
inadvertently overwriting the changes made by the other.
4. How do you structure a query to ensure that the data has not changed from
when you originally read the data?
You set a where clause to update the record only if the original
values are still in the various columns.
5. How do you tell the Parameter object which version of data you want in a
DataRow?
Set the SourceVersion property to one of the DataRowVersion
enumerated constants (Current or Original).
6. What is the job of the CommandBuilder?
The CommandBuilder can generate Select, Insert, or Update
commands and greatly simplify concurrency support.
7. What are the principal restrictions when using CommandBuilder?
All the rows must come from a single table and that table must
have a unique key, and that key must have been used to fill the
table. Further, the table name must have no spaces, periods,
quotes, or other special characters.
Concurrency
C# Professional Skills Development 18-29
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 18:
Advanced ADO
Data Update
Lab 18:
Advanced ADO Data Update
18-30 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 18 Overview
In this lab youll learn advanced techniques for updating a database using
ADO.NET.
To complete this lab, youll need to work through two exercises:
Updating Data in DataSets
Updating the Database with CommandBuilder
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-bystep

instructions.
Updating Data in DataSets
C# Professional Skills Development 18-31
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Updating Data in DataSets
Objective
In this exercise, youll update columns in a dataset and then batch update the
backing database.
Things to Consider
You can update the dataset repeatedly without updating the database.
When you do update the database you can call a transaction and pass
in each value that has been updated.
Your job is to identify the parameters, the DataSet will take care of
finding all the affected rows.
The DataRow has a number of versions, the Current version is the
value as updated in the DataSet.
Step-by-Step Instructions
1. Create a stored procedure called spDBUpdateNoTransactions using SQL
Server Enterprise Manager. To do this perform the following steps:
a. Open SQL Server Enterprise Manager.
b. Drill down the hierarchical tree structure in the left pane by
clicking on the plus signs until the Northwind database is visible.
c. Drill down from the Northwind database one more level to Stored
Procedures.
d. Click on Stored Procedures. There will be a list of stored
procedures visible in the right pane, as shown in Figure 14.
Lab 18:
Advanced ADO Data Update
18-32 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 14. Stored Procedures in Enterprise Manager.
e. Right-click on Stored Procedure in the left pane and select New
Stored Procedure.
f. A Stored Procedure Properties dialog box appears with a large text
editing window containing the following boilerplate code:
CREATE PROCEDURE [OWNER].[PROCEDURE NAME] AS
g. Replace that boilerplate code with the following code:
Updating Data in DataSets
C# Professional Skills Development 18-33
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
CREATE PROCEDURE spDBUpdateNoTransactions
@ProductID int,
@ProductName varChar(40),
@UnitPrice money,
@CategoryName varChar(5000),
@CategoryID int
as
Update products set
productName = @ProductName,
UnitPrice = @UnitPrice
where productID = @productID
Update Categories set CategoryName = @CategoryName where
CategoryID = @CategoryID
h. When you are done, click the Check Syntax button to verify that
there are no syntax errors.
i. Click the OK button to save the new sproc. The new sproc will
now be visible in the list of sprocs in the right pane.
2. Open DataSetUpdatesStarter.sln in the DataSetUpdatesStarter folder.
3. Notice the DataAdapter, DataSet, and DataTable member variables.
private SqlDataAdapter dataAdapter;
private DataSet dataSet;
private DataTable dataTable;
4. Review the FillLB method provided in the starter file as well as the
ShowLB method and the lbProducts_SelectedIndexChanged method.
5. Implement the event handler for the Update DataSet click event.
private void btnUpdateDataSet_Click(
object sender, System.EventArgs e)
{
6. Get the selected row.
Lab 18:
Advanced ADO Data Update
18-34 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
DataRow selectedRow =
dataTable.Rows[lbProducts.SelectedIndex];
7. Fill the columns in the DataRow from the text boxes in the dialog box.
selectedRow["ProductName"] = txtProductName.Text;
selectedRow["ProductID"] = txtProductID.Text;
selectedRow["CategoryName"] = txtCategory.Text;
selectedRow["UnitPrice"] = txtUnitPrice.Text;
8. Show the list box from the DataSet by calling ShowLB.
ShowLB();
9. Implement the UpdateDB button event handler.
private void btnUpdateDB_Click(object sender,
System.EventArgs e)
{
10. Create the connection string. Remember to use the correct server name,
userID, and password for your SQL Server account.
string connectionString =
"server=YourServer; uid=sa;
pwd=YourPassword; database=Northwind";
11. Create the connection object.
System.Data.SqlClient.SqlConnection connection =
new System.Data.SqlClient.SqlConnection(
connectionString);
12. Open the connection.
connection.Open();
Updating Data in DataSets
C# Professional Skills Development 18-35
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
13. Create a SqlCommand object.
System.Data.SqlClient.SqlCommand command =
new System.Data.SqlClient.SqlCommand();
14. Create a SqlTransaction object and initialize it by calling
BeginTransaction on the connection object.
SqlTransaction transaction =
connection.BeginTransaction();
15. Set the commands transaction and connection objects.
command.Transaction = transaction;
command.Connection = connection;
16. Set the command text for the command to the sproc
spDBUpdateNoTransactions.
command.CommandText=
"spDBUpdateNoTransactions";
17. Set the CommandType of the command object to Stored Procedure.
command.CommandType = CommandType.StoredProcedure;
18. Create a SqlParameter object.
System.Data.SqlClient.SqlParameter param;
19. Review the settings of the parameters in the starter file.
20. Set the UpdateCommand property of the DataAdapter to the command and
set the Transaction property of the UpdateCommand to the transaction.
Lab 18:
Advanced ADO Data Update
18-36 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
dataAdapter.UpdateCommand = command;
dataAdapter.UpdateCommand.Transaction =
transaction;
21. Create a try block. Within the try block, call Update on the DataAdapter
and pass in the DataSet object and the table name (Products). Use a
MessageBox to display the number of rows updated.
try
{
int rowsUpdated =
dataAdapter.Update(dataSet,"Products");
transaction.Commit();
MessageBox.Show(rowsUpdated + " rows updated!" );
}
22. Create a catch block to catch any exceptions and use a MessageBox to
display the exceptions message.
catch (System.Exception ex)
{
MessageBox.Show(ex.Message);
transaction.Rollback();
}
23. Call FillLB to refill the list box.
FillLB();
The final result should look like Figure 15.
Updating Data in DataSets
C# Professional Skills Development 18-37
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 15. Running the program.
Lab 18:
Advanced ADO Data Update
18-38 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Updating the Database with
CommandBuilder
Objective
In this exercise, youll update the database using the Frameworks
CommandBuilder class.
Things to Consider
You may use a CommandBuilder only if all the rows you are updating
must come from a single table. That table must have a unique key and
the unique key must be returned by the query used to fill the table.
Note that the name of the table you use must have no spaces, periods,
quotes, or other special characters.
The CommandBuilder provides a very simple way to manage
concurrency.
No stored procedure is neededthe DataSet will know which data to
update.
Step-by-Step Instructions
1. Open CommandBuilderStarter.sln in the CommandBuilderStarter
folder.
2. Create an SqlCommandBuilder instance.
SqlCommandBuilder bldr =
new SqlCommandBuilder(dataAdapter);
3. Set DataAdapters UpdateCommand object by calling
GetUpdateCommand on DataAdapter.
dataAdapter.UpdateCommand = bldr.GetUpdateCommand();
Updating the Database with CommandBuilder
C# Professional Skills Development 18-39
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
4. Set DataAdapters DeleteCommand object by calling GetDeleteCommand
on DataAdapter.
dataAdapter.DeleteCommand = bldr.GetDeleteCommand();
5. Set DataAdapters InsertCommand object by calling GetInsertCommand
on DataAdapter.
dataAdapter.InsertCommand = bldr.GetInsertCommand();
6. Fill member variable dataTable with the table you created above.
dataTable = dataSet.Tables[0];
7. Call ShowLB to show the list box.
ShowLB();
8. Implement the ShowLB method.
private void ShowLB()
{
9. Empty the list box.
lbProducts.Items.Clear();
10. Iterate over the Rows collection to fill the list box.
foreach (DataRow dataRow in dataTable.Rows)
{
lbProducts.Items.Add(dataRow["ProductName"]);
}
11. Implement the SelectedIndexChanged event handler.
Lab 18:
Advanced ADO Data Update
18-40 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
private void lbProducts_SelectedIndexChanged(
object sender, System.EventArgs e)
{
12. Extract the selected row.
DataRow selectedRow =
dataTable.Rows[lbProducts.SelectedIndex];
13. Fill the four text boxes.
txtProductName.Text =
selectedRow["ProductName"].ToString();
txtProductID.Text = selectedRow["ProductID"].ToString();
txtCategoryID.Text =
selectedRow["CategoryID"].ToString();
txtUnitPrice.Text = selectedRow["UnitPrice"].ToString();
14. Implement the event handler for the Update DataSet button.
private void btnUpdateDataSet_Click(
object sender, System.EventArgs e)
{
15. Declare a local DataRow and fill with a reference to the selected row.
DataRow selectedRow =
dataTable.Rows[lbProducts.SelectedIndex];
16. Set the Selected Rows values from the text boxes.
selectedRow["ProductName"] = txtProductName.Text;
selectedRow["ProductID"] = txtProductID.Text;
selectedRow["CategoryID"] = txtCategoryID.Text;
selectedRow["UnitPrice"] = txtUnitPrice.Text;
Updating the Database with CommandBuilder
C# Professional Skills Development 18-41
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
17. Update the list box by calling ShowLB.
ShowLB();
18. Implement the event handler for the UpdateDB button.
private void btnUpdateDB_Click(
object sender, System.EventArgs e)
{
19. Create a try block.
try
{
20. Display a MessageBox with the Command Text from the
UpdateCommand object held by the DataAdapter.
MessageBox.Show(dataAdapter.UpdateCommand.CommandText );
21. Call the Update method on the DataAdapter (passing in the dataSet, and
the string "Products"). Assign the value returned to a local variable.
int rowsUpdated =
dataAdapter.Update(dataSet,"Products");
22. Display that value in a MessageBox as the number of rows updated.
MessageBox.Show(rowsUpdated + " rows updated!" );
23. Implement a catch block to display a MessageBox with the message
"Concurrency error!".
Lab 18:
Advanced ADO Data Update
18-42 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
catch
{
MessageBox.Show("Concurrency error!" );
}
24. Call FillLB to refill the list box.
FillLB();
When you run the program, it should look like Figure 16.
Figure 16. Running the program.
Programming Web Forms
C# Professional Skills Development 19-1
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Programming Web
Forms
Objectives
Use Web Forms to create Web applications.
Handle events in Web forms.
Understand how state is managed.
Explore the various ASP.NET controls.
Use data bound controls to display data from a database.
Examine rich controls.
Programming Web Forms
19-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
ASP.NET
The .NET platform refers to the suite of technologies for creating Web
applications as ASP.NET. The part of ASP.NET we care about here, however,
is the Web Forms technology that allows Rapid Application Development of
Web programs.
The Web Forms design pattern is extremely similar to the Windows Forms
design pattern. Once again you are presented with a form on which you drag
and drop controls, adding code-behind to support event handling.
The key difference is that when you run the Web application, it is served by
your Web server (IIS) and viewed remotely using a browser, as illustrated in
Figure 1.
Figure 1. Web hosting.
Web Forms
Web Forms dynamically generate Web pages on the server and send them to
the client. What is viewed on the client browser is simply HTML. This allows
the Web Forms technology to support any browser. At the time the page is
generated, the host knows what browser the client is using and has an
opportunity to take advantage of the capabilities of up-level browsers (e.g., IE

6) but this is invisible to you as the author of the page.
Creating Web Forms
Web forms are just HTML with a C# source code file behind them. You can
create Web forms in a simple editor (such as Notepad), but most of the time
ASP.NET
C# Professional Skills Development 19-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
you wont want to. Visual Studio .NET provides extensive support for building
Web pages, including a WYSIWYG (What You See Is What You Get)
designer to support drag-and-drop manipulation of controls.
A Web form consists of two files: the HTML file (.aspx) and the code-behind
file (.aspx.cs). The code-behind file has C# code that is compiled. (Of course,
you can write code-behind in Visual Basic .NET or any other .NET language,
but why on Earth would you want to?)
Event Driven
A key distinction between ASP.NET and its precursor technology is that
ASP.NET is event-driven. That is, there are many events that you handle in an
ASP.NET page, and you write your event handlers in the code behind, much as
you do with Windows forms.
The Life Cycle Model
The model for ASP.NET is this: the server draws a page and sends it to the
user. The user reads the page and interacts with it. Some of the interactions
post the page back to the server. When the page is posted back, it is updated
and then sent back to the browser.
PostBack
Not all controls postback, but many do including:
Button
Calendar
DataGrid
DataList
ImageButton
LinkButton
Many other controls have non-postback events. These events are stored and
handled during the next postback.
State
The Web is inherently stateless. That is, each time you request a page, a
normal Web server simply dispenses the page and then breaks the connection.
There is no state maintained that allows the server to understand the concept of

Programming Web Forms
19-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
a session. ASP.NET imposes a session on top of this stateless environment
through the use of cookiesa small file kept on the users machine.
ASP.NET provides extensive support for state. Control state (the state of the
controls on the page) is no longer maintained by hand; it is maintained for you
through view-state. View state is implemented as a hidden encrypted field on
the HTML page. You simply provide the control and the page maintains the
state of the control. Session state is maintained on the server and can be writt
en
to a database to support large scale Web Farms.
NOTE Web Forms created with Visual Studio .NET have files in two
different directories on your local computer. The bulk of the files
are in a subdirectory under the localhost virtual directory. On most
machines, the default physical location for localhost corresponds
to the directory c:\inetpub\wwwroot.
In addition, there is also a solution file, with an extension of sln,
located in the default projects directory. This directory will vary
from machine to machine, and is specified in Visual Studio .NET
under Tools|Options|Environment|Projects and Solutions.
In order to utilize the sample projects included with this chapter, you will nee
d
to perform the following steps:
1. The projects included as part of this courseware are contained within
two directories: From inetpub and From Visual Studio Projects. Copy
these two directories to a location on your local machine. They do not
necessarily have to be copied to the existing inetpub or projects
directories.
2. Create a virtual directory for each project that you want to open in
Visual Studio .NET.
3. Click Start|Settings|Control Panel|Administrative Tools|Computer
Management.
4. In the left pane, drill down by clicking on the plus signs next to
Services and Applications, Internet Information Services, Internet
Information Services, and Default Web Site.
5. Right-click on Default Web Site and select New|Virtual Directory.
6. Follow the wizard to create a new virtual directory for the project you
want to work on. The alias name can be any name you wish, but to
avoid confusion name the alias the same as the directory name.
7. For the Directory, click the Browse button to browse to the correct
subdirectory under the From inetpub directory. For Access
Permissions, use the defaults.
ASP.NET
C# Professional Skills Development 19-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
8. Edit the sln file so that it points to the correct URL incorporating the
virtual directory you just created. The sln file is contained in the From
Visual Studio Projects directory.
9. Open the sln file in a text editor such as Notepad.
10. The second line will have the name of the project followed by a URL.
11. Edit the URL (in quotes in the sln file) so that it uses the virtual
directory you just created.
12. Note that the final node of the URL is a file with a csproj extension.
13. Save and close the sln file.
14. Now you can open the project in Visual Studio .NET either by doubleclicking
the sln file in Windows Explorer or opening Visual Studio
.NET by clicking the Open Project button on the Start page, and
navigating to the sln file.
Try It Out!
To see how easy it is to build a Web Forms application and to see the support
provided by Visual Studio .NET, youll build a simple Web application.
1. Create a new application in Visual Studio .NET by clicking on New
Project.
2. In the Templates window click on ASP.NET Web Application. The
Location will be a folder under http://localhost, but you are free to create
subdirectories to hold your project.
3. Name the project HelloWeb and click OK. Visual Studio .NET will create
a Web page and virtual directory for you. It will also save the project file
in a subdirectory, typically under My Documents/Visual Studio Projects.
4. The form has two Layout options: Grid, which allows you to precisely
place objects (and uses absolute positioning) or Flow, which simply flows
one object after another. To change the Layout, right-click on the form,
choose Properties, and set the Page Layout property. For now, leave it as
GridLayout.
5. Drag a label onto the form. Stretch the label and set its ID to lblOutput and

its text to Hello Web. While you are at it, click on the + in front of the
Font attribute, and set Bold to true and Size to Larger.
6. Drag a button onto the form. Set its ID to btnChange and its Text to
Change!, as shown in Figure 2.
See HelloWeb.sln
Programming Web Forms
19-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 2. Creating the Web form.
7. Start the application. The message prints and clicking the button posts the
page back to the server, though it is redrawn unchanged. Youll fix that in
a moment.
8. Click the HTML tab and look at the HTML youve created so far. Notice
that you are using absolute positioning.
9. Double-click on the button to open an event handler. You should find
yourself in the btnChange_Click event handler method. Add the following
code:
lblOutput.Text = "Goodbye Web";
ASP.NET
C# Professional Skills Development 19-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
10. Notice that when you type lblOutput Visual Studio .NET offers you help
in finding the method or property you want.
11. Start the application. This time when you click the button the page is
redrawn with a new message: Hey Presto! Now youre a Web
programmer.
12. Go back to the designer, and drag a list box onto the form. Set the ID for
the list to lbNames.
13. Double-click on the form to open the default event handler: Page_Load.
14. In the Page_Load method, add this code:
lbNames.Items.Add("Jesse");
lbNames.Items.Add("Joe");
lbNames.Items.Add("Jim");
lbNames.Items.Add("Jack");
15. Start the application. Notice that the four names fill the list box.
16. Click Change. The list box now has eight names, so what happened?
When you clicked change, the page was posted to the server. The new HTML
was sent down for the text for the label. The page was then reloaded.
Unfortunately, on page load you add items to the list box. You dont want to
do that, because the view_state is already maintaining the state of the list box

and you dont need to add them again.
You want to differentiate between the first time the page is loaded (in which
case you do want to add these names) and when the page is being reloaded
because of a postback.
17. Modify the code in Page_Load as follows:
Programming Web Forms
19-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
private void Page_Load(
object sender, System.EventArgs e)
{
if ( ! IsPostBack )
{
lbNames.Items.Add("Jesse");
lbNames.Items.Add("Joe");
lbNames.Items.Add("Jim");
lbNames.Items.Add("Jack");
}
}
Here you are testing whether the IsPostBack property is true. If not (and this i
s
the first time the page is being loaded) then you will add the four names.
18. Rerun the application. Click Change and the four names are shown with
no additional names added. You can trust the view state to maintain the
state of your controls through a postback. Try clicking on one name
(highlighting it) and then click Change. Notice that after the postback the
state of the control is maintained. You do not need to write code to do this,
ASP.NET does it for you through the use of View_State.
19. Examine the source code sent to the browser by clicking View|Source in
the browser, as shown in Figure 3.
ASP.NET
C# Professional Skills Development 19-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 3. Viewing the HTML.
There are two things to notice when examining the HTML. First, what is sent
to the browser is just HTML; no source code. This is key; all the source code is

on the server.
Second, there is a hidden text field named _VIEWSTATE that has all sorts of
gibberish in it.
20. Keep the source window open, but go back to your HTML. Notice that the
list box is marked as an object of type ASP:ListBox:
Programming Web Forms
19-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
<asp:ListBox id="lbNames" style="Z-INDEX: 103; LEFT:
115px; POSITION: absolute; TOP: 132px"
runat="server"></asp:ListBox>
21. Examine the HTML sent. The ASP:ListBox has been transformed into a
simple Select HTML tag. In addition, the items you added using object
syntax,
lbNames.Items.Add("Jesse");
lbNames.Items.Add("Joe");
lbNames.Items.Add("Jim");
lbNames.Items.Add("Jack");
have been transformed into <option> tags.
<option value="Jesse">Jesse</option>
<option selected="selected" value="Joe">Joe</option>
<option value="Jim">Jim</option>
<option value="Jack">Jack</option>
The power of ASP.NET is that you design and code using objects, but what is
sent to the browser is just HTML.
ASP.NET Controls
ASP.NET programming supports five types of controls.
HTML Controls
HTML Server Controls
Web (ASP) Server Controls
Validation Controls
User Controls/Custom Controls
HTML Controls
You can continue to program using simple HTML controls just as you might
with ASP or any other Web technology.
ASP.NET
C# Professional Skills Development 19-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
HTML Server Controls
You can transform a simple HTML control into a programmable server-side
control just by adding the attribute to a traditional HTML control.
runat = "Server"
This allows you to program events for the HTML control that will be
implemented at the server.
Web (ASP) Server Controls
ASP.NET provides a new set of controls that implement all the functionality of
HTML controls, but do so in a more uniform object model. These controls are
prefixed with the ASP tag, and offer what many programmers consider a
superior programming model. This chapter uses Web Server controls rather
than HTML Server controls.
The browser never sees your Web Server controls. What is sent to the browser
is always traditional HTML. You saw this earlier when the Web Server List
control was converted to a traditional HTML Select control.
Web controls include the following:
Label: displays text
Text box: provides single or multi-line text entry
Button
LinkButton (like a button with a link to a URL)
ImageButton (like an image with a link to a URL)
CheckBox: provides yes/no semantics
RadioButton: provides mutually exclusive choices
ListBox: provides single or multiselect choices from a list
CheckBoxList: combines check boxes with a list box
RadioButtonList: for building lists of radio buttons
DropDownList: for choosing one from a list
In addition, ASP.NET provides a suite of rich new controls including:
Calendar: for date manipulation
Ad rotator: for displaying advertising
Programming Web Forms
19-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Validation Controls
ASP.NET offers a series of controls for implementing data validation on your
Web pages. These allow you to ensure that fields are not left blank, or that
values are within a specified range or meet a given regular expression pattern.
Validation controls can run on the server or the client, and can be set to decid
e
where to run based on the capabilities of the browser.
User Controls/Custom Controls
If the controls provided by .NET are not sufficient to meet your needs, you are
free to write your own. You can combine a set of controls into a small reusable
HTML page (User Controls) or you can derive entirely new controls from the
Controls base class.
Data Bound Controls
It is possible to bind a control to a data source and have ASP.NET do much of
the processing for you. ASP.NET offers a number of rich and powerful
controls for displaying and manipulating data, typically binding controls to
DataSets and DataTables.
Try It Out!
To see how to work with Web Controls and especially how to bind data to a
Web control, youll build a simple demonstration Web page.
1. Create a new ASP.NET application or open the WebControlsStarter
project.
2. If you are creating your own application from scratch, drag a ListBox,
three Buttons, nine Labels, nine Textboxes, and one DropDownList onto
your form, as shown in Figure 4.
See
WebControls.sln
ASP.NET
C# Professional Skills Development 19-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 4. WebControlsStarter.
3. If you are writing this from scratch, set the IDs as shown in Table 1.
Programming Web Forms
19-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Control ID Text
ListBox lbProducts
Button btnUpdate Update
Button btnDelete Delete
Button btnNew New
TextBox txtProductName
TextBox txtCategoryID
TextBox txtUnitPrice
TextBox txtUnitsOnOrder
TextBox txtQuantityPerUnit
TextBox txtUnitsInStock
TextBox ReorderLevel
TextBox SupplierID
DropDownList cbDiscontinued
Table 1 Controls with their IDs and Text.
The Labels are not given special IDs (i.e., they remain Label1, Label2, etc.),
but you can see which text box aligns with each label based on the name of the
text box.
In addition, there is a text box above txtQuantityPerUnit which is named
txtProductID, but which has no label.
4. Run the application. It doesnt do much except display the labels and text
boxes and buttons. Notice that the drop-down list is empty. Youll fix all
of this in the next steps.
5. Notice that this form is quite similar to the Windows Form application you
built in a previous chapter. The code is very similar as well. To get started,
open the code behind and add this line:
using System.Data.SqlClient;
6. The first programming task is to fill in the contents of the list box. Youll
do that in the Page_Load method. Once again you must check that you are
not in a postback page. You only want to go to the database the first time
you load the page.
ASP.NET
C# Professional Skills Development 19-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
private void Page_Load(
object sender, System.EventArgs e)
{
//*** new
if (!IsPostBack)
{
}
}
7. Within the test for PostBack, create a connection string to connect to the
database.
string strConnection =
"server=YourServer; uid=sa; pwd=YourPW;
database=northwind";
8. Create the command string to retrieve records from the Products table.
string strCommand = "Select * from Products";
9. Instantiate a DataAdapter, passing in the command and connection strings.
SqlDataAdapter dataAdapter =
new SqlDataAdapter(strCommand, strConnection);
10. Create the DataSet and fill it from the DataAdapter.
DataSet dataSet = new DataSet();
dataAdapter.Fill(dataSet,"Products");
11. Get the DataTable from the DataSet.
DataTable dataTable = dataSet.Tables[0];
Programming Web Forms
19-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
12. You will need this DataTable when a choice is made in the list box. You
must store it away in session state, because when the page is reposted, it
will no longer exist (remember, the DataSet is only created the first time
the page is loaded, and then it is lostthere is no state in a Web page).
Session["dataTable"] = dataTable;
To store an object in Session state, you must add it to the Session object. You
do so by using indexer syntax. If the object at the index you provide exists, it
is
updated, otherwise it is created. In this case, youve created a Session variable
dataTable just by naming it in the index operator and assigning the dataTable
object to the Session object at that string.
13. Call FillLB to fill the list box. Youll write this method in Step 15.
FillLB();
14. Fill the DropDown list by adding two strings, true and false. Close the
method:
cbDiscontinued.Items.Add("True");
cbDiscontinued.Items.Add("False");
}
}
15. Implement the FillLB method. Start by retrieving the DataTable from
Session state.
private void FillLB()
{
DataTable dataTable = (DataTable) Session["dataTable"];
16. Clear the list box and fill it, by iterating over the Rows in the DataTable.

For each row you extract from the Rows collection, index on the
ProductName column, and call ToString on the results to get a string that
you can add to the Items collection of the list box.
ASP.NET
C# Professional Skills Development 19-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
lbProducts.Items.Clear();
// for each row in the table, fill the list
foreach (DataRow dataRow in dataTable.Rows)
{
lbProducts.Items.Add(
dataRow["ProductName"].ToString());
}
}
17. Return to the designer and double-click on the list box. This opens the
default event handler, which is called each time the user changes the
selection.
private void lbProducts_SelectedIndexChanged(
object sender, System.EventArgs e)
{
18. When the user changes the selection you want to fill the text boxes with
the data from the appropriate DataRow. To do so, get the DataTable from
session state, and then get the appropriate row based on the new
SelectedIndex property of the list box lbProducts.
DataTable dataTable =
(DataTable) Session["dataTable"];
DataRow selectedRow =
dataTable.Rows[lbProducts.SelectedIndex];
19. Use the name of the column as an index into the row; extract the value and
assign it to the appropriate text box.
txtProductName.Text =
selectedRow["ProductName"].ToString();
20. Do the same for all the other text boxes.
Programming Web Forms
19-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
txtProductID.Text =
selectedRow["ProductID"].ToString();
txtSupplierID.Text =
selectedRow["SupplierID"].ToString();
txtCategoryID.Text =
selectedRow["CategoryID"].ToString();
txtQuantityPerUnit.Text =
selectedRow["QuantityPerUnit"].ToString();
txtUnitPrice.Text =
selectedRow["UnitPrice"].ToString();
txtUnitsInStock.Text =
selectedRow["UnitsInStock"].ToString();
txtUnitsOnOrder.Text =
selectedRow["UnitsOnOrder"].ToString();
txtReorderLevel.Text =
selectedRow["ReorderLevel"].ToString();
21. Set the value for the drop-down list, setting the selected index in the list
to
1 if the value is false, or 0 if it is true.
cbDiscontinued.SelectedIndex =
selectedRow["Discontinued"].ToString() ==
"False" ? 1 : 0;
Implementing Update, etc.
You wont bother implementing update, delete, and so forth, because the
issues here are identical to those covered in other chapters. The important fact

here is this: the fact that you are implementing this as a Web application rathe
r
than a Windows application does not change how you interact with the
database. All the issues of updating apply, and you must be especially careful
about concurrency, because Web applications typically are written for multiple
users.
ASP.NET
C# Professional Skills Development 19-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Binding Data
Many of the controls allow you to bind data to them directly rather than filling

them by hand. In the previous example, you filled the ListBox control in the
FillLB method:
foreach (DataRow dataRow in dataTable.Rows)
{
lbProducts.Items.Add(
dataRow["ProductName"].ToString());
}
If you prefer, you can just as easily bind the control to the DataTable.
Try It Out!
1. Reopen the previous example.
2. Eliminate the call to FillLB in PageLoad.
// FillLB();
3. Set the DataSource property of the ListBox to the DefaultView of the
DataTable.
lbProducts.DataSource = dataTable.DefaultView;
4. Set the DataTextField of the ListBox.
lbProducts.DataTextField = "ProductName";
5. Call DataBind to bind the data from the table to the list box.
lbProducts.DataBind();
See
WebControls.sln
Programming Web Forms
19-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
6. Run the application; the list box is filled just as it was when you filled it

manually.
Other Rich Controls
ASP.NET provides a variety of powerful rich data controls. One of the most
interesting is the Calendar control. You can get started with this just by
dragging it onto your form.
Try It Out!
1. Reopen the previous project.
2. Drag a Calendar onto the form.
3. Run the application.
This adds a pretty powerful calendar widget to your form, as illustrated in
Figure 5.
Figure 5. The Calendar control.
4. The Calendar supports a number of events. One useful event is
SelectionChanged. To use this event, create three labels, and place them
one below the other, each to the right of the calendar.
5. Name the three labels lblToday, lblSelected, and lblCount.
6. Set the text of all three to blank.
7. Size the first label so its wide enough to hold a long message.
See
WebControls.sln
ASP.NET
C# Professional Skills Development 19-21
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
8. Shift click on the other labels and use Format|Align|Lefts to align the
labels, and Format|MakeSameSize|Both to make all the labels the same
size, as shown in Figure 6.
Figure 6. Setting the labels.
9. Click on the calendar control in your designer.
10. Click on the lightning bolt to see the events.
11. Navigate to SelectionChanged and fill in OnSelectionChanged.
12. Click enter; you should be taken to the event handler
13. Add code to get the current date and set the text of lblToday.
lblToday.Text = "Today's Date is " +
cal.TodaysDate.ToShortDateString();
14. Test to see if the date has been selected. To do so, test the selected date
against the constant DateTime.MinValue. If the selected date is not the
MinValue date, then a date has been selected, and you can set the
lblSelected label.
if (cal.SelectedDate != DateTime.MinValue)
lblSelected.Text = "The date selected is "
+ cal.SelectedDate.ToShortDateString();
15. Get the Count property of the SelectedDates collection from the Calendar,
and convert it to a string that you can assign to the Text property of
lblCount.
Programming Web Forms
19-22 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
lblCount.Text = "Count of Days Selected: "
+ cal.SelectedDates.Count.ToString();
16. Run the application and click on a Date. The labels are populated, as
shown in Figure 7.
Figure 7. Populating the labels.
17. Return to the designer and click on the calendar. Change the
SelectionMode property from Day to DayWeekMonth and rerun the
application. You may have to reposition the labels because the calendar
will resize. You can now select an entire week, as shown in Figure 8.
Figure 8. Selecting a week.
18. Before you leave this example, play with the properties. You can set colors
and fonts and otherwise make your calendar look more professional and
appealing (or ugly and absurd) as shown in Figure 9.
ASP.NET
C# Professional Skills Development 19-23
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 9. Using color and adjusting fonts.
Programming Web Forms
19-24 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
ASP.NET is the successor technology to ASP.
Web forms provide a Rapid Application Development (RAD) for Web
application development.
ASP.NET is an event-driven environment.
Many events cause the page to be posted back to the server where your
code-behind page runs on the server.
You can test for postback with the IsPostBack property of the form.
The Web is inherently stateless.
WebForms support state for controls with the hidden ViewState field.
ASP.NET supports session state.
ASP.NET supports five types of controls: HTML, HTML Server,
Web, Validation, and User/Custom.
Whatever controls you use, HTML is sent to the browser.
ASP.NET
C# Professional Skills Development 19-25
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. What are the key differences between ASP and ASP.NET?
2. Are WebForms the same as WindowsForms?
3. How do you use the IsPostBack property of Form?
4. How do you maintain the state of your controls in ASP.NET?
5. How do you add an object to Session state?
Programming Web Forms
19-26 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. What are the key differences between ASP and ASP.NET?
ASP.NET is an event-driven technology. In addition, the HTML is
separated from the code, with the code in a compiled codebehind
page.
2. Are WebForms the same as WindowsForms?
While these two technologies are very similar, they are distinct.
The controls available in the WebForms may look very much like
the controls in WindowsForms but they have different abilities,
and are transformed to simple HTML when sent to the browser.
3. How do you use the IsPostBack property of Form?
The IsPostBack property is used to test whether this is the first
time the form has been loaded (false) or whether the form is
being reloaded due to a postback event (true).
4. How do you maintain the state of your controls in ASP.NET?
Control state is maintained for you by the ASP.NET framework in
the VIEWSTATE hidden text control.
5. How do you add an object to Session state?
You add an object to Session state by using the index operator
and passing in a string representing the name of the Session
variable.
ASP.NET
C# Professional Skills Development 19-27
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 19:
Programming Web
Forms
Lab 19:
Programming Web Forms
19-28 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 19 Overview
In this lab youll learn to work with Web forms.
To complete this lab, youll need to work through three exercises:
Lab Setup
Creating a Web Application
Creating Web Controls
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-bystep

instructions.
Lab Setup
C# Professional Skills Development 19-29
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab Setup
Web forms created with Visual Studio .NET have files in two different
directories on your local computer. The bulk of the files are in a subdirectory
under the localhost virtual directory. On most machines, the default physical
location for localhost corresponds to the directory c:\inetpub\wwwroot.
In addition, there is also a solution file, with an extension of sln, located in
the
default projects directory. This directory will vary from machine to machine,
and is specified in Visual Studio .NET under
Tools|Options|Environment|Projects and Solutions.
In order to utilize the sample projects included with this lab, you will need to

perform the following steps:
1. The projects included as part of this lab are contained within two
directories: From inetpub and From Visual Studio Projects. Copy these
two directories to a location on your local machine. They do not
necessarily have to be copied to the existing inetpub or projects directories.
2. Create a virtual directory for each project that you wish to open in Visual
Studio .NET. To do this:
a. Click Start|Settings|Control Panel|Administrative
Tools|Computer Management.
b. In the left pane, drill down by clicking on the plus signs next to
Services and Applications, Internet Information Services, Internet
Information Services, and Default Web Site.
c. Right-click on Default Web Site and select New|Virtual
Directory.
d. Follow the wizard to create a new virtual directory for the project
you want to work on. The alias name can be any name you wish,
but to avoid confusion, it makes sense to name the alias the same
as the directory name.
So for example, if the project you will be working on is in the
subdirectory WebHelloWorldCompleted, then that would also be
a good alias name.
e. For the Directory, click the Browse button to browse to the correct
subdirectory under the From inetpub directory.
For Access Permissions, use the defaults.
3. Edit the sln file so that it points to the correct URL incorporating the
virtual directory you just created. The sln file is contained in the From
Visual Studio Projects directory.
Lab 19:
Programming Web Forms
19-30 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
a. Open the sln file in a text editor such as Notepad.
b. The second line will have the name of the project followed by a
URL that will look something like the following.
"http://localhost/ASPNET/Labs/WebHelloWorldCompleted/
WebHelloWorldCompleted.csproj"
c. Edit the URL (in quotes in the sln file) so that it uses the virtual
directory you just created. In this example, the URL would be
edited to look like the following.
"http://localhost/WebHelloWorldCompleted/
WebHelloWorldCompleted.csproj"
d. Note that the final node of the URL is a file with a csproj
extension.
e. Save and close the sln file.
4. Now you can open the project in Visual Studio .NET either by doubleclicking
the sln file in Windows Explorer or opening Visual Studio .NET,
clicking the Open Project button on the Start page, and navigating to the
sln file.
Creating a Web Application
C# Professional Skills Development 19-31
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Creating a Web Application
Objective
In this exercise, youll recreate the Hello World Web application using Visual
Studio.
Things to Consider
Visual Studio is writing much of the infrastructure for you, but the
fundamentals are unchanged.
Your form will be separated from your code-behind file. The codebehind
will house the event handlers.
You need to decide whether to use the grid layout, which will use
explicit coordinates to place your controls. If not, you can use the flow
layout, which will just add the controls one by one; you may then
decide to put the controls into a table to place them as you like.
Step-by-Step Instructions
1. Create a new Visual C# ASP.NET Web application in Visual Studio
named WebHelloWorld.
2. Drag a label onto the form, and set the text to Hello World From
ASP.NET.
3. Set its name to lblOutput.
4. Stretch the label to fit the words.
5. Drag a button onto the form. Name it btnChange and set its text to
Change!.
6. Double-click on the button to open the event handler.
7. Enter the code to change the text.
lblOutput.Text = "Goodbye world!";
Lab 19:
Programming Web Forms
19-32 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
8. Press CTRL+F5 to start the application. Your Web application should look
like Figure 10.
Figure 10. Running the application.
9. Click the Change button to change the text. Your application should look
like Figure 11.
Creating a Web Application
C# Professional Skills Development 19-33
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 11. The application after clicking the Change button.
10. Close the browser.
Lab 19:
Programming Web Forms
19-34 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Creating Web Controls
Objective
In this exercise, youll recreate a form tied to the database to examine a subset
of the data known about the products table.
Things to Consider
If your page is not posted back, youll want to fill the list box.
Double-clicking on the list box will generate the
SelectedIndexChanged method handler.
When you set the drop-down list control, you must set the appropriate
index value.
Step-by-Step Instructions
1. Open the WebControlsStarter project in Visual Studio .NET by
following the steps in the Lab Setup section of this lab. This starter project
has all the controls already in place.
2. In the Page_Load event handler, test that you are not responding to a post
back.
if (!IsPostBack)
{
3. Set up the connection string to the Northwind database. Be certain to use
the server name, userID, and password for your SQL Server account.
string strConnection =
"server=YourServer; uid=sa; pwd=YourPassword;
database=northwind";
4. Set up a command string to select all the fields from the Products table.
Creating Web Controls
C# Professional Skills Development 19-35
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
string strCommand = "Select * from Products";
5. Instantiate a SQL Server specific DataAdapter with the command and
connection strings.
SqlDataAdapter dataAdapter =
new SqlDataAdapter(strCommand, strConnection);
6. Instantiate a DataSet.
DataSet dataSet = new DataSet();
7. Fill the DataSet using the DataAdapter.
dataAdapter.Fill(dataSet,"Products");
8. Extract the table you just created.
DataTable dataTable = dataSet.Tables[0];
9. Preserve the DataTable in session state.
Session["dataTable"] = dataTable;
10. Call FillLB (a method youll write in Step 12).
FillLB();
11. Add the values true and false to the drop-down list control.
cbDiscontinued.Items.Add("True");
cbDiscontinued.Items.Add("False");
12. Implement the FillLB method.
Lab 19:
Programming Web Forms
19-36 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
private void FillLB()
{
13. Extract the DataTable from Session state to a local object.
DataTable dataTable = (DataTable) Session["dataTable"];
14. Clear the contents of the list box.
lbProducts.Items.Clear();
15. Iterate over the rows in the DataTable, filling the list box.
foreach (DataRow dataRow in dataTable.Rows)
{
lbProducts.Items.Add(dataRow["ProductName"].ToString());
}
16. Implement the SelectedIndexChanged method.
private void lbProducts_SelectedIndexChanged(
object sender, System.EventArgs e)
{
17. Extract the DataTable from session state and save to a local object.
DataTable dataTable = (DataTable) Session["dataTable"];
18. Extract the Selected row from the DataTable and store in a local DataRow
object.
DataRow selectedRow =
dataTable.Rows[lbProducts.SelectedIndex];
19. Fill the text fields from the selected row.
Creating Web Controls
C# Professional Skills Development 19-37
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
txtProductName.Text =
selectedRow["ProductName"].ToString();
txtProductID.Text = selectedRow["ProductID"].ToString();
txtSupplierID.Text = selectedRow["SupplierID"].ToString();
txtCategoryID.Text = selectedRow["CategoryID"].ToString();
txtQuantityPerUnit.Text =
selectedRow["QuantityPerUnit"].ToString();
txtUnitPrice.Text = selectedRow["UnitPrice"].ToString();
txtUnitsInStock.Text =
selectedRow["UnitsInStock"].ToString();
txtUnitsOnOrder.Text =
selectedRow["UnitsOnOrder"].ToString();
txtReorderLevel.Text =
selectedRow["ReorderLevel"].ToString();
20. Set the drop-down list control to true or false.
cbDiscontinued.SelectedIndex =
selectedRow["Discontinued"].ToString() ==
"False" ? 1 : 0;
21. Build and run the application. Scroll to Teatime Chocolate Biscuits and
click on it. The fields will be filled in as shown in figure Figure 12.
Lab 19:
Programming Web Forms
19-38 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 12. Running the application.
Attributes and Reflection
C# Professional Skills Development 20-1
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Attributes and
Reflection
Objectives
Understand the difference between custom and intrinsic attributes.
Understand what an attribute target is and how targets are described.
Create custom attributes and designate their target elements.
Use reflection to view the metadata youve added to your program
with attributes.
Use reflection to discover the types and methods of classes in
assemblies.
Use reflection to dynamically invoke a method in an assembly.
Attributes and Reflection
20-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Attributes
.NET applications consist of code, data, and metadata. The metadata is
information about your program that is stored along with your program. This
makes your program fully self-contained and (ideally) self-documenting.
Key Term
Attribute An object that represents metadata.
Attributes come in two flavors: intrinsic and custom. Intrinsic attributes are
part of the C.L.R. (Common Language Runtime), while custom attributes are
created by you to use in your own code. Most programmers will use only
intrinsic attributes.
You associate an attribute with an element in your program. That element is
said to be the target of the attribute. The designer of the attribute determines

what the legal targets for that attribute are. Potential targets are:
Assembly
Class
Constructor
Delegate
Enum
Event
Field
Interface
Method
Module
Parameter
Property
ReturnValue
Struct
The All target is the same as listing all of the above. The ClassMembers
attribute is the same as listing all the bold elements above.
Attributes
C# Professional Skills Development 20-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Most intrinsic attributes are used when interoperating with COM (a topic
covered in another chapter), though some are used when remoting or
serializing objects (also covered in other chapters). Some attributes are used i
n
helping to organize large projects.
Custom Attributes
You can create your own attributes, and doing so is one of the best ways to
understand what attributes are and how they are used. In the next example, you
will create your own attribute to assist in commenting your code. Using an
attribute allows you to access the comment at run time, through a process
called reflection, which is discussed later in this chapter.
Try It Out!
Imagine your development team wants to keep track of code reviews. Each
time you finish a code review youd like that noted in the code itself, along
with the date of the review and the name of the reviewer. By having this
information in the code, you can see it in context, but you can also extract the

information to a database to make searching easier. You will write another
application that will go through your code and find the reviews using reflection

(youll learn about reflection shortly).
In this example youll write a simple console application to demonstrate how
to create custom attributes. To get started, create an Employee class, so that
you can assign attributes to the methods and properties of the class.
1. Start by creating a new console application.
2. Create a class Employee and give it a few private member variables.
class Employee
{
private string name;
private string empID;
private int age;
private int salLevel = 1;
3. Add a constructor and a SalaryLevel property.
See Attributes.sln
Attributes and Reflection
20-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public Employee(string name, string empID, int age)
{
this.name = name;
this.empID = empID;
this.age = age;
}
public int SalaryLevel
{
get
{
return salLevel;
}
set
{
salLevel = value;
}
}
4. You can create an Employee and get or set the Employees salary. Add
one more method, BumpSalary that takes an integer and increments the
salary by that amount.
public void BumpSalary(int bump)
{
salLevel += bump;
}
5. Create a Tester class. Implement a Run method to instantiate an Employee
object and bump the salary.
Attributes
C# Professional Skills Development 20-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
class Tester
{
static void Main()
{
Tester t = new Tester();
t.Run();
}
public void Run()
{
Employee emp1 = new Employee("Fred","101",35);
Console.WriteLine(
"emp1 sal before bump: {0}", emp1.SalaryLevel);
emp1.BumpSalary(4);
Console.WriteLine(
"emp1 sal after bump: {0}", emp1.SalaryLevel);
} // end Run
} // end Tester class
The output is shown in Figure 1.
Figure 1. Testing the Employee class.
Creating the Attribute
You are ready to create the Attribute. It is important to realize that attribute
s
are classes. All attribute classes derive (ultimately) from System.Attribute.
When you create an attribute class you must identify the target. To do so use a
(surprise!) attribute. The attribute you use is the AttributeUsage attribute. Th
e
AttributeUsage attribute is metadata about your metadata, or meta-metadata.
Attributes and Reflection
20-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
Return to the earlier example and add a new Attribute class.
1. Add a new class CodeReviewAttribute and derive from System.Attribute.
class CodeReviewAttribute : System.Attribute
{
2. Set the targets for your new class, using the AttributeUsage attribute. To
set the targets, use the AttributeTargets enumeration. You may or multiple
values together. (When you or two values, you combine them using the
OR operator). Set the AllowMultiple attribute to true. That allows you to
assign more than one CodeReviewAttribute to any given target:
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Interface |
AttributeTargets.Method |
AttributeTargets.Field |
AttributeTargets.Property, AllowMultiple=true)]
class CodeReviewAttribute : System.Attribute
{
3. Give the new attribute class three private members.
private string reviewDate;
private string reviewerName;
private string reviewComments;
4. Create three properties. The Date and Name properties will be read-only
and the Comment property will be read/write.
See Attributes.sln
Attributes
C# Professional Skills Development 20-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public string date
{
get
{
return reviewDate;
}
}
public string name
{
get
{
return reviewerName;
}
}
public string comment
{
get
{
return reviewComments;
}
set
{
reviewComments = value;
}
}
5. Create a constructor that allows you to set two of the attributes.
public CodeReviewAttribute( string date, string name)
{
reviewDate = date;
reviewerName = name;
}
Attributes and Reflection
20-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Positional and Named Attributes
Attributes that are set in the constructor are called Positional attributes.
Attributes that are set through a property are called Named attributes. In the
previous example, the date and name are positional. The date must be passed
as the first argument to the constructor and the name must be set as the second.

The comment itself, however, is a positional parameter.
You can see how this works, by adding a CodeReviewAttribute to the
Employee class. Since AttributeTargets.Class is one of the targets of the
CodeReviewAttribute it is legal to add a comment to the class itself:
[CodeReviewAttribute("08/01/2005", "Jesse Liberty",
comment="Do we have a conflict with this name?")]
class Employee
{
Notice that the date and name are just passed in positionally, but the comment
attribute must be identified by name.
AllowMultiple
The AllowMultiple attribute that you added to the AttributeUsage attribute
determines if any given target can be the recipient of more than one
CodeReviewAttribute. Since you set this to true, you may add additional
CodeReviewAttributes to the class:
[CodeReviewAttribute("08/01/2005", "Jesse Liberty",
comment="Do we have a conflict with this name?")]
[CodeReviewAttribute("08/02/2005", "James Supervisor",
comment="Let's find another name for this class")]
class Employee
{
Attributes
C# Professional Skills Development 20-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
Return to the earlier example and add Attributes to the Employee class.
1. Add attributes to the class as discussed earlier.
[CodeReviewAttribute("08/01/2005", "Jesse Liberty",
comment="Do we have a conflict with this name?")]
[CodeReviewAttribute("08/02/2005", "James Supervisor",
comment="Let's find another name for this class")]
class Employee
{
2. Add a CodeReviewAttribute to the salLevel property.
[CodeReviewAttribute("08/02/2005", "Jesse Liberty",
comment = "does a default make sense for this?")]
private int salLevel = 1;
3. Add an attribute to the constructor.
[CodeReviewAttribute("08/02/2005", "James Supervisor",
comment = "Let's change the empID to int")]
public Employee(string name, string empID, int age)
{
this.name = name;
this.empID = empID;
this.age = age;
}
4. Add an attribute to the method.
See Attributes.sln
Attributes and Reflection
20-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
[CodeReviewAttribute("08/02/2005", "James Supervisor",
comment = "Do we need this method if there is a Salary
property?")]
public void BumpSalary(int bump)
{
salLevel += bump;
}
5. Add an attribute to a property.
[CodeReviewAttribute("08/02/2005", "James Supervisor",
comment = "Should this be read only?")]
public int SalaryLevel
{
get
{
return salLevel;
}
set
{
salLevel = value;
}
}
6. Build the application and run it. The results are shown in Figure 2. Notice
that it is totally unchanged! The attributes are in the code, but there is no
change to the output.
Figure 2. Running the application with attributes.
7. Perhaps you should be skeptical that the attributes are really there? Open
ILDasm (the Intermediate Language DisASseMbler) as shown in Figure 3.
To open your program in the disassembler, click on File|Open and
navigate to your project, then navigate into the Bin and Debug directories
and click on the executable program.
Attributes
C# Professional Skills Development 20-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 3. Opening ILDasm.
8. Double-click on the Employee class to open it. Double-click on the first
entry under the class to open the meta information about the class, as
shown in Figure 4. You see that the two class comments are there, as
expected (circled and highlighted in the Figure 4).
Figure 4. Meta information about the class.
Return to the disassembler, and double-click on BumpSalary. Once again, you
see the expected attribute for that method, as shown in Figure 5.
Attributes and Reflection
20-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 5. The attributes for BumpSalary.
The attributes are in the class, but what good are they if you cant get to them,
and they have no impact on the running of the program? This is what reflection
is for.
Reflection
C# Professional Skills Development 20-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Reflection
Reflection is the process of a program looking at itself. The goal of reflection
is
to obtain objects within the running program that describe either the currently
running program, or by extension, any other program.
With reflection you cannot only examine the methods and structure of the
program, but you can also get hold of the metadata. Reflection is tightly
coupled with attributes, so it makes attributes useful.
Reflection is used for viewing metadata as well as type discovery, late binding,

and dynamic invocation.
View MetaData
This section will look at using reflection to view metadata. Later sections will

examine type discovery, late binding, and dynamic invocation.
Reflection allows you to examine the metadata embedded with your program,
including metadata created by custom attributes.
This section examines the metadata from the previous example. Youll then go
on, in subsequent examples, to discover the types in an assembly and then to
bind to those types and invoke methods on them.
Try It Out!
In the next example, youll reflect on the attributes you created in the previous
example.
1. Reopen the previous example.
2. At the top of the program, add the namespace youll need for reflection.
using System.Reflection;
3. In the Run method, add an object of type MemberInfo. Initialize this object
by calling the typeOf operator. TypeOf returns an object of type
System.Type. MemberInfo is the base class of Type and allows you to
obtain information for all members of a class.
See Attribute.sln
Attributes and Reflection
20-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
MemberInfo inf = typeof(Employee);
4. Call the GetCustomAttributes method on the MemberInfo object. This
returns an array of the custom attributes. You specify, in the parameter,
whether you want the type to search up its inheritance chain; in this case
you do not want the attributes of the base class (and in this case there is no
base class) so you specify false. The attributes are returned as an array of
objects.
object[] attributes = inf.GetCustomAttributes(false);
5. Iterate over the array of attributes. Cast each to a CodeReviewAttribute
(this is safe, you have only one kind of attribute in this example) and then
use that CodeReviewAttribute to print the value of the date, name, and
comment.
foreach (object attribute in attributes)
{
CodeReviewAttribute attr =
(CodeReviewAttribute) attribute;
Console.WriteLine(
"\nThis class was reviewed on {0} by {1}. " +
"\nComments: {2}",
attr.date, attr.name, attr.comment);
}
6. Use the typeof keyword again; this time store the results in a Type object.
Type derives from MemberInfo and provides methods to get an array of
MemberInfo objects about each member in the type.
Type employeeType = typeof(Employee);
MemberInfo[] mbrInfoArray = employeeType.GetMembers();
7. Iterate over the collection of MemberInfo objects and get the attributes for
each member of the class.
foreach (MemberInfo mbrInfo in mbrInfoArray)
{
attributes = mbrInfo.GetCustomAttributes( false);
Reflection
C# Professional Skills Development 20-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
8. Iterate over the collection of attributes returned for each member of the
class, and display the attribute information.
foreach (object attribute in attributes)
{
CodeReviewAttribute attr =
(CodeReviewAttribute) attribute;
Console.WriteLine(
"\nThis code was reviewed on {0} by {1}. " +
"\nComments: {2}",
attr.date, attr.name, attr.comment);
}
9. Compile and run the program. This time the attributes are reflected and
displayed to the console, as shown in Figure 6.
Figure 6. Displaying the attributes.
Type Discovery
Using reflection, you can examine the types in an assembly and instantiate
them. This can be very useful in creating custom scripts.
Attributes and Reflection
20-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
To see how Type Discovery works, youll create a simple console application
to discover the types in the Mscorlib.dll provided with .NET.
1. Create a new console application and name it Discovery.
2. Create a class Discovery and a Run method.
3. In the Run method, create an instance of the Assembly class. An assembly
is the basic unit of compilation; representing a DLL or an EXE file.
Initialize the Assembly with the static Load method, passing in the name
of the DLL you want to examine.
class Discovery
{
static void Main(string[] args)
{
Discovery d = new Discovery();
d.Run();
}
public void Run()
{
Assembly asm = Assembly.Load("Mscorlib.dll");
4. Call GetTypes on the Assembly object, getting back an array of Type
objects representing all the types in the assembly.
Type[] types = asm.GetTypes();
5. Iterate over the types. You can pass the type to WriteLine. The ToString()
method will be called implicitly and the Type will display its name. Use
the Length property of the array to display how many types were found.
foreach (Type type in types)
{
Console.WriteLine("Type is {0}", type);
}
Console.WriteLine("\n{0} types found.",types.Length);
See discovery.sln
Reflection
C# Professional Skills Development 20-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
6. Compile and run the program. The results can be quite long. An excerpt is
shown in Figure 7.
Figure 7. An excerpt of the types found.
Late Binding and Dynamic Invocation
Late binding to a method allows you to creating dynamically instantiated
objects. That is, you can bind to objects that you discover through type
discovery, and then once bound, you can use those objects as if they were
compiled into your program (though you will pay a performance penalty).
Once youve discovered an object, and bound to it, you can use reflection to
invoke methods at run time. You can pass parameters to methods of a late
bound object, and you can get back return values. Again, the only problem is
that the performance of late bound objects is far slower than with compiled
objects.
Try It Out!
To see how late binding and dynamic invocation works, youll create a simple
console application to dynamically invoke the Pow method from the
System.Math dll.
To be perfectly clear, there is no good reason to invoke this method
dynamically. You know the method exists, you can just instantiate an object
and invoke the method. Youll do so here just to demonstrate how late binding
and dynamic invocation works.
See lateBinding.sln
Attributes and Reflection
20-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
1. Create a new console application and name it lateBinding.
2. Create a Tester class, and within it a Run method. In the Run method,
instantiate a Type object and assign to it the result of calling the static
method GetType, passing in the name of the type you are interested in
using.
class Tester
{
static void Main(string[] args)
{
Tester t = new Tester();
t.Run();
}
public void Run()
{
Type mathType = Type.GetType("System.Math");
3. Instantiate an instance of the type youve asked for by calling the static
method CreateInstance on the Activator class, passing in the Type object
you just obtained.
Object obj =
Activator.CreateInstance(mathType);
4. obj now represents the math type. You want to call a method on that type.
To do so, you need to call GetMethod and pass in an array of Type objects
that describe the types of the parameters. To do so, youll create the array
and then fill the members with the Type objects for a Double (Pow takes
two doubles).
Type[] parameterTypes = new Type[2];
parameterTypes[0] = Type.GetType("System.Double");
parameterTypes[1] = Type.GetType("System.Double");
What is going on here is that you pass the string System.Double to the static
method GetType of the Type class. What you get back is a Type object for the
System.Double class. You add that to your array of Type objects.
5. You are now ready to call GettMethod to get the Pow method. You pass in
a string representing the name of the method and the array of Type objects
Reflection
C# Professional Skills Development 20-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
describing the parameters for the method. Remember, methods can be
overloaded by varying the number or type of the parameters; this allows
the system to return the one method that matches the signature: name and
parameters.
MethodInfo powInfo =
mathType.GetMethod("Pow",parameterTypes);
6. You can now invoke the method. You must call Invoke on the MethodInfo
object you got back in Step 5, passing in the Object you got back from
CreateInstance in Step 3, along with an array of objects representing the
parameters.
object[] parameters = new object[2];
parameters[0] = 5;
parameters[1] = 3;
object returnValue = powInfo.Invoke(obj,parameters);
7. The value returned by Pow is an object that you can cast to a Double if you
need to manipulate the return value. In this case, youll just pass that
returned object to WriteLine which will call ToString on the object and
display its value:
Console.WriteLine(
"5 to the 3rd power is {0}",returnValue);
8. Compile and run the program. The result is shown in Figure 8. Youve
invoked the Pow instance method from the System.Math dll without ever
instantiating an object. This is dynamic invocation; it may not be pretty,
but it is a powerful technique that can be used in dynamic scripting.
Figure 8. Invoking the method dynamically.
Attributes and Reflection
20-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
The .NET Framework provides a number of intrinsic attributes that
you might use when working with COM or network I/O.
You are free to create your own custom attributes.
When you create a custom attribute, you must designate the target for
that attribute (e.g., assembly, class, method, etc.).
You describe the target of your attribute with an attribute!
Attributes are created as classes derived from System.Attribute.
Attributes can be positional or named. Positional attributes are
implemented in the constructor, named attributes are implemented
with properties.
Reflection allows you to inspect the metadata in your program at run
time.
Reflection also allows you to discover the types in an assembly and
the members of a class, both at run time.
Reflection allows you to dynamically invoke methods at run time.
Reflection
C# Professional Skills Development 20-21
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. What does the ClassMembers target indicate?
2. How can you see the attributes embedded in your program?
3. What is the difference between a named and a positional attribute?
4. What does the AllowMultiple attribute do?
5. What does type discovery mean?
6. What does dynamic invocation mean?
Attributes and Reflection
20-22 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. What does the ClassMembers target indicate?
The ClassMembers target indicates that the attribute can be
applied to any of the targets that indicate members of the class
(e.g., method, property, field, etc.).
2. How can you see the attributes embedded in your program?
You can inspect the source code, you can use ILDasm to inspect
the IL code, or you can use reflection to inspect the metadata.
3. What is the difference between a named and a positional attribute?
A named attribute is entered with the name of the attribute
followed by an equal sign and then the value (comment =
hello). With a positional attribute you simply enter the value,
but you must enter positional attributes first, and in the order
designated in the constructor.
4. What does the AllowMultiple attribute do?
The AllowMultiple attribute is used in the attribute describing the
custom attribute, and it allows multiple instances of your custom
attribute to be applied to any given target.
5. What does type discovery mean?
Type discovery is the process of finding out what classes are
defined in an assembly.
6. What does dynamic invocation mean?
Dynamic invocation is the process of calling a method at run time
on an object that was not defined in your program, but that is
accessed through reflection.
Reflection
C# Professional Skills Development 20-23
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 20:
Attributes and
Reflection
Lab 20:
Attributes and Reflection
20-24 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 20 Overview
In this lab youll learn about using reflection for Discovery and Late Binding.
To complete this lab, youll need to work through two exercises:
Using Reflection for Discovery
Using Reflection for Late Binding
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-bystep

instructions.
Using Reflection for Discovery
C# Professional Skills Development 20-25
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Using Reflection for Discovery
Objective
In this exercise, youll use the Reflection methods to discover the methods of
the Array class.
Things to Consider
A Type object can be used to gather information about any known
type.
The Type object has a GetMembers method that returns an array of
MemberInfo objects, each of which has information about a particular
member of the type.
Step-by-Step Instructions
1. Create a new Windows Console Application named Discovery.
2. At the top of the file, add a using statement for System.Reflection.
using System.Reflection;
3. Change the namespace to DiscoveryStarter.
namespace DiscoveryStarter
{
4. Implement the class DiscoveryStarter.
class DiscoveryStarter
{
5. Implement Main to run the program.
Lab 20:
Attributes and Reflection
20-26 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
static void Main(string[] args)
{
DiscoveryStarter d = new DiscoveryStarter();
d.Run();
}
6. Implement the Run method.
public void Run()
{
7. Create a Type object and fill it with the Type object returned by the Static
method GetType. Pass in the string "System.Array".
Type theType = Type.GetType("System.Array");
8. Display the Type you received.
Console.WriteLine("Got this type: {0}", theType);
9. Call GetMembers on the Type object and assign the resulting array to a
local array of type MemberInfo.
MemberInfo[] memberInfoArray = theType.GetMembers();
10. Iterate over the array, and display each MemberInfo object and also the
MemberType property of that object.
foreach (MemberInfo mi in memberInfoArray)
{
Console.WriteLine("{0} is a member of {1}",
mi, mi.MemberType);
}
Using Reflection for Discovery
C# Professional Skills Development 20-27
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
11. End the Run method, the DiscoveryStarter class, and the namespace.
} // end Run method
} // end class DiscoveryStarter
} // end namespace
12. Run the application. Your results should look like Figure 9.
Lab 20:
Attributes and Reflection
20-28 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 9. Running the Discovery application.
Using Reflection for Late Binding
C# Professional Skills Development 20-29
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Using Reflection for Late Binding
Objective
In this exercise, youll late bind to an existing method in the Math class,
through reflection.
Things to Consider
You can get a Type object by calling the static GetType method on the
Type class.
You can get info about a particular method by calling GetMethod on
the Type object.
When you ask for a method, you must pass in an array describing the
parameter types.
Step-by-Step Instructions
1. Create a new console application named LateBinding.
2. Add a using statement to the top of the file for System.Reflection.
using System.Reflection;
3. Change the namespace to LateBindingStarter.
namespace LateBindingStarter
{
4. Implement the class Tester.
class Tester
{
5. Implement Main to run the program.
Lab 20:
Attributes and Reflection
20-30 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
static void Main(string[] args)
{
Tester t = new Tester();
t.Run();
}
6. Implement the Run method.
public void Run()
{
7. Create a Type object for the math type and initialize it by calling the stati
c
GetType method, passing in the string "System.Math".
Type mathType = Type.GetType("System.Math");
8. Create an instance of the Math type by calling the static method
CreateInstance of the Activator class.
Object obj =
Activator.CreateInstance(mathType);
9. Create an array to hold the parameter types. In this case there is only one
parameter.
Type[] parameterTypes = new Type[1];
10. Fill the array with the Double type you retrieve by passing the string
"System.Double" to the static GetType method of the Type class.
parameterTypes[0] = Type.GetType("System.Double");
11. Call the GetMethod instance method of the Type object you created in
Step 7. Pass in the string "Sqrt" to get the square root method; also pass in
your array of parameter types. Assign the resulting MethodInfo object to a
local instance.
Using Reflection for Late Binding
C# Professional Skills Development 20-31
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
MethodInfo methodInfo =
mathType.GetMethod("Sqrt",parameterTypes);
12. Create an array to hold the parameter.
object[] parameters = new object[1];
13. Assign the value 16 to the first member of the array of parameters.
parameters[0] = 16;
14. Create an object to hold the value returned by calling the instance member
Invoke on the MethodInfo object. Pass in the object you created in Step 8,
as well as the array of parameters.
object returnValue = methodInfo.Invoke(obj,parameters);
15. Use the object you got back in Step 14 to display the square root of the
parameter you passed in.
Console.WriteLine("The square root of 16 is
{0}",returnValue);
16. End the Run method, the Tester class, and the namespace.
} // end Run method
} // end class Tester
} // end namespace
17. Run the application. Your results should look like Figure 10.
Lab 20:
Attributes and Reflection
20-32 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 10. Running the late binding application.
Threads
C# Professional Skills Development 21-1
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Threads
Objectives
Understand how threads support time slicing and apparently
simultaneous operations.
Create multiple threads running the same or different methods.
Use sleep to delay a thread or yield control of the processor.
Use Abort to request that a thread kill itself.
Instruct a thread to stop processing until other threads complete.
Synchronize two threads using a lock.
Synchronize threads using the Interlocked class.
Threads
21-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Threads in .NET
As a computer user you tend to think of an application or a program. When
you start up your favorite word processor or game, you are actually starting a
process. Each program runs in its own process.
As a programmer, you know that within a process, you may want to have more
than one thing happening at a time. A thread acts as a lightweight process; it
allows more than one thing to appear to happen at the same time without the
same overhead of a full process.
Of course, on most computers there is only one chip, and so only one thing can
actually happen at a time, but the computer is able to switch among a few
things so rapidly that it appears that multiple things are happening all at once
,
as illustrated in Figure 1. In this illustration you have four tasks running at t
he
same time. The chip divides its time, here illustrated as ten time slices per
second, though that is just for illustration purposes. Notice that each task get
s a
time slice and then the chip moves to the next task. Also notice that not every
task receives the same percentage of time; some tasks get two slices per
second, some get more (or less).
Figure 1. Time slicing.
Threads in .NET
C# Professional Skills Development 21-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
It turns out that adding threads to your program can actually slow it down!
This is because time slicing has overhead; the processor must store away its
current state before it switches context to the next task.
Threading can greatly increase the performance of your application, however,
if the multitasking allows the chip to be busy with other tasks, while you
would otherwise be waiting for something slow, like a file read or (even
slower!) an Internet access.
Most programmers will never create their own explicit threads, because the
.NET framework provides higher-level classes for managing most situations
where you would otherwise need to create a thread. That said, if you do want
to create your own threads, you certainly can do so and the .NET framework
provides extensive support for managing your threads.
Creating Threads
The easiest way to create a thread is to instantiate a Thread object. The Thread

constructor requires a delegate of type ThreadStart. The ThreadStart delegate
is given a reference to the method you want to run in the thread.
The delegate describes the signature and return value of the method it will
encapsulate. Thread methods must return void and take no parameters.
Try It Out!
It is easy to create a thread in your program as illustrated in this first simpl
e
example.
1. Create a new console application and name it Threads1.
2. Create a Run method.
3. In the Run method, declare a Thread object and initialize it with a
ThreadStart delegate wrapping the CountUp method (that you will write
shortly) and start the thread.
public void Run()
{
Thread threadOne =
new Thread(new ThreadStart(CountUp));
threadOne.Start();
}
See Threads1.sln
Threads
21-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
4. Create the CountUp method to count from 0 to 10,000 and display the
results on the console.
public void CountUp()
{
for (int i = 0; i < 10000; i++)
Console.WriteLine("Count up: {0}", i);
}
5. Start the application. A command window opens and the thread counts up
to 9,999, as shown in Figure 2.
Figure 2. Running a single thread.
Creating Multiple Threads
While this shows how easy it is to create a thread, it doesnt exactly show off
the power of multithreading. You could have accomplished precisely the same
effect without a thread.
To show the effect of multithreading youll need a second thread. You also
want a way to identify which thread is doing what work. The Thread class has
a static property CurrentThread that returns the currently running thread. This
allows you access to the thread itself from within the running method. The
Thread class also has an instance property Name that allows you to set or
query the name of the thread; a string you provide to uniquely identify a given
thread.
Threads in .NET
C# Professional Skills Development 21-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
In the next example youll create multiple threads and display which thread is
writing to the console at any given moment.
1. Reopen Threads1, the application you just created.
2. Modify the Run method to create a second thread, and initialize it with a
delegate to the same CountUp method.
public void Run()
{
Thread threadOne =
new Thread(new ThreadStart(CountUp));
Thread threadTwo =
new Thread(new ThreadStart(CountUp));
3. Assign names to the threads and then start them.
threadOne.Name = "Thread One";
threadTwo.Name = "Thread Two";
threadOne.Start();
threadTwo.Start();
}
4. Modify the CountUp method to display the name of the thread. Do so by
displaying the Name property of the current thread. Obtain the current
thread using the static property CurrentThread.
public void CountUp()
{
for (int i = 0; i < 10000; i++)
Console.WriteLine("{0},Count up: {1}",
Thread.CurrentThread.Name, i);
}
5. Compile and run the program. Youll see the threads switch off. One
thread runs briefly, then the other, switching back and forth until both have
run their course, as shown in Figure 3.
See Threads1.sln
Threads
21-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 3. Threads switch off.
Notice that the local integer variable i is independent in Thread Two from the
same variable in Thread One. The threads maintain their own instances of
these variables.
You do not have to assign the same method to two threads, of course. You may
assign any method to the delegate as long as it matches the signature for the
delegate.
Multiple Tasks
The various threads can do different things; they can call different methods
that will (appear to) run simultaneously.
Try It Out!
In the next variation on your example, youll create a thread with a second
method, CountDown.
1. Reopen the Threads1 project and add a second method, CountDown.
See Threads1.sln
Threads in .NET
C# Professional Skills Development 21-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public void CountDown()
{
for (int i = 10000; i > 0; i--)
Console.WriteLine("{0},\t\tCount down: {1}",
Thread.CurrentThread.Name,i);
}
2. Modify the Run method to create a third thread, and have the third thread
invoke the CountDown method.
public void Run()
{
Thread threadOne =
new Thread(new ThreadStart(CountUp));
Thread threadTwo =
new Thread(new ThreadStart(CountUp));
Thread threadThree =
new Thread(new ThreadStart(CountDown));
threadOne.Name = "Thread One";
threadTwo.Name = "Thread Two";
threadThree.Name = "Thread Three";
threadOne.Start();
threadTwo.Start();
threadThree.Start();
}
3. Compile and run the program. All three threads start, and take turns
running. The third thread counts down from 10,000 while the others count up.
All three run independently of one another, as shown in Figure 4.
Threads
21-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 4. Running three threads.
Arrays of Threads
You can manage your threads in arrays, rather than creating each one
individually. You do this by simply creating an array of Thread objects and
initializing the array with newly instantiated threads.
Thread[] threads =
{
new Thread( new ThreadStart(CountUp) ),
new Thread( new ThreadStart(CountUp) ),
new Thread( new ThreadStart(CountDown) ),
};
Rather than individually naming each by hand, you can use a counter to set the
name in a loop. You can iterate over the array of threads with a foreach loop.
int counter = 1;
foreach (Thread thread in threads)
{
thread.Start();
thread.Name="Thread Number " + counter.ToString();
Console.WriteLine("Started thread {0}", thread.Name);
counter++;
}
Threads in .NET
C# Professional Skills Development 21-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Sleep
In the previous examples, you allowed the chip to switch among your threads
however frequently it wanted to. There are times, however, when you will
want to suspend a thread momentarily; perhaps to allow some time to pass, or
perhaps to allow another thread time to run. To do so, use the static Sleep
method. You pass in an integer representing the number of milliseconds you
want the thread to sleep (or you can pass in a TimeSpan object, if that is easie
r
in your particular program).
When a thread encounters the Sleep call, the thread is suspended for the
designated time and other threads are allowed to run.
Try It Out!
To see sleep at work (and creating threads in arrays) create a new simple test
application.
1. Reopen the project you were working on in the previous example.
2. Modify Run to create an array of threads, and to start them in a foreach
loop.
See Threads2.sln
Threads
21-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public void Run()
{
Thread[] threads =
{
new Thread( new ThreadStart(CountUp) ),
new Thread( new ThreadStart(CountUp) ),
new Thread( new ThreadStart(CountDown) ),
};
int counter = 1;
foreach (Thread thread in threads)
{
thread.Start();
thread.Name="Thread Number " + counter.ToString();
Console.WriteLine("Started thread {0}",
thread.Name);
counter++;
}
}
3. Modify the CountUp and CountDown methods to count to and from 10
(rather than 10,000) to simplify the display. Add a Sleep call for 1
millisecond to each method. This yields control of the processor and
causes the next thread to run.
Threads in .NET
C# Professional Skills Development 21-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public void CountUp()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("{0}: Count up: {1}",
Thread.CurrentThread.Name,i);
Thread.Sleep(1);
}
}
public void CountDown()
{
for (int j= 10; j > 0; j--)
{
Console.WriteLine("{0}: Count down: {1}",
Thread.CurrentThread.Name,j);
Thread.Sleep(1);
}
}
4. Build the application and run it. As shown in Figure 5, each thread
displays its value and then yields to the next thread. The result is that the
threads run interleaved, one line per thread until they are all completed.
Threads
21-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 5. Yielding with Sleep.
Try increasing the Sleep from 1 to 100. The display should stutter down the
page. Try increasing it to 500 and then to 1,000. You begin to see the delaying
effects of Sleep.
Joining Threads
At times, youll want your current thread to wait until another thread
completes working before your current thread continues. For example, suppose
you wanted to modify the previous example to display the words, All the
threads are done when all three threads complete. You cant just put a
Console.WriteLine statement after you launch the three threads, because the
threads run independently and your Run() method will continue right to the
output as soon as it launches the other threads.
Threads in .NET
C# Professional Skills Development 21-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
Try modifying the previous version to print All threads completed after the
threads are launched,
1. Reopen the previous project.
2. Modify the Run method and add a WriteLine statement as the last line in
the method.
public void Run()
{
Thread[] threads =
{
new Thread( new ThreadStart(CountUp) ),
new Thread( new ThreadStart(CountUp) ),
new Thread( new ThreadStart(CountDown) ),
};
int counter = 1;
foreach (Thread thread in threads)
{
thread.Start();
thread.Name="Thread Number " + counter.ToString();
Console.WriteLine("Started thread {0}",
thread.Name);
counter++;
}
Console.WriteLine("All threads completed.");
}
3. Build and run the application. The results are shown in Figure 6.
See Threads3.sln
Threads
21-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 6. Displaying All threads completed.
Notice that the threads are started and then the All threads completed message
is displayed immediately! It turns out that the Run method is itself in a thread
.
This is the global thread that all programs run. You do not have to create that
thread, it is created for you and it is not named, though you are free to name i
t.
4. Add a line to the top of Run to name the current thread.
public void Run()
{
Thread.CurrentThread.Name = "Global thread";
5. Modify the WriteLine at the bottom of Run to display the name of the
thread.
Threads in .NET
C# Professional Skills Development 21-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Console.WriteLine("{0}: {1}",
Thread.CurrentThread.Name,
"All threads completed.");
6. Run the application again. As you can see in Figure 7, Run is running in a
thread. Threads 1, 2, and 3 are spawned from within the global thread and
are considered child threads of the global thread.
Figure 7. Seeing the global thread.
The reason the display did not work as intended is that you must pause the
global thread, while the child threads run. You do so by joining the global
thread to its children. This instructs the global thread to wait until they
complete before it runs any further. You must explicitly join the global thread
to each thread you want it to wait for.
Threads
21-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
7. Add a line of code to Run to join each thread just before you display the
display of the message All threads completed. Iterate over the threads
array, and for each thread, call the Join method. This joins the current
thread (in this case, the global thread) to the thread on which you call Join.
The current thread will now wait for that thread to complete before
proceeding:
foreach(Thread thread in threads)
thread.Join();
8. Build and run the application. As shown in Figure 8, the global thread now
waits for the others to complete before printing the All threads completed
message.
Figure 8. Joining threads.
Threads in .NET
C# Professional Skills Development 21-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
9. You can make the demise of the threads more explicit by displaying the
end of the thread in each of the CountDown and CountUp methods. After
the loop completes, but just before the method ends, display that the thread
is ending:
public void CountUp()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("{0} Count up: {1}",
Thread.CurrentThread.Name,i);
Thread.Sleep(1);
}
Console.WriteLine("{0} Exiting",
Thread.CurrentThread.Name);
}
public void CountDown()
{
for (int i= 10; i > 0; i--)
{
Console.WriteLine("{0} Count down: {1}",
Thread.CurrentThread.Name,i);
Thread.Sleep(1);
}
Console.WriteLine("{0} Exiting",
Thread.CurrentThread.Name);
}
10. Take the Sleep call out of CountDown to eliminate its yielding the
processor each time it runs a single line.
11. Build and run the application once more. The results are shown in
Figure 9. You can see that thread3 runs its entire course and then exits.
Threads 1 and 2 continue to take turns and then exit when they are done.
Only when all the joined threads have exited does the original thread
resume and print its happy message.
Threads
21-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 9. Exiting threads.
Killing Threads
Normally, threads die of old age. At times, however, due to conditions that
arise while your program is running, you may need to kill a thread. You dont
actually kill a thread so much as ask the thread to commit suicide. To do so,
you call the threads Abort method. When you do this, an exception of type
ThreadAbortedException is thrown, and the thread catches the exception and
cleans up after itself.
Threads in .NET
C# Professional Skills Development 21-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
Try modifying the previous version to kill a couple of threads and handle the
thread exception that is thrown.
1. Reopen the previous example.
2. Create four threads in your initial array.
public void Run()
{
Thread[] threads =
{
new Thread(new ThreadStart(CountUp)),
new Thread(new ThreadStart(CountDown)),
new Thread(new ThreadStart(CountUp)),
new Thread(new ThreadStart(CountDown))
};
int counter = 1;
foreach(Thread thread in threads)
{
thread.Start();
thread.Name = "Thread Number " + counter.ToString();
Console.WriteLine("Started thread {0}",
thread.Name);
counter++;
Thread.Sleep(1);
}
3. Immediately Abort the second and third threads. Then join the threads and
wait for them all to complete.
See Threads4.sln
Threads
21-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
threads[2].Abort();
foreach(Thread thread in threads)
thread.Join();
Console.WriteLine("All threads completed");
} // end Run
4. Modify CountUp to iterate over its value in a try block.
public void CountUp()
{
try
{
for (int i = 0; i < 500; i++)
Console.WriteLine(
"{0},Count up: {1}",
Thread.CurrentThread.Name, i);
}
5. Create a catch for the ThreadAbortException.
catch(ThreadAbortException)
{
Console.WriteLine(
"Thread {0} caught ThreadAbortException. Cleaning up.",
Thread.CurrentThread.Name);
}
In this case you are doing nothing but displaying a message when the thread is
destroyed, but you can imagine cleaning up allocated resources when the
exception is caught.
6. Add a finally block as well, so that your thread can exit cleanly whether it
was aborted or not.
Threads in .NET
C# Professional Skills Development 21-21
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Finally
{
Console.WriteLine(
"{0} Exiting...", Thread.CurrentThread.Name);
}
7. Do the same for the CountDown method.
public void CountDown()
{
try
{
for (int i = 500; i >= 0; i--)
Console.WriteLine(
"{0},Count Down: {1}",
Thread.CurrentThread.Name, i);
}
catch(ThreadAbortException)
{
Console.WriteLine(
"Thread {0} caught ThreadAbortException. Cleaning
up.",
Thread.CurrentThread.Name);
}
finally
{
Console.WriteLine(
"{0} Exiting...", Thread.CurrentThread.Name);
}
}
8. Compile and run the application. The various threads start and then the
second and third thread are stopped by a ThreadAbortException, as shown
in Figure 10. You may need to use CTRL+S to freeze the scrolling display
so that you can see the threads exit.
Threads
21-22 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 10. Aborting threads.
Synchronization
C# Professional Skills Development 21-23
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Synchronization
When you introduce multithreading to your application you expose yourself to
a number of difficult and advanced issues. Various threads may access the
same objects simultaneously and this can cause profound synchronization
problems.
How do you control access to a resource so that only one thread can access it
at the same time? To understand how this is done, consider how you control
access to any limited precious resource, such as a restroom on an airplane.
There may be only one or two restrooms for three or four hundred people. It is
not uncommon for there to be more requests for the resource than can be
accommodated at any one time, but it is imperative that access to the bathroom
be synchronized; you do not want one passenger interrupting another.
Locking
The solution is to lock the door when the resource is in use. When the
passenger is finished with the resource, the bathroom is unlocked. You
perform a similar operation on objects you must synchronize among various
threads. When the first thread wants the object it locks the door. A little ligh
t
goes on above the object saying occupied and the object cannot be accessed
until the first thread is finished, washes its hands, and opens the lock.
Consider the example of a record in a database (or an object in memory for
that matter). The record has the value 20 (to keep things simple). You access
the value from two threads: thread1 and thread2. Thread1 updates the value to
30 and thread2 updates the value to 40. Thread1 then writes the value back,
and then thread2 writes its value back, overwriting the work of thread1. This is

very similar to the problem of two processes interacting with a disconnected
database record.
You see this all the time in threads when you must update a value held in
memory. Thread1 accesses the value, then thread2 does. Thread 1 updates the
value. Thread2 should then be working with the updated value, but because it
got the value before it was updated by thread1, thread2 is now working with
corrupted data.
To solve this, .NET provides a number of locking mechanisms.
Threads
21-24 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
To see the problem youre trying to solve, imagine that you have a shared
resource such as a database record. In this simple example, youll just add to a
class variable.
1. Reopen the previous example.
2. Add a class field to act as the resource shared by various threads.
private int synchValue = 0;
3. Delete the CountDown method; you wont need it.
4. Modify the CountUp method to modify the synchValue field. To do so,
assign the value in synchValue to a local variable, temp.
public void CountUp()
{
try
{
for (int i = 0; i < 20; i++)
{
int temp = synchValue;
This is not unlike fetching a value from a database or other repository. Now
that you have a copy of the value, increment it.
temp++;
5. Sleep for two milliseconds to allow other threads to access the processor.
This mimics the fact that your method might run long enough that it could
be interrupted.
Thread.Sleep(2);
6. When you get access to the processor again, assign the newly incremented
temp back to the member field, and then display the value.
See
Synchronization1.
sln
Synchronization
C# Professional Skills Development 21-25
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
synchValue = temp;
Console.WriteLine("{0} Count up: {1}",
Thread.CurrentThread.Name,synchValue);
7. These are the only changes. Compile and run the program. The results are
shown in Figure 11. You can see that rather than counting up cleanly, the
various threads are duplicating the values.
Figure 11. Unsynchronized threads.
What is happening is that thread1 gets the value 1 and then goes to sleep.
Thread2 gets the value, then thread1 increments the value (from 1 to 2) and
thread2 increments it (also from 1 to 2). They then display the new values: 2
and 2. As thread3 and thread4 come online, they do the same thing. You get
four threads all incrementing the original value in each cycle. This is not good
.
To fix this problem, youll use a lock.
8. Modify the CountUp method. The keyword lock takes a reference to an
object to lock. In this case, youll lock the current object (this) while you
make the changes. Once you have completed your work on the resource,
youll release the lock. The implementation is that you create a scope for
the lock by using braces. When your code exits the block described by the
braces, the lock is released:
See
Synchronization
.sln
Threads
21-26 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public void CountUp()
{
try
{
for (int i = 0; i < 20; i++)
{
// *** new ***
lock(this)
{
int temp = synchValue;
temp++;
Thread.Sleep(2);
synchValue = temp;
}
Console.WriteLine("{0} Count up: {1}",
Thread.CurrentThread.Name,synchValue);
}
}
9. Compile and run the application, as shown in Figure 12.
Synchronization
C# Professional Skills Development 21-27
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 12. Using Lock(this).
Now the values are incremented properly, with no overlap. That said, the
application begins to stutter a bit. This is no surprise, since some of the thre
ads
must wait to get a lock on the object.
The resource (in this case synchValue) is locked from the opening brace until
the closing brace. Notice that within the block you call Sleep, the effect of
which is to release control of the processor (just as if the thread were
interrupted). When the next thread tries to lock the object it is blocked from
doing so; your first object has the lock.
Threads
21-28 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
You can make this explicit with a few WriteLine comments.
10. Before you lock the object, write to the console that you are about to do so
.
Console.WriteLine("{0} trying to lock the object...",
Thread.CurrentThread.Name);
11. Once you get the lock, indicate that you have it by displaying a message to
the console.
lock(this)
{
Console.WriteLine("{0} locked the object...",
Thread.CurrentThread.Name);
12. When you are done with the lock, indicate that youre done with the lock
by displaying a message to the console.
synchValue = temp;
Console.WriteLine("{0} unlocking the object...",
Thread.CurrentThread.Name);
}
13. Modify the Run method to run only a single thread and comment out the
calls to Abort.
Synchronization
C# Professional Skills Development 21-29
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public void Run()
{
Thread[] threads =
{
new Thread( new ThreadStart(CountUp) ),
new Thread( new ThreadStart(CountUp) ),
// new Thread( new ThreadStart(CountUp) ),
// new Thread( new ThreadStart(CountUp) ),
};
int counter = 1;
foreach (Thread thread in threads)
{
thread.Start();
thread.Name="Thread Number " + counter.ToString();
Console.WriteLine(
"Started thread {0}", thread.Name);
counter++;
Thread.Sleep(2);
}
// threads[1].Abort();
// threads[2].Abort();
foreach(Thread thread in threads)
thread.Join();
Console.WriteLine("All threads completed.");
}
14. Compile and run the program. The output is shown in Figure 13.
Threads
21-30 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 13. Waiting for the lock.
Examine the seventh line carefully (highlighted in Figure 13, where the arrow
is drawn). Thread Number 1 is trying to lock the object. It locks the object and

then Thread Number 2 tries to lock the object, but it must wait for the object t
o
be unlocked by Thread Number 1. Thread Number 1 then unlocks the object
and Thread Number 2 can lock it. This is synchronization in a nutshell.
Two lines later you see Thread Number 1 waiting to lock the object until
Thread Number 2 unlocks it. They go back and forth, waiting their turn until
the object is unlocked.
Interlocked Increment
Because incrementing a resource under lock is such a common requirement,
the .NET framework provides an Interlocked class that offers an Increment
static method. You pass in a reference to an integer (note the ref keyword) and
the value is incremented under lock. This greatly simplifies your code.
Synchronization
C# Professional Skills Development 21-31
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
To see how the Increment method works, youll make a small revision to the
previous example.
1. Reopen the previous example.
2. Update the CountUp method, delete the lock(this) keyword and block the
current threads execution.
3. Call the Increment method of the Interlocked class, passing in a reference
to the resource.
try
{
for (int i = 0; i < 20; i++)
{
Interlocked.Increment(ref synchValue);
int temp = synchValue;
temp++;
Thread.Sleep(2);
synchValue = temp;
Console.WriteLine("{0} Count up: {1}",
Thread.CurrentThread.Name,synchValue);
}
}
4. Compile and run the program, as shown in Figure 14. The results are the
same as in the previous example using lock(this), but the code is far
simpler and easier to maintain.
See
Synchronization2.
sln
Threads
21-32 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 14. Using Interlocked.Increment.
Race Conditions
You do need to be very careful when using thread synchronization. A typical
problem is a race condition. A race condition exists when the success of one
thread depends on the order of execution of another thread. For example:
Thread A opens a file
Thread B writes to the file
Synchronization
C# Professional Skills Development 21-33
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
This creates a race condition. Thread B might try to write to the file before
Thread A opens it. This is particularly difficult to debug, because most of the
time Thread A opens the file in time; but now and then it might be delayed for
one reason or another, and then Thread B will fail.
Deadly Embrace
One particularly nasty version of a race condition is when two threads depend
on each other, and they get locked waiting for one another. For example:
Thread A locks the Employee object and waits for a lock on a database
row.
Thread B locks the database row (unfortunately the one needed by
Thread A) and waits to get a lock on the Employee object.
Thread A cant get its lock on the database row until Thread B lets the lock go.
Thread B cant let go of the lock on the Database row until it gets a lock on the
Employee, but that Employee is locked by thread A. Thread A cant let go of
its Employee object until it completes its Database update.
This is known as a Deadly Embrace or as Deadlock. Typically, this hangs your
program until it either times out (if you were smart enough to put in a time-out

mechanism) or you reboot the computer. When you are locking resources you
want to be very careful to avoid the dreaded Deadly Embrace.
Threads
21-34 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
Threads are lightweight processes.
Threads allow the appearance of simultaneous processing, but can
actually slow your program if not used carefully.
Threads are helpful when your program would otherwise wait for a
slow operation such as reading from a database or file.
When you create a thread, pass in a delegate to the method you want to
start in the thread.
Call Sleep on a thread to pause the thread or to release control of the
processor.
Join a thread to block the current thread until the joined thread
completes.
Request that a thread kill itself by calling Abort.
Synchronize access to a resource by calling lock and passing in a
reference to the resource.
The Interlocked class offers a static method Interlocked that takes a
reference to an integer and increments that integer in a thread safe
manner.
Synchronization
C# Professional Skills Development 21-35
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. How can multithreading reduce the performance of your application?
2. How can multithreading enhance the performance of your application?
3. How do you create a new thread?
4. How do you block the current thread until a given thread completes?
5. How does a thread respond to a call to Abort?
6. How do you release the lock on an object?
Threads
21-36 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. How can multithreading reduce the performance of your application?
If all the threads are process intensive, the overhead in switching
among the threads can actually reduce performance.
2. How can multithreading enhance the performance of your application?
If one or more of the threads must wait for a slow operation such
as reading from a database or from a file, then multiprocessing
allows the chip to remain busy while waiting for the other
operation to complete.
3. How do you create a new thread?
You launch a new thread by instantiating a Thread object and
passing in a delegate that wraps the method you want the thread
to run.
4. How do you block the current thread until a given thread completes?
To block the current thread until ThreadA completes, call Join()
on ThreadA.
5. How does a thread respond to a call to Abort?
The thread catches the ThreadAbortException object. This gives
the thread an opportunity to clean up before exiting.
6. How do you release the lock on an object?
When you create the lock you create a block using braces. When
the block exits the lock is released.
Synchronization
C# Professional Skills Development 21-37
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 21:
Threads
Lab 21:
Threads
21-38 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 21 Overview
In this lab youll learn to work with multithreaded applications and to
implement synchronization mechanisms.
To complete this lab, youll need to work through two exercises:
Creating Multiple Threads
Working with Synchronization
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-bystep

instructions.
Creating Multiple Threads
C# Professional Skills Development 21-39
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Creating Multiple Threads
Objective
In this exercise, youll create a multithreaded application and youll abort one
thread before it completes.
Things to Consider
Threads are created with the ThreadStart delegate.
You can run more than one thread at a time.
You can give a thread a name by assigning to the Name property of
the thread.
Step-by-Step Instructions
1. Open ThreadingStarter.sln in the ThreadingStarter folder.
2. Add a using statement for the Threading namespace.
using System.Threading;
3. Implement the Run method.
public void Run()
{
4. Create an array of threads. Initialize with two Threads, that will both call
the CountUp method that you will create in Step 14.
Thread[] threads =
{
new Thread(new ThreadStart(CountUp)),
new Thread(new ThreadStart(CountUp)),
};
Lab 21:
Threads
21-40 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
5. Create a local counter for naming the threads.
int counter = 1;
6. Start the thread.
thread.Start();
7. Set the threads name using the counter.
thread.Name = "Thread Number " + counter.ToString();
8. Display to the console that youve started the thread.
Console.WriteLine("Started thread {0}", thread.Name);
9. Increment the counter.
counter++;
10. Call the static Sleep method on the Thread class to cause the current thread

to sleep for 10 milliseconds.
Thread.Sleep(10);
11. Ask the first thread to abort.
threads[0].Abort();
12. Iterate over the threads and join each.
foreach(Thread thread in threads)
thread.Join();
13. Display a message that the threads are started.
Creating Multiple Threads
C# Professional Skills Development 21-41
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Console.WriteLine("All threads Started");
14. Implement the CountUp method you referred to in Step 4.
public void CountUp()
{
15. Use a constant for the value to count to. A reasonable number is 200, but
you can use any number you like.
const int MagicNumber = 200;
16. Create a try block so that you can catch an abort exception.
try
{
17. Create a for loop to do the work. Iterate once from 0 up to the constant
value you declared in Step 15.
for (int i = 1; i < MagicNumber; i++)
{
Console.WriteLine(
"{0}, i: {1}",
Thread.CurrentThread.Name, i);
}
18. Create a catch block to catch a ThreadAbortException. In that block, write
a message to the console that you are aborting.
catch(ThreadAbortException)
{
Console.WriteLine(
"Thread {0} caught ThreadAbortException. Cleaning up.",
Thread.CurrentThread.Name);
} // end catch
Lab 21:
Threads
21-42 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
19. Implement a finally block. In that block, write a message to the Console
indicating that you are exiting.
finally
{
Console.WriteLine(
"{0} Exiting...", Thread.CurrentThread.Name);
} // End finally
20. Run the application. Your results should look like Figure 15.
Figure 15. Running the threading application.
Working with Synchronization
C# Professional Skills Development 21-43
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Working with Synchronization
Objective
In this exercise, youll use Interlocked Increment to synchronize two threads
that increment a shared class member.
Things to Consider
The threads are independently accessing the shared member, so you
need to provide some kind of locking.
Interlocked increment does all the locking for you. You do not need to
manage the locking yourself.
Step-by-Step Instructions
1. Open SynchronizationStarter.sln in the SynchronizationStarter folder.
2. Add the Threading namespace.
using System.Threading;
3. Give the class a member variable sharedObject of type int, and initialize it
to the value 0 (zero).
private int sharedObject = 0;
4. Implement the CountUp method.
public void CountUp()
{
5. Create a constant for the for loop.
const int MagicNumber = 20;
Lab 21:
Threads
21-44 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
6. Create the for loop, counting from 0 up to the MagicNumber-1.
for (int i = 0; i < MagicNumber; i++)
{
7. Increment the sharedObject using the static Increment method of the
Interlocked class. Remember that you must pass the object in by reference.
Interlocked.Increment(ref sharedObject);
8. Display the value.
Console.WriteLine("{0} Count up: {1}",
Thread.CurrentThread.Name,sharedObject);
9. Build and run the application. Your results should look like Figure 16.
Working with Synchronization
C# Professional Skills Development 21-45
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 16. Running the synchronized application.
Lab 21:
Threads
21-46 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Streams
C# Professional Skills Development 22-1
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Streams
Objectives
Use the DirectoryInfo object to explore directory and file information.
Use the Stream class to read and write files.
Use Buffered streams to optimize file reading and writing.
Use Asynchronous I/O to enhance performance.
Streams
22-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Stream Fundamentals
When you move data to and from a file, or to and from the Internet, your data
must be streamed. When data is streamed, it is serialized; that is one packet is

sent after another much like bubbles on a stream.
In .NET files and directories, and streams are all abstracted as classes. You
create a stream, associate the stream with a file, and then give your object to
the stream object to have it sent to the file.
Streams may be buffered or they may be unbuffered. Buffered streams are
mediated by the Framework to optimize transmission performance.
Directory Information
Streams are implemented by classes within the IO namespace. This namespace
also provides support for Directory and DirectoryInfo classes that you can use
to explore and manipulate directories on your drive.
Try It Out!
To see how DirectoryInfo objects work, youll create a simple test application
to print information about the directories in the WinNT directory.
1. Create a new console application and name it Streams1.
2. Add a using statement for the IO namespace.
using System.IO;
3. Create a Run method. Within the Run method create a string variable to
hold the directory name.
See Streams1.sln
Stream Fundamentals
C# Professional Skills Development 22-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
class Tester
{
static void Main(string[] args)
{
Tester t = new Tester();
t.Run();
}
public void Run()
{
// initial directory
string dir = @"C:\WinNT";
Notice that you can use a verbatim string (beginning with the at symbol @),
which allows you to avoid escaping the backslash. That is, with the verbatim
string you can write C:\WinNT rather than C:\\WinNT.
4. Instantiate a DirectoryInfo object, passing in the name of the directory you
are interested in.
DirectoryInfo dirInfo = new DirectoryInfo(dir);
5. You want to explore your directory and all its sub-directories, so you need
a method that can be called recursively. Create a new method,
ExploreDirectory. This new method will take two parameters: a
DirectoryInfo for the directory you want to explore and a string for the
path. Since you are invoking it for the first time, the path string will be
blank. ExploreDirectory will return the total number of directories found,
and you can then print that value.
// completed. print the statistics
Console.WriteLine("\n\n{0} directories found.\n",
dirsFound);
} // end Run
6. Create the dirsFound member variable to keep track of the number of
directories found. Initialize it to 0:
Streams
22-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
class Tester
{
long dirsFound = 0;
7. Write the ExploreDirectory method.
private int ExploreDirectory(
DirectoryInfo dirInfo, string path)
{
8. Display the full path to the current directory by displaying the path,
followed by a slash, followed by the current directorys name. You get the
path from the parameter and the current directorys name from the
DirectoryInfo object passed in as a parameter. You can also display the
last time the directory was accessed, by using the LastAccessTime property
of the DirectoryInfo object:
Console.WriteLine("{0}\\{1} [{2}]\n",
path, dirInfo.Name, dirInfo.LastAccessTime);
9. Update the path with the name of the current directory. Youll use this new
path when you recurse into the subdirectories.
path+=("\\" + dirInfo.Name);
10. Get all the subdirectories for the current directory, by calling
GetDirectories. This method returns an array of DirectoryInfo objects:
DirectoryInfo[] directories = dirInfo.GetDirectories();
11. Iterate over all the subdirectories and recurse into the current directory,
passing in the subdirectory and the current path.
foreach (DirectoryInfo newDir in directories)
{
ExploreDirectory(newDir,path);
}
Stream Fundamentals
C# Professional Skills Development 22-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
12. Return the counter that now has the running total of directories found.
dirsFound++;
} // end ExploreDirectory
13. Compile and run this application. You should see all the subdirectories
under WinNT scrolling by. To simplify the output, weve created a test
directory and subdirectories, as shown in Figure 1.
Figure 1. The test subdirectory tree.
The output of the program applied against these subdirectories is shown in
Figure 2.
Figure 2. The test subdirectories.
Streams
22-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Streams and Files
The Stream class can be used to read a file from the disk, or it can be used to
write a file to disk. When you open a stream for reading, you can call the Read
method, and pass in a buffer of bytes that will be read from the file and that
you may then either write to another file or display to the console.
Try It Out!
To see how streams work, youll read the text from a file and write it out to a
new file.
1. Create a new console application named Streams2.
2. Within the Run method, create an input stream and initialize that stream by
calling the OpenRead static method on the File class. That method takes
the name and path of a file:
class Tester
{
static void Main(string[] args)
{
Tester t = new Tester();
t.Run();
}
public void Run()
{
Stream inputStream = File.OpenRead(
@"C:\test\story\Odyssey.txt");
3. Notice that the Stream object is named inputStream, but it is just a vanilla
stream object. Similarly, you can create an output stream by calling
OpenWrite on the File class:
Stream outputStream = File.OpenWrite(
@"C:\test\story\Odyssey.bak");
4. Define a buffer of bytes and size it using a constant.
See Streams2.sln
Stream Fundamentals
C# Professional Skills Development 22-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
const int SizeBuff = 255;
byte[] buffer = new Byte[SizeBuff];
5. Call the Read method on the first stream; pass in the buffer and the offset
at which you want to begin reading (0). Also pass in the size of the buffer
youve provided. You will get back an integer value indicating the number
of bytes successfully read from the file.
int bytesRead;
bytesRead = inputStream.Read(buffer,0,SizeBuff);
6. Create a loop to continue reading as long as you get any bytes back from
the call to the Read method. Each time you fill the buffer, write it to the
output buffer, using the Write method. The Write method also takes three
parameters; the buffer, the offset, and the number of bytes you are writing.
while( bytesRead > 0 )
{
outputStream.Write(buffer,0,bytesRead);
7. Write the number of bytes youve read to the console and then read
another buffer-full.
Console.WriteLine("Bytes read: {0}", bytesRead);
bytesRead = inputStream.Read(buffer,0,SizeBuff);
} // end while
8. Once the while loop is completed, youve read the entire file. You can now
close both the input and the output stream.
inputStream.Close();
outputStream.Close();
} // end Run
9. To test this, weve created a file in the text\story subdirectory named
Odyssey.txt. This contains the beginning of Homers Odyssey, an excerpt
from the public domain version of the file available through the
Guttenberg Project. An excerpt is shown in Figure 3.
Streams
22-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 3. The Odyssey file.
10. Compile and run the program. You will see that buffers full of 255 bytes
of text will be read until the final buffer-full is read, as shown in Figure 4.
At that point the file has been copied and the streams are closed.
Stream Fundamentals
C# Professional Skills Development 22-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 4. Reading the buffers full.
11. Examine the directory and youll find a backup version has been created,
as shown in Figure 5.
Figure 5. The backup file in the directory
12. Examine the files side-by-side, youll find that they are identical as shown
in Figure 6.
Figure 6. The copy is identical.
Streams
22-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Buffered Streams
In the previous examples, each time you read from the file you created your
own buffer to read to. It may turn out, however, that the reading can be done
much more efficiently with a different sized buffer. By using buffered streams,
you allow the operating system to read from the disk (and write to the disk)
using the optimal sized buffer. You then read from that buffered stream to and
from your own buffer. With a large buffer, this can significantly enhance the
efficiency of your program.
A BufferedStream object is created around an existing stream object. The
BufferedStream just provides the additional efficiency.
Try It Out!
To see how to use buffered streams, youll create a simple application to read a
file from disk and to write a copy.
1. Create a new application named Streams3.
2. Create a Run method, and create two stream objects, one to read a file and
the other to write a file.
class Tester
{
static void Main(string[] args)
{
Tester t = new Tester();
t.Run();
}
public void Run()
{
// open a stream for reading
Stream inputStream = File.OpenRead(
@"C:\test\story\Odyssey.txt");
// open a stream for writing
Stream outputStream = File.OpenWrite(
@"C:\test\story\Odyssey.bak");
See Streams3. sln
Stream Fundamentals
C# Professional Skills Development 22-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
3. Create a BufferedStream object. Name it buffIn and pass the input stream
to the constructor. Do the same for a buffered output stream:
BufferedStream buffIn =
new BufferedStream(inputStream);
BufferedStream buffOut =
new BufferedStream(outputStream);
4. Create a local buffer of bytes.
const int SizeBuff = 255;
byte[] buffer = new Byte[SizeBuff];
int bytesRead;
5. Fill the buffer from the BufferedStream, by calling the Read method. Pass
in the buffer, the offset (0), and the size of the buffer. The Read method
will return the number of bytes read. As long as you are getting bytes, keep
reading and write the bytes out to the output buffer:
while ( (bytesRead =
buffIn.Read(buffer,0,SizeBuff)) > 0 )
{
buffOut.Write(buffer,0,bytesRead);
}
6. Remember to flush the output buffer when you are done and to write any
remaining bytes to the file.
buffOut.Flush();
7. Close the BufferedStream objects. The original stream objects will be
closed for you by the BufferedStream objects.
buffIn.Close();
buffOut.Close();
Streams
22-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Once again youve read the Odyssey.txt file, and created a backup. The results
are identical to the previous example, but with a large file, this version of th
e
program should run noticeably faster.
Text Files
The stream class provides special support for text files. A text file has no
binary information in it (no database records, no images, etc.), only text. If y
ou
have such a file, it is easier, and more efficient to use the text file Stream
objects StreamReader and StreamWriter.
To create a StreamReader you start by creating a FileInfo object. The
constructor for a FileInfo object takes the name and path of the file you want t
o
open. You then call OpenText on the FileInfo object, which returns a
StreamReader object.
You can instantiate a StreamWriter by passing a path and file name to the
constructor, along with a second parameter indicating whether or not to append
to a file if it already exists.
Try It Out!
To see how StreamReader and StreamWriter work, youll create a new console
application to read and write the Odyssey text file.
1. Create a new console application named Streams4.
2. Within the Run method, create a FileInfo object for the Odyssey.txt file.
class Tester
{
static void Main(string[] args)
{
Tester t = new Tester();
t.Run();
}
public void Run()
{
FileInfo theSourceFile = new FileInfo(
@"C:\test\story\Odyssey.txt");
See Streams4.sln
Stream Fundamentals
C# Professional Skills Development 22-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
3. Create a StreamReader object and initialize it by calling OpenText on the
FileInfo object.
StreamReader reader = theSourceFile.OpenText();
4. Create a new StreamWriter object.
StreamWriter writer = new StreamWriter(
@"C:\test\story\Odyssey.bak",false);
5. Create a local string variable named text to hold the text read from the
StreamReader object.
string text;
6. Create a do while loop to read through the file. Each time through the
loop youll assign to the string variable the result of calling ReadLine on
the StreamReader. This will return one line of text that you can then write
to the file (using WriteLine) or to the console. Continue to do this as long
as the text string is not null.
do
{
text = reader.ReadLine();
writer.WriteLine(text);
Console.WriteLine(text);
} while (text != null);
7. Once you have read through the entire file, the text string will be null and
you can close the StreamReader and StreamWriter objects.
reader.Close();
writer.Close();
The effect on the disk is the same as in the previous examples (a copy is
made), but this time the output to the console is the text of the file as well,
as
shown in Figure 7.
Streams
22-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 7. Using the TextReader object.
Asynchronous I/O
C# Professional Skills Development 22-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Asynchronous I/O
The previous examples read from the file synchronously; that is, the program
waited while the file was read. Reading from a file can be a slow process,
however, and it is tempting to use threading to create a multitasking
application that can be busy with other work while the reading is completed.
Threading can be complex, however, and the designers of .NET realized that
asynchronous reading and writing would be so common that there would be
great benefit in building in support for asynchronous I/O.
With asynchronous I/O you instruct the stream to read from the file and then
you continue with your work. When the buffer is read, a method you designate
is called back indicating that the data is ready to be handled. You can then sto
p
what you are doing just long enough to manipulate the data, and then continue
with your other work while the stream fetches the next buffer-full.
The asynchronous I/O classes support the creation of highly-efficient programs
without the difficulties of managing threads directly.
Heres how it works. As illustrated in Figure 8, you create an Asynchronous
Input Stream and call BeginRead, passing in a buffer to fill and the name of
the method you want called when the read is completed (e.g.,
OnCompletedRead). The stream begins reading, while you go on with other
work.
Figure 8. Asynchronous I/O.
When the stream has read the data, it calls your callback method
(OnCompletedRead) passing in a token. In that method you call the Streams
EndRead method passing back the token and you get back the data. You can
then kick off another read and resume work.
Streams
22-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
To see how AsynchStreams allows you painless multitasking, youll create a
console application and read from a file asynchronously.
1. Create a new console application named AsynchStreams.
2. Create a class, AsynchTest and give it a private Stream object named
inStream.
public class AsynchTest
{
private Stream inStream;
3. To call a callback method you will need a delegate of type AsyncCallback.
This delegate type is defined by the .NET Framework. Youll call your
instance of this delegate type callBackMethod.
private AsyncCallback callBackMethod;
4. Create a private buffer of bytes and establish a constant value for the
buffer size.
private byte[] buffer;
const int BufferSize = 256;
5. In the constructor you will open the input stream file by the static
OpenRead method of the file class. You will also initialize your buffer and
your delegate.
See
AsynchStreams.sln
Asynchronous I/O
C# Professional Skills Development 22-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
// constructor
AsynchTest()
{
// open the input stream
inStream =
File.OpenRead(
@"C:\test\story\Odyssey.txt");
// allocate a buffer
buffer = new byte[BufferSize];
// assign the call back
callBackMethod =
new AsyncCallback(this.OnCompletedRead);
}
6. In the Run method you call BeginRead on the input stream object, passing
in the buffer you want to fill, the offset, the size of the buffer, and the
delegate for your callback method. You can also pass in a state object to
hold the current state of your object, but youll pass in null because you do
not need to track the state of your object.
void Run()
{
inStream.BeginRead(
buffer, // holds the results
0, // offset
buffer.Length, // (BufferSize)
callBackMethod, // call back delegate
null); // local state object
7. The BeginRead method will operate asynchronously; you can go on to do
other work. Youll simulate other work by counting to 500,000 displaying
a value every 1000 increments.
Streams
22-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
// do some work while data is read
for (long i = 0; i < 500000; i++)
{
if (i%1000 == 0)
{
Console.WriteLine("i: {0}", i);
} // end if
} // end for
} // end run
8. Implement the method to be called back. The delegate requires that your
method take a single parameter: a reference to an object implementing
IAsyncResult.
void OnCompletedRead(IAsyncResult asyncResult)
{
9. When this method is called, you will call the EndRead method on your
input stream, passing in the IAsyncResult object you received as a
parameter: you get back a value indicating how many bytes were read.
int bytesRead =
inStream.EndRead(asyncResult);
10. The actual bytes were put into the buffer you passed in to the BeginRead
method. If you got back a non-zero amount from EndRead you are ready
to create a string from the buffer full of data and write that string to the
console. You will then fire off BeginRead again passing in the same
parameters as last time.
Asynchronous I/O
C# Professional Skills Development 22-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
if (bytesRead > 0)
{
String s =
Encoding.ASCII.GetString(
buffer, 0, bytesRead);
Console.WriteLine(s);
inStream.BeginRead(
buffer, 0, buffer.Length, callBackMethod, null);
}
This requests that the asynchronous I/O stream read another buffer-full and
notify you when it is ready.
11. Compile and run the program. As shown in Figure 9, the program begins
counting while the asynchronous I/O stream fetches the first buffer-full.
When the buffer-full is ready you can display it to the console, and then go
back to work as shown in Figure 9.
Streams
22-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 9. Reading the data asynchronously.
This continues, oscillating back and forth between your other work and
displaying the read data until all the data is read as shown in Figure 10.
Asynchronous I/O
C# Professional Skills Development 22-21
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 10. Asynchronous I/O and other work.
Streams
22-22 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
When you move data to or from a file or across the network you must
stream the data. When data is streamed it is serialized: put into a series
of byte packages, one after the other.
The .NET Framework provides extensive support for streaming.
The DirectoryInfo object can be used to find information about
directories and their subdirectories.
The File class has a number of methods, many static, for manipulating
files.
Buffered Streams allow the operating system to decide the optimal
buffer size for streaming. You wrap a BufferedStream object around a
normal Stream object.
The StreamReader and StreamWriter classes provide optimized
support for working with Text streams.
Rather than writing your own multitasking thread support for
asynchronous I/O, you may use the overlapped I/O classes provided by
the Framework Class Library.
Overlapped I/O works by the use of callback methods, implemented
with delegates.
Asynchronous I/O
C# Professional Skills Development 22-23
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. How do you find the subdirectories within a directory?
2. What are the parameters passed to the Read method of Stream?
3. What is the advantage of using a Buffered Stream?
4. What is the advantage of using asynchronous I/O?
5. How is your callback method designated, that is, how does the stream
know which method to call?
Streams
22-24 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. How do you find the subdirectories within a directory?
Call GetDirectories on a DirectoryInfo object. This method
returns an array of DirectoryInfo objects representing the
subdirectories.
2. What are the parameters passed to the Read method of Stream?
The Read method takes three parameters. The first is an array of
bytes to be filled. The second is an offset into that array,
generally set to 0. The third parameter is the size of the buffer
youve passed.
3. What is the advantage of using a Buffered Stream?
The Buffered Stream allows the operating system to decide on
the optimal size for the buffer. You continue to pass in a buffer of
whatever size you choose, but the data for your buffer is
retrieved from the BufferedStreams buffer, rather than from the
disk. This can greatly reduce the number of disk reads, and disk
reads are very slow.
4. What is the advantage of using asynchronous I/O?
Asynchronous I/O allows your program to continue processing,
while the system is busy retrieving data from the disk. This is the
heart of multitasking. The Framework provides extensive support
for overlapped I/O, allowing you to create multithreaded
programs without having to manage the threads yourself.
5. How is your callback method designated, that is, how does the stream
know which method to call?
You designate the callback method with a delegate. The
delegates signature is specified by the Framework. You initialize
the delegate in your constuctor, passing in the name of the
method to call.
Asynchronous I/O
C# Professional Skills Development 22-25
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 22:
Streams
Lab 22:
Streams
22-26 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 22 Overview
In this lab youll learn to work with text streams and with asynchronous
streams.
To complete this lab, youll need to work through two exercises:
Reading Text from a File
Using Overlapped I/O to Read a File
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-bystep

instructions.
Reading Text from a File
C# Professional Skills Development 22-27
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Reading Text from a File
Objective
In this exercise, youll use a text stream to read text from a file.
Things to Consider
You obtain a StreamReader by calling OpenText on a FileInfo object.
You can obtain a StreamWriter class by passing in to the StreamWriter
constructor the name of the file you want to create.
StreamReader and StreamWriter are optimized for text and support the
ReadLine and WriteLine methods, respectively.
Step-by-Step Instructions
1. Create a new Windows console application named Streams.
2. Add a using statement for System.IO.
using System.IO;
3. Change the namespace to StreamsStarter.
namespace StreamsStarter
{
4. Create a class Tester and establish the Main method to call the Run
method.
class Tester
{
5. Implement the Main method.
Lab 22:
Streams
22-28 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
static void Main(string[] args)
{
Tester t = new Tester();
t.Run();
}
6. Implement the Run method.
public void Run()
{
7. Open a FileInfo instance on the supplied file Odyssey.txt. Be sure to put
the full path to the Odyssey.txt file.
FileInfo theSourceFile = new FileInfo(
@"C:\test\story\Odyssey.txt");
8. Create a StreamReader instance for that file.
StreamReader reader = theSourceFile.OpenText();
9. Create a StreamWriter instance for a new file. Put the new file in the same
directory but call it Odyssey.bak.
StreamWriter writer = new StreamWriter(
@"C:\test\story\Odyssey.bak",false);
10. Create a text variable to hold each line of text as it is retrieved from the

file.
string text;
11. Walk through the file and read every line, writing both to the console and
to the file.
Reading Text from a File
C# Professional Skills Development 22-29
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Do
{
text = reader.ReadLine();
writer.WriteLine(text);
Console.WriteLine(text);
} while (text != null);
12. Close the reader and writer and exit.
reader.Close();
writer.Close();
13. End the Run method, the Tester class, and the namespace.
} // end Run method
} // end class Tester
} // end namespace
14. Build and run the Streams application. Your results should be similar to
Figure 11.
Lab 22:
Streams
22-30 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 11. Running the Streams application.
Using Overlapped I/O to Read a File
C# Professional Skills Development 22-31
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Using Overlapped I/O to Read a File
Objective
In this exercise, youll use overlapped I/O to read a file from disk.
Things to Consider
Overlapped IO provides multithread support without you having to
manage the threads.
You can continue other work, while the stream is reading from the file.
Your callback method will be passed a token of type IAsyncResult
use that to call EndRead to get the data.
Step-by-Step Instructions
1. Open OverlappedIOStarter.sln in the OverlappedIOStarter folder.
2. Add using statements for System.IO, System.Threading, and System.Text.
using System.IO;
using System.Threading;
using System.Text;
3. Create a private member of type Stream named inputStream.
private Stream inputStream;
4. Declare a private delegate of type AsyncCallBack named
callBackMethod.
private AsyncCallback callBackMethod;
5. Declare a private array of bytes named buffer to hold the read data.
Lab 22:
Streams
22-32 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
private byte[] buffer;
6. Declare a constant to hold the size of the buffer.
const int BufferSize = 256;
7. Implement the constructor.
AsynchTest()
{
8. Open the input stream by calling the static OpenRead method on the File
class, passing in the full path of the supplied file Odyssey.txt.
inputStream =
File.OpenRead(
@"C:\test\story\Odyssey.txt");
9. Allocate a buffer to hold the text.
buffer = new byte[BufferSize];
10. Assign the callback method to the OnStarterRead method of this class.
callBackMethod =
new AsyncCallback(this.OnStarterRead);
11. Implement the Run method.
void Run()
{
12. Call BeginRead on the input stream object. Pass in the buffer. Use 0 for
the offset. Pass in the length of the buffer and the delegate encapsulating
the callback method. For the final parameter, the local state object, pass in
null.
Using Overlapped I/O to Read a File
C# Professional Skills Development 22-33
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
inputStream.BeginRead(
buffer, // buffer for results
0, // offset
buffer.Length, // (BufferSize)
callBackMethod, // call back delegate
null); // local state object
13. Implement the callback method, OnStarterRead.
void OnStarterRead(IAsyncResult asyncResult)
{
14. Call EndRead and get back an integer value; store bytesRead to a local
integer variable.
int bytesRead =
inputStream.EndRead(asyncResult);
15. Build and run the application. You should see the counting interleaved
with the text read from the file, as shown in Figure 12.
Lab 22:
Streams
22-34 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 12. Overlapped I/O implemented.
Network Streams
C# Professional Skills Development 23-1
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Network Streams
Objectives
Read files across the Web using standard URLs.
Read files across a local area network.
Implement asynchronous I/O across a network.
Control the serialization of your object.
Implement IDeserializationCallBack to control which parts of your
object will be serialized.
Network Streams
23-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Long Distance I/O
In the previous lesson you learned about using streams to read data from a disk
file and write data out to a new file. Because .NET abstracts this process out t
o
a stream, it is easy to reapply the same approach to reading and writing files
across the Web or across a local area network. The idiom is the same: create a
stream and read from or write to that stream. The only tricky part is connecting

that stream to a file on another machine or to a URL.
Web I/O
One of the truly powerful and elegant aspects of I/O in .NET is that you can
read from the network or the Web just as easily as you can read from a file. To
read from the Web you will use a WebRequest object to request a URI.
A URI is a Uniform Resource Identifier. The most common type of URI is a
URL Uniform Resource Locator): the familiar Web address
(http://www.LibertyAssociates.com).
The WebRequest object creates a WebResponse object that encapsulates the
URI you provide. You call GetResponseStream on the WebResponse object
and get back a stream that you can write to or read from.
The steps to creating a stream for a Web site are as follows:
1. Call WebRequest.Create(). This static method returns a WebRequest
object. Youll cast this to the derived class HttpWebRequest. If you pass
the Create method a valid URL, you get back an HttpWebRequest, which
is a class derived from WebRequest.
2. Call webRequest.GetResponse(). This instance method returns an
HttpWebResponse object.
3. Create a StreamReader object. In the constructor, pass in the value
returned by the instance method webResponse.GetResponseStream().
Note that GetResponseStream returns a Stream object. The constructor for
StreamReader takes a Stream object and an Encoding object that represents, in
this case, ASCII encoding.
4. Call streamReader.ReadToEnd(). This instance method returns a string
with the entire stream representing the Web page.
Long Distance I/O
C# Professional Skills Development 23-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
To see how to write a Web page to a stream, youll create a simple console
application to read a Web page.
1. Create a simple console application named WebStream.
2. In the Run method, create the HttpWebRequest object by calling the static
method Create on the WebRequest object. Pass in the URL for the Web
page you want to retrieve.
HttpWebRequest webRequest =
(HttpWebRequest) WebRequest.Create
("http://www.libertyassociates.com");
3. Call GetResponse on the WebRequest object and get back an
HttpWebResponse object.
HttpWebResponse webResponse =
(HttpWebResponse) webRequest.GetResponse();
4. Generate the StreamReader object.
StreamReader streamReader = new StreamReader(
webResponse.GetResponseStream(), Encoding.ASCII);
5. Call ReadToEnd() on the StreamReader. If no exception is thrown, this
will fill the string with the contents of the Web page. Display this page to
the console:
See WebStream.sln
Network Streams
23-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try
{
string outputString;
outputString = streamReader.ReadToEnd();
Console.WriteLine(outputString);
}
catch
{
Console.WriteLine("Exception reading from web page");
}
6. Close the StreamReader.
streamReader.Close();
7. Compile and run the program. The Web page is requested and displayed,
as shown in Figure 1.
Figure 1. Reading a Web page.
Long Distance I/O
C# Professional Skills Development 23-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Networking I/O
Just as you read from the Web, you can read and write across a local area
network or over the Internet since it all just streams.
Network I/O streams depend on sockets. A socket is an object that represents
an end point. Sockets work with various protocols (such as UDP and TCP/IP)
but this course focuses on the most popular protocol: TCP/IP.
Ports
It is possible that more than one application on a single computer will talk to
various clients at the same time. Each application must, therefore, have its own

unique ID. An application ID is called a port. If your I/P address is analogous
to a phone number, then a port is analogous to an extension.
When a client connects to a server over TCP/IP, it connects to a specific I/P
address:
Yahoo: 216.114.108.245
CNN: 207.24.71.20
The client must also connect to a specific port. All Web browsers connect to
port 80 by default, and Web browsers generally make their services available
on port 80.
By international convention ports are divided into the following ranges:
0-1023 Well-known ports
e.g., 80=web browser
1024-49151 Registered Ports
49152-65535 Dynamic or private ports
For a list of registered and well-known ports check:
http://www.isi.edu/in-notes/iana/assignments/port-numbers
To create a client/server application you will:
1. Create a socket.
2. Call Start() on the socket. This tells the socket to begin accepting
connections.
3. Call Accept() on the socket. This tells the socket to begin responding to
client requests.
Network Streams
23-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
In the .NET Framework, sockets are encapsulated by the Socket class. The
TcpListener class builds on the Socket class to provide high-level TCP/IP
services. The steps are:
1. Create a TcpListener with a port ID.
2. Tell it to start.
3. Call Accept(). This method returns a socket for the client.
Try It Out!
To see how to work with sockets, youll create a simple server application and
a simple client application. This application will allow you to stream the
contents of the Oddysey.txt file across the network to a client application that

you will write as well.
1. Create a console application and name it NetworkIOServer.
2. Add using statements for the namespaces youll require.
using System;
using System.Net.Sockets;
using System.IO;
3. In the Run method, create an instance of TcpListener. Pass the port you
want to listen to on to the constructor (in this case port 65000). Call Start
on that object.
See NetworkIO
Server1.sln and
NetworkIO
Client1.sln
Long Distance I/O
C# Professional Skills Development 23-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public class NetworkIOServer
{
public static void Main()
{
NetworkIOServer app =
new NetworkIOServer();
app.Run();
}
private void Run()
{
// create a new TcpListener and start it up
// listening on port 65000
TcpListener tcpListener = new TcpListener(65000);
tcpListener.Start();
4. In this simplified program youll keep listening until you accept a
connection to a client, and then youll keep listening while there is a file to
send. When the file is sent, youll break out of the listening loop and shut
down. Start by creating a forever loop. You can do this with:
for(;;)
or if you prefer:
while (true)
Within the forever loop, instantiate a socket by calling AcceptSocket on the
TcpListener object.
for (;;)
{
Socket clientSocket =
tcpListener.AcceptSocket();
Network Streams
23-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
5. Test the socket to see if it is connected. It will return false until a clien
t
connects.
if (clientSocket.Connected)
{
6. When the Connected property returns true, update the console and call a
helper method to send the file to the client. Update the console again, and
then close the socket and break out of the loop.
if (clientSocket.Connected)
{
Console.WriteLine("Client connected");
// call the helper method to send the file
StreamFile(clientSocket);
Console.WriteLine(
"Disconnecting from client...");
// clean up and go home
clientSocket.Close();
Console.WriteLine("Exiting...");
break;
} // end if clientSocket.Connected
} // end for(;;)
7. Create the helper method StreamFile. You can make this a private method
and it will take the socket you created in Run as a parameter.
private void StreamFile(
Socket clientSocket )
{
8. In the StreamFile method youll create a NetworkStream object, passing in
the socket you received as a parameter.
Long Distance I/O
C# Professional Skills Development 23-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
NetworkStream networkStream =
new NetworkStream(clientSocket);
9. Create an instance of StreamWriter, passing in the NetworkStream as a
parameter to the constructor.
StreamWriter streamWriter =
new System.IO.StreamWriter(networkStream);
10. Instantiate a StreamReader object for the file.
StreamReader streamReader =
new System.IO.StreamReader(
@"C:\test\story\odyssey.txt");
11. Read the file line-by-line. Write the file out to the StreamWriter and flush

the StreamWriter each time you do to ensure that the entire line is sent to
the client.
do
{
strStream = streamReader.ReadLine();
if( strStream != null )
{
Console.WriteLine(
"Sending {0}", strStream);
streamWriter.WriteLine(strStream);
streamWriter.Flush();
}
}
while( strStream != null );
12. Close the StreamReader, the NetworkStream, and the StreamWriter.
Network Streams
23-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
// tidy up
streamReader.Close();
networkStream.Close();
streamWriter.Close();
} // end StreamFile method
13. Build the server.
14. Open a new instance of Visual Studio .NET to create the client.
15. Create a new console application and name it NetworkIOClient1.
16. Add the following namespaces:
using System;
using System.Net.Sockets;
using System.IO;
17. In the Run method, create a TcpClient object to represent the TCP/IP
connection.
public class Client
{
static public void Main( )
{
Client c = new Client();
c.Run();
}
public void Run()
{
// create a TcpClient to talk to the server
TcpClient serverSocket;
18. In a try block (so that you can catch exceptions thrown if the connection
fails), create a new instance of the TcpClient, passing in the host name and
the port.
Long Distance I/O
C# Professional Skills Development 23-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try
{
serverSocket =
new TcpClient("localHost", 65000);
}
19. Create a catch block to handle any exceptions thrown.
catch
{
Console.WriteLine(
"Failed to connect to server at {0}:65000",
"localhost");
return;
}
20. If you get past the connection you are ready to read the file. Create a
NetworkStream and a StreamReader, much as you did in the server.
NetworkStream networkStream =
serverSocket.GetStream();
StreamReader streamReader =
new StreamReader(networkStream);
21. In a second try block read line-by-line from the stream. As you get lines,
display them to the Console.
Network Streams
23-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try
{
string strOut;
// read the data from the host and display it
do
{
strOut = streamReader.ReadLine();
if( strOut != null )
{
Console.WriteLine(strOut);
}
}
while( strOut != null );
}
catch
{
Console.WriteLine(
"Exception reading from Server");
}
22. Close the NetworkStream.
networkStream.Close();
} // end Run()
23. Compile the client and then run it. You should see an exception, as shown
in Figure 2.
Figure 2. The server not found exception.
Long Distance I/O
C# Professional Skills Development 23-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
24. Stop the client and run the server. Then run the client. You should see the
server connect to the client, and then the file will be streamed over the
network to the client as shown in Figure 3.
Figure 3. The server and client.
You can run these programs on the same machine, or across a local area
network, or across the Internet.
Asynchronous Network I/O
The previous example works great if you are only going to manage one
request. Busy network servers, however, can be expected to receive multiple
requests from many clients. You need a way to combine asynchronous reads
and writes with network I/O. Not surprisingly, the .NET Framework provides
extensive support for this.
The idiom for creating an asynchronous server is to:
Get a connect.
Instantiate a handler for the client.
Pass the socket to the client handler.
Get client handler to manage the request, while the server code listens
for the next client.
Client Handler
The job of the client handler is to create a copy of the socket and use that to
open a NetworkStream object. The client handler then uses overlapped I/O to
read and write to the NetworkStream, similar to using overlapped
(asynchronous) I/O to read and write from a file.
Network Streams
23-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
To get your feet wet with asynchronous network I/O youll build a simple
server and a client. The client will send a string to the server. The server wil
l
read the string using overlapped I/O and then echo it back to the client, using
overlapped I/O.
1. Create a new console application and name it AsynchNetworkIOServer.
While some of the code is similar to the previous example, the changes are
extensive and youre better off starting clean.
2. Create a class AsynchNetworkIOServer. In the Run method create a
TcpListener on port 65000 and start it up:
public class AsynchNetworkIOServer
{
public static void Main()
{
AsynchNetworkIOServer app =
new AsynchNetworkIOServer();
app.Run();
}
private void Run()
{
// create a new TcpListener and start it up
// listening on port 65000
TcpListener tcpListener = new TcpListener(65000);
tcpListener.Start();
3. In the forever loop youll create a Socket class, and check if it is connected
(as you did previously). Once connected youll create an instance of a
ClientHandler, passing in the socket. ClientHandler is a class youll create
in Step 4. Once youve instantiated the ClientHandler, call StartRead.
for (;;)
{
// if a client connects, accept the connection
// and return a new socket named socketForClient
// while tcpListener keeps listening
See Asynch
NetworkIO
Server.sln and
NetworkIO
Client2.sln
Long Distance I/O
C# Professional Skills Development 23-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Socket socketForClient =
tcpListener.AcceptSocket();
if (socketForClient.Connected)
{
Console.WriteLine("Client connected");
ClientHandler handler =
new ClientHandler(socketForClient);
handler.StartRead();
}
}
Compare this to the way you handled the forever loop in the previous example.
In this example you do not break the loop after calling the handler. More
important, you dont pass the socket to your own member method, but to a
new object you instantiate of type ClientHandler.
The goal in this example is for the Run method to be able to dispatch the client

request quickly (by handing it to the ClientHandler object) and go back to
retrieving new client requests. So, Run becomes a dispatcher, with individual
instances of ClientHandler actually handling each client request.
4. Declare the ClientHandler class as a nested class within the
AsynchNetworkIOServer class. Give the client handler class private
members to hold the buffer, Socket, NetworkStream object, and a pair of
call back delegates:
class ClientHandler
{
private byte[] buff;
private Socket socket;
private NetworkStream networkStream;
private AsyncCallback cbRead;
private AsyncCallback cbWrite;
5. Create a constructor that takes a socket and initializes the NetworkStream
and the two delegates.
Network Streams
23-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public ClientHandler( Socket socketForClient )
{
socket = socketForClient;
buff = new byte[256];
networkStream =
new NetworkStream(socketForClient);
cbRead =
new AsyncCallback(this.OnReadComplete);
cbWrite =
new AsyncCallback(this.OnWriteComplete);
}
6. Implement the StartRead method called by AsynchNetworkIOServer .Run.
The job of StartRead is to call the BeginRead (overlapped I/O) method on
the NetworkStream, passing in the buffer, the offset, the length of the
buffer, and most important: the delegate to call when a read is completed.
public void StartRead()
{
networkStream.BeginRead(
buff, // buffer to fill
0, // offset
buff.Length, // length of the buffer
cbRead, // delegate to call
null // state object
);
}
7. Implement the method encapsulated by the cbRead delegate:
OnReadComplete. This method receives an object implementing
IAsyncResult. You will call EndRead on the NetworkStream, passing in
this IAsyncResult object and you will get back a value indicating how
many bytes have been read.
Long Distance I/O
C# Professional Skills Development 23-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
private void OnReadComplete( IAsyncResult ar )
{
int bytesRead = networkStream.EndRead(ar);
8. If you have a positive number in bytesRead then you know your buffer has
been filled.
At this point you would do something with the data youve retrieved. In this
case, youll simply display it to the console and echo it back to the client.
Extract the value from the buffer and convert it to a string by calling the stat
ic
GetString method on System.Text.Encoding.ASCII. Pass that string to the
Console.Write method and then call BeginWrite to write the same string back
to the client using overlapped I/O.
if( bytesRead > 0 )
{
string s =
System.Text.Encoding.ASCII.GetString(
buff, 0, bytesRead);
Console.Write(
"Received {0} bytes from client: {1}",
bytesRead, s );
networkStream.BeginWrite(
buff, 0, bytesRead, cbWrite, null);
}
9. If the value of bytesRead is not greater than zero then the connection has
been dropped (or the client has stopped sending data). Close the
NetworkStream and the socket, and set them both to null:
else
{
Console.WriteLine( "Read connection dropped");
networkStream.Close();
socket.Close();
networkStream = null;
socket = null;
} // end else
} // end OnReadComplete
Network Streams
23-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
10. Write the OnWriteComplete method to implement the second delegates
method. This method will be called when the write request completes.
Once again you are passed an IAsyncResult and you finalize the write by
calling EndWrite on the NetworkStream. Update the status to the Console
and call BeginRead to start the next asynchronous read.
private void OnWriteComplete( IAsyncResult ar )
{
networkStream.EndWrite(ar);
Console.WriteLine( "Write complete");
networkStream.BeginRead(
buff, 0, buff.Length,
cbRead, null);
} // end OnWriteComplete
} // end ClientHandler
} // end AsynchNetworkIOServer
11. Create a new application and name it NetworkIOClient2.
12. Add the necessary using statements to the top of the cs file.
using System;
using System.IO;
using System.Net.Sockets;
using System.Threading;
using System.Runtime.Serialization.Formatters.Binary;
13. Create the AsynchClient class and give it a private member of type
NetworkStream. In the constructor set the server name and instantiate a
TcpClient object with the server name and port 65000. Assign to the
member variable the result of calling GetStream on the TcpClient.
Long Distance I/O
C# Professional Skills Development 23-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public class AsynchClient
{
private NetworkStream streamToServer;
AsynchClient()
{
string serverName = "localhost";
Console.WriteLine("Connecting to {0}", serverName);
TcpClient tcpSocket =
new TcpClient(serverName, 65000);
streamToServer = tcpSocket.GetStream();
}
14. Create a run method. In Run create a string named message. Write the
message to the Console:
static public int Main()
{
AsynchClient client =
new AsynchClient();
return client.Run();
}
private int Run()
{
string message =
"This is a string sent from the client!";
Console.WriteLine(
"Sending {0} to server.", message);
15. Create a StreamWriter object and pass to its constructor the member
variable streamToServer. Call WriteLine on that StreamWriter and pass in
the message, flush the buffer.
Network Streams
23-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
StreamWriter writer =
new StreamWriter(streamToServer);
writer.WriteLine(message);
writer.Flush();
16. Create a StreamReader object to retrieve the response from the server (this
will be the string echoed back to the client).
StreamReader reader =
new StreamReader(streamToServer);
17. Retrieve the string from the server by calling ReadLine and displaying the
string on the console.
string strResponse = reader.ReadLine();
Console.WriteLine("Received: {0}", strResponse);
18. Close the NetworkStream and exit the program.
streamToServer.Close();
return 0;
} // end Run
19. Build and run the server, then build and run the client. As shown in Figure
4, the client comes up and reports that it is connecting to the server. The
server reflects that the client has connected. The client sends This is a
string sent from the client!, which is received on the server. The server
then writes it back to the client, and you see it displayed on the client. The
client then exits and the server reports that the Read connection was
dropped.
Long Distance I/O
C# Professional Skills Development 23-21
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 4. The asynchronous network I/O.
Combining File and Network
Asynchronous I/O
To build a useful asynchronous application, you may need to combine local
asynchronous reading and writing with network overlapped I/O. In the next
example youll send a file name from the client to the server, and then youll
write the contents of the file from the server to the client.
To do this, you will use three delegates on the server and three callback
methods. OnReadComplete will be called when the system has read a buffer
from the client. OnFileReadComplete will be called when the server has read a
buffer full of data from the file. Finally, OnWriteComplete will be called when
the server has finished writing a buffer full of data to the client.
The program will work like this:
1. The server starts.
2. The client starts.
3. The server creates a TcpListener to listen to the port, and when a client
connects a ClientHandler is created to handle the clients request Once the
server creates the ClientHandler it calls StartRead and goes back to listen
for additional clients.
4. The StartRead method of ClientHandler starts an asynchronous read from
the client. When the read completes the OnReadComplete method is
called.
Network Streams
23-22 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
5. In OnReadComplete youll call EndRead on the NetworkStream. If bytes
were received youll treat the string received as a file name. Youll open
that file and read from it asynchronously, passing in a delegate to
OnFileReadComplete.
6. When a buffer from the file is filled, OnFileReadComplete is called. This
method calls EndRead on the FileStream to finish the read and then it
passes that buffer to BeginWrite on the NetworkStream. This writes the
string to the client asynchronously.
7. When the write completes OnWriteComplete is called. OnWriteComplete
updates the console and then calls BeginRead on the FileStream.
In this way, the program ping-pongs back and forth between reading the file
asynchronously and writing it out to the client. This is tremendously efficient;

you can read the file asynchronously and you can write it asynchronously.
When the file is completed close all the streams and youre done.
Try It Out!
Youll see how this works by implementing a new server and client. The server
will implement asynchronous files and network I/O.
1. Create a new server console application named NetworkIOServer3.
2. Add the required namespaces.
using System;
using System.Net.Sockets;
using System.Text;
using System.IO;
3. Create a Run method that opens the TcpListener and waits for a
connection. Delegate handling the connection to the ClientHandler object
(as seen in previous examples):
public class AsynchNetworkIOServer
{
public static void Main()
{
AsynchNetworkIOServer app =
new AsynchNetworkIOServer();
See NetworkIO
Server3.sln and
NetworkIOClient3.
sln
Long Distance I/O
C# Professional Skills Development 23-23
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
app.Run();
}
private void Run()
{
TcpListener tcpListener = new TcpListener(65000);
tcpListener.Start();
for (;;)
{
Socket socketForClient =
tcpListener.AcceptSocket();
if (socketForClient.Connected)
{
Console.WriteLine("Client connected");
ClientHandler handler =
new ClientHandler(socketForClient);
handler.StartRead();
} // end if
} // end for ever loop
} // end run method
4. Create the nested class ClientHandler. Add private fields as in previous
examples; this time add a third delegate for OnFileReadComplete and add
a stream for the file.
class ClientHandler
{
private const int BufferSize = 256;
private byte[] buff;
private Socket socket;
private NetworkStream networkStream;
private Stream fileStream;
private AsyncCallback cbRead;
private AsyncCallback cbWrite;
private AsyncCallback cbFileRead;
Network Streams
23-24 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
5. In the constructor you will create the NetworkStream by passing in the
socket you receive as a parameter and initializing the three delegates.
public ClientHandler(
Socket socketForClient )
{
socket = socketForClient;
buff = new byte[256];
networkStream =
new NetworkStream(socketForClient);
cbFileRead =
new AsyncCallback(this.OnFileReadComplete);
cbRead =
new AsyncCallback(this.OnReadComplete);
cbWrite =
new AsyncCallback(this.OnWriteComplete);
}
6. Implement the StartRead method to begin the read across the network.
public void StartRead()
{
// read from the network
// get a file name
networkStream.BeginRead(
buff, 0, buff.Length,
cbRead, null);
}
7. Implement OnReadComplete to handle the completed read callback. Read
the buffer as a string and assume that it holds the name of the file the
server is to read:
Long Distance I/O
C# Professional Skills Development 23-25
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
private void OnReadComplete( IAsyncResult ar )
{
int bytesRead = networkStream.EndRead(ar);
if( bytesRead > 0 )
{
string fileName =
System.Text.Encoding.ASCII.GetString(
buff, 0, bytesRead);
8. Use the file name to open a FileStream object by calling the static
OpenRead method on the File class.
Console.Write(
"Opening file {0}", fileName);
fileStream =
File.OpenRead(fileName);
9. Call BeginRead on the FileStream, passing in a buffer to hold the contents
of the file, along with the delegate for the method to callback when the
buffer is filled.
fileStream.BeginRead(
buff, // holds the results
0, // offset
buff.Length, // BufferSize
cbFileRead, // call back delegate
null); // local state object
10. If the number of bytes read is not greater than zero, close the
NetworkStream and socket, and set both to null.
Network Streams
23-26 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
} // end if bytesRead > 0
else
{
Console.WriteLine( "Read connection dropped");
networkStream.Close();
socket.Close();
networkStream = null;
socket = null;
} // end else
} // end OnReadComplete
11. Implement OnFileReadComplete. This method is called when the file has
been read into the buffer. End the read with a call to the EndRead method
of the FileStream instance. If you did get bytes from the file, write them to
the NetworkStream. Pass in the delegate for the method to call when the
write completes (OnWriteComplete).
void OnFileReadComplete(IAsyncResult asyncResult)
{
int bytesRead =
fileStream.EndRead(asyncResult);
// if you read some file
if (bytesRead > 0)
{
// write it out to the client
networkStream.BeginWrite(
buff, 0, bytesRead, cbWrite, null);
}
}
12. Implement OnWriteComplete. End the Write by calling EndWrite on the
NetworkStream. Having written to the client, call BeginRead on the
fileStream, starting the next round of reading and writing.
Long Distance I/O
C# Professional Skills Development 23-27
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
private void OnWriteComplete( IAsyncResult ar )
{
networkStream.EndWrite(ar);
Console.WriteLine( "Write complete");
fileStream.BeginRead(
buff, // holds the results
0, // offset
buff.Length, // (BufferSize)
cbFileRead, // *** call back delegate
null); // local state object
} // end OnWriteComplete
} // end ClientHandler
} // end AsynchNetworkIOServer
13. Build the server and start it. Build the client and run it. As shown in Figu
re
5, the server starts up and waits for a client. The client connects to the
server and that is reflected in the server window. The server opens the file
requested by the client. The server then sends buffers-full until the file is
completely read and displayed on the client.
Figure 5. The asynchronous network file server.
Network Streams
23-28 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Serialization
When an object is saved to disk or sent over the Internet it must be serialized
that is written out as a stream of bytes, one after the other. When you serializ
e
an object, instruct each member field to serialize itself. Intrinsic types (such
as
integer, double, etc.) know how to serialize themselves, as illustrated in
Figure 6.
Figure 6. Streaming data.
Member fields that are not primitive types must stream themselves, as
illustrated in Figure 7. This works because all user-defined types are either
composed of primitive types or of other user-defined types. Each user-defined
type delegates responsibility for streaming to all its fields; ultimately every
user-defined type reduces to primitive types, and primitive types know how to
stream themselves.
Serialization
C# Professional Skills Development 23-29
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 7. User-defined types stream themselves.
Marking Types for Serialization
It turns out that by default user-defined types are not serializable; you must
explicitly mark them using an attribute. To mark your class serializable use the

following attribute:
[serializable]
Formatters
A formatter is used to determine the format of the serialization. The .NET
Framework provide two formatters: SOAP and Binary. Youll typically use the
SOAP formatter for serializing across the Web, and the Binary formatter for
serializing to disk.
You are also free to write your own formatter, but this is a nontrivial task.
Network Streams
23-30 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Try It Out!
To see how serialization works, youll build a simple console application with
a class to serialize.
1. To get started, create a new console application and call it Serialization.
2. Create a class named Products. This class will create an array of products
such that the nth entry is equal to n time the value at n-1. So, the fifth entry

is equal to five times the value at the fourth entry. This class needs three
private members: a starting value (1), an ending number, and an array to
hold the products.
class Products
{
private int startNumber = 1;
private int endNumber;
private int[] theProducts;
3. Add a constructor to set the start number and end number. Within the
constructor youll call the two methods ComputeProducts and
DisplayProducts. The former fills the array, the latter displays their
contents:
public static void Main()
{
Products p = new Products(1,12);
}
public Products(int start, int end)
{
startNumber = start;
endNumber = end;
ComputeProducts();
DisplayProducts();
}
4. ComputeProducts creates the array and fills it with values.
See
Serialization0.sln
Serialization
C# Professional Skills Development 23-31
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
private void ComputeProducts()
{
int count = endNumber - startNumber + 1;
theProducts = new int[count];
theProducts[0] = startNumber;
for (int i=1,j=startNumber + 1;i<count;i++,j++)
{
theProducts[i] = j * theProducts[i-1];
}
}
5. DisplayProducts just iterates over the array, displaying each value.
private void DisplayProducts()
{
foreach(int i in theProducts)
{
Console.WriteLine("{0}, ",i);
}
}
6. Compile and run the program. The results are shown in Figure 8. You can
see that the fifth value (120) is the fourth value (24) times five.
Figure 8. Running the Products test program.
Network Streams
23-32 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
7. To serialize the class, add the Serialization namespaces.
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
8. Add the Serializable attribute to your class. Note, that this is not a custom

attribute, its an intrinsic attribute provided by the .NET Framework.
[Serializable]
class Products
{
9. Modify the Main method to Serialize the class after it is created. This
requires a Serialize method that you will create in Step 11.
public static void Main()
{
Console.WriteLine("Creating first one with new...");
Products p = new Products(1,10);
p.Serialize();
10. You can imagine that another program will deserialize the class youve
just serialized to disk. To simulate that, youll now create a second
instance of the Products class by deserializing that same file. This requires
writing the Deserialize method, which you will do in Step 15.
Console.WriteLine(
"Creating second one with deserialize...");
Products p2 = Products.DeSerialize();
p2.DisplayProducts();
} // end Main()
11. Write the Serialize method. The job of this method is to write the object to

a file.
Serialization
C# Professional Skills Development 23-33
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public void Serialize()
{
Console.Write("Serializing...");
12. Open a FileStream object and pass in the name of the output file, along
with a flag indicating that you want to create a new file.
FileStream fileStream =
new FileStream("DoProduct1.out",FileMode.Create);
13. You want this file written using the Binary formatter (as opposed to the
SOAP formatter or a custom formatter youd write yourself.) Instantiate a
BinaryFormatter, and call Serialize on it, passing in the FileStream you
just created and a reference to the object you want to serialize.
BinaryFormatter binaryFormatter =
new BinaryFormatter();
binaryFormatter.Serialize(fileStream,this);
14. Youve serialized the object, you may now close the FileStream.
Console.WriteLine("...completed");
fileStream.Close();
} // end Serialize
15. Write the Deserialize method to retrieve the object from the disk. Start by
creating a FileStream object to read the file you just saved. Pass in the
name of the file and a flag indicating that you want to open an existing file.
public static Products DeSerialize()
{
FileStream fileStream =
new FileStream("DoProduct1.out",FileMode.Open);
16. The file was saved using a BinaryFormatter, so youll need another
BinaryFormatter to read it. Call DeSerialize on the BinaryFormatter and
cast the result to an object of type Products. Close the FileStream.
Network Streams
23-34 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public static Products DeSerialize()
{
FileStream fileStream =
new FileStream("DoProduct1.out",FileMode.Open);
BinaryFormatter binaryFormatter =
new BinaryFormatter();
Products p = (Products)
binaryFormatter.Deserialize(fileStream);
fileStream.Close();
return p;
}
17. Compile and run the program. As shown in Figure 9, you can see that the
first instance was created and run, then serialized. The second instance is
created by deserializing the object from disk, and when you call
DisplayProducts on the second instance it displays properly.
Figure 9. Deserializing the object from disk.
Serialization
C# Professional Skills Development 23-35
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Dont Serialize Everything
In some classes, you may decide that there is data you do not want to serialize.

Typically, this would happen when the data will take a lot of room on the disk
and when you can easily reproduce the data at run time.
You can mark specific members of your class not to be serialized with the
[NonSerialized] attribute. Mark the array with this attribute to signal to the
compiler not to serialize this field:
[NonSerialized] private long[] theProducts;
Implementing IDeserializationCallBack
This presents a bit of a problem, however. If you do not serialize the array,
how will you restore the array? The Framework supports an interface
IDeserializationCallBack for this very purpose. To have an opportunity to
restore your file you will implement this interface. This interface describes
only a single method: OnDeserialization. You implement OnDeserialization,
and in that method you restore the array, in this case by recomputing the
numbers.
The Time/Space Continuum
It turns out that you are trading disk space for performancethat is, by not
serializing the array your file will be smaller, but your reconstruction will ta
ke
a bit longer. This tradeoff makes little sense (or little difference) in this tr
ivial
example, but can be a terrific optimization with a large object.
Try It Out!
To see how you can control what gets serialized, modify the previous example
to avoid serializing the array.
1. Reopen the previous example.
2. Search and replace int and make it long.
3. Change the parameters to the constructor from 1,10 to 1,20; this will create
an array twice as large.
See
Serialization2.sln
Network Streams
23-36 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
public static void Main()
{
Console.WriteLine("Creating first one with new...");
Products app = new Products(1,20);
4. Run the program. Navigate to the file produced (DoProduct.out) and rightclick

on it. The file is 361 bytes (see Figure 10).
Figure 10. Examining the output file.
You can imagine that with a far larger array, this file would be much bigger.
At some point, it might be worth a bit of effort to cut down the size of this fi
le.
5. Mark the class to implement IDeserializationCallBack.
[Serializable]
class Products : IDeserializationCallback
{
Serialization
C# Professional Skills Development 23-37
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
6. Mark the array not to be serialized.
private long startNumber = 1;
private long endNumber;
[NonSerialized] private long[] theProducts;
7. Implement the OnDeserialization method. In this case, you can just call
ComputeProducts.
public virtual void OnDeserialization(object sender)
{
Console.WriteLine("Recomputing...");
ComputeProducts();
}
8. Compile and run the program. As shown in Figure 11, the output is
identical, though you may notice a brief stutter while the numbers are
recomputed.
Network Streams
23-38 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 11. Implementing IdeserializationCallBack.
9. Right-click on the file produced. As shown in Figure 12, the file size has
been cut significantly. By not storing the array of values youve saved 183
bytes.
Serialization
C# Professional Skills Development 23-39
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 12. Reduced file size.
Network Streams
23-40 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
Because the Stream object is properly abstracted, reading and writing
to the network is not very different from reading and writing to a file.
Read from the Web by using a WebRequest object to create a
WebResponse object, and then call GetResponseStream on the
WebResponse instance.
Read from a file across the network just as you would read from a
local file, except that you use sockets to make the connection to the
remote machine.
Identify the application to which you will connect by using a port.
If you have an application that will provide I/O support, youll want to
use the overlapped I/O support provided by the Framework to allow
your application to scale to handle more than one request at a time.
When an object is written to a stream, it must be serialized.
Intrinsic types know how to serialize themselves.
User-defined types delegate responsibility for serialization to each of
their members.
You can control which fields are serialized, but if you do you will
want to implement IdeserializationCallBack.
Serialization
C# Professional Skills Development 23-41
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. What is a URI?
2. What is a port?
3. What is a socket?
4. What is the advantage of asynchronous network I/O?
5. How do user-defined types handle requests for serialization?
6. How do you mark a class for serialization?
7. How do you mark a field to avoid serialization?
Network Streams
23-42 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. What is a URI?
A URI is a Uniform Resource Identifier. The most common type of
URI is a URLthe typical Web address.
2. What is a port?
A port uniquely identifies access to a particular application
running at a given IP address.
3. What is a socket?
A socket is an object that represents an end point a specific
server to which your client may connect.
4. What is the advantage of asynchronous network I/O?
Servers typically receive requests from many clients
simultaneously. They must handle these requests in a timely
fashion; by using asynchronous network I/O the server can hand
the request off to another object and continue to field requests
for connections from other clients.
5. How do user-defined types handle requests for serialization?
User defined types delegate responsibility to their member fields.
Those fields will either be intrinsic types or other user-defined
types. Intrinsic types know how to serialize themselves; other
user-defined types will continue to delegate. Ultimately, all
requests will arrive at an intrinsic type that will serialize itself.
6. How do you mark a class for serialization?
You mark a class for serialization with the [serializable] attribute.
7. How do you mark a field to avoid serialization?
You mark the field with the [Nonserializable] attribute, but if you
do, be sure to implement the IDeserializationCallBack interface
so that you have an opportunity to recreate the field.
Serialization
C# Professional Skills Development 23-43
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 23:
Network Streams
Lab 23:
Network Streams
23-44 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 23 Overview
In this lab youll learn how to stream data across the Web and how to control
the serialization of your objects.
To complete this lab, youll need to work through two exercises:
Accessing Data with Web Streams
Controlling Serialization
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-bystep

instructions.
Accessing Data with Web Streams
C# Professional Skills Development 23-45
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Accessing Data with Web Streams
Objective
In this exercise, youll access a Web page using a URL and youll stream the
data retrieved to the console.
Things to Consider
A URL is a unique identifier of a specific page on the World Wide
Web.
The .NET Framework encapsulates a request for a Web page in an
HttpWebRequest object, and it encapsulates a Web page returned by a
HttpWebRequest in an HttpWebResponse object.
Any time you request an object across the Web, you want to use a
try/catch block to handle the exceptions that may arise from missing
pages or unavailable networks.
Step-by-Step Instructions
1. Open WebStreamStarter.sln in the WebStreamStarter folder.
2. Add using statements for System.Net, System.Net.Sockets, System.IO, and
System.Text.
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
3. Create an HttpWebRequest object named webRequest for the page
www.libertyassociates.com/pages/book_edit.htm.
HttpWebRequest webRequest =
(HttpWebRequest) WebRequest.Create
("http://www.libertyassociates.com/pages/book_edit.htm");
Lab 23:
Network Streams
23-46 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
4. Ask the Web request for an HttpWebResponse object named webResponse
encapsulating that page.
HttpWebResponse webResponse =
(HttpWebResponse) webRequest.GetResponse();
5. Get the StreamReader object from the response.
StreamReader streamReader = new StreamReader(
webResponse.GetResponseStream(), Encoding.ASCII);
6. Assign to the outputString the text returned by calling ReadToEnd on the
StreamReader object.
outputString = streamReader.ReadToEnd();
7. Write the string to the console.
Console.WriteLine(outputString);
8. Close the StreamReader object.
streamReader.Close();
9. Compile and run the program as shown in Figure 13.
Accessing Data with Web Streams
C# Professional Skills Development 23-47
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 13. The network stream results.
Lab 23:
Network Streams
23-48 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Controlling Serialization
Objective
In this exercise, youll create a class, serialize it to disk, and reconstitute it
.
You will also dictate which parts of the class will be serialized by
implementing IdeserializationCallback.
Things to Consider
You mark a class for serialization with the Serializable attribute.
To be notified when the class is reconstituted, youll implement
IDeserializationCallBack.
You can mark some members of your class not to be serialized with
the NonSerialized attribute.
Step-by-Step Instructions
1. Open SerializationStarter.sln in the SerializationStarter folder.
2. Add using statements for System.IO, System.Runtime.Serialization, and
System.Runtime.Serialization.Formatters.Binary.
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
3. Create a class named Sums. Mark it as Serializable and have it implement
the IDeserializationCallback interface.
[Serializable]
class Sums : IDeserializationCallback
{
4. There are three private member variables. The first two are startNumber
and endNumber, both of type long.
Controlling Serialization
C# Professional Skills Development 23-49
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
private long startNumber = 1;
private long endNumber;
5. The third member is an array of longs named theSums. Mark this one so it
isnt serialized.
[NonSerialized] private long[] theSums;
6. Implement the OnDeserialization method that takes one parameter of type
object.
public virtual void OnDeserialization(object sender)
{
7. Display a message to the console that you are recomputing the sums.
Console.WriteLine("Recomputing...");
8. Call ComputeSums.
ComputeSums();
9. Implement the Constructor to take two parameters. The first parameter is a
long representing the start value, and the second parameter is a long
representing the end value.
public Sums(long start, long end)
{
10. Initialize the member variables.
startNumber = start;
endNumber = end;
11. Call ComputeSums.
Lab 23:
Network Streams
23-50 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
ComputeSums();
12. Call DisplaySums.
DisplaySums();
13. Call Serialize.
Serialize();
14. Implement ComputeSums.
private void ComputeSums()
{
15. Create a local variable to hold the number of sums to compute (the end
number less the start number plus one).
long count = endNumber - startNumber + 1;
16. Initialize the member array with the right number of values.
theSums = new long[count];
17. Set the first value to the start number.
theSums[0] = startNumber;
18. Iterate over the array, adding the sums. Each member of the array is the
sum of the index, plus the previous value in the array.
Controlling Serialization
C# Professional Skills Development 23-51
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
for (long i=1,j=startNumber + 1;i<count;i++,j++)
{
theSums[i] = j + theSums[i-1];
}
19. Implement DisplaySums.
private void DisplaySums()
{
20. Iterate over the array and show the value of each member of the array.
foreach(long i in theSums)
{
Console.WriteLine("{0}, ",i);
}
21. Implement Serialize.
private void Serialize()
{
22. Display a message that you are serializing the object.
Console.Write("Serializing...");
23. Open a FileStream object for the output file.
FileStream fileStream =
new FileStream("DoSum.out",FileMode.Create);
24. Instantiate a BinaryFormatter for the object.
Lab 23:
Network Streams
23-52 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
BinaryFormatter binaryFormatter =
new BinaryFormatter();
25. Call Serialize on the binary formatter, passing in the file stream object an
d
a reference to this object.
binaryFormatter.Serialize(fileStream,this);
26. Display a message that the formatting is completed.
Console.WriteLine("...completed");
27. Close the file stream.
fileStream.Close();
28. Implement DeSerialize.
public static Sums DeSerialize()
{
29. Open a file stream on the output file.
FileStream fileStream =
new FileStream("DoSum.out",FileMode.Open);
30. Instantiate a Binary formatter for the object.
BinaryFormatter binaryFormatter =
new BinaryFormatter();
31. Deserialize the object and cast it to type Sums. Assign to a local object.
Sums theObject =
(Sums) binaryFormatter.Deserialize(fileStream);
Controlling Serialization
C# Professional Skills Development 23-53
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
32. Close the file stream object.
fileStream.Close();
33. Return the object you deserialized.
return theObject;
34. Compile and run the program as shown in Figure 14.
Lab 23:
Network Streams
23-54 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 14. The serialization results.
COM Interop
C# Professional Skills Development 24-1
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
COM Interop
Objectives
Create an ActiveX control in Visual Basic 6 to represent a legacy
COM control.
Import the ActiveX control into a .NET application.
Create a COM DLL in Visual Basic 6 to represent a legacy COM
DLL.
Import the COM DLL using early binding.
Import the COM DLL using reflection (late binding).
COM Interop
24-2 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
ActiveX
When a new technology comes along with the power and grace of .NET, it is
tempting to throw away all your old code and start clean on every new project.
Unfortunately, that is totally unrealistic; many companies have invested a great

deal of time, money, and effort into building COM and ActiveX objects, and
should reuse these tested and working components with the new technology.
Anticipating this requirement, Microsoft has provided a clean and easy path
for integrating existing ActiveX and COM DLL components into new .NET
applications.
The steps to using an existing ActiveX component with .NET are:
1. Import the ActiveX control into the .NET environment.
2. Add the ActiveX control to your Toolbox.
3. Drag it onto your form and use it like any other control.
Thats all there is to it.
Try It Out!
To demonstrate using ActiveX controls youll create a control in Visual Basic
6 and then youll import it into a .NET application.
1. Open the Visual Basic 6 development environment and choose ActiveX
Control as the new project type, as shown in Figure 1.
See ActiveX.sln
ActiveX
C# Professional Skills Development 24-3
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 1. Creating a new ActiveX control.
2. Right-click on the form (UserControl1) and choose Properties. Rename it
Calculator in the Properties window.
3. Click on the project in the Project Explorer and in the Properties window,
rename it ActiveXCalculatorControl.
4. Now you can add the four calculator functions by right-clicking on the
CalcControl form, selecting View Code from the popup menu and typing
in the following code:
COM Interop
24-4 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Public Function _
Add(left As Double, right As Double) _
As Double
Add = left + right
End Function
Public Function _
Subtract(left As Double, right As Double) _
As Double
Subtract = left right
End Function
Public Function _
Multiply(left As Double, right As Double) _
As Double
Multiply = left * right
End Function
Public Function _
Divide(left As Double, right As Double) _
As Double
Divide = left / right
End Function
5. Return to the form and shrink it as small as possibleit will have no user
interface.
6. Compile this to the ActiveXCalculatorControl.ocx file by choosing
File|Make ActiveXCalculatorControl.ocx.
7. Open a second project in Visual Basic as a Standard EXE.
8. Name the form TestForm and name the project ActiveXCalcTest.
9. Save the file and project as ActiveXCalcTest.
10. Add the ActiveX control as a component by pressing CTRL+T and
choosing CalcControl from the Controls tab.
11. This action puts a new control on the Toobox, as shown in Figure 2.
ActiveX
C# Professional Skills Development 24-5
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 2. Adding the new control.
12. Drag the new control onto the form named TestForm and name it
calcControl. Note that the new control will not be visible; this control has no
user interface.
13. Add two text boxes, four buttons, and one label, as shown in Figure 3.
Figure 3. Creating the controls.
14. Name the buttons btnAdd, btnSubtract, btnMultiply, and btnDivide.
COM Interop
24-6 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
15. Implement the event handlers.
Private Sub btnAdd_Click()
Label1.Caption = _
calcControl.Add(CDbl(Text1.Text), _
CDbl(Text2.Text))
End Sub
Private Sub btnDivide_Click()
Label1.Caption = _
calcControl.Divide(CDbl(Text1.Text), _
CDbl(Text2.Text))
End Sub
Private Sub btnMultiply_Click()
Label1.Caption = _
calcControl.Multiply(CDbl(Text1.Text), _
CDbl(Text2.Text))
End Sub
Private Sub btnSubtract_Click()
Label1.Caption = _
calcControl.Subtract(CDbl(Text1.Text), _
CDbl(Text2.Text))
End Sub
16. Run the program. The test program delegates responsibility for computing
the sum, difference, product or quotient to the calculator control you
created, as shown in Figure 4.
Figure 4. Testing the code.
ActiveX
C# Professional Skills Development 24-7
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Importing to .NET
You now have a working ActiveX control. You can copy that control to your
.NET environment and import it into your next .NET application.
Try It Out!
To see how to import the ActiveX control, youll create a simple Windows
Test form, not unlike the test form you created in Visual Basic 6.
1. Copy the .OCX file produced in the previous example to your .NET
development environment.
2. Register the ocx file using Regsvr32. Do so by opening a command
window and navigating to the directory with the control. Type regsvr32
activeXCalculatorControl.ocx. The system will respond now that the
control has been registered, as shown in Figure 5.
Figure 5. Registering the control.
3. Create a new .NET Windows Forms project named ActiveXTest.
4. Design a form like the TestForm form you created in Visual Basic, as
shown in Figure 6.
Figure 6. Create the test form in .NET.
See ActiveXTest.sln
COM Interop
24-8 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
5. Import the control. To do so, choose Tools|Customize Toolbox from the
menu. On the Com Components tab find the object you just registered, as
shown in Figure 7.
Figure 7. Adding the control.
6. The control appears on your Toolbox, as shown in Figure 8.
ActiveX
C# Professional Skills Development 24-9
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 8. The control added to the Toolbox.
7. Drag the new control onto your Windows form.
8. Add event handlers for each of the four buttons. The easiest way is to
double-click on each of the buttons in turn. You need to convert the string
in the text box to a double. There are two ways to do this: you can use the
Parse static method of the Double class, or you can use the ToDouble
static method of the Convert class. These are illustrated in the btnAddclick
event handler:
private void btnAdd_Click(
object sender, System.EventArgs e)
{
// note two ways to do this!
double left = double.Parse(textBox1.Text);
double right = Convert.ToDouble(textBox2.Text);
COM Interop
24-10 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
label1.Text =
axCalculator1.Add( ref left, ref right).ToString();
}
private void btnDivide_Click(
object sender, System.EventArgs e)
{
double left = double.Parse(textBox1.Text);
double right = double.Parse(textBox2.Text);
label1.Text =
axCalculator1.Divide(ref left, ref right).ToString();
}
private void btnMultiply_Click(
object sender, System.EventArgs e)
{
double left = double.Parse(textBox1.Text);
double right = double.Parse(textBox2.Text);
label1.Text =
axCalculator1.Multiply(
ref left, ref right).ToString();
}
private void btnSubtract_Click(
object sender, System.EventArgs e)
{
double left = double.Parse(textBox1.Text);
double right = double.Parse(textBox2.Text);
label1.Text =
axCalculator1.Subtract(
ref left, ref right).ToString();
}
9. Compile and build the project, and then run the application. It appears
nearly identical to the test application you built in Visual Basic 6, as
shown in Figure 9.
ActiveX
C# Professional Skills Development 24-11
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 9. Testing the imported ActiveX control.
What Happened?
Think about what you just did. You created an ActiveX control in Visual Basic
6 to represent an existing legacy ActiveX control. You built a new Visual
Studio .NET Windows Form application and you used that control in your new
form just by registering it on your .NET system and then adding it to your
toolbar!
COM Interop
24-12 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
COM DLLs
Not all existing COM applications are ActiveX controls, however. Many are
COM DLLs. You can use an existing legacy COM DLL in .NET just about as
easily as you can use an ActiveX control.
Try It Out!
To represent an existing legacy COM DLL youll create a new COM project in
Visual Basic 6.
1. Return to the Visual Basic 6 development environment, and create a new
project. This time, choose ActiveX DLL. This is the easiest way to create
a standard COM DLL in Visual Basic 6.
2. Name the class ComCalc and name the project ComCalculator. Save the
file and project.
3. Use the same methods in this example as you did in the ActiveXControl.
Public Function _
Add(left As Double, right As Double) _
As Double
Add = left + right
End Function
Public Function _
Subtract(left As Double, right As Double) _
As Double
Subtract = left - right
End Function
Public Function _
Multiply(left As Double, right As Double) _
As Double
Multiply = left * right
End Function
See
ComCalculator.vbb
COM DLLs
C# Professional Skills Development 24-13
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Public Function _
Divide(left As Double, right As Double) _
As Double
Divide = left / right
End Function
4. Build the DLL by clicking File|Make ComCalculator.dll.
5. Reopen the earlier test program you created.
6. Remove the Calculator control from the form.
7. Try to run the program, but it should fail as shown in Figure 10.
Figure 10. After removing the control.
8. Open the Project Reference window. Add the ComCalculator you just
created. In the event handler, instantiate a comcalc object and call its
methods.
Private Sub btnAdd_Click()
Dim theCalc As New ComCalc
Label1.Caption = _
theCalc.Add(CDbl(Text1.Text), _
CDbl(Text2.Text))
End Sub
9. Make this change in each of the four methods.
COM Interop
24-14 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Private Sub btnAdd_Click()
Dim theCalc As New ComCalc
Label1.Caption = _
theCalc.Add(CDbl(Text1.Text), _
CDbl(Text2.Text))
End Sub
Private Sub btnDivide_Click()
Dim theCalc As New ComCalc
Label1.Caption = _
theCalc.Divide(CDbl(Text1.Text), _
CDbl(Text2.Text))
End Sub
Private Sub btnMultiply_Click()
Dim theCalc As New ComCalc
Label1.Caption = _
theCalc.Multiply(CDbl(Text1.Text), _
CDbl(Text2.Text))
End Sub
Private Sub btnSubtract_Click()
Dim theCalc As New ComCalc
Label1.Caption = _
theCalc.Subtract(CDbl(Text1.Text), _
CDbl(Text2.Text))
End Sub
10. Run the program. The results are identical to the previous test, as shown in

Figure 11.
COM DLLs
C# Professional Skills Development 24-15
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 11. Testing the COM DLL in Visual Basic 6.
Importing the DLL back into .NET
You are ready to import the DLL back to the .NET environment and use it in
your next Windows Forms application. You have a decision to make, however.
There are two ways to bind to a COM control. The first is called early binding
and the second is called late binding.
With early binding, you bind to the COM DLL as if it were part of your
program. Early binding requires that you have a type library, but it is much
more efficient than late binding. If you do not have a type library you must use

late binding. Late binding uses reflection to discover the capabilities of the
control in the DLL and is less efficient.
Try It Out!
To try out early binding, youll return to the previous .NET test example and
modify it to use the new COM DLL with early binding.
1. Copy the COM DLL to the .NET environment.
2. Register the DLL using RegSvr32 just as you did previously, as shown in
Figure 12.
Figure 12. Registering the DLL.
3. Reopen the test program.
See ActiveXTest.sln
COM Interop
24-16 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
4. Go to the designer and remove the ActiveX control.
5. Rebuild the program. It will not compile, because it can no longer find
axCalculator1.
6. Add the new COM DLL by navigating to Project|Add Reference and
then clicking the COM tab. Double-click on the ComCalculator to add it to
the list of Selected Components, as shown in Figure 13.
Figure 13. Adding the ComCalculator to the project.
7. When you click OK, Visual Studio .NET tells you that this assembly is not
a .NET assembly, as shown in Figure 14. Click the Yes button and Visual
Studio .NET builds the required wrapper class for you.
Figure 14. Importing the COM DLL with a wrapper.
COM DLLs
C# Professional Skills Development 24-17
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
8. Modify the code in btnAdd_Click:
Double left, right, result;
left = Double.Parse(textBox1.Text);
right = Double.Parse(textBox2.Text);
9. To instantiate a ComCalc object, type ComCalculator followed by a
period. The IDE tries to help you identify the object you want, as shown in
Figure 15.
Figure 15. Visual Studio .NET recognizes your COM DLL.
Choose ComCalc and instantiate the object:
ComCalculator.ComCalc theCalc = new
ComCalculator.ComCalc();
10. You want to assign to the variable result the value returned by calling Add
on the ComCalc object. When you open the Add method, the IDE reminds
you that the parameters must be references, as shown in Figure 16.
Figure 16. The IDE helps with the parameters.
Add the local variables left and right, passing them in as references:
result = theCalc.Add(ref left, ref right);
COM Interop
24-18 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
11. Set the text for the label.
label1.Text = result.ToString();
12. Make the same changes in the other methods.
private void btnDivide_Click(
object sender, System.EventArgs e)
{
double left, right, result;
left = Double.Parse(textBox1.Text);
right = Convert.ToDouble(textBox2.Text);
ComCalculator.ComCalc theCalc =
new ComCalculator.ComCalc();
result = theCalc.Divide(ref left, ref right);
label1.Text = result.ToString();
}
private void btnMultiply_Click(
object sender, System.EventArgs e)
{
double left, right, result;
left = Double.Parse(textBox1.Text);
right = Convert.ToDouble(textBox2.Text);
ComCalculator.ComCalc theCalc =
new ComCalculator.ComCalc();
result = theCalc.Multiply(ref left, ref right);
label1.Text = result.ToString();
}
private void btnSubtract_Click(
object sender, System.EventArgs e)
{
double left, right, result;
left = Double.Parse(textBox1.Text);
COM DLLs
C# Professional Skills Development 24-19
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
right = Convert.ToDouble(textBox2.Text);
ComCalculator.ComCalc theCalc =
new ComCalculator.ComCalc();
result = theCalc.Subtract(ref left, ref right);
label1.Text = result.ToString();
}
13. Compile and run the program. The results are identical to the previous test,

as shown in Figure 17.
Figure 17. Testing the COM DLL.
Late Binding
You were able to use early binding with this COM DLL because Visual Basic
6 included a type library in the DLL when it created the DLL. If, however, you
have a legacy COM DLL that does not have a type library, then you will need
to use reflection to run the program.
Try It Out!
To see how to do late binding, youll go back to modify the Visual Studio
.NET test application just one more time.
1. Reopen the test application from the previous example.
2. Add the Reflection namespace to the top of the program.
See ActiveXTest.sln
COM Interop
24-20 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Reflection;
3. Factor the code for creating the calculator object into a new method
method: Invoke. Invoke will take a single parameter and a string indicating
which operation is to be accomplished. Give Invoke a local variable to
hold the values in the two text boxes and to hold the result.
private void Invoke(string whichMethod)
{
Double left, right, result;
left = Double.Parse(textBox1.Text);
right = Double.Parse(textBox2.Text);
4. Create a Type object to hold the information about the type youll be
binding to.
Type comCalcType;
5. Assign to the Type object the result of calling the static method
GetTypeFrom ProgID, passing in the ProgID of the DLL.
comCalcType =
Type.GetTypeFromProgID(
"ComCalculator.ComCalc");
6. Call the static method CreateInstance on the Activator class, passing in the
Type object you created in Step 5.
object comCalcObject =
Activator.CreateInstance(comCalcType);
COM DLLs
C# Professional Skills Development 24-21
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
7. Create an array of objects to hold the arguments for the method youll
invoke.
object[] inputArguments = {left, right};
8. Invoke the method, passing in the string you receive as a parameter (e.g.,
Add), the object you created with CreateInstance, and the argument array.
result = (Double) comCalcType.InvokeMember(
whichMethod, // the method to invoke
BindingFlags.InvokeMethod, // how to bind
null, // binder
comCalcObject, // the COM object
inputArguments); // the method arguments
9. Put the results in the label.
label1.Text = result.ToString();
10. Go back to the event handlers and call this new method.
private void btnAdd_Click(
object sender, System.EventArgs e)
{
Invoke("Add");
}
private void btnDivide_Click(
object sender, System.EventArgs e)
{
Invoke("Divide");
}
private void btnMultiply_Click(
object sender, System.EventArgs e)
{
Invoke("Multiply");
COM Interop
24-22 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
}
private void btnSubtract_Click(
object sender, System.EventArgs e)
{
Invoke("Subtract");
}
11. Compile and build the application. The results are identical to the previous

(early binding) example, as shown in Figure 18.
Figure 18. Late binding.
Late binding is somewhat slower and less efficient, but it works and allows
you to use legacy COM applications even if you dont have a type library.
COM DLLs
C# Professional Skills Development 24-23
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Summary
The .NET Framework supports using legacy ActiveX and COM
objects as if they were native .NET assemblies.
Visual Studio .NET provides extensive support for importing both
ActiveX controls and COM DLLs.
Once you add an ActiveX control to your toolbar, you may use it like
you would any other item on the toolbar.
Once you import a COM DLL object into your environment, you may
instantiate it (early binding). This requires that a type library be
available.
If a type library is not available for your COM object, you can use
reflection to accomplish late binding to the object.
COM Interop
24-24 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
(Review questions and answers on the following pages.)
COM DLLs
C# Professional Skills Development 24-25
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Questions
1. Why would you import an ActiveX control into .NET?
2. How do you register the ActiveX control on your .NET machine?
3. How do you add the ActiveX control to your Toolbox?
4. How do you register a COM DLL on your .NET machine?
5. How do you add a reference to the COM DLL in your .NET program?
6. How do you accomplish late binding with COM objects?
COM Interop
24-26 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Answers
1. Why would you import an ActiveX control into .NET?
You would import an ActiveX control into .NET to use a control
that was built and tested with COM before the advent of .NET.
This allows you to continue to leverage the investment of
previous work.
2. How do you register the ActiveX control on your .NET machine?
You register an ActiveX control with Regsvr32.
3. How do you add the ActiveX control to your Toolbox?
On the Tools menu, choose Customize Toolbox. Click on the COM
Components tab and click on the control you want to add.
4. How do you register a COM DLL on your .NET machine?
Trick question: you register the COM DLL exactly as you did the
ActiveX control, with Regsvr32.
5. How do you add a reference to the COM DLL in your .NET program?
On the Project menu, choose Add Reference. Click on the COM
tab and double-click on the object you want to add. Click OK to
add it to the project. When Visual Studio .NET offers to build a
wrapper class, choose YES. This requires a type library.
6. How do you accomplish late binding with COM objects?
You accomplish late binding with reflection.
COM DLLs
C# Professional Skills Development 24-27
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 24:
COM Interop
NOTE These exercises do not have starter files because they are very
easy to reproduce from scratch, and they do not have completed
files because the registration of the ActiveX control would be far
more complicated than simply recreating the controls from
scratch.
Lab 24:
COM Interop
24-28 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Lab 24 Overview
In this lab youll learn to create an ActiveX control in Visual Basic 6 and then
to import that control into your C# application.
To complete this lab, youll need to work through two exercises:
Creating the ActiveX Control
Importing the ActiveX Control
Each exercise includes an Objective section that describes the purpose of the
exercise. You are encouraged to try to complete the exercise from the
information given in the Objective section. If you require more information to
complete the exercise, the Objective section is followed by detailed step-bystep

instructions
Creating the ActiveX Control
C# Professional Skills Development 24-29
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Creating the ActiveX Control
Objective
In this exercise, youll create an ActiveX control in Visual Basic 6 and youll
test that control in a simple Visual Basic 6 application
Things to Consider
ActiveX controls can be created in virtually any programming
language, but Visual Basic 6 makes it simpler.
The control youll create now is built in a Visual Basic 6 rather than in
a .NET environment, because ActiveX controls represent legacy
applications.
Step-by-Step Instructions
1. Open the Visual Basic 6 development environment and choose ActiveX
Control as the new project type. Click Open.
2. Right-click on the form (UserControl1) and choose Properties. Rename
the form MathMaker in the Properties window.
3. Click on the project in Project Explorer. In the Properties window, rename
the project ActiveXMathControl.
4. Right-click on the MathMaker form, select View Code from the popup
menu, and type in the code for Square.
Public Function Square(value As Double) As Double
Square = value * value
End Function
5. Type in the code for DoDouble.
Public Function DoDouble(value As Double) As Double
DoDouble = value * 2
End Function
Lab 24:
COM Interop
24-30 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
6. Type in the code for DoTriple.
Public Function DoTriple(value As Double) As Double
DoTriple = value * 3
End Function
7. Return to the form and make it as small as possibleit will have no user
interface.
8. Click File|Save MathMaker and save the file as MathMaker.ctl.
9. Choose File|Make ActiveXMathControl.ocx to compile the ActiveX
control.
10. Open a second project in Visual Basic as a Standard EXE. You will be
prompted to save your existing project. Do this by using the default name.
11. In the new project, name the form TestForm and name the project
ActiveXMathTest.
12. Save the file and project as ActiveXMathTest.
13. Add the ActiveX control as a component by pressing CTRL+T and
choosing ActiveXMathControl from the Controls tab. This action puts a
new control on the Toolbox.
14. Drag the new control onto the form named TestForm and name it
MathControl. Note that the new control will not be visible; this control
has no user interface.
15. Add a text box and name it txtInput. Set its text to a blank string (so that

the text box appears to be empty).
16. Add a button and name it btnSquare. Set its caption to Square.
17. Add two more buttons, one named btnDouble with the caption Double and
the third named btnTriple with the caption Triple. Make all three buttons
the same size and distribute them evenly below the text box.
18. Add a label named lblOutput with the caption No value computed. The
form should now look like Figure 19.
Creating the ActiveX Control
C# Professional Skills Development 24-31
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Figure 19. The form for testing the ActiveX control.
19. Implement the event handler for the Square button. Double-click the
Square button to bring up the event handler.
20. Add code to extract the value from the text box and convert it to a double,
then pass that value to the MathMaker object and invoke the Square
function.
Private Sub btnSquare_Click()
lblOutput.Caption = _
MathControl.Square(CDbl(txtInput.Text))
End Sub
21. Implement the event handler for the Double and Triple buttons in the same
way.
Private Sub btnSquare_Click()
lblOutput.Caption = _
MathControl.DoDouble(CDbl(txtInput.Text))
End Sub
Private Sub btnTriple_Click()
lblOutput.Caption = _
MathControl.DoTriple(CDbl(txtInput.Text))
End Sub
22. Run the program. The test program delegates responsibility for computing
the square, double, and triple of the typed-in value to control what youve
created.
Lab 24:
COM Interop
24-32 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
Importing the ActiveX Control
Objective
In this exercise, youll import the ActiveX control you created in the previous
exercise into the .NET environment.
Things to Consider
After you copy the ActiveX control into the .NET work area, youll
need to register it.
Visual Studio .NET makes it easy to import ActiveX controls
Step-by-Step Instructions
1. Copy the .OCX file produced in the previous exercise to your .NET
development environment.
2. Register the OCX file using Regsvr32. Do so by opening a command
window and navigating to the directory with the control. Type regsvr32
activeXMathControl.ocx. The system will let you know that the control
has been registered, as shown in Figure 20.
Figure 20. Registering the control.
3. Create a new .NET Windows Forms project named ActiveXImport.
4. Change the name and the text of the form to ActiveXTest.
5. Change the default form name in the Main method from Form1 to
ActiveXTest.
6. Drag a text box onto the form and name it txtValue. Set its text to a blank
string.
Importing the ActiveX Control
C# Professional Skills Development 24-33
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
7. Drag three buttons onto the form, and name them btnSquare, btnDouble,
and btnTriple. Set their text to Square, Double, and Triple, respectively.
Make them the same size and space them evenly apart.
8. Drag a label onto the form and name it lblOutput. Set its text to No value
computed.
9. Your form should now look like the form shown in Figure 21.
Figure 21. The ActiveXTest form.
10. To import the control, choose Tools|Customize Toolbox from the menu.
On the COM Components tab find the object you just registered, as shown
in Figure 22.
Figure 22. Importing the ActiveXMathControl.
Lab 24:
COM Interop
24-34 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
11. Drag the MathMaker control from your Toolbox to the form. Visual Studio
.NET will give it a default name of axMathMaker1.
12. Add event handlers for the Square button. Double-click the Square button
to open the default event handler for the click event.
13. Convert the text in the text box to a double.
private void btnSquare_Click(
object sender, System.EventArgs e)
{
double theVal = double.Parse(txtValue.Text);
14. Set the value of the output label by invoking the Square method on the
MathMaker object. Be sure to pass the value by reference.
lblOutput.Text =
axMathMaker1.Square(ref theVal).ToString();
15. Implement the event handler for the Double button.
private void btnDouble_Click(
object sender, System.EventArgs e)
{
double theVal = double.Parse(txtValue.Text);
lblOutput.Text =
axMathMaker1.DoDouble(ref theVal).ToString();
}
16. Implement the event handler for the Triple button.
private void btnTriple_Click(
object sender, System.EventArgs e)
{
double theVal = double.Parse(txtValue.Text);
lblOutput.Text =
axMathMaker1.DoTriple(ref theVal).ToString();
}
Importing the ActiveX Control
C# Professional Skills Development 24-35
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.
17. Run the application. The output should look like Figure 23.
Figure 23. Running the application.
Lab 24:
COM Interop
24-36 C# Professional Skills Development
Copyright by Application Developers Training Company and AppDev Products Company
, LLC
All rights reserved. Reproduction is strictly prohibited.

Você também pode gostar