Sunteți pe pagina 1din 938

Part Number: X08-76381

Course Number: 2349B




Released: 02/2002
Delivery Guide
Programming with the
Microsoft .NET Framework
(Microsoft Visual C#

.NET)



Information in this document, including URL and other Internet Web site references, is subject to
change without notice. Unless otherwise noted, the example companies, organizations, products,
domain names, e-mail addresses, logos, people, places, and events depicted herein are fictitious,
and no association with any real company, organization, product, domain name, e-mail address,
logo, person, places or events is intended or should be inferred. Complying with all applicable
copyright laws is the responsibility of the user. Without limiting the rights under copyright, no
part of this document may be reproduced, stored in or introduced into a retrieval system, or
transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or
otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual
property rights covering subject matter in this document. Except as expressly provided in any
written license agreement from Microsoft, the furnishing of this document does not give you any
license to these patents, trademarks, copyrights, or other intellectual property.

20012002 Microsoft Corporation. All rights reserved.

Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint,
Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and
Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A.
and/or other countries.

The names of actual companies and products mentioned herein may be the trademarks of their
respective owners.


Course Number: 2349B
Part Number: X08-76381
Released: 02/2002
Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET) iii


Contents

Introduction
Introduction..............................................................................................................1
Course Materials ......................................................................................................2
Prerequisites.............................................................................................................3
Course Outline .........................................................................................................4
Microsoft Certified Professional Program...............................................................9
Facilities.................................................................................................................11
Module 1: Overview of the Microsoft .NET Framework
Overview..................................................................................................................1
Overview of the Microsoft .NET Framework..........................................................2
Overview of Namespaces ......................................................................................13
Review...................................................................................................................17
Module 2: Introduction to a Managed Execution Environment
Overview..................................................................................................................1
Writing a .NET Application.....................................................................................2
Compiling and Running a .NET Application.........................................................11
Lab 2: Building a Simple .NET Application..........................................................29
Review...................................................................................................................32
Module 3: Working with Components
Overview..................................................................................................................1
An Introduction to Key .NET Framework Development Technologies ..................2
Creating a Simple .NET Framework Component ....................................................4
Lab 3.1: Creating a .NET Framework Component ................................................11
Creating a Simple Console Client ..........................................................................14
Lab 3.2: Creating a Simple Console-Based Client.................................................19
Demonstration: Creating a Windows Forms Client ...............................................22
Creating an ASP.NET Client .................................................................................27
Lab 3.3: Calling a Component Through an ASP.NET Page..................................36
Review...................................................................................................................40
Module 4: Deployment and Versioning
Overview..................................................................................................................1
Introduction to Application Deployment .................................................................2
Application Deployment Scenarios .........................................................................7
Related Topics and Tools.......................................................................................31
Lab 4: Packaging and Deployment ........................................................................37
Review...................................................................................................................42
Module 5: Common Type System
Overview..................................................................................................................1
An Introduction to the Common Type System........................................................2
Elements of the Common Type System...................................................................8
Object-Oriented Characteristics.............................................................................25
Lab 5: Building Simple Types ...............................................................................39
Review...................................................................................................................44
iv Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET)


Module 6: Working with Types
Overview................................................................................................................. 1
System.Object Class Functionality ......................................................................... 2
Specialized Constructors....................................................................................... 12
Type Operations.................................................................................................... 18
Interfaces............................................................................................................... 28
Managing External Types ..................................................................................... 34
Lab 6: Working with Types .................................................................................. 38
Review.................................................................................................................. 43
Module 7: Strings, Arrays, and Collections
Overview................................................................................................................. 1
Strings ..................................................................................................................... 2
Terminology Collections.................................................................................... 20
.NET Framework Arrays....................................................................................... 21
.NET Framework Collections ............................................................................... 39
Lab 7: Working with Strings, Enumerators, and Collections................................ 57
Review.................................................................................................................. 63
Module 8: Delegates and Events
Overview................................................................................................................. 1
Delegates................................................................................................................. 2
Multicast Delegates............................................................................................... 12
Events.................................................................................................................... 21
When to Use Delegates, Events, and Interfaces.................................................... 31
Lab 8: Creating a Simple Chat Server................................................................... 32
Review.................................................................................................................. 42
Module 9: Memory and Resource Management
Overview................................................................................................................. 1
Memory Management Basics.................................................................................. 2
Non-Memory Resource Management ................................................................... 12
Implicit Resource Management ............................................................................ 13
Explicit Resource Management ............................................................................ 26
Optimizing Garbage Collection ............................................................................ 36
Lab 9: Memory and Resource Management ......................................................... 49
Review.................................................................................................................. 56
Module 10: Data Streams and Files
Overview................................................................................................................. 1
Streams.................................................................................................................... 2
Readers and Writers................................................................................................ 5
Basic File I/O.......................................................................................................... 8
Lab 10: Files ......................................................................................................... 21
Review.................................................................................................................. 26
Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET) v


Module 11: Internet Access
Overview..................................................................................................................1
Internet Application Scenarios.................................................................................2
The WebRequest and WebResponse Model ............................................................3
Application Protocols.............................................................................................16
Handling Errors......................................................................................................25
Security..................................................................................................................28
Best Practices.........................................................................................................35
Lab 11: Creating a DateTime Client/Server Application.......................................36
Review...................................................................................................................41
Course Evaluation..................................................................................................43
Module 12: Serialization
Overview..................................................................................................................1
Serialization Scenarios.............................................................................................2
Serialization Attributes ............................................................................................4
Object Graph............................................................................................................5
Serialization Process ................................................................................................7
Serialization Example ..............................................................................................9
Deserialization Example ........................................................................................10
Custom Serialization..............................................................................................12
Custom Serialization Example...............................................................................14
Security Issues .......................................................................................................17
Lab 12: Serialization..............................................................................................18
Review...................................................................................................................27
Module 13: Remoting and XML Web Services
Overview..................................................................................................................1
Remoting..................................................................................................................2
Remoting Configuration Files................................................................................19
Demonstration: Remoting......................................................................................22
Lab 13.1: Building an Order-Processing Application by Using Remoted Servers 28
XML Web Services................................................................................................36
Lab 13.2: Using an XML Web Service..................................................................48
Review...................................................................................................................54
Course Evaluation..................................................................................................56
vi Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET)


Optional Modules

Module 14 (Optional): Threading and Asynchronous Programming
Overview................................................................................................................. 1
Introduction to Threading ....................................................................................... 2
Using Threads in .NET ........................................................................................... 9
Thread Safety........................................................................................................ 29
Special Thread Topics........................................................................................... 51
Asynchronous Programming in .NET................................................................... 74
Lab 14: Working With Multithreaded Applications ............................................. 92
Review................................................................................................................ 108
Module 15 (Optional): Interoperating Between Managed and
Unmanaged Code
Overview................................................................................................................. 1
Integration Services................................................................................................. 2
Platform Invoke ...................................................................................................... 7
Lab 15.1: Calling Win32 APIs.............................................................................. 16
Calling COM Objects from Managed Code.......................................................... 20
Lab 15.2: Calling COM Objects ........................................................................... 39
Calling .NET Objects from COM Objects............................................................ 43
Review.................................................................................................................. 55
Module 16 (Optional): Using Microsoft ADO.NET to Access Data
Overview................................................................................................................. 1
Overview of ADO.NET.......................................................................................... 2
Connecting to a Data Source................................................................................. 10
Accessing Data with DataSets .............................................................................. 12
Using Stored Procedures....................................................................................... 26
Lab 16: Using ADO.NET to Access Data ............................................................ 34
Accessing Data with DataReaders ........................................................................ 42
Binding to XML Data ........................................................................................... 50
Review.................................................................................................................. 56
Module 17 (Optional): Attributes
Overview................................................................................................................. 1
Overview of Attributes............................................................................................ 2
Defining Custom Attributes .................................................................................. 13
Retrieving Attribute Values .................................................................................. 22
Demonstration: Custom Attributes ....................................................................... 26
Lab 17: Defining and Using Attributes................................................................. 27
Review.................................................................................................................. 36
Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET) vii


About This Course
This section provides you with a brief description of the course, audience,
suggested prerequisites, course objectives, and information about course
customization.
Description
The goal of this five day course is to help application developers understand the
Microsoft .NET Framework. In addition to offering an overview of the .NET
Framework and an introduction to key concepts and terminology, the course
provides a series of labs, which introduce and explain .NET Framework
features that are used to code, debug, tune, and deploy applications.
Audience
This course is intended for experienced, professional software developers who
work in independent software vendors (ISVs) or work on corporate enterprise
development teams. Most students will be Microsoft Visual C++ (or C++) and
Java developers.
Student Prerequisites
This course requires that students meet the following prerequisites:
Students should be experienced professional developers and have a basic
understanding of the C# language.
Students can meet the C# language prerequisite by taking Course 2124,
Introduction to C# Programming for the Microsoft .NET Platform.

Course Objectives
After completing this course, the student will be able to:
! List the major elements of the .NET Framework and explain how they fit
into the .NET platform.
! Explain the main concepts behind the common language runtime and use
the features of the .NET Framework to create a simple application.
! Create and use components in Microsoft Windows Forms-based and
ASP.NET-based applications.
! Use the deployment and versioning features of the .NET runtime to deploy
multiple versions of a component.
! Create, use, and extend types by understanding the Common Type System
architecture.
! Create classes and interfaces that are functionally efficient and appropriate
for specific programming scenarios.
! Use the .NET Framework class library to efficiently create and manage
strings, arrays, collections, and enumerators.
viii Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET)


! Use delegates and events to have an event sender object signal the
occurrence of an action to an event receiver object.
! Describe and control how memory and other resources are managed in the
.NET Framework.
! Read from and write to data streams and files.
! Use the basic request/response model to send and receive data over the
Internet.
! Serialize and deserialize an object graph.
! Create distributed applications through XML Web services and Object
Remoting.

Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET) ix


Course Timing
This section provides estimated course timings for all of the modules, labs, and
breaks in Course 2349B, Programming with the Microsoft .NET Framework
(Microsoft Visual C#

.NET). Your timing may vary.


Day 1
Start End Module
9:00 9:30 Introduction
9:30 10:00 Module 1: Overview of the Microsoft .NET Framework
10:00 10:15 Break
10:15 11:00 Module 2: Introduction to a Managed Execution Environment
11:00 11:15 Lab 2: Building a Simple .NET Application
11:15 11:45 Module 3: Working with Components
11:45 12:00 Lab 3.1: Creating a .NET Framework Component
12:00 1:00 Lunch
1:00 1:15 Module 3: Working with Components (continued)
1:15 1:30 Lab 3.2: Creating a Simple Console-Based Client
1:30 2:00 Module 3: Working with Components (continued)
2:00 2:30 Lab 3.3: Calling a Component Through an ASP .NET Page
2:30 2:45 Break
2:45 4:15 Module 4: Deployment and Versioning

Day 2
Start End Module
9:00 9:50 Lab 4: Packaging and Deployment
9:50 10:00 Break
10:00 11:30 Module 5: Common Type System
11:30 12:30 Lunch
12:30 1:15 Lab 5: Building Simple Types
1:15 2:30 Module 6: Working with Types
2:30 2:45 Break
2:45 3:30 Lab 6: Working with Types
3:30 4:00 Module 7: Strings, Arrays, and Collections

x Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET)


Day 3
Start End Module
9:00 10:30 Module 7: Strings, Arrays, and Collections (continued)
10:30 10:45 Break
10:45 11:45 Lab 7: Working with Strings, Enumerators, and Collections
11:45 12:45 Lunch
12:45 2:00 Module 8: Delegates and Events
2:00 2:15 Break
2:15 3:30 Lab 8: Creating a Simple Chat Server
3:30 4:00 Module 9: Memory and Resource Management

Day 4
Start End Module
9:00 10:30 Module 9: Memory and Resource Management (continued)
10:30 10:45 Break
10:45 11:45 Lab 9: Memory and Resource Management
11:45 12:45 Lunch
12:45 1:30 Module 10: Data Streams and Files
1:30 2:15 Lab 10: Files
2:15 2:30 Break
2:30 3:30 Module 11: Internet Access
3:30 4:15 Lab 11: Creating a DateTime Client/Server Application

Day 5
Start End Module
9:00 9:30 Module 12: Serialization
9:30 10:15 Lab 12: Serialization
10:15 10:30 Break
10:30 11:30 Module 13: Remoting and Web Services
11:30 12:30 Lunch
12:30 1:20 Lab 13.1: Building an Order-Processing Application by Using
Remoted Servers
1:20 2:20 Module 13: Remoting and Web Services (continued)
2:20 2:35 Break
2:35 3:30 Lab 13.2: Using an XML Web Service

Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET) xi


Trainer Materials Compact Disc Contents
The Trainer Materials compact disc contains the following files and folders:
! Autorun.exe. When the compact disc is inserted into the compact disc drive,
or when you double-click the Autorun.exe file, this file opens the compact
disc and allows you to browse the Student Materials or Trainer Materials
compact disc.
! Autorun.inf. When the compact disc is inserted into the compact disc drive,
this file opens Autorun.exe.
! Default.htm. This file opens the Trainer Materials Web page.
! Readme.txt. This file explains how to install the software for viewing the
Trainer Materials compact disc and its contents and how to open the Trainer
Materials Web page.
! 2349B_ms.doc. This file is the Manual Classroom Setup Guide. It contains
the steps for manually setting up the classroom computers.
! 2349B_sg.doc. This file is the Automated Classroom Setup Guide. It
contains a description of classroom requirements, classroom configuration,
instructions for using the automated classroom setup scripts, and the
Classroom Setup Checklist.
! Powerpnt. This folder contains the Microsoft PowerPoint slides that are
used in this course.
! Pptview. This folder contains the Microsoft PowerPoint Viewer, which is
used to display the PowerPoint slides.
! Setup. This folder contains the files that install the course and related
software to computers in a classroom setting.
! StudentCD. This folder contains the Web page that provides students with
links to resources pertaining to this course, including additional reading,
review and lab answers, lab files, multimedia presentations, and course-
related Web sites.
! Tools. This folder contains files and utilities used to complete the setup of
the instructor computer.
! Webfiles. This folder contains the files that are required to view the course
Web page. To open the Web page, open Windows Explorer, and in the root
directory of the compact disc, double-click Default.htm or Autorun.exe.

xii Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET)


Student Materials Compact Disc Contents
The Student Materials compact disc contains the following files and folders:
! Autorun.exe. When the compact disc is inserted into the CD-ROM drive, or
when you double-click the Autorun.exe file, this file opens the compact
disc and allows you to browse the Student Materials compact disc.
! Autorun.inf. When the compact disc is inserted into the compact disc drive,
this file opens Autorun.exe.
! Default.htm. This file opens the Student Materials Web page. It provides
students with resources pertaining to this course, including additional
reading, review and lab answers, lab files, multimedia presentations, and
course-related Web sites.
! Readme.txt. This file explains how to install the software for viewing the
Student Materials compact disc and its contents and how to open the
Student Materials Web page.
! 2349B_ms.doc. This file is the Manual Classroom Setup Guide. It contains a
description of classroom requirements, classroom setup instructions, and the
classroom configuration.
! Democode. This folder contains demonstration code.
! Flash. This folder contains the installer for the Macromedia Flash 5.0
browser plug-in.
! Fonts. This folder contains fonts that are required to view the PowerPoint
presentation and Web-based materials.
! Labs. This folder contains files that are used in the hands-on labs. These
files may be used to prepare the student computers for the hands-on labs.
! Media. This folder contains files that are used in multimedia presentations
for this course.
! Mplayer. This folder contains the setup file to install Microsoft Windows
Media

Player.
! Webfiles. This folder contains the files that are required to view the course
Web page. To open the Web page, open Windows Explorer, and in the root
directory of the compact disc, double-click Default.htm or Autorun.exe.
! Wordview. This folder contains the Word Viewer that is used to view any
Word document (.doc) files that are included on the compact disc.

Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET) xiii


Document Conventions
The following conventions are used in course materials to distinguish elements
of the text.
Convention Use

" "" " Indicates an introductory page. This symbol appears next
to a topic heading when additional information on the topic
is covered on the page or pages that follow it.
bold Represents commands, command options, and syntax that
must be typed exactly as shown. It also indicates
commands on menus and buttons, dialog box titles and
options, and icon and menu names.
italic In syntax statements or descriptive text, indicates argument
names or placeholders for variable information. Italic is
also used for introducing new terms, for book and course
titles, and for emphasis in the text.
Title Capitals Indicate domain names, user names, computer names,
directory names, and folder and file names, except when
specifically referring to case-sensitive names. Unless
otherwise indicated, you can use lowercase letters when
you type a directory name or file name in a dialog box or
at a command prompt.
ALL CAPITALS Indicate the names of keys, key sequences, and key
combinations for example, ALT+SPACEBAR.
monospace Represents code samples or examples of screen text.
[ ] In syntax statements, enclose optional items. For example,
[filename] in command syntax indicates that you can
choose to type a file name with the command. Type only
the information within the brackets, not the brackets
themselves.
{ } In syntax statements, enclose required items. Type only the
information within the braces, not the braces themselves.
| In syntax statements, separates an either/or choice.
! Indicates a procedure with sequential steps.
... In syntax statements, specifies that the preceding item may
be repeated.
.
.
.
Represents an omitted portion of a code sample.





THIS PAGE INTENTIONALLY LEFT BLANK









Contents
Introduction 1
Course Materials 2
Prerequisites 3
Course Outline 4
Microsoft Certified Professional Program 9
Facilities 11

Introduction



Information in this document, including URL and other Internet Web site references, is subject to
change without notice. Unless otherwise noted, the example companies, organizations, products,
domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious,
and no association with any real company, organization, product, domain name, e-mail address,
logo, person, place or event is intended or should be inferred. Complying with all applicable
copyright laws is the responsibility of the user. Without limiting the rights under copyright, no
part of this document may be reproduced, stored in or introduced into a retrieval system, or
transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or
otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual
property rights covering subject matter in this document. Except as expressly provided in any
written license agreement from Microsoft, the furnishing of this document does not give you any
license to these patents, trademarks, copyrights, or other intellectual property.

2001-2002 Microsoft Corporation. All rights reserved.

Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint,
Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and
Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A.
and/or other countries.

The names of actual companies and products mentioned herein may be the trademarks of their
respective owners.



Introduction iii


Instructor Notes
The Introduction module provides students with an overview of the course
content, materials, and logistics for Course 2349B, Programming with the
Microsoft .NET Framework (Microsoft Visual C#

.NET).
Course Materials and Preparation
Required Materials
To teach this course, you need the following materials:
! Delivery Guide
! Trainer Materials compact disc

Preparation Tasks
To prepare for this course, you must complete the Course Preparation Checklist
that is included with the trainer course materials.
Module Strategy
Use the following strategy to present this module:
! Course 2349B, Programming with the Microsoft .NET Framework
(Microsoft Visual C# .NET)
Show the slide that displays the course number and course title.
! Introduction
Welcome students to the course and introduce yourself. Provide a brief
overview of your background to establish credibility.
Have students introduce themselves and provide their background, product
experience, and expectations of the course.
Record student expectations on a white board or flip chart that you can
reference later in class.
! Course Materials
Explain the purpose of all materials used in this course.
! Prerequisites
Provide the students with the list of prerequisites that they should have met
before taking this course. This is an opportunity for you to identify students
who may not have the appropriate background or experience to attend this
course.
Presentation:
30 Minutes
iv Introduction


! Course Outline
Provide an overview of each module and what students will learn.
Explain how this course will meet students expectations by relating the
information covered in individual modules to their expectations.
Providing reasonably complete coverage of the Microsoft .NET Framework
within a five day class is a challenging undertaking. The thirteen modules
that encompass Course 2349B, Programming with the Microsoft.NET
Framework (Microsoft Visual C# .NET), will provide most students with a
common baseline for working with the .NET Framework.

For more information about customizing this course see the Optional
Course Presentation Strategies section in this module.

! Setup
Provide the students with any necessary setup information for the course.
! Microsoft Certified Professional Program
Inform students about the Microsoft Certified Professional (MCP) program
and the various certification options.
! Facilities
Explain the facility information for the training site.

Note
Introduction v


Optional Course Presentation Strategies
Because of the complexity and amount of material that makes up the .NET
Framework, additional resources are included to provide instructors with some
flexibility in the course delivery.
Course Customization
The thirteen modules that make up the five day Course 2349B, Programming
with the Microsoft .NET Framework (Microsoft Visual C# .NET), will provide
most students with a common baseline for working with the .NET Framework.
In addition to the first thirteen modules that make up the five days of material,
the course contains four optional modules, thus providing you with the
opportunity for customization.
You are not required to cover the optional modules as part of Course 2349B,
Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET).
The decision about whether to cover the optional modules has been left entirely
to you, the instructor, as you will need to consider the circumstances of each
class separately, including student levels and preferences, and any requirements
of an individual Microsoft Certified Technical Education Center (Microsoft
CTEC).
However, as part of the course materials, your students will receive all
seventeen modules. While Modules 14 through 17 are considered optional, you
should be prepared to discuss what you plan to cover during the course
introduction. If you only intend to cover the first thirteen modules that make up
the official five days of course delivery of Course 2349B, Programming with
the Microsoft .NET Framework (Microsoft Visual C# .NET), then state that
clearly and suggest that the optional modules may be used as additional self-
study. Alternatively, you may want to discuss covering one or more of the
optional modules if there is sufficient interest in those topics. You will then
need to consider with your students what module(s) you may safely leave out in
order to accommodate the optional module(s).
For example, an alternative approach for more advanced students who already
have a basic understanding of .NET Framework applications and the Microsoft
Visual C#

.NET object model would be to omit Modules 2 through 6 and


teach Modules 7 through 17.
vi Introduction


Course Flow
As a general guideline, modules may be grouped accordingly, as shown in the
following table.
Module Course Flow

Module 1: Overview of the Microsoft
.NET Framework
This short module is designed to provide a
high level overview of the .NET
Framework.
Module 2: Introduction to a Managed
Execution Environment
Module 3: Working with Components
Module 4: Deployment and Versioning
Modules 2 through 4 cover core .NET
Framework concepts, including managed
execution, assemblies, and deployment.
Module 5: Common Type System
Module 6: Working with Types
Modules 5 and 6 cover core .NET
Framework object oriented topics.
Module 7: Strings, Arrays, and
Collections
Module 8: Delegates and Events
Module 9: Memory and Resource
Management
Module 10: Data Streams and Files
Module 11: Internet Access
Module 12: Serialization
Module 13: Remoting and XML Web
Services
Modules 7 through 13 cover more
advanced material than the preceding
modules and may be considered as
standalone material. Modules 10 through
12 have some relevance to Module 13:
Remoting and XML Web Services.
Module 14: Threading and Asynchronous
Programming
Module 15: Interoperating Between
Managed and Unmanaged Code
Module 16: Using Microsoft ADO.NET to
Access Data
Module 17: Attributes
Modules 14 through 17 may be considered
as optional, standalone, and generally
more advanced than modules 1 through 6.

Introduction vii


Course Timing
This section provides estimated course timings for all of the modules, labs, and
breaks in Course 2349B, Programming with the Microsoft .NET Framework
(Microsoft Visual C# .NET). The following schedule options are provided as a
guide to help with ideas about how to organize your class if you decide to
customize the course.
Option 1
The following schedule is an estimate of the course timing if you choose to
teach Modules 1 through 13. This is the basic approach for students needing
additional instruction regarding Visual C# .NET and the .NET Framework
approach to assemblies, packaging and the object model. If you do not intend to
customize Course 2349B, Programming with the Microsoft .NET Framework
(Microsoft Visual C# .NET), this is the approach you should use. Your timing
may vary.
Day 1
Start End Module
9:00 9:30 Introduction
9:30 10:00 Module 1: Overview of the Microsoft .NET Framework
10:00 10:15 Break
10:15 11:00 Module 2: Introduction to a Managed Execution Environment
11:00 11:15 Lab 2: Building a Simple .NET Application
11:15 11:45 Module 3: Working with Components
11:45 12:00 Lab 3.1: Creating a .NET Framework Component
12:00 1:00 Lunch
1:00 1:15 Module 3: Working with Components (continued)
1:15 1:30 Lab 3.2: Creating a Simple Console-Based Client
1:30 2:00 Module 3: Working with Components (continued)
2:00 2:30 Lab 3.3: Calling a Component Through an ASP .NET Page
2:30 2:45 Break
2:45 4:15 Module 4: Deployment and Versioning

Day 2
Start End Module
9:00 9:50 Lab 4: Packaging and Deployment
9:50 10:00 Break
10:00 11:30 Module 5: Common Type System
11:30 12:30 Lunch
12:30 1:15 Lab 5: Building Simple Types
1:15 2:30 Module 6: Working with Types
2:30 2:45 Break
2:45 3:30 Lab 6: Working with Types
3:30 4:00 Module 7: Strings, Arrays, and Collections
viii Introduction


Day 3
Start End Module
9:00 10:30 Module 7: Strings, Arrays, and Collections (continued)
10:30 10:45 Break
10:45 11:45 Lab 7: Working with Strings, Enumerators, and Collections
11:45 12:45 Lunch
12:45 2:00 Module 8: Delegates and Events
2:00 2:15 Break
2:15 3:30 Lab 8: Creating a Simple Chat Server
3:30 4:00 Module 9: Memory and Resource Management

Day 4
Start End Module
9:00 10:30 Module 9: Memory and Resource Management (continued)
10:30 10:45 Break
10:45 11:45 Lab 9: Memory and Resource Management
11:45 12:45 Lunch
12:45 1:30 Module 10: Data Streams and Files
1:30 2:15 Lab 10: Files
2:15 2:30 Break
2:30 3:30 Module 11: Internet Access
3:30 4:15 Lab 11: Creating a DateTime Client/Server Application

Day 5
Start End Module
9:00 9:30 Module 12: Serialization
9:30 10:15 Lab 12: Serialization
10:15 10:30 Break
10:30 11:30 Module 13: Remoting and Web Services
11:30 12:30 Lunch
12:30 1:20 Lab 13.1: Building an Order-Processing Application by Using
Remoted Servers
1:20 2:20 Module 13: Remoting and Web Services (continued)
2:20 2:35 Break
2:35 3:30 Lab 13.2: Using an XML Web Service
Introduction ix


Option 2
The following schedule is an estimate of the course timing if you choose to
teach Module 1 followed by Modules 7 through 17. This approach would serve
more advanced students who already have a solid understanding of Visual C#
.NET and the .NET Framework approach to assemblies, packaging and the
object model. Your timing may vary.
Day 1
Start End Module
9:00 9:30 Introduction
9:30 10:00 Module 1: Overview of the Microsoft .NET Framework
10:00 10:15 Break
10:15 11:45 Module 7: Strings, Arrays, and Collections
11:45 12:45 Lunch
12:45 1:15 Module 7: Strings, Arrays, and Collections (continued)
1:15 2:15 Lab 7: Working with Strings, Enumerators, and Collections
2:15 2:30 Break
2:30 3:55 Module 8: Delegates and Events
3:55 4:15 Break
4:15 5:15 Lab 8: Creating a Simple Chat Server

Day 2
Start End Module
9:00 10:30 Module 9: Memory and Resource Management
10:30 10:45 Break
10:45 11:20 Module 9: Memory and Resource Management (continued)
11:20 11:50 Lab 9: Memory and Resource Management
11:50 1:00 Lunch
1:00 1:30 Lab 9: Memory and Resource Management (continued)
1:30 2:15 Module 10: Data Streams and Files
2:15 3:00 Lab 10: Files
3:00 3:15 Break
3:15 4:15 Module 11: Internet Access
4:15 5:00 Lab 11: Creating a DateTime Client/Server Application

x Introduction


Day 3
Start End Module
9:00 9:30 Module 12: Serialization
9:30 10:15 Lab 12: Serialization
10:15 10:30 Break
10:30 11:30 Module 13: Remoting and XML Web Services
11:30 12:30 Lunch
12:30 1:20 Lab 13.1: Building an Order-Processing Application by Using
Remoted Servers
1:20 2:20 Module 13: Remoting and XML Web Services (continued)
2:20 2:35 Break
2:35 3:30 Lab 13.2: Using an XML Web Service

Day 4
Start End Module
9:00 10:15 Module 14: Threading and Asynchronous Programming
10:15 10:30 Break
10:30 11:45 Module 14: Threading and Asynchronous Programming
(continued)
11:45 12:45 Lunch
12:45 1:45 Lab 14: Working with Multithreaded Applications
1:45 2:00 Break
2:00 2:45 Module 15: Interoperating Between Managed and Unmanaged
Code
2:45 3:15 Lab 15.1: Calling Win32 APIs
3:15 3:30 Break
3:30 4:15 Module 15: Interoperating Between Managed and Unmanaged
Code (continued)
4:15 4:45 Lab 15.2: Calling COM Objects

Day 5
Start End Module
9:00 10:30 Module 16: Using Microsoft ADO.NET to Access Data
10:30 10:45 Break
10:45 11:15 Module 16: Using Microsoft ADO.NET to Access Data
(continued)
11:15 12:15 Lab 16: Using ADO.NET to Access Data
12:15 1:15 Lunch
1:15 2:15 Module 17: Attributes
2:15 2:30 Break
2:30 3:15 Lab 17: Defining and Using Attributes
Introduction xi


Other Options
The standard approach could be further customized by substituting one or more
of Modules 7 through 13 with Modules 14 through 17. In addition, a more
advanced option of delivery would be to extend the course by offering all
seventeen modules. However, the decision to offer this delivery option would
need to be reached in accordance with the policies and business requirements of
individual Microsoft Certified Technical Education Centers (Microsoft
CTECs).
The following table lists the times for individual labs and modules.

Module Title
Lecture Time
(only)
Lab Time
(only)
Lecture and Lab
Time (combined)

Module 1: Overview of the Microsoft .NET
Framework
30 minutes None 30 minutes
Module 2: Introduction to a Managed Execution
Environment
45 minutes 20 minutes 65 minutes
Module 3: Working with Components 70 minutes 60 minutes 130 minutes
Module 4: Deployment and Versioning 90 minutes 50 minutes 140 minutes
Module 5: Common Type System 90 minutes 45 minutes 130 minutes
Module 6: Working with Types 75 minutes 45 minutes 125 minutes
Module 7: Strings, Arrays, and Collections 120 minutes 60 minutes 180 minutes
Module 8: Delegates and Events 75 minutes 75 minutes 150 minutes
Module 9: Memory and Resource Management 120 minutes 60 minutes 180 minutes
Module 10: Data Streams and Files 45 minutes 45 minutes 90 minutes
Module 11: Internet Access 60 minutes 45 minutes 105 minutes
Module 12: Serialization 30 minutes 45 minutes 75 minutes
Module 13: Remoting and XML Web Services 120 minutes 105 minutes 225 minutes
Module 14: Threading and Asynchronous
Programming
150 minutes 60 minutes 210 minutes
Module 15: Interoperating Between Managed and
Unmanaged Code
90 minutes 60 minutes 150 minutes
Module 16: Using Microsoft ADO.NET to Access
Data
120 minutes 60 minutes 180 minutes
Module 17: Attributes 60 minutes 45 minutes 105 minutes


Introduction 1


Introduction
! Name
! Company Affiliation
! Title/Function
! Job Responsibility
! Programming Experience
! .NET Framework Experience
! Expectations for the Course

*****************************ILLEGAL FOR NON-TRAINER USE******************************

Topic Objective
To introduce yourself,
establish credibility, meet
students, and set student
expectations for the course.
Lead-in
Good morning. Welcome to
Course 2349B,
Programming with the
Microsoft .NET Framework
(Microsoft Visual C# .NET).

My name is...
Introduce yourself.

Provide a brief overview of
your background to
establish credibility as a
.NET Framework instructor.

Ask students to introduce
themselves, addressing the
bulleted items on the slide.
Delivery Tip
As students introduce
themselves, use a white
board or flip chart to record
their expectations of the
course.
2 Introduction


Course Materials
! Name Card
! Student Workbook
! Student Materials Compact Disc
! Course Evaluation

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The following materials are included with your kit:
! Name card. Write your name on both sides of the name card.
! Student workbook. The student workbook contains the material covered in
class, in addition to the hands-on lab exercises.
! Student Materials compact disc. The Student Materials compact disc
contains the Web page that provides you with links to resources pertaining
to this course, including additional readings, review and lab answers, lab
files, multimedia presentations, and course-related Web sites.

To open the Web page, insert the Student Materials compact disc into
the CD-ROM drive, and then in the root directory of the compact disc,
double-click Autorun.exe or Default.htm.

! Course evaluation. To provide feedback on the course, training facility, and
instructor, you will have the opportunity to complete an online evaluation
near the end of the course.
To provide additional comments or inquire about the Microsoft Certified
Professional program, send e-mail to mcphelp@microsoft.com.

Topic Objective
To identify and describe the
course materials.
Lead-in
We have provided
everything you need for this
course. You will find the
following materials at your
desk...
Describe the contents of the
student workbook and the
Student Materials compact
disc.

Have students write their
names on both sides of the
name card.

Tell students where they
can send comments with
feedback on the course.
Delivery Tip
Demonstrate how to open
the Web page provided on
the Student Materials
compact disc. On the
Trainer Materials compact
disc, double-click
Autorun.exe or
Default.htm in the
StudentCD folder.
Note
Introduction 3


Prerequisites
! Proficiency in the C++ or Java Programming Languages
! A basic understanding of the C# Language

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This course requires that you meet the following prerequisites:
! Proficiency in the C++ or Java programming languages
! A basic understanding of the C# language
Students can meet the C# language prerequisite by taking Course 2124,
Introduction to C# Programming for the Microsoft .NET Platform.

Topic Objective
To present and describe the
prerequisites for this course.
Lead-in
The following prerequisite
knowledge is needed for this
course.
4 Introduction


Course Outline
! Module 1: Overview of the Microsoft .NET Framework
! Module 2: Introduction to a Managed Execution
Environment
! Module 3: Working with Components
! Module 4: Deployment and Versioning
! Module 5: Common Type System
! Module 6: Working with Types

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Module 1, Overview of the Microsoft .NET Framework, defines terminology
specific to the Microsoft .NET Framework and describes its key features and
benefits. This module also discusses the namespaces taught in this course.
There will be minimal lecture and no lab. After completing this module, you
will be able to list the major elements of the .NET Framework.
Module 2, Introduction to a Managed Execution Environment, introduces the
concept of managed execution and shows developers how to quickly build
applications that take advantage of the new .NET Framework common
language runtime environment. After completing this module, you will be able
to explain the main concepts behind the common language runtime and use the
features of the .NET Framework to create a simple application.
Module 3, Working with Components, discusses how to create a small,
componentized application where modules can easily be written in either
Microsoft Visual C#

or Microsoft Visual Basic. The steps necessary to
construct, compile, and run each program are covered in detail. This module
also explains how to build the client application by using the Microsoft
Windows Forms library and ASP.NET Web Forms. After completing this
module, you will be able to create and use components in Windows Forms-
based and ASP.NET-based applications.
Module 4, Deployment and Versioning, explains how to use deployment and
versioning features of the .NET Framework common language runtime to build
and deploy applications that are fully managed and protected. After completing
this module, you will be able to use the deployment and versioning features of
the .NET Framework common language runtime to deploy multiple versions of
a component.
Topic Objective
To provide an overview of
each module and what
students will learn.
Lead-in
In this course, we will
cover...
Briefly describe each
module.

As you describe each
module, acknowledge any
information that will meet
the student expectations
that you recorded earlier.
Introduction 5


Module 5, Common Type System, introduces the Common Type System.
The module discusses how to differentiate between value types and reference
types and examines how classes, interfaces, properties, methods, events, and
values are represented in the .NET Framework. After completing this module,
you will be able to create, use, and extend types by understanding the Common
Type System architecture.
Module 6, Working with Types, discusses the use of attributes to control
visibility and inheritance on types and explains how to work with various type
operations, such as boxing and unboxing, and type operators. In addition, this
module discusses how to build an interface that supports methods and
properties and how to make interface designs more efficient. Finally, this
module highlights features that are designed to help you work with unmanaged
types, such as COM types. After completing this module, you will be able to
create classes and interfaces that are functionally efficient and appropriate for
specific programming scenarios.
6 Introduction


Course Outline (continued)
! Module 7: Strings, Arrays, and Collections
! Module 8: Delegates and Events
! Module 9: Memory and Resource Management
! Module 10: Data Streams and Files
! Module 11: Internet Access
! Module 12: Serialization
! Module 13: Remoting and XML Web Services

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Module 7, Strings, Arrays, and Collections, describes some of the key classes
in the .NET Framework class library. This module explains how to work with
strings, arrays, collections, and enumerators. After completing this module, you
will be able to use the .NET Framework class library to efficiently create and
manage strings, arrays, collections, and enumerators.
Module 8, Delegates and Events, explains how the .NET Framework uses
delegates in callback and event-handling scenarios. After completing this
module, you will be able to use delegates and events to have an event sender
object signal the occurrence of an action to an event receiver object.
Module 9, Memory and Resource Management, discusses how the .NET
Framework automatically handles the allocation and release of an objects
memory resources through garbage collection. After completing this module,
you will be able to describe and control how memory and other resources are
managed in the .NET Framework.
Module 10, Data Streams and Files, introduces the System.IO namespace
and discusses the types that it contains that allow synchronous and
asynchronous reading from and writing to data streams and files. This module
discusses synchronous operations only, as asynchronous operations are beyond
the scope of this course. After completing this module, you will be able to read
from and write to data streams and files.
Module 11, Internet Access, discusses the use of the System.Net classes to
communicate with other applications by using common protocols, such as
HTTP, Transmission Control Protocol (TCP), User Datagram Protocol (UDP),
and Socket Internet protocols. After completing this module, you will be able to
use the basic request/response model to send and receive data over the Internet.
Introduction 7


Module 12, Serialization, explains how to use serialization to convert a graph
of objects into a linear sequence of bytes, which can then be sent to a remote
computer and deserialized, thereby making a clone in the remote memory of the
original graph of objects. After completing this module, you will be able to
serialize and deserialize an object graph.
Module 13, Remoting and XML Web Services, explains how .NET Remoting
supports communication between objects in different application domains, in
different processes, and on different computers. The module describes how the
common language runtime remoting infrastructure provides a rich set of classes
that enable you to ignore most of the complexities of deploying and managing
remote objects. After completing this module, you will be able to create
distributed applications by means of XML Web services and Object Remoting.
8 Introduction


Course Outline (continued)
! Module 14 (Optional): Threading and Asynchronous
Programming
! Module 15 (Optional): Interoperating Between Managed
and Unmanaged Code
! Module 16 (Optional): Using Microsoft ADO.NET to
Access Data
! Module 17 (Optional): Attributes

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The following modules are optional and can also be used as further reading
about .NET Framework topics.
Module 14, Threading and Asynchronous Programming, covers the support
that the Microsoft .NET Framework provides for working with multithreaded
applications and asynchronous programming. After completing this module, in
addition to creating and managing threads, you will be able to handle thread
synchronization to maintain application responsiveness and avoid potential data
corruption and other problems.
Module 15, Interoperating between Managed and Unmanaged Code,
introduces the services that are provided by the .NET Framework to allow
managed code to interoperate with unmanaged code. After completing this
module, you will be able to use Platform Invoke to call unmanaged functions
that are implemented in a DLL. You will also be able to call COM objects from
managed code.
Module 16, Using Microsoft ADO.NET to Access Data, presents ADO.NET
as an evolution of the ADO data access model, with a rich suite of data
handling and data binding functions for manipulating all types of data. After
completing this module, you will use the rapid application design facilities of
Microsoft Visual Studio .NET to create an XML Web service whose methods
can be used to read and update a database by using ADO.NET. You will also
Use Visual Studio.NET rapid application design facilitates to create a Windows
Forms application that displays and enables a user to update a DataGrid
control that is bound to a DataSet. The DataSet is obtained and updated by
using an XML Web service.
Module 17, Attributes, provides details about how to use attributes in code. It
describes the predefined attributes that are provided by the Microsoft .NET
Framework and provides some simple examples of how to use some common
attributes. After completing this module, you will be able to use custom
predefined attributes, create simple custom attributes, and query attribute
information at run time.
Introduction 9


Microsoft Certified Professional Program
http://www.microsoft.com/traincert/

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The Microsoft Certified Professional program is a leading certification program
that validates your experience and skills to keep you competitive in todays
changing business environment. The following table describes each certification
in more detail.
Certification Description

MCSA on Microsoft
Windows 2000
The Microsoft Certified Systems Administrator (MCSA) certification is designed for
professionals who implement, manage, and troubleshoot existing network and system
environments based on Microsoft Windows 2000 platforms, including the Windows
.NET Server family. Implementation responsibilities include installing and configuring
parts of the systems. Management responsibilities include administering and supporting
the systems.
MCSE on Microsoft
Windows 2000
The Microsoft Certified Systems Engineer (MCSE) credential is the premier
certification for professionals who analyze the business requirements and design
and implement the infrastructure for business solutions based on the Microsoft
Windows 2000 platform and Microsoft server software, including the Windows .NET
Server family. Implementation responsibilities include installing, configuring, and
troubleshooting network systems.
MCSD The Microsoft Certified Solution Developer (MCSD) credential is the premier
certification for professionals who design and develop leading-edge business solutions
with Microsoft development tools, technologies, platforms, and the Microsoft Windows
DNA architecture. The types of applications MCSDs can develop include desktop
applications and multi-user, Web-based, N-tier, and transaction-based applications. The
credential covers job tasks ranging from analyzing business requirements to maintaining
solutions.

Topic Objective
To provide students with
information about the
Microsoft Certified
Professional Program.
Lead-in
The Microsoft Certified
Professional Program
includes these
certifications
10 Introduction


(continued)
Certification Description

MCDBA on Microsoft
SQL Server 2000
The Microsoft Certified Database Administrator (MCDBA) credential is the premier
certification for professionals who implement and administer Microsoft SQL Server

databases. The certification is appropriate for individuals who derive physical database
designs, develop logical data models, create physical databases, create data services by
using Transact-SQL, manage and maintain databases, configure and manage security,
monitor and optimize databases, and install and configure SQL Server.
MCP The Microsoft Certified Professional (MCP) credential is for individuals who have the
skills to successfully implement a Microsoft product or technology as part of a business
solution in an organization. Hands-on experience with the product is necessary to
successfully achieve certification.
MCT Microsoft Certified Trainers (MCTs) demonstrate the instructional and technical skills
that qualify them to deliver Microsoft Official Curriculum through Microsoft Certified
Technical Education Centers (Microsoft CTECs).

Certification Requirements
The certification requirements differ for each certification category and are
specific to the products and job functions addressed by the certification. To
become a Microsoft Certified Professional, you must pass rigorous certification
exams that provide a valid and reliable measure of technical proficiency and
expertise.

See the Microsoft Training and Certification Web site at
http://www.microsoft.com/traincert.
You can also send e-mail to mcphelp@microsoft.com if you have specific
certification questions.

Acquiring the Skills Tested by an MCP Exam
Microsoft Official Curriculum (MOC) and MSDN Training Curriculum can
help you develop the skills that you need to do your job. They also complement
the experience that you gain while working with Microsoft products and
technologies. However, no one-to-one correlation exists between MOC and
MSDN Training courses and MCP exams. Microsoft does not expect or intend
for the courses to be the sole preparation method for passing MCP exams.
Practical product knowledge and experience is also necessary to pass the MCP
exams.
To help prepare for the MCP exams, use the preparation guides that are
available for each exam. Each Exam Preparation Guide contains exam-specific
information, such as a list of the topics on which you will be tested. These
guides are available on the Microsoft Training and Certification Web site at
http://www.microsoft.com/traincert/.
For More Information
Introduction 11


Facilities
Building Hours
Parking
Rest Rooms
Meals
Phones
Messages
Smoking
Recycling
Class Hours

*****************************ILLEGAL FOR NON-TRAINER USE******************************

Topic Objective
To inform students of class
logistics and rules for the
training site.
Lead-in
Before we start, lets go over
the class logistics.
Explain the class hours,
extended building hours for
labs, parking, rest room
location, meals, phones,
message posting, and
where smoking is or isnt
allowed.

Let students know if your
facility has Internet access
that is available for them to
use during class breaks.

Also make sure that the
students are aware of the
recycling program if one is
available.



THIS PAGE INTENTIONALLY LEFT BLANK









Contents
Overview 1
Overview of the Microsoft .NET Framework 2
Overview of Namespaces 13
Review 17

Module 1: Overview of
the Microsoft .NET
Framework


Information in this document, including URL and other Internet Web site references, is subject to
change without notice. Unless otherwise noted, the example companies, organizations, products,
domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious,
and no association with any real company, organization, product, domain name, e-mail address,
logo, person, place or event is intended or should be inferred. Complying with all applicable
copyright laws is the responsibility of the user. Without limiting the rights under copyright, no
part of this document may be reproduced, stored in or introduced into a retrieval system, or
transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or
otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual
property rights covering subject matter in this document. Except as expressly provided in any
written license agreement from Microsoft, the furnishing of this document does not give you any
license to these patents, trademarks, copyrights, or other intellectual property.

2001-2002 Microsoft Corporation. All rights reserved.

Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint,
Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and
Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A.
and/or other countries.

The names of actual companies and products mentioned herein may be the trademarks of their
respective owners.



Module 1: Overview of the Microsoft .NET Framework iii


Instructor Notes
This module provides students with an overview of the Microsoft .NET
Framework. It defines some of the terminology that is specific to the .NET
Framework and describes the key features and benefits of the .NET Framework.
The module starts with an overview of the .NET Framework, and then
introduces the namespaces in the .NET Framework. It explains which modules
teach which namespaces, and which namespaces are not covered in this course.
Do not spend too much time on this module. This module is designed to
provide only an overview, so do not go into too much detail.
This module contains no labs.
After completing this module, students will be able to:
! Describe the .NET Framework and its components.
! Explain the relationship between the .NET Framework class library and
namespaces.

Materials and Preparation
This section provides the materials and preparation tasks that you need to teach
this module.
Required Materials
To teach this module, you need the Microsoft PowerPoint file 2349B_01.ppt.
Preparation Tasks
To prepare for this module, you should read all of the materials for this module.
Presentation:
30 Minutes

Lab:
00 Minutes
iv Module 1: Overview of the Microsoft .NET Framework


Module Strategy
Use the following strategy to present this module:
! The .NET Framework
Explain each part of the .NET Framework. One important goal of this slide
is to explain what this course covers. This course primarily teaches the
common language runtime and the .NET Framework class library. This
course uses Microsoft Visual C#

to teach the .NET Framework. Only


minimal coverage of XML Web services, user interfaces, ADO.NET, and
ASP.NET is provided. Other courses will cover these technologies in more
detail. In addition, ADO.NET is covered more fully in Module 16, Using
Microsoft ADO.NET to Access Data, in Course 2349B, Programming with
the Microsoft .NET Framework (Microsoft Visual C# .NET).
! Common Language Runtime
This is a build slide. Explain each of the following topics as they appear.
Definitions of these topics are found in the content.
Class Loader
Microsoft Intermediate Language (MSIL) to Native compilers, Code
Manager, and Garbage Collection
Security Engine, Debugger, Type Checker, Exception Manager, Thread
Support, and COM Marshaler
.NET Framework Class Library Support
! The .NET Framework Class Library
Explain the benefits of the .NET Framework class library. Explain that the
common type system is covered in more detail in Module 5, Common
Type System, and Module 6, Working with Types, in Course 2349B,
Programming with the Microsoft .NET Framework (Microsoft Visual C#
.NET). Specific classes are covered in Module 7, Strings, Arrays, and
Collections, and other modules as appropriate.
! ADO.NET: Data and XML
This is a build slide. Explain the following topics as they appear.
System.Data
Explain how this namespace works primarily with data, such as data
from databases.
System.Xml
Explain how this namespace works primarily with XML and Extensible
Stylesheet Language (XSL).
Module 1: Overview of the Microsoft .NET Framework v


! What Is an XML Web Service?
This slide is the key slide for explaining XML Web services. Be sure that
everyone understands the role of Web Services Description Language
(WSDL), Universal Description, Discovery, and Integration (UDDI),
SOAP, XML, and HTTP. Also explain that the .NET software development
kit (SDK) and Microsoft Visual Studio .NET provide tools to simplify the
creation of XML Web services.
Note that XML Web services are appropriate for internal applications in
addition to external applications. An organization is likely to run multiple
platforms; XML Web services are a good way of working across platforms
because they rely on XML and SOAP. Any platform that supports XML and
SOAP can use or expose XML Web services.
! Web Forms and XML Web Services
Explain how ASP.NET classes make it easier to work with user data on
Web pages.
! Namespaces
Briefly explain the purpose of each namespace. The key point to explain is
that there are a lot of data types and functionality in the .NET Framework.
Namespaces arrange the types in a hierarchy that make it easier to work
with the types.
! Namespaces Used in this Course
Briefly explain which modules teach which namespaces. Explain that not all
of the namespaces are taught in their entirety. For example, the
System.Reflection namespace is mentioned in Module 4, Deployment and
Versioning, in Course 2349B, Programming with the Microsoft .NET
Framework (Microsoft Visual C# .NET), only in terms of versioning.
System.Reflection is also mentioned in Module 17, Attributes, in Course
2349B, Programming with the Microsoft .NET Framework (Microsoft
Visual C# .NET).
Also, explain that the ADO.NET namespace is covered more fully in
Module 16, Using Microsoft ADO.NET to Access Data, in Course
2349B, Programming with the Microsoft .NET Framework (Microsoft
Visual C# .NET).
The security namespace is not taught in this course as security is covered
extensively in Course 2350A, Securing and Deploying Microsoft .NET
Assemblies.

Module 1: Overview of the Microsoft .NET Framework 1


Overview
! Overview of the Microsoft .NET Framework
! Overview of Namespaces

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The Microsoft .NET Framework provides tools and technologies that you need
to build distributed Web applications. In this module, you will learn the
architecture of the .NET Framework. You will also learn how the .NET
Framework class library is divided into namespaces, and which namespaces are
taught in this course.
After completing this module, you will be able to:
! Describe the .NET Framework and its components.
! Explain the relationship between the .NET Framework class library and
namespaces.

Topic Objective
To provide an overview of
the module topics and
objectives.
Lead-in
In this module, you will be
introduced to the .NET
Framework. You will then
learn about the namespaces
and which modules teach
certain namespaces.
2 Module 1: Overview of the Microsoft .NET Framework


" "" " Overview of the Microsoft .NET Framework
! The .NET Framework
! Common Language Runtime
! The .NET Framework Class Library
! ADO.NET: Data and XML
! What is an XML Web Service?
! Web Forms and Services

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this section, you will learn about the .NET Framework. The .NET
Framework is a set of technologies that form an integral part of the Microsoft
.NET platform. It provides the basic building blocks for developing Web
applications and XML Web services.
This section includes the following topics:
! The .NET Framework
! Common Language Runtime
! The .NET Framework Class Library
! ADO.NET: Data and XML
! What is an XML Web Service?
! Web Forms and Services

Topic Objective
To provide an overview of
the .NET Framework topics.
Lead-in
In this section, you will learn
about the Microsoft .NET
Framework.
Module 1: Overview of the Microsoft .NET Framework 3


The .NET Framework
Win32 Win32
Message Message
Queuing Queuing
COM+ COM+
(Transactions, Partitions, (Transactions, Partitions,
Object Pooling) Object Pooling)
IIS IIS WMI WMI
Common Language Runtime Common Language Runtime
.NET Framework Class Library .NET Framework Class Library
ADO.NET: Data and XML ADO.NET: Data and XML
Web Services Web Services User Interface User Interface
VB C++ C#
ASP.NET ASP.NET
Perl Python

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The .NET Framework
The .NET Framework provides the necessary compile time and runtime
foundation to build and run .NET applications. The .NET Framework is the
primary focus of this course.
Platform Substrate
The .NET Framework must run on an operating system. Currently, the .NET
Framework is built to work on the Microsoft Win32 operating systems. In the
future, the .NET Framework will be extended to run on other platforms, such as
Microsoft Windows CE.
Application Services
When running on Microsoft Windows 2000, application services, such as
COM+, Message Queuing, Windows Internet Information Server (IIS), and
Windows Management Instrumentation (WMI), are available to the developer.
The .NET Framework exposes application services through classes in the .NET
Framework class library.
Common Language Runtime
The common language runtime simplifies application development, provides a
robust and secure execution environment, supports multiple languages, and
simplifies application deployment and management.
The common language runtime environment is also referred to as a managed
environment, in which common services, such as garbage collection and
security, are automatically provided.
Topic Objective
To understand the
architecture of the .NET
Framework.
Lead-in
The .NET Framework is an
architecture consisting of a
runtime, the class library,
and language support.
4 Module 1: Overview of the Microsoft .NET Framework


The .NET Framework Class Library
The .NET Framework class library exposes features of the runtime and provides
other high-level services that every developer needs. The classes simplify
development of .NET applications. Developers can extend them by creating
their own libraries of classes.
ADO.NET
ADO.NET is the next generation of Microsoft ActiveX Data Object (ADO)
technology. ADO.NET provides improved support for the disconnected
programming model. It also provides rich XML support.
ASP.NET
Microsoft ASP.NET is a programming framework that is built on the common
language runtime. ASP.NET can be used on a server to build powerful Web
applications. ASP.NET Web Forms provide an easy and powerful way to build
dynamic Web user interfaces (UI).
XML Web Services
The .NET Framework provides tools and classes for building, testing, and
distributing XML Web services.
User Interfaces
The .NET Framework supports three types of user interfaces:
! Web Forms, which work through ASP.NET
! Windows Forms, which run on Win32 clients
! Console applications, which for simplicity, are used for most of the labs in
this course

Module 1: Overview of the Microsoft .NET Framework 5


Languages
Any language that conforms to the Common Language Specification (CLS) can
run on the common language runtime. In the .NET Framework, Microsoft
provides Microsoft Visual Basic, Microsoft Visual C++, Microsoft
Visual C#

, and Microsoft JScript support. Third parties can provide


additional languages.

C# has been submitted for standardization to ECMA, a vendor-neutral
international standards organization committed to driving industry-wide
adoption of information and communications technologies. This standardization
will make it possible for any company which wishes to implement C#
programming tools on any platform to do so. Microsoft has also submitted a
subset of the Microsoft .NET Framework, called the Common Language
Infrastructure (CLI), to ECMA. This will make it possible for other vendors to
implement the CLI on a variety of platforms, so that software written using the
basic architectural model presented by the .NET Framework can be created
using a variety of tools on a variety of platforms.

Building Components in the .NET Framework
In the .NET Framework, components are built on a common foundation. You
no longer need to write the code to allow objects to interact directly with each
other. In addition, you no longer need to write component wrappers in the .NET
environment, because components do not use wrappers. The .NET Framework
can interpret the constructs that developers are accustomed to using in object-
oriented languages. The .NET Framework fully supports class, inheritance,
methods, properties, events, polymorphism, constructors, and other object-
oriented constructs.
Note
6 Module 1: Overview of the Microsoft .NET Framework


Common Language Runtime
.NET Framework Class Library Support .NET Framework Class Library Support
Thread Support Thread Support COM Marshaler COM Marshaler
Type Checker Type Checker Exception Manager Exception Manager
MSIL to Native MSIL to Native
Compilers Compilers
Code Code
Manager Manager
Garbage Garbage
Collection Collection
Security Engine Security Engine Debugger Debugger
Class Loader Class Loader

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The common language runtime simplifies application development, provides a
robust and secure execution environment, supports multiple languages, and
simplifies application deployment and management.
The common language runtime environment is also referred to as a managed
environment, one in which common services, such as garbage collection and
security, are automatically provided. The common language runtime features
are described in the following table.
Component Description

Class loader Manages metadata, and the loading and layout of classes.
Microsoft Intermediate Language
(MSIL) to native compiler
Converts MSIL to native code on a just-in-time basis.
Code manager Manages code execution.
Garbage collection Provides automatic lifetime management of all of objects in the .NET
Framework, garbage collection is a multiprocessor, scalable garbage collector.
Security engine Provides evidence-based security, based on user identity and the origin of the
code.
Debugger Enables the developer to debug an application and trace the execution of code.
Type checker Does not allow unsafe casts or uninitialized variables. MSIL can be verified to
guarantee type safety.
Exception manager Provides structured exception handling, which is integrated with Windows
Structured Exception Handling (SEH). Error reporting has been improved.
Thread support Provides classes and interfaces that enable multithreaded programming.
COM marshaler Provides marshaling to and from COM.
.NET Framework class library
support
Integrates code with the runtime that supports the .NET Framework class library.

Topic Objective
To highlight some of the key
components in the common
language runtime.
Lead-in
This topic will give you an
overview of the components
of the common language
runtime. I will briefly
describe each component.
As a C# developer, you will
never see these discrete
pieces, but this discussion
will give you a better
understanding of the
richness of the runtime.
Module 1: Overview of the Microsoft .NET Framework 7


The .NET Framework Class Library
! Spans All Programming Languages
# Enables cross-language inheritance and debugging
# Integrates well with tools
! Is Object-Oriented and Consistent
# Enhances developer productivity by reducing the number of APIs to
learn
! Has a Built-In Common Type System
! Is Extensible
# Makes it easy to add or modify framework features
! Is Secure
# Allows creation of secure applications

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The .NET Framework class library exposes features of the runtime and provides
other high-level services that every developer needs.
Because there are hundreds of classes in the .NET Framework class library,
classes are grouped into namespaces. The first part of the full name, which is
located before the rightmost dot, is the namespace name. The last part of the
name, which is located after the dot, is the type name.
For example, System.Collections.ArrayList represents the ArrayList class,
which belongs to the System.Collections namespace. The types in
System.Collections namespace can be used to manipulate collections of
objects.
Spans All Programming Languages
The .NET Framework class library is language-independent so it enables cross-
language inheritance and debugging. The .NET Framework class library also
integrates fully with Microsoft Visual Studio .NET, making it easy to develop
applications with the library.
Is Object-Oriented and Consistent
Unlike flat APIs that are numerous and unorganized, the .NET Framework class
library is organized into namespaces and classes. This object-oriented approach
groups related functionality and data together and enables the developer to work
with the library in a more natural way.
Topic Objective
To provide an overview of
the .NET Framework class
library and the most
common namespace:
System.
Lead-in
In this topic, you will learn
how .NET Framework class
library exposes features of
the runtime and provides
other high-level services.
8 Module 1: Overview of the Microsoft .NET Framework


Has a Built-In Common Type System
The .NET Framework class library is type-safe. Type safety is ensured through
the common type system, which is part of the common language runtime.
Is Extensible
You can extend the library by creating your own classes and compiling them
into libraries. If designed properly, your class library will also be object-
oriented and language-independent.
Is Secure
The .NET Framework class library provides rich security for your applications.
You can use code access security and role-based security, and configure your
own security policies. Furthermore, there are numerous security tools to assist
in certificate creation, permission viewing, and so on.
Module 1: Overview of the Microsoft .NET Framework 9


ADO.NET: Data and XML
ADO.NET: Data and XML ADO.NET: Data and XML
OleDb OleDb SqlClient SqlClient
Common Common SQLTypes SQLTypes
System.Data System.Data
XSL XSL
Serialization Serialization
XPath XPath
System.Xml System.Xml

*****************************ILLEGAL FOR NON-TRAINER USE******************************
ADO.NET, the next generation of ADO technology provides improved support
for disconnected programming. It also provides rich XML support in the
System.Xml namespace.
System.Data Namespace
The System.Data namespace consists of classes that constitute the ADO.NET
object model. At a high level, the ADO.NET object model is divided into two
layers: the connected layer and the disconnected layer.
The System.Data namespace includes the DataSet class, which represents
multiple tables and their relations. These data sets are completely self-contained
data structures that can be populated from a variety of data sources. One data
source could be XML; another data source could be an OLE DB; and a third
data source could be the direct adapter for Microsoft SQL Server

.
System.Xml Namespace
The System.Xml namespace provides support for XML. It includes an XML
parser and a writer, which are W3C-compliant. The Extensible Stylesheet
Language for Transformation (XSLT) is provided by the System.Xml.Xsl
namespace. The implementation of XPath, a comprehensive language for
document addressing, enables data graph navigation in XML. The
System.Xml.Serialization namespace provides the entire core infrastructure
for XML Web services, including such features as moving back and forth from
objects to an XML representation.
Topic Objective
To explain the data and
XML support in the runtime.
Lead-in
The .NET Framework
provides a new set of
ADO.NET classes to handle
data.
10 Module 1: Overview of the Microsoft .NET Framework


What Is an XML Web Service?
SOAP SOAP
! XML Web services consumers can send and
receive messages using XML
WSDL WSDL
Web Services Web Services
Description Language Description Language
! XML Web services are defined in terms of the
formats and ordering of messages
! Built using open Internet protocols XML & HTTP XML & HTTP
UDDI UDDI
Universal Description, Universal Description,
Discovery, and Integration Discovery, and Integration
! Provide a Directory of Services on the Internet
A programmable application component A programmable application component
accessible via standard Web protocols accessible via standard Web protocols
Open Open
Internet Internet
Protocols Protocols
XML Web XML Web
service service

*****************************ILLEGAL FOR NON-TRAINER USE******************************
XML Web services are an integral part of the .NET platform. They are the
fundamental mechanism for exposing and consuming data and functionality
across Web applications, both inside organizations and across organizations.
XML Web services are units of application logic that provide data and services
to other applications. Applications access XML Web services by means of
industry standard Web protocols and data formats, such as HTTP, XML, and
Simple Object Access Protocol (SOAP), regardless of how each XML Web
service is implemented.
XML and HTTP
XML Web services are built by using XML and HTTP. Because they are built
with XML and HTTP, XML Web services operate without firewall restrictions.
Also, because XML and HTTP are industry standards, any platform supporting
XML and HTTP can work with XML Web services.
SOAP
SOAP defines how messages are formatted, sent, and received when working
with XML Web services. SOAP is also an industry standard that is built on
XML and HTTP. Any platform that supports the SOAP standard can support
XML Web services.
Topic Objective
To define an XML Web
service.
Lead-in
XML Web services are the
fundamental building blocks
of the .NET platform.
Module 1: Overview of the Microsoft .NET Framework 11


Web Services Description Language
The Web Services Description Language (WSDL) is an XML format for
describing the network services that are offered by the server. You use WSDL
to create a file that identifies the services that are provided by the server and the
set of operations within each service that the server supports. For each of the
operations, the WSDL file also describes the format that the client must follow
when requesting an operation.
Universal Description, Discovery, and Integration
Universal Description, Discovery, and Integration (UDDI) is an industry
standard for registering and searching for XML Web services. By using UDDI,
developers can discover and use XML Web services that are available publicly
over the Internet.
For more information on UDDI, see Web Service Discovery in Module 13,
Remoting and XML Web Services, in Course 2349B, Programming with the
Microsoft.NET Framework (Microsoft Visual C# .NET), and the UDDI Web
site at http://www.uddi.org.
12 Module 1: Overview of the Microsoft .NET Framework


Web Forms and Services
ASP.NET ASP.NET
System.Web System.Web
Configuration Configuration SessionState SessionState
Caching Caching Security Security
Services Services
Description Description
Discovery Discovery
Protocols Protocols
UI UI
HtmlControls HtmlControls
WebControls WebControls

*****************************ILLEGAL FOR NON-TRAINER USE******************************
ASP.NET is a programming framework built on the common language runtime
that can be used on a server to build powerful Web applications. ASP.NET
Web Forms provide an easy and powerful way to build dynamic Web UI pages.
ASP.NET XML Web services provide the building blocks for constructing
distributed Web-based applications. XML Web services are based on open
Internet standards, such as HTTP and XML.
The common language runtime provides built-in support for creating and
exposing XML Web services by using a programming abstraction that is
consistent and familiar to both ASP Web Forms and Visual Basic developers.
The resulting model is both scalable and extensible. This model is based on
open Internet standards, such as HTTP, XML, SOAP, and WSDL, so it can be
accessed and interpreted by any client or Internet-enabled device. Some of the
more common ASP.NET classes are described in this topic as follows:
System.Web
In the System.Web namespace, there are lower-level services, such as caching,
security, and configuration, which are shared between XML Web services and
Web UIs.
System.Web.Services
The System.Web.Services namespace has classes that handle XML Web
services, such as protocols and discovery.
Controls
There are two types of controls: HTML controls and Web controls. The
System.Web.UI.HtmlControls namespace gives you direct mapping of HTML
tags, such as input. The System.Web.UI.WebControls namespace enables you
to structure controls with templates, such as grid controls.
Topic Objective
To show where Web Forms
and XML Web services are
found in the ASP.NET
programming model.
Lead-in
The Internet is quickly
evolving from todays Web
sites that simply deliver UI
pages to browsers to a next
generation of programmable
Web sites that link
organizations, applications,
services, and devices
directly.
Module 1: Overview of the Microsoft .NET Framework 13


" "" " Overview of Namespaces
! Namespaces
! Namespaces Used in this Course
! Namespaces Covered in Optional Modules

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this section, you will learn about the namespaces in the Microsoft .NET
Framework. You will also learn about which namespaces are taught in this
course.
This section includes the following topics:
! Namespaces
! Namespaces Used in this Course
! Namespaces Covered in Optional Modules

Topic Objective
To provide an overview of
the namespaces in the .NET
Framework.
Lead-in
In this section, you will learn
about the namespaces in
the .NET Framework.
14 Module 1: Overview of the Microsoft .NET Framework


Namespaces
System System
Globalization Globalization
Diagnostics Diagnostics
Configuration Configuration
Collections Collections
Resources Resources
Reflection Reflection
Net Net
IO IO
Threading Threading
Text Text
Security Security Runtime Runtime
ServiceProcess ServiceProcess

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The .NET Framework includes a large set of class library assemblies, which
contain hundreds of types. These assemblies provide access to system
functionality in your development process.
The Purpose of Namespaces
Because the .NET Framework class library includes definitions for so many
types, the library is organized in a hierarchical namespace structure.
Namespaces use a dot-syntax naming scheme to group logically related classes
together so that they can be easily searched and referenced. For example, the
System.Data namespace contains the classes that constitute the ADO.NET
architecture. The System.Xml namespace is the overall namespace for the
XML classes that provide standards-based support for processing XML.
The System Namespace
The System namespace is the root namespace for types in the .NET
Framework. The System namespace contains the base type Object, from which
all other types are derived.
The System namespace also contains types for exception handling, garbage
collection, console I/O, various tool types, format data types, random number
generators, and math functions.
Topic Objective
To understand how
namespaces provide an
easy-to-use hierarchy of
types and functionality.
Lead-in
The .NET Framework
includes a large set of class
library assemblies, which
contain hundreds of types.
These assemblies provide
access to system
functionality in your
development process.
Module 1: Overview of the Microsoft .NET Framework 15


Namespaces Used in this Course
Module 2
# System.Console
Module 3
# System.Windows.Forms
# System.Drawing
Module 4
# System.Reflection
Module 7
# System.Text
# System.Collections
Module 10
# System.IO
Module 11
# System.Net
# System.Net.Sockets
Module 12
# System.Runtime.Serialization
Module 13
# System.Runtime.Remoting.Channels
# System.Web.Services

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This course covers many of the namespaces in the Microsoft .NET Framework.
Module 2 teaches the System.Console namespace for printing output to the
console. Module 3 teaches the System.Windows.Forms and System.Drawing
namespaces for building a form with buttons that interacts with the user.
Module 4 teaches the System.Reflection namespace for storing version and key
file information in an assembly. Module 7 teaches the System.Text namespace
for advanced string management, and System.Collections for maintaining
collections of data.
Module 10 teaches the System.IO namespace for reading and writing to files.
Module 11 teaches the System.Net and System.Net.Sockets namespaces for
transmitting data over the network.
Module 12 teaches the System.Runtime.Serialization namespace for
persisting objects to storage. Module 13 teaches the
System.Runtime.Remoting.Channels and System.Web.Services namespaces
for invoking remote objects, and building XML Web services.
Topic Objective
To explain which
namespaces are taught in
this course, and which
namespaces are not taught.
Lead-in
This course covers many of
the System namespaces.
Not all namespaces are
covered, for example the
System.Security
namespaces.
16 Module 1: Overview of the Microsoft .NET Framework


Namespaces Covered in Optional Modules
Module 14
# System.Threading
Module 16
# System.Data
Module 17
# System.Reflection

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Modules 14 through 17 are optional modules.
Module 14 teaches the System.Threading namespace for enabling
multithreaded programming. Module 16 teaches the System.Data namespace,
which provides the base objects and types for the ADO.NET programming
model. ADO.NET also provides rich XML support in the System.Xml
namespace. Finally, Module 17 teaches the System.Reflection namespace,
which contains classes that you can use for examining metadata.
Namespaces Not Covered
This course does not teach the System.Security namespace. For more
information about System.Security and related security namespaces, see
Course 2350A, Securing and Deploying Microsoft .NET Assemblies.
Topic Objective
To complete the namespace
information that was
presented in the preceding
slide.
Lead-in
Here are some more
namespaces that are
covered in optional Modules
14 through 17 of this course.
Module 1: Overview of the Microsoft .NET Framework 17


Review
! Overview of the Microsoft .NET Framework
! Overview of Namespaces

*****************************ILLEGAL FOR NON-TRAINER USE******************************
1. List the components of the .NET Framework.
The common language runtime, .NET Framework class library, data
and XML, XML Web services and Web Forms, and Windows Forms.


2. What is the purpose of the common language runtime?
It provides an environment in which you can execute code.


3. What is the purpose of the common language specification?
It defines a set of features that all .NET languages should support.


4. What is an XML Web service?
An XML Web service is a programmable Web component that can be
shared among applications on the Internet or an intranet.


5. What is a managed environment?
A managed environment is an environment that provides services, such
as garbage collection, security, and other related features.




Topic Objective
To reinforce module
objectives by reviewing
key points.
Lead-in
The review questions cover
some of the key concepts
taught in the module.



THIS PAGE INTENTIONALLY LEFT BLANK








Contents
Overview 1
Writing a .NET Application 2
Compiling and Running a .NET Application 11
Lab 2: Building a Simple .NET Application 29
Review 32

Module 2: Introduction
to a Managed Execution
Environment


Information in this document, including URL and other Internet Web site references, is subject to
change without notice. Unless otherwise noted, the example companies, organizations, products,
domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious,
and no association with any real company, organization, product, domain name, e-mail address,
logo, person, place or event is intended or should be inferred. Complying with all applicable
copyright laws is the responsibility of the user. Without limiting the rights under copyright, no
part of this document may be reproduced, stored in or introduced into a retrieval system, or
transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or
otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual
property rights covering subject matter in this document. Except as expressly provided in any
written license agreement from Microsoft, the furnishing of this document does not give you any
license to these patents, trademarks, copyrights, or other intellectual property.

2001-2002 Microsoft Corporation. All rights reserved.

Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint,
Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and
Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A.
and/or other countries.

The names of actual companies and products mentioned herein may be the trademarks of their
respective owners.



Module 2: Introduction to a Managed Execution Environment iii


Instructor Notes
After completing this module, students will be able to:
! Create simple console applications in C#.
! Explain how code is compiled and executed in a managed execution
environment.
! Explain the concept of garbage collection.

Materials and Preparation
This section provides the materials and preparation tasks that you need to teach
this module.
Required Materials
To teach this module, you need the following materials:
! Microsoft PowerPoint file 2349B_02.ppt
! Sample managed module HelloDemoCS.exe

Preparation Tasks
To prepare for this module, you should:
! Read all of the materials for this module.
! Practice the demonstrations.
! Review the animation.
! Complete the lab.

Presentation:
45 Minutes

Lab:
20 Minutes
iv Module 2: Introduction to a Managed Execution Environment


Demonstrations
This section provides demonstration procedures that will not fit in the margin
notes or are not appropriate for the student notes.
Hello World
This demonstration shows how to build a simple application in C#.
In the following procedures, use Notepad to create the simple Hello World
application, and build and run the HelloDemoCS.exe application from the
command line.
! To create the source code in C#
1. Open Notepad and type the following code:
// Allow easy reference to System namespace classes
using System;

// Create class to hold program entry point
class MainApp {
public static void Main() {

// Write text to the console
Console.WriteLine(Hello World using C#!);
}
}

2. Save the file as HelloDemoCS.cs.


To use Microsoft Visual Studio .NET tools within a command
prompt window, the command prompt window must have the proper
environment settings. The Visual Studio .NET Command Prompt window
provides such an environment. To run a Visual Studio .NET Command Prompt
window, click Start, All Programs, Microsoft Visual Studio .NET,
Visual Studio .NET Tools, and Visual Studio .NET Command Prompt.

Important
Module 2: Introduction to a Managed Execution Environment v


! To compile the source code and build an executable program
From a Visual Studio .NET Command Prompt window, type the following
syntax:
csc HelloDemoCS.cs

Running the resulting executable file will generate the following output:
Hello World using C#!


Viewing Assembly Metadata by Using the MSIL
Disassembler
This demonstration shows how to use the Microsoft Intermediate Language
(MSIL) Disassembler (Ildasm.exe) to view an assemblys metadata.
The code for this demonstration is contained in one project and is located in
<install folder>\Democode\Mod02. To demonstrate how to use the MSIL
Disassembler to view the contents of the HelloDemoCS.exe assembly, follow
the directions in Demonstration: Using the MSIL Disassembler in this module.
vi Module 2: Introduction to a Managed Execution Environment


Multimedia
This section lists the multimedia items that are part of this module. Instructions
for launching and playing the multimedia are included with the relevant slides.
Application Loading and Single-File Assembly Execution
This animation will show students how a single-file private assembly is loaded
and executed.
Module Strategy
Use the following strategy to present this module:
! Writing a .NET Application
Stress the importance of understanding the process of compiling and
running Microsoft .NET Framework applications, by using the simple Hello
World application. Focus primarily on the compilation and execution
processes.
! Compiling and Running a .NET Application
This section introduces basic concepts of a managed execution environment
and presents new terminology. Many of these concepts are covered in
greater detail in subsequent modules in this course, in subsequent courses,
and in the .NET Framework software development kit (SDK)
documentation.
Emphasize that you are primarily introducing new concepts and
terminology. Be prepared to postpone answering questions that pertain to
information that is covered in later modules. Encourage students to start
reading the .NET Framework SDK documentation.

Module 2: Introduction to a Managed Execution Environment 1


Overview
! Writing a .NET Application
! Compiling and Running a .NET Application

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This module introduces the concept of managed execution and shows you how
to quickly build applications that use the Microsoft .NET Framework common
language runtime environment. A simple Hello World version of a console
application illustrates most of the concepts that are introduced in this module.
Because this course is an introduction to programming in the .NET Framework,
you should spend some time reading the .NET Framework software
development kit (SDK) documentation. In fact, the labs, demonstrations, and
material for this module and other modules in this course are based on several
tutorials in the .NET Framework SDK.
After completing this module, you will be able to:
! Create simple console applications in C#.
! Explain how code is compiled and executed in a managed execution
environment.
! Explain the concept of garbage collection.

Topic Objective
To provide an overview of
the module topics and
objectives.
Lead-in
This module introduces the
concept of managed
execution and shows you
how to quickly build
applications that use the
Microsoft .NET Framework
common language runtime
environment.
2 Module 2: Introduction to a Managed Execution Environment


" "" " Writing a .NET Application
! Using a Namespace
! Defining a Namespace and a Class
! Entry Points, Scope, and Declarations
! Console Input and Output
! Case Sensitivity

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Because all supported languages use the Common Type System and the .NET
Framework base class library, and run in the common language runtime,
programs in the supported languages are similar. The most significant
difference in programming with the supported languages is syntax.

In this module, and in Modules 3 and 4, Notepad is used as the source
code editor, instead of the Microsoft Visual Studio .NET development
environment. The examples in these modules are simple enough to be compiled
and built directly from a command prompt window. Working in Notepad will
allow you to focus on the compilation and execution processes.

Topic Objective
To introduce the topics in
the section.
Lead-in
Because all supported
languages use the Common
Type System and the .NET
Framework class library,
and run in the common
language runtime, programs
in the supported languages
are similar.
Delivery Tip
Stress the importance of
understanding the process
of compiling and running the
.NET applications. Using
Visual Studio .NET at this
time may obscure the
underlying processes.
Note
Module 2: Introduction to a Managed Execution Environment 3


Demonstration: Hello World

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this demonstration, you will learn how to build a simple application in C#.
! To create the source code in C#
1. Open Notepad and type the following code:
// Allow easy reference to System namespace classes
using System;

// Create class to hold program entry point
class MainApp {
public static void Main() {

// Write text to the console
Console.WriteLine(Hello World using C#!);
}
}

2. Save the file as HelloDemoCS.cs

Topic Objective
To demonstrate how to build
a simple application in C#.
Lead-in
In this demonstration, you
will learn how to build a
simple application in C#.
Delivery Tip
As this is a short, simple
demonstration, you may
want to let students try it
themselves.
4 Module 2: Introduction to a Managed Execution Environment


! To compile the source code and build an executable program

To use Visual Studio .NET tools within a command prompt
window, the command prompt window must have the proper environment
settings. The Visual Studio .NET Command Prompt window provides such an
environment. To run a Visual Studio .NET Command Prompt window, click
Start, All Programs, Microsoft Visual Studio .NET, Visual Studio .NET
Tools, and Visual Studio .NET Command Prompt.

From a Visual Studio .NET Command Prompt window, type the following
syntax:
csc HelloDemoCS.cs

Running the resulting executable will generate the following output:
Hello World using C#!


Important
Module 2: Introduction to a Managed Execution Environment 5


Using a Namespace
! Classes Can Be Fully Referenced
! Or the Namespace of a Class Can Be Referenced
# No need to fully qualify contained class names
// declares a FileStream object
System.IO.FileStream aFileStream;
// declares a FileStream object
System.IO.FileStream aFileStream;
using System.IO;
...
FileStream aFileStream;
using System.IO;
...
FileStream aFileStream;

*****************************ILLEGAL FOR NON-TRAINER USE******************************
You can fully reference classes, as in the following example, in which an
instance of System.IO.FileStream is declared by using C#:
System.IO.Filestream aFileStream;

However, it is more convenient to reference the required namespaces in your
program. Using the namespace effectively disposes of the need to qualify all
class library references, as in the following example:
using System.IO;
...
FileStream aFileStream;

For example, in order to have convenient access to System objects, you must
use the System namespace.
Topic Objective
To describe how to use
namespaces in the .NET
Framework.
Lead-in
You can fully reference
classes in which an instance
of System.IO.FileStream is
declared by using C#:
6 Module 2: Introduction to a Managed Execution Environment


Defining a Namespace and a Class
! C# Supports Creation of Custom Namespaces and
Classes Within Those Namespaces
namespace CompCS {
public class StringComponent {
...
}
}
namespace CompCS {
public class StringComponent {
...
}
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
C# supports the creation of custom namespaces and classes within those
namespaces.

The following is the general rule for naming namespaces:
CompanyName.TechnologyName
For example:
Microsoft.Office
This is merely a guideline. Third-party companies can choose other names.

Namespaces in C#
In C#, you use the namespace statement to define a new namespace, which
encapsulates the classes that you create, as in the following example:
namespace CompCS {
public class StringComponent {
...
}
}

Note that a namespace may be nested in other namespaces, and a single
namespace may be defined in multiple files. A single source code file may also
define multiple namespaces.
Topic Objective
To describe how to define
namespaces and classes in
C#.
Lead-in
C# supports the creation of
custom namespaces and
classes within those
namespaces.
Tip
Module 2: Introduction to a Managed Execution Environment 7


Entry Points, Scope, and Declarations
! In C#, the External Entry Point for a Program Is in a Class
! C# Supports the Use of a Period As a Scope Resolution Operator
! In C#, Objects Must Be Declared Before They Can Be Used and Are
Instantiated Using the New Keyword
class MainApp
{ public static void Main()
{. . .}
}
class MainApp
{ public static void Main()
{. . .}
}
Console.WriteLine ("First String");
Console.WriteLine ("First String");
Lib.Comp myComp = new Lib.Comp();
Lib.Comp myComp = new Lib.Comp();

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Every executable program must contain an external entry point, where the
application begins its execution. In C#, all code must be contained in methods
of a class.
Entry Points in C#
To accommodate the entry point code in C#, you must first specify the class, as
in the following example:
class MainApp {...}

Next, you specify the entry point for your program. The compiler requires this
entry point to be a public static method called Main, as in the following
example:
public static void Main () {...}

Scope
C# uses the period as a scope resolution operator. For example, you use the
syntax Console.WriteLine when referencing the WriteLine method of the
Console object.
Declaring and Instantiating Variables
In C#, you must declare a variable before it can be used. To instantiate the
object, use the new keyword. The following example in C# shows how to
declare an object of type Comp, in namespace Lib, with the name myComp:
Lib.Comp myComp = new Lib.Comp();

Topic Objective
To describe how to create
program entry points in C#.
Lead-in
Every executable program
must contain an external
entry point, where the
application begins its
execution.
8 Module 2: Introduction to a Managed Execution Environment


Console Input and Output
! Console Class Methods
# Read, ReadLine, Write, and WriteLine
Console.WriteLine("Hello World using C#!");
Console.WriteLine("Hello World using C#!");

*****************************ILLEGAL FOR NON-TRAINER USE******************************
You can use the common language runtime Console class of the System
namespace for input and output to the console of any string or numeric value by
using the Read, ReadLine, Write, and WriteLine methods.
The following example shows a C# program that outputs a string to the console:
using System;

class MainApp {
public static void Main() {

// Write text to the console
Console.WriteLine(Hello World using C#!);
}
}

Topic Objective
To describe how to use
Console class methods
in C#.
Lead-in
You can use the runtime
Console class of the
System namespace for
input and output to the
console of any string or
numeric value by using the
Read, ReadLine, Write,
and WriteLine methods.
Module 2: Introduction to a Managed Execution Environment 9


Case Sensitivity
! Do Not Use Names That Require Case Sensitivity
# Components should be fully usable from both case-
sensitive and case-insensitive languages
# Case should not be used to distinguish between
identifiers within a single name scope
! Avoid the Following
class customer {...}
class Customer {...}
void foo(int X, int x)
class customer {...}
class Customer {...}
void foo(int X, int x)

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Microsoft Visual C++ and C# are case-sensitive, but Microsoft Visual Basic
is not case-sensitive. To ensure that a program is compliant with the Common
Language Specification (CLS), however, you must take special care with public
names. You cannot use case to distinguish between identifiers within a single
name scope, such as types within assemblies and members within types.
The following examples show situations to avoid:
! Do not have two classes or namespaces whose names differ only by case.
class customer { ... }
class Customer { ... }

! Do not have a function with two parameters whose names differ only by
case.
void foo(int X, int x)


This constraint enables Visual Basic (and potentially other case-insensitive
languages) to produce and use components that have been created in other case-
sensitive languages. This constraint does not apply to your definitions of private
classes, private methods on public classes, or local variables.
Topic Objective
To describe case sensitivity
issues in programming
languages.
Lead-in
C++ and C# are case-
sensitive, but Visual Basic is
not case-sensitive.
10 Module 2: Introduction to a Managed Execution Environment



To fully interact with other objects regardless of the language they were
implemented in, objects must expose to callers only those features that are
common to all the languages they must interoperate with. For this reason, a set
of language features has been defined, called the Common Language
Specification (CLS), which includes common language features that are needed
by many applications. The CLS rules define a subset of the common type
system; that is, all the rules that apply to the common type system apply to the
CLS, except where stricter rules are defined in the CLS. If your component uses
only CLS features in the API that it exposes to other code (including derived
classes), the component is guaranteed to be accessible from any programming
language that supports the CLS. Components that adhere to the CLS rules and
use only the features included in the CLS are said to be CLS-compliant
components.

Note
Module 2: Introduction to a Managed Execution Environment 11


" "" " Compiling and Running a .NET Application
! Compiler Options
! The Process of Managed Execution
! Metadata
! Microsoft Intermediate Language
! Assemblies
! Common Language Runtime Tools
! Just-In-Time Compilation
! Application Domains
! Garbage Collection

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Most aspects of programming in the .NET Framework are the same for all
compatible languages; each supported language compiler produces self-
describing, managed Microsoft intermediate language (MSIL) code. All
managed code runs using the common language runtime, which provides
cross-language integration, automatic memory management, cross-language
exception handling, enhanced security, and a consistent and simplified
programming model.
This section introduces basic concepts of a managed execution environment and
presents new terminology. Many of these concepts are covered in greater detail
in subsequent modules in this course, in subsequent courses, and in the .NET
Framework SDK documentation.


Topic Objective
To introduce the topics in
the section.
Lead-in
Most aspects of
programming in the .NET
Framework are the same for
all compatible languages;
each supported language
compiler produces self-
describing, managed
Microsoft intermediate
language (MSIL) code.
Delivery Tip
Emphasize that you are
primarily introducing new
concepts and terminology.
Be prepared to postpone
answering questions that
pertain to later modules.
Encourage students to start
reading the .NET
Framework SDK
documentation.
Key Points
String literals in an
application are stored and
transported as clear text.
Therefore, you should avoid
putting sensitive information
such as passwords in string
literals.
12 Module 2: Introduction to a Managed Execution Environment


Compiler Options
! Compile Directly from a Command Prompt Window
! Use /t to indicate target
! Use /reference to reference assemblies
>csc HelloDemoCS.cs
>csc HelloDemoCS.cs
>csc /t:exe HelloDemoCS.cs
>csc /t:exe HelloDemoCS.cs
>csc /t:exe /reference:assemb1.dll HelloDemoCS.cs
>csc /t:exe /reference:assemb1.dll HelloDemoCS.cs

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The .NET Framework includes a command line compiler for C#. The file name
of the compiler is Csc.exe.
Compiling in C#
To compile the source code for the Hello World application presented in the
Hello World demonstration at the beginning of this module, type the following:
csc HelloDemoCS.cs

This syntax invokes the C# compiler. In this example, you only need to specify
the name of the file to be compiled. The compiler generates the program
executable, HelloDemoCS.exe.
Topic Objective
To introduce compiler
options in C#.
Lead-in
The .NET Framework
includes a command line
compiler for C#.
Module 2: Introduction to a Managed Execution Environment 13


Command Line Options
In C#, you can obtain the complete list of command line options by using the
/? switch as follows:
csc /?

Common options include the /out switch, which specifies the name of the
output file, and the /target switch, which specifies the target type. By default,
the name of the output file is the name of the input file with an .exe extension.
The default for the target type is an executable program.
The following example shows the use of both the /out and /t switches in C#:
csc /out:HelloDemoCS.exe /t:exe HelloDemoCS.cs

The /t switch is equivalent to the /target switch.
For more information about compiler options, see the .NET Framework SDK
documentation.
Using the /reference Compilation Option
When referring to other assemblies, you must use the /reference compilation
switch. The /reference compilation option allows the compiler to make
information in the specified libraries available to the source that is currently
being compiled.
The following example shows how to build an executable program from the
command line by using the /reference compilation option.
csc /r:assemb1.dll,assemb2.dll /out:output.exe input.cs

The /r switch is equivalent to the /reference compilation switch.
14 Module 2: Introduction to a Managed Execution Environment


The Process of Managed Execution
Class Loader Class Loader Class Loader
JIT Compiler
with optional
verification
JIT Compiler JIT Compiler
with optional with optional
verification verification
Execution Execution
Security Checks Security Checks
EXE/DLL
(MSIL and
metadata)
EXE/DLL
(MSIL and
metadata)
Class
Libraries
(MSIL and
metadata)
Class
Libraries
(MSIL and
metadata)
Trusted,
pre-JITed
code only
Call to an
uncompiled
method
Runtime Engine
Managed
Native
Code
Compiler Compiler Source
Code
Source
Code

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In the .NET Framework, the common language runtime provides the
infrastructure for a managed execution environment. This topic introduces
fundamental concepts of compiling and executing code in a managed execution
environment and identifies new terminology.
Compiling Source Code
When you develop an application in the .NET Framework, you can write the
source code in any programming language as long as the compiler that you use
to compile the code targets the common language runtime. Compilation of the
source code produces a managed module. The managed module is contained
within a physical file also known as a portable executable (PE) file.
The file may contain the following items:
! Microsoft Intermediate Language (MSIL)
The compiler translates the source code into MSIL, a CPU-independent set
of instructions that can be efficiently converted to native code.
! Type Metadata
This information fully describes types, members, and other references, and
is used by the common language runtime at run time.
! A Set of Resources
For example, .bmp or .jpg files.

Topic Objective
To introduce fundamental
concepts of compiling and
executing code in a
managed execution
environment.
Lead-in
In the .NET Framework, the
common language runtime
provides the infrastructure
for a managed execution
environment.
Module 2: Introduction to a Managed Execution Environment 15


If the C# compilers target option is either exe or library, then the compiler
produces a managed module that is an assembly. Assemblies are a fundamental
part of programming with the .NET Framework. Assemblies are the
fundamental units of sharing, deployment, security, and versioning in the
common language runtime. The .NET common language runtime only executes
MSIL code that is contained in an assembly.
If the C# compilers target option is module, then the compiler produces a
managed module that is not an assembly, it does not contain a manifest and
cannot be executed by the common language runtime. A managed module can
be added to an assembly by the C# compiler, or by using the .NETs Assembly
Generation Tool, Al.exe.
Subsequent topics in this module cover MSIL, metadata, and assemblies in
more detail.
Executing Code
When a user executes a managed application, the operating system loader loads
the common language runtime, which then begins executing the modules
managed MSIL code. Because current host CPUs cannot execute the MSIL
instructions directly, the common language runtime must first convert the MSIL
instructions into native code.
The common language runtime does not convert all of the modules MSIL code
into CPU instructions at load time. Instead, it converts the instructions when
functions are called. The MSIL is compiled only when needed. The component
of the common language runtime that performs this function is called the just-
in-time (JIT) compiler. JIT compilation conserves memory and saves time
during application initialization.
For more information about the JIT compiler, see Just-In-Time Compilation in
this module.
Application Domain
Operating systems and runtime environments typically provide some form of
isolation between applications. This isolation is necessary to ensure that code
running in one application cannot adversely affect other, unrelated applications.
Application domains provide a secure and versatile unit of processing that the
common language runtime can use to provide isolation between applications.
Application domains are typically created by runtime hosts, which are
responsible for bootstrapping the common language runtime before an
application is run.
16 Module 2: Introduction to a Managed Execution Environment


Metadata
! Declarative Information Emitted at Compile Time
! Included with All .NET Framework Files and Assemblies
! Metadata Allows the Runtime to:
# Load and locate code
# Enforce code security
# Generate native code at runtime
# Provide reflection

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Every compiler that targets the common language runtime is required to emit
full metadata into every managed module. This topic explains how the common
language runtime uses metadata.
Definition of Metadata
Metadata is a set of data tables, which fully describe every element that is
defined in a module. This information can include data types and members with
their declarations and implementations, and references to other types and
members.
Metadata provides the common language runtime with all the information that
is required for software component interaction. It replaces older technologies,
such as Interface Definition Language (IDL) files, type libraries, and external
registration. Metadata is always embedded in the .exe or .dll file containing the
MSIL code. Therefore, it is impossible to separate metadata from the MSIL
code.
Topic Objective
To explain how metadata is
used in the common
language runtime.
Lead-in
Every compiler that targets
the common language
runtime is required to emit
full metadata into every
managed module.
Module 2: Introduction to a Managed Execution Environment 17


Uses for Metadata
Metadata has many uses, but the following uses are most important:
! Locating and loading classes
Because metadata and MSIL are included in the same file, all type
information in that file is available to the common language runtime at
compile time. There is no need for header files because all types in a
particular assembly are described by the assemblys manifest.
! Enforcing security
The metadata may or may not contain the permissions required for the code
to run. The security system uses permissions to prevent code from accessing
resources that it does not have authority to access.

Other uses for metadata include:
! Resolving method calls.
! Setting up runtime context boundaries.
! Providing reflection capability.

For more information about metadata, see Metadata and Self-Describing
Components in the .NET Framework SDK documentation.
For more information about verification of type safety, see Module 4,
Deployment and Versioning, in Course 2349B, Programming with the
Microsoft .NET Framework (Microsoft Visual C# .NET).
18 Module 2: Introduction to a Managed Execution Environment


Microsoft Intermediate Language
! Produced by Each Supported Language Compiler
! Converted to Native Language by the Common
Language Runtime's JIT Compilers

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Microsoft intermediate language (MSIL), sometimes called managed code, is
the set of instructions that the compiler produces as it compiles source code.
This topic explains MSIL and the general process for converting MSIL to
native code.
Compiled MSIL
Regardless of their logical arrangement, most assemblies contain code in the
form of MSIL. MSIL is a CPU-independent machine language created by
Microsoft in consultation with third-party compiler vendors. However, MSIL is
a much higher-level language than most CPU machine languages.
MSIL contains instructions for many common operations, including
instructions for creating and initializing objects, and for calling methods on
objects. In addition, it includes instructions for arithmetic and logical
operations, control flow, direct memory access, and exception handling.
Conversion to Native Code
Before MSIL code can be executed, it must be converted to CPU-specific or
native code by a JIT compiler. The common language runtime provides an
architecture-specific JIT compiler for each CPU architecture. These
architecture-specific JIT compilers allow you to write managed code that can be
JIT compiled and executed on any supported architecture.

Any managed code that calls platform-specific native APIs or libraries
can only run on a specific operating system.

For more information about MSIL, see the .NET Framework SDK
documentation.
Topic Objective
To introduce MSIL and JIT
compilation.
Lead-in
Microsoft intermediate
language, sometimes called
managed code, is the set of
instructions that the
compiler produces as it
compiles source code.
Note
Module 2: Introduction to a Managed Execution Environment 19


Assemblies
Assembly
Assembly
Manifest Manifest
Multiple Managed
Modules and
Resource Files
Are Compiled to
Produce an Assembly
Managed Module
(MSIL and Metadata)
Managed Module
(MSIL and Metadata)
Managed Module
(MSIL and Metadata)
Managed Module
(MSIL and Metadata)
.html
.gif
Resource Files

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The common language runtime uses an assembly as the functional unit of
sharing and reuse.
Definition of an Assembly
An assembly is a unit of class deployment, analogous to a logical .dll. Each
assembly consists of all the physical files that make up the functional unit: any
managed modules, and resource or data files.
Conceptually, assemblies provide a way to consider a group of files as a single
entity. You must use assemblies to build an application, but you can choose
how to package those assemblies for deployment.
An assembly provides the common language runtime with the information that
it needs to understand types and their implementations. As such, an assembly is
used to locate and bind to referenced types at run time.
Topic Objective
This topic introduces the
concept of an assembly and
the role of the assembly
manifest.
Lead-in
The common language
runtime uses an assembly
as the functional unit of
sharing and reuse.
20 Module 2: Introduction to a Managed Execution Environment


The Assembly Manifest
An assembly contains a block of data known as a manifest, which is a table in
which each entry is the name of a file that is part of the assembly. The manifest
includes the metadata that is needed to specify the version requirements,
security identity, and the information that is used to define the scope of the
assembly and resolve references to resources and classes. Because the metadata
makes an assembly self-describing, the common language runtime has the
information it requires for each assembly to execute.
All applications that are executed by the common language runtime must be
composed of an assembly or assemblies. All files that make up an assembly
must be listed in the assemblys manifest. The manifest can be stored in single-
file assemblies or multi-file assemblies:
! Single-file assemblies
When an assembly has only one associated file, the manifest is integrated
into a single PE file.
! Multi-file assemblies
When an assembly has more than one associated file, the manifest can be a
standalone file, or it can be incorporated into one of the PE files in the
assembly.

For more information about assemblies, see Assemblies in the .NET
Framework SDK documentation.
Module 2: Introduction to a Managed Execution Environment 21


Common Language Runtime Tools
! Runtime Utilities for Working with MSIL
# MSIL Assembler (ilasm.exe) produces a final executable
binary
# MSIL Disassembler (ildasm.exe) inspects metadata and
code of a managed binary

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The common language runtime provides two tools that you can use to test and
debug MSIL code.
! The MSIL Assembler
The MSIL Assembler (Ilasm.exe) takes MSIL as text input and generates a
PE file, which contains the binary representation of the MSIL code and
required metadata. The basic syntax is as follows:
ilasm [options] filename [options]
! The MSIL Disassembler
You can use the MSIL Disassembler (Ildasm.exe) to examine the metadata
and disassembled code of any managed module. You will use this tool to
examine MSIL code in Lab 2, Building a Simple .NET Application.

Topic Objective
This topic describes how the
MSIL Assembler and MSIL
Disassembler work.
Lead-in
The common language
runtime provides two tools
that you can use together to
test and debug MSIL code.
22 Module 2: Introduction to a Managed Execution Environment


Demonstration: Using the MSIL Disassembler

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This demonstration shows how to use the MSIL Disassembler to view an
assemblys metadata.
Viewing Assembly Metadata by Using the MSIL
Disassembler
In the following procedures, you will see how to use the MSIL Disassembler
(Ildasm.exe) to view the contents of the HelloDemoCS.exe assembly.
! To run the MSIL Disassembler on the HelloDemoCS.exe assembly
At a Visual Studio .NET Command Prompt window, change the directory to
<install folder>\Democode\Mod02 where the sample file
HelloDemoCS.exe has been copied, and type the following command:
ildasm HelloDemoCS.exe


Topic Objective
To demonstrate how the
MSIL Disassembler works.
Lead-in
This demonstration shows
how to use the MSIL
Disassembler to view an
assemblys metadata.
Module 2: Introduction to a Managed Execution Environment 23


After you expand the MainApp icon, the MSIL Disassembler graphical user
interface (GUI) displays information about the file HelloDemoCS.exe, as
shown in the following illustration:

! To display the contents of the manifest
1. Double-click MANIFEST.
The MANIFEST window appears, as follows:

2. Close the Manifest window, and then close ILDASM.

24 Module 2: Introduction to a Managed Execution Environment


Just-In-Time Compilation
! Process for Code Execution
# MSIL converted to native code as needed
# Resulting native code stored for subsequent calls
# JIT compiler supplies the CPU-specific conversion

*****************************ILLEGAL FOR NON-TRAINER USE******************************
As previously stated in this module, MSIL code must be converted into native
code before it can execute. Because an intermediate step is involved, the
common language runtime optimizes the compilation process for efficiency.
The Code Execution Process
The common language runtime compiles MSIL as needed. This just-in-time, or
JIT, compiling saves time and memory. The basic process is as follows:
1. When the common language runtime loads a class type, it attaches stub code
to each method.
2. For subsequent method calls, the stub directs program execution to the
common language runtime component that is responsible for compiling a
methods MSIL into native code. This component of the common language
runtime is frequently referred to as the JIT compiler.
3. The JIT compiler compiles the MSIL and the methods stub is substituted
with the address of the compiled code.
Future calls to that method will not involve the JIT compiler because the
compiled native code will simply execute.

Topic Objective
To describe JIT compilation
and introduce JIT compiler
options.
Lead-in
MSIL code must be
converted into native code
before it can execute.
Because an intermediate
step is involved, the
common language runtime
optimizes the compilation
process for efficiency.
Module 2: Introduction to a Managed Execution Environment 25


Application Domains
! Historically, Process Boundaries Used to Isolate
Applications
! In the Common Language Runtime, Application
Domains Provide Isolation Between Applications
# The ability to verify code as type-safe enables isolation
at a much lower performance cost
# Several application domains can run in a single process
! Faults in One Application Cannot Affect Other
Applications

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Historically, process boundaries have been used to isolate applications running
on the same computer. Each application is loaded into a separate process, which
isolates the application from other applications running on the same computer.
The applications are isolated because memory addresses are process-relative; a
memory pointer passed from one process to another cannot be used in any
meaningful way in the target process. In addition, you cannot make direct calls
between two processes. Instead, you must use proxies, which provide a level of
indirection.
Managed code must be passed through a verification process before it can be
run (unless the administrator has granted permission to skip the verification).
The verification process determines whether the code can attempt to access
invalid memory addresses or perform some other action that could cause the
process in which it is running to fail to operate properly.
Code that passes the verification test is said to be type-safe. The ability to verify
code as type-safe enables the common language runtime to provide as great a
level of isolation as the process boundary, at a much lower performance cost.
Application domains provide a secure and versatile unit of processing that the
common language runtime can use to provide isolation between applications.
You can run several application domains in a single process with the same level
of isolation that would exist in separate processes, but without incurring the
additional overhead of making cross-process calls or switching between
processes. The ability to run multiple applications within a single process
dramatically increases server scalability.
Topic Objective
To describe how application
domains provide application
isolation.
Lead-in
Historically, process
boundaries have been used
to isolate applications
running on the same
computer.
26 Module 2: Introduction to a Managed Execution Environment


Isolating applications is also important for application security. For example,
you can run controls from several Web applications in a single browser process
in such a way that the controls cannot access each other's data and resources.
The isolation provided by application domains has the following benefits:
! Faults in one application cannot affect other applications. Because type-safe
code cannot cause memory faults, using application domains ensures that
code running in one domain cannot affect other applications in the process.
! Individual applications can be stopped without stopping the entire process.
Using application domains enables you to unload the code running in a
single application.


You cannot unload individual assemblies or types. Only a complete
domain can be unloaded.

Code running in one application cannot directly access code or resources from
another application. The common language runtime enforces this isolation by
preventing direct calls between objects in different application domains.
Objects that pass between domains are either copied or accessed by proxy. If
the object is copied, the call to the object is local. That is, both the caller and the
object being referenced are in the same application domain. If the object is
accessed through a proxy, the call to the object is remote. In this case, the caller
and the object being referenced are in different application domains. Cross-
domain calls use the same remote call infrastructure as calls between two
processes or between two computers.
Note
Module 2: Introduction to a Managed Execution Environment 27


Multimedia: Application Loading and Single-File Assembly
Execution

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Applications that are implemented by using MSIL code require the common
language runtime to be installed on the users computer in order to run. The
common language runtime manages the execution of code. For example, when
an application that is implemented as a single-file private assembly is run, the
following tasks are performed:
! The Microsoft Windows loader loads the PE file.
The PE file contains a call to a function found in the file MSCorEE.dll.
! Windows loads the file MSCorEE.dll and transfers control to it to initialize
the common language runtime.
! The common language runtime parses the metadata of the application
assembly, and then the JIT compiler compiles the code.
! The common language runtime then locates the PE files managed entry-
point (the Main method in the case of a C# program), and transfers control
to this entry point.

If the application calls a private assembly:
! The common language runtime uses probing to locate the referenced
assembly, beginning in the applications root directory and then traversing
the subfolders until the assembly is located.
If the assembly is not found, a TypeLoadException error occurs.
If the assembly is found, it is loaded by the common language runtime.

The common language runtime loader parses the manifest in the referenced
assembly. The JIT compiler then compiles the required code in the assembly
and passes control to the called function.
Topic Objective
To describe how assembly
code is executed.
Lead-in
In this demonstration, you
will see how a single-file
private assembly is loaded
and executed.
Delivery Tip
To start the animation, click
the button in the lower-left
corner of the slide. The
animation plays
automatically. To pause or
rewind the animation, click
the controls in the lower-left
of the screen.
28 Module 2: Introduction to a Managed Execution Environment


Garbage Collection
! Garbage Collection Provides Automatic Object Memory
Management in the .NET Framework
! You No Longer Need to Track and Free Object Memory

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This topic introduces memory management in the .NET Framework.
For more information about memory and resource management, see Module 9,
Memory and Resource Management, in Course 2349B, Programming with
the Microsoft .NET Framework (Microsoft Visual C# .NET).
Current Memory Management Model
When you create an object programmatically, you generally follow these steps:
1. Allocate memory for the object
2. Initialize the memory
3. Use the object
4. Perform cleanup on the object
5. Free the objects memory

If you forget to free memory when it is no longer required or try to use memory
after it has been freed, you can generate programming errors. The tracking and
correction of such errors are complicated tasks because the consequences of the
errors are unpredictable.
Memory Management in the .NET Framework
The common language runtime uses a heap called the managed heap to allocate
memory for all objects. This managed heap is similar to a C-Runtime heap,
however you never free objects from the managed heap. In the common
language runtime, garbage collection is used to manage memory deallocation.
The garbage collection process frees objects when they are no longer needed by
the application.
For more information about garbage collection, see Module 9, Memory and
Resource Management, in Course 2349B, Programming with the Microsoft
.NET Framework (Microsoft Visual C# .NET).
Topic Objective
To introduce memory and
resource management in
the .NET Framework.
Lead-in
Every program uses
resources, such as files,
screen space, network
connections, and database
resources.
Module 2: Introduction to a Managed Execution Environment 29


Lab 2: Building a Simple .NET Application

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives
After completing this lab, you will be able to:
! Write, compile, and run a simple application in C#.
! Use the MSIL Disassembler to examine an assembly.

Lab Setup
Only solution files are associated with this lab. The solution files for this lab are
in the folder <install folder>\Labs\Lab02\Solution.
Estimated time to complete this lab: 20 minutes
Topic Objective
To introduce the lab.
Lead-in
In this lab, you will learn
how to write, compile, and
run a simple application in
C# and how to use the MSIL
Disassembler to examine an
assembly.
30 Module 2: Introduction to a Managed Execution Environment


Exercise 1
Creating the Program in C#
In this exercise, you will create the source code for a small console application
that takes user input and writes a string to the console by using C#. The lab uses
the classic Hello World application to allow you to focus on basic concepts in a
managed execution environment.
To help you concentrate on the syntactical aspects of this lab, you will use
Notepad to create and edit the source files. From a command prompt window,
you will then compile the application and test the resulting executable program.
! Write the source code
1. Open Notepad and create a class in C# called MainApp.
2. Import the System namespace.
3. Define the program entry point.
The entry point takes no arguments and does not return a value.
4. Create methods that accomplish the following:
a. Print the following text to the console: Type your name and press
Enter.
b. Read in the resulting user input.
c. Print the text Hello and append to the text the value that was read in.
5. Save the file as HelloLabCS.cs in the <install folder>\Labs\Lab02 folder.

! Build and test the program

To use Microsoft Visual Studio .NET tools within a command
prompt window, the command prompt window must have the proper
environment settings. The Visual Studio .NET Command Prompt window
provides such an environment. To run a Visual Studio .NET Command Prompt
window, click Start, All Programs, Microsoft Visual Studio .NET,
Visual Studio .NET Tools, and Visual Studio .NET Command Prompt.

1. From a Visual Studio .NET Command Prompt window, type the syntax to
build an executable program from HelloLabCS.cs.
2. Run the resulting executable program.
Your C# program should generate the following output:
Type your name and press Enter

When you press ENTER, the program outputs the text Hello and
whatever text you typed as input.

Important
Module 2: Introduction to a Managed Execution Environment 31


Exercise 2
Using the MSIL Disassembler
In this exercise, you will use the MSIL Disassembler to open a single assembly
and familiarize yourself with the assembly manifest.
In subsequent labs, you will explore assemblies in greater detail.
! Examine the metadata for the Hello World applications
1. Open a Visual Studio .NET Command Prompt window.
2. From the Visual Studio .NET Command Prompt window, type:
>ildasm /source

3. Open HelloLabCS.exe and double-click Manifest.
4. Note the following:
a. The externally referenced library named mscorlib
b. The assembly name HelloLabCS
c. Version information (for the HelloLabCS assembly and mscorlib)
5. Close the Manifest window, double-click MainApp, double-click Main,
and view the MSIL and source code.

32 Module 2: Introduction to a Managed Execution Environment


Review
! Writing a .NET Application
! Compiling and Running a .NET Application

*****************************ILLEGAL FOR NON-TRAINER USE******************************
1. Name the root namespace for types in the .NET Framework.
The System namespace is the root namespace for types in the .NET
Framework.


2. What class and methods can your application use to input and output to the
console?
You can use the common language runtimes Console class of the
System namespace for input and output to the console of any string or
numeric value by using the Read, ReadLine, Write, and WriteLine
methods.


3. When compiling code that makes references to classes in assemblies other
than mscorlib.dll what must you do?
You must use the /reference compilation switch. The /reference
compilation option allows the compiler to make information in the
specified libraries available to the source that is currently being
compiled. The /r switch is equivalent to /reference.


4. What is the code produced by a .NET compiler called?
Microsoft intermediate language (MSIL), sometimes called managed
code.


Topic Objective
To reinforce module
objectives by reviewing
key points.
Lead-in
The review questions cover
some of the key concepts
taught in the module.
Module 2: Introduction to a Managed Execution Environment 33


5. What .NET component compiles MSIL into CPU specific native code?
The just-in-time (JIT) compiler.


6. What feature of .NET ensures that object memory is freed?
The garbage collection process.







THIS PAGE INTENTIONALLY LEFT BLANK









Contents
Overview 1
An Introduction to Key .NET Framework
Development Technologies 2
Creating a Simple .NET Framework
Component 4
Lab 3.1: Creating a .NET Framework
Component 11
Creating a Simple Console Client 14
Lab 3.2: Creating a Simple Console-Based
Client 19
Demonstration: Creating a Windows
Forms Client 22
Creating an ASP.NET Client 27
Lab 3.3: Calling a Component Through
an ASP.NET Page 36
Review 40

Module 3: Working with
Components


Information in this document, including URL and other Internet Web site references, is subject to
change without notice. Unless otherwise noted, the example companies, organizations, products,
domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious,
and no association with any real company, organization, product, domain name, e-mail address,
logo, person, place or event is intended or should be inferred. Complying with all applicable
copyright laws is the responsibility of the user. Without limiting the rights under copyright, no
part of this document may be reproduced, stored in or introduced into a retrieval system, or
transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or
otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual
property rights covering subject matter in this document. Except as expressly provided in any
written license agreement from Microsoft, the furnishing of this document does not give you any
license to these patents, trademarks, copyrights, or other intellectual property.

2001-2002 Microsoft Corporation. All rights reserved.

Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint,
Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and
Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A.
and/or other countries.

The names of actual companies and products mentioned herein may be the trademarks of their
respective owners.



Module 3: Working with Components iii


Instructor Notes
After completing this module, students will be able to:
! Create a simple Microsoft .NET Framework component in C#.
! Implement structured exception handling.
! Create a simple .NET Framework console application that calls a
component.
! Create a .NET Framework client application by using the Windows Forms
library.
! Create an ASP.NET page that uses the previously developed .NET
Framework component to create an ASP.NET application.

Materials and Preparation
This section provides the materials and preparation tasks that you need to teach
this module.
Required Materials
To teach this module, you need the Microsoft PowerPoint file 2349B_03.ppt.
Preparation Tasks
To prepare for this module, you should:
! Read all of the materials for this module.
! Practice the demonstration.
! Review the animation.
! Complete the lab.

Presentation:
70 Minutes

Lab:
60 Minutes
iv Module 3: Working with Components


Demonstrations
This section provides demonstration procedures that will not fit in the margin
notes or are not appropriate for the student notes.
Creating a Windows Forms Client
This demonstration shows how to build a simple Windows Forms client in
Microsoft Visual Studio .NET. If time permits, you can show the
demonstration by using Microsoft Visual Basic. The solution code for this
demonstration is located in <install folder>\DemoCode\Mod03\C# and
<install folder>\DemoCode\Mod03\VB.
In this demonstration, point out how the Windows Form is created in the Main
method. Also, point out how the Windows Forms Designer adds code to create
and initialize the controls. Consider stepping through the code to explain the
code to students.
! To create a Windows Form
1. Open Visual Studio .NET and create a Microsoft Visual C#

project from
the Windows Application template. Name the project WinForm_Client.
2. Add two buttons and a listbox to the form. Set the following properties.
a. Set the button1 Text property to &Execute.
b. Set the button2 Text property to &Close.
c. Set the Form1 Text property to Client.

! To add references
1. On the Project menu, click Add Reference.
2. In the Add Reference dialog box, click the Browse button.
3. Select the CompCS.dll and CompVB.dll assemblies as references. These
assemblies are located in <install folder>\DemoCode\Mod03\C#. Then click
the Open button.
4. Click OK.
5. Add references to the assemblies in the form code. Use aliases to avoid
name conflicts.
using CSStringComp = CompCS.StringComponent;
using VBStringComp = CompVB.StringComponent;


Module 3: Working with Components v


! To add button event handlers
1. In the form, double-click the Execute button to create a button-click event
handler.
2. Add the following code to the event handler to create an instance of the C#
component and Visual Basic component, and add their strings to the listbox.
//Local Variables
CSStringComp myCompCS = new CSStringComp();
VBStringComp myCompVB = new VBStringComp();
int stringCount=0;

//Display results from C# Component
for (stringCount=0; stringCount<myCompCS.Count;!
stringCount++)
{
listBox1.Items.Add(myCompCS.GetString(stringCount));
}

//Display results from Visual Basic Component
for (stringCount=0; stringCount<myCompVB.Count;!
stringCount++)
{
listBox1.Items.Add(myCompVB.GetString(stringCount));
}

3. In the form, double-click the Close button to create a button-click event
handler.
4. In the Close button event handler, call the Close method to close the form.
5. Compile and run the application. When you click the Execute button, the
list box should be filled with four C# strings and four Visual Basic strings.

Testing the ASP.NET Client
To test the ASP.NET page, the following software must be installed on the test
computer:
! Microsoft Internet Information Services (IIS)
! The .NET Framework common language runtime
! The .aspx file that contains the client code
! All compiled components that are required by the client application

You must configure a virtual directory that points to the directory that contains
the .aspx file. You can use the New Directory Wizard in the IIS snap-in to do
this.
vi Module 3: Working with Components


! To configure a virtual directory
1. Click the Start menu, click Control Panel, click Performance and
Maintenance, click Administrative Tools, and then click Internet
Information Services.
2. Expand the computer icon, expand the Web Sites folder, and then expand
and select Default Web Site.
3. On the Action menu, point to New, and then click Virtual Directory.
The Virtual Directory Creation Wizard is launched.
4. Click Next to continue.
5. In the Alias text box, type Test and click Next.
6. Browse to the <install folder>\Labs\Lab03.3\SOLUTION\ASP.NET_Client
folder that contains the file ClientASP.NET.aspx, click OK, and then click
Next.
7. On the Access Permissions page, accept the default selections, click Next,
and then click Finish.
The Test folder is added to the Default Web Site.
Ensure that the compiled component DLLs, CompCS.dll and CompVB.dll,
are in a \Bin subdirectory under the starting point for the application virtual
directory.

Module 3: Working with Components vii


! To test the ASP.NET Client
Open Microsoft Internet Explorer, and type the following text in the
Address bar:
http://localhost/Test/ClientASP.NET.aspx
When you press ENTER, the following display, or one similar to it, appears.

Multimedia
This section lists the multimedia items that are part of this module. Instructions
for launching and playing the multimedia are included with the relevant slides.
ASP.NET Execution Model
This animation will show students how ASP.NET pages are processed on the
server.
viii Module 3: Working with Components


Module Strategy
Use the following strategy to present this module:
! An Introduction to Key .NET Framework Technologies
Briefly introduce Windows Forms, Web Forms, and XML Web services.
Explain that you will show an example of a Windows Form in this module.
Tell students that they will learn about XML Web services in Module 13,
Remoting and XML Web Services, in Course 2349B, Programming with
the Microsoft .NET Framework (Microsoft Visual C# .NET).
! Creating a Simple .NET Framework Component
Show the basic approach to creating reusable classes by using C# and
Visual Basic. Spend some time on the new structured exception handling
techniques. Use the command line to compile the component.
Break the lecture at this point and instruct students to do Lab 3.1, Creating a
.NET Framework Component.
! Creating a Simple Console Client
Show how to write a simple console application that calls the .NET
Framework runtime-compatible component that was created in Creating a
Simple .NET Framework Component. Be sure to fully explain how to use a
namespace alias to remove ambiguity to type references.
Instruct students to do Lab 3.2, Creating a Simple Console-Based Client.
! Creating an ASP.NET Client
Emphasize the .NET Frameworks capability to execute the component
code that was created in Creating a Simple .NET Framework Component in
a variety of environments. Contrast the use of the component by the stand-
alone client applications with that of an IIS ASP.NET page.
Be sure to present the ASP.NET Execution Model animation to help
students understand how ASP.NET pages are processed on the server.
Instructions for running the animation are included in the Instructor Notes in
the margins.
The main objective of this section is to show students how to use an
ASP.NET page to obtain the string data from the .NET Framework
component, to format that data in HTML, and to return this HTML to a Web
browser for display.
Be sure to demonstrate how to test the ASP.NET page that was created in
Creating an ASP.NET Client in this module. Students must understand how
to configure a virtual directory that points to the directory that contains the
.aspx file.
Instruct students to do Lab 3.3, Calling a Component Through an ASP.NET
Page.

Module 3: Working with Components 1


Overview
! An Introduction to Key .NET Framework
Development Technologies
! Creating a Simple .NET Framework Component
! Creating a Simple Console Client
! Creating an ASP.NET Client

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this module, you will learn how to create a small client/server application in
C#. The steps that are necessary to construct, compile, and run each program
are covered in detail. You will also learn how to build the client application by
using the Windows Forms library and an ASP.NET page.
As in Module 2, Introduction to a Managed Execution Environment in
Course 2349B, Programming with the Microsoft .NET Framework (Microsoft
Visual C#

.NET), the content of this module is based on the Introduction to


Developing with the .NET Framework tutorial in the .NET Framework
Software Development Kit (SDK).
After completing this module, you will be able to:
! Create a simple Microsoft .NET Framework component in C#.
! Implement structured exception handling.
! Create a simple .NET Framework console application that calls a
component.
! Create a .NET Framework client application by using the Windows Forms
library.
! Create an ASP.NET page that uses the previously developed .NET
Framework component to create an ASP.NET application.

Topic Objective
To provide an overview of
the module topics and
objectives.
Lead-in
In this module, you will learn
how to create a small
client/server application.
For Your Information
Tell students that the code
examples used in this
module are from the tutorial,
Introduction to Developing
with the .NET Framework.
This tutorial is found in the
.NET Framework Software
Development Kit (SDK).
2 Module 3: Working with Components


An Introduction to Key .NET Framework Development
Technologies
! Windows Forms
! Web Forms
! XML Web Services

*****************************ILLEGAL FOR NON-TRAINER USE******************************
To develop applications in the .NET Framework, you should be familiar with
the following technologies: Windows Forms, ASP.NET Web Forms, and XML
Web services. Your choice of technology, or technologies, depends on the type
of software solution that you wish to implement. This topic briefly introduces
the technologies and provides references to additional information.
Windows Forms
Windows Forms are used to develop applications in which the client computer
handles most of the application processing. Classic Microsoft Win32 desktop
applications, such as drawing and graphics applications, data-entry systems,
point-of-sale systems, and games, are well suited to Window Forms. All of
these applications rely on the power of the desktop computer for processing and
for high-performance content display.
The System.Windows.Forms namespace contains the classes used to create
Windows Forms.
Topic Objective
To introduce the
technologies that are
used to develop .NET
Framework applications.
Lead-in
To develop applications in
the .NET Framework, you
should be familiar with
Windows Forms, ASP.NET
Web Forms, and XML Web
services.
Module 3: Working with Components 3


Web Forms
ASP.NET Web Forms are used to create applications in which the primary user
interface is a browser. Obviously, you can use Web Forms to create
applications that are available on the World Wide Web, such as e-commerce
applications, but you will also find them helpful for creating other types of
applications, such as intranet applications, which users can run by using only
their browser, which is already installed on their computers.
Because Web Forms applications are platform-independent, users can interact
with your application regardless of the type of browser or computer that they
are using. You can also optimize Web Forms applications to use features that
are built into the most recent browsers, such as Dynamic Hypertext Markup
Language (DHTML) and HTML 4.0, which enhance performance and
responsiveness.
The System.Web namespace contains the classes used to create Web Forms.
For more information about Windows Forms and ASP.NET Web Forms, see
Windows Forms and Web Forms Recommendations in the .NET Framework
SDK documentation.
XML Web Services
An XML Web Service is a programmable entity that resides on a Web server
and is exposed through standard Internet protocols. In many respects, the
programming model for creating and using XML Web services is similar to the
programming model for creating and using COM-based components.
However, unlike COM, the XML Web services programming model is based
on simple, open standards that are broadly supported. Instead of using binary
communication methods between applications, XML Web services use
communication based on the SOAP protocol to transport XML messages
between applications. In an environment of integrated, programmable XML
Web services, XML is the universal language for communication of raw data
and information that can be understood and acted upon.
For more information about XML Web services, see Module 13, Remoting
and XML Web Services in Course 2349B, Programming with the Microsoft
.NET Framework (Microsoft Visual C# .NET).
4 Module 3: Working with Components


" "" " Creating a Simple .NET Framework Component
! Using Namespaces and Declaring the Class
! Creating the Class Implementation
! Implementing Structured Exception Handling
! Creating a Property
! Compiling the Component

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this section, you will learn how to create a simple component using C#. The
component provides a wrapper for an array of strings and includes a GetString
method that takes an integer and returns a string. The component includes a
read-only Count property that contains the number of elements in the string
array, which is used to iterate over all of the array members. The GetString
method also demonstrates the use of structured exception handling.
In subsequent sections of this module, you will learn how clients use the
GetString method and the Count property to determine and display the output
from the components string array.
The simple string component that is described in this section shows the basic
approach to creating reusable classes in C#.
Topic Objective
To provide an overview of
the section topics.
Lead-in
In this section, you will learn
how to use C# to create a
simple component.
Delivery Tip
If you prefer, you can use
Notepad to demonstrate the
code in this section, instead
of discussing the content of
each slide.
Module 3: Working with Components 5


Using Namespaces and Declaring the Class
! Create a New Namespace
! Declare the Class
using System;
namespace CompCS {...}
using System;
namespace CompCS {...}
public class StringComponent {...}
public class StringComponent {...}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
To create a new namespace that encapsulates the classes that you will be
creating, you use the namespace statement, as in the following example:
using System;

namespace CompCS {...}

The following statement denotes that instances of the StringComponent class
will now be created by the runtime and managed in the garbage-collected heap.
public class StringComponent {...}

Topic Objective
To explain how to create a
new namespace that
encapsulates classes.
Lead-in
To create a new namespace
that encapsulates the
classes that you will be
creating, you use the
namespace statement.
6 Module 3: Working with Components


Creating the Class Implementation
! Declare a Private Field of Type Array of String Elements
! Create a Public Default Constructor
! Assign the stringSet Field to an Array of Strings
stringSet = new string[] {
"C# String 0",
"C# String 1",
...
};
stringSet = new string[] {
"C# String 0",
"C# String 1",
...
};
private string[] stringSet;
private string[] stringSet;
public StringComponent() {...}
public StringComponent() {...}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The component provides a wrapper for an array of strings. You can declare a
private field of type array of string elements, as in the following example:
private string[ ] stringSet;

The following example shows how to create a public default constructor, which
executes each time a new instance of the class is created, and how to assign the
stringSet field to an array of strings:
public StringComponent() {
stringSet = new string[ ] {
"C# String 0",
"C# String 1",
"C# String 2",
"C# String 3"
};
}

In the preceding example, you can see that the constructor has the same name as
the class and does not have a return type.
Topic Objective
To explain how to create the
class implementation.
Lead-in
The component provides a
wrapper for an array of
strings.
Module 3: Working with Components 7


Implementing Structured Exception Handling
! Implement the GetString Method
! Create and Throw a New Object of Type
IndexOutOfRangeException
! Exceptions Caught by the Caller Using a try/catch/finally Statement
! Structured Exception Handling Replaces HRESULT-Based Error
Handling in COM
public string GetString(int index) {...}
public string GetString(int index) {...}
if((index < 0) || (index >= stringSet.Length)) {
throw new IndexOutOfRangeException();
}
return stringSet[index];
if((index < 0) || (index >= stringSet.Length)) {
throw new IndexOutOfRangeException();
}
return stringSet[index];

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Programs must be able to uniformly handle errors and exceptions that occur
during execution. The common language runtime helps you design error-
tolerant software by providing a platform for notifying programs of errors in a
uniform manner. All .NET Framework methods indicate failure by throwing
exceptions.
Traditionally, a languages error-handling model relied on either the languages
unique way of detecting errors and locating handlers for them, or on the error-
handling mechanism that is provided by the operating system. The runtime
implements exception handling with the following features:
! It handles exceptions without regard for the language that generates the
exception or the language that will be called upon to handles the exception.
! It does not require any particular language syntax for handling exceptions,
but allows each language to define its own syntax.
! It allows exceptions to be thrown across process boundaries and machine
boundaries.

Exceptions offer several advantages over other methods of error notification.
Failures do not go unnoticed. Invalid values do not continue to propagate
through the system. You do not have to check return codes. Exception-handling
code can be easily added to increase program reliability. Finally, the runtimes
exception handling is faster than Windows-based C++ error handling.
Topic Objective
To explain how to
implement structured
exception handling.
Lead-in
The component uses the
GetString method to return
the strings in the stringSet
array.
8 Module 3: Working with Components


The component uses the GetString method to return the strings in the stringSet
array. You can use a throw statement to implement structured exception
handling within the GetString method, as in the following example:
public string GetString(int index) {
if ((index < 0) || (index >= stringSet.Length)) {
throw new IndexOutOfRangeException();
}
return stringSet[index];
}

In the preceding example, the program evaluates whether the integer that is
passed as an argument to GetString is a valid index for the stringSet array. If
the index is invalid, the following statement creates and throws a new object of
type IndexOutOfRangeException:
throw new IndexOutOfRangeException();

If the index is valid, the program returns the string element of the stringSet
array at that particular index.
Exceptions may be caught by the caller by using a try/catch/finally statement.
Place the sections of code that might throw exceptions in a try block and place
code that handles exceptions in a catch block. You can also write a finally block
that always runs regardless of how the try block runs. The finally block is
useful for cleaning up resources after a try block. For example, in C#:
try {
// code that might throw exceptions
}
catch(Exception e) {
// place code that handles exceptions
}
finally {
// place code that runs after try or catch runs
}

In general, it is good programming practice to catch a specific type of exception
rather than using the preceding general catch statement that catches any
exception.
Structured exception handling replaces the HRESULT-based error-handling
system that is used in COM. All .NET Framework exception-handling classes,
such as IndexOutOfRangeException and user-defined exceptions, must derive
from System.Exception.
For more information about exception handling, see Handling and Throwing
Exceptions in the .NET Framework SDK documentation.
Module 3: Working with Components 9


Creating a Property
! Create a Read-Only Count Property to Get the Number
of String Elements in the stringSet Array
public int Count {
get { return stringSet.Length; }
}
public int Count {
get { return stringSet.Length; }
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
To provide the number of string elements in the stringSet array, you create a
read-only property called Count, as in the following example:
public int Count {
get { return stringSet.Length; }
}

Topic Objective
To explain how to create a
property.
Lead-in
To provide the number of
string elements in the
stringSet array in C#, you
create a read-only property
called Count.
For Your Information
Point out that the GetString
method and Count property
may be used by a client to
iterate over the components
string elements. This
information will help
students when they create
the clients in subsequent
labs.
10 Module 3: Working with Components


Compiling the Component
! Use the /target:library Switch to Create a DLL
# Otherwise, an executable with a .dll file extension is
created instead of a DLL library
csc /out:CompCS.dll /target:library CompCS.cs
csc /out:CompCS.dll /target:library CompCS.cs

*****************************ILLEGAL FOR NON-TRAINER USE******************************
To compile the source code for the C# component, first you must save the
source file, and then enter the following command from a command prompt
window:

To use Microsoft Visual Studio .NET tools within a command
prompt window, the command prompt window must have the proper
environment settings. The Visual Studio .NET Command Prompt window
provides such an environment. To run a Visual Studio .NET Command Prompt
window, click Start, All Programs, Microsoft Visual Studio .NET,
Visual Studio .NET Tools, and Visual Studio .NET Command Prompt.

csc /out:CompCS.dll /target:library CompCS.cs

This command directs the compiler to produce the file CompCS.dll. The
/target:library switch is required to actually create a DLL library, instead of an
executable.
Topic Objective
To explain how to compile
the source code for a
component.
Lead-in
To compile the source code,
first you must save the
source file.
Important
Module 3: Working with Components 11


Lab 3.1: Creating a .NET Framework Component

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objective
After completing this lab, you will be able to create a simple .NET Framework
component in C#.
Lab Setup
Only solution files are associated with this lab. The solution files for this lab are
in the folder <install folder>\Labs\Lab03.1\Solution.
Scenario
This lab is based on a small client server application scenario in which you
write both the client application and server component using C#.
This lab focuses on building the server component.
Lab 3.2, Creating a Simple Console-Based Client, focuses on building a
client program that calls the server component.
Estimated time to complete this lab: 15 minutes
Topic Objective
To introduce the lab.
Lead-in
In this lab, you will learn
how to create a simple
.NET Framework
component in C#.
For Your Information
In this introductory module,
the lab problems have
intentionally been made
very similar to the problems
presented in the module.
This approach is designed
to allow the student to
experience a wide range of
.NET software approaches
within a very limited amount
of time.
12 Module 3: Working with Components


Exercise 1
Creating a Component in C#
In this exercise, you will use C# to create a component that provides a wrapper
for a private array of strings. The wrapper includes a read-only Count property,
which returns the number of strings in the array, and a public GetString
method, which takes an integer argument and returns the string in the private
array at that index. The GetString method will also implement structured
exception handling to ensure that the argument of the index is within range of
the array.
! Specify namespace information and class declarations
1. Open Notepad and reference the System namespace.
2. Create a new namespace named CompCS.
CompCS will contain the class for your component.

! Create the class implementation
1. Create a public class named StringComponent.
2. Declare a private field named StringSet of type array of string elements.
3. Create a public default constructor.
The default constructor takes no arguments.
4. Assign to the field StringSet an array of strings initialized with the
following four strings:
"C# String 0",
"C# String 1",
"C# String 2",
"C# String 3"

5. Create the public GetString method, which takes an integer as an argument.
That integer must be a valid index for the StringSet array.
a. If the integer is an invalid index, throw an index out of range exception.
b. If the integer is a valid index, return the StringSet string at that index.
6. Create a read-only Count property that gets the number of string elements
in the StringSet array.
7. Save the file as CompCS.cs in the <install folder>\Labs\Lab03.1 folder.

Module 3: Working with Components 13


! Compile the source code

To use Microsoft Visual Studio .NET tools within a command
prompt window, the command prompt window must have the proper
environment settings. The Visual Studio .NET Command Prompt window
provides such an environment. To run a Visual Studio .NET Command Prompt
window, click Start, All Programs, Microsoft Visual Studio .NET,
Visual Studio .NET Tools, and Visual Studio .NET Command Prompt.

From a Visual Studio .NET command prompt window, enter the command
to build a library named CompCS.dll from the CompCS.cs. source. You
must specify that the target is a library.

Important
14 Module 3: Working with Components


" "" " Creating a Simple Console Client
! Using the Libraries
! Instantiating the Component
! Calling the Component
! Building the Client

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this section, you will learn how to write a simple console application that
calls the .NET Framework runtime-compatible component that was created in
the previous section.
Topic Objective
To provide an overview of
the section topics.
Lead-in
In this section, you will learn
how to write a simple
console application that
calls a .NET Framework
runtime-compatible
component.
Module 3: Working with Components 15


Using the Libraries
! Reference Types Without Having to Fully Qualify the
Type Name
! If Multiple Namespaces Contain the Same Type Name,
Create a Namespace Alias to Remove Ambiguity
using CompCS;
using CompVB;
using CompCS;
using CompVB;
using CSStringComp = CompCS.StringComponent;
using VBStringComp = CompVB.StringComponent;
using CSStringComp = CompCS.StringComponent;
using VBStringComp = CompVB.StringComponent;

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This topic explains how to reference types in your applications by using
namespaces and how to use a namespace alias if you need to remove ambiguity
to type references.
Using the Namespaces
By using the namespaces in the program, you can reference types in the library
without fully qualifying the type name.
To use the component namespaces in C#, you must enter a using statement
followed by the name of the component namespaces:
using CompCS;
using CompVB;

Using an Alias
Consider a scenario where similar C# and Microsoft Visual Basic components
use the same type name (StringComponent). You must still fully qualify the
type name to remove any ambiguity when referring to the GetString method
and the Count property. In C#, you can create and use aliases to solve this
problem.
The following C# example shows how to alias the component namespaces so
that you do not have to fully qualify the type names:
using CSStringComp = CompCS.StringComponent;
using VBStringComp = CompVB.StringComponent;

Topic Objective
To explain how to import
libraries and to use
namespace aliasing to
remove ambiguity.
Lead-in
By using the namespaces in
the program, you can
reference types in the library
without fully qualifying the
type name.
16 Module 3: Working with Components


Instantiating the Component
! Declare a Local Variable of Type StringComponent
! Create a New Instance of the StringComponent Class
//
using CSStringComp = CompCS.StringComponent;
//
CSStringComp myCSStringComp = new
CSStringComp();
//
using CSStringComp = CompCS.StringComponent;
//
CSStringComp myCSStringComp = new
CSStringComp();

*****************************ILLEGAL FOR NON-TRAINER USE******************************
To instantiate the StringComponent class, you declare a local variable of type
StringComponent and create a new instance of StringComponent, as in the
following example:
//
using CSStringComp = CompCS.StringComponent;

class MainApp
{
public static void Main() {

CSStringComp myCSStringComp = new CSStringComp();
//
}
}

Topic Objective
To describe how to
instantiate the component
in C#.
Lead-in
To instantiate the
StringComponent class,
you declare a local variable
of type StringComponent
and create a new instance
of StringComponent.
Module 3: Working with Components 17


Calling the Component
! Iterate over All the Members of StringComponent and
Output the Strings to the Console
for (int index = 0;
index < myCSStringComp.Count; index++) {
Console.WriteLine
(myCSStringComp.GetString(index));
}
for (int index = 0;
index < myCSStringComp.Count; index++) {
Console.WriteLine
(myCSStringComp.GetString(index));
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
After instantiating the StringComponent class, the client can iterate over the
string array in the component and return the appropriate output, as in the
following example:
using System;
using CSStringComp = CompCS.StringComponent;

class MainApp
{
public static void Main() {

CSStringComp myCSStringComp = new CSStringComp();
// Display result strings from CS component
Console.WriteLine("Strings from CS StringComponent");
for (int index = 0; index < myCSStringComp.Count;
index++) {
Console.WriteLine(myCSStringComp.GetString(index));
}
}
}

Topic Objective
To describe how to call a
component after it has been
instantiated.
Lead-in
After instantiating the
StringComponent class,
the client can iterate over
the string array in the C# or
Visual Basic component and
return the appropriate
output.
18 Module 3: Working with Components


Building the Client
! Use the /reference Switch to Reference the Assemblies
That Contain the StringComponent Class
csc /reference:CompCS.dll,CompVB.dll!
/out:ClientCS.exe ClientCS.cs
csc /reference:CompCS.dll,CompVB.dll!
/out:ClientCS.exe ClientCS.cs

*****************************ILLEGAL FOR NON-TRAINER USE******************************
To build the client, you can compile the source code from a command prompt
window. You must use the /reference option to reference all of the assemblies
that contain the StringComponent class, as in the following example:
csc /reference:CompCS.dll,CompVB.dll!
/out:ClientCS.exe ClientCS.cs

Running a client program that first iterates over the C# components strings and
then iterates over the Visual Basic components strings will produce the
following output:
Strings from C# StringComponent
C# String 0
C# String 1
C# String 2
C# String 3

Strings from VB StringComponent
VB String 0
VB String 1
VB String 2
VB String 3

Topic Objective
To show how to build the
client in C#.
Lead-in
To build the client, you can
compile the source code
from a command prompt
window.
Module 3: Working with Components 19


Lab 3.2: Creating a Simple Console-Based Client

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives
After completing this lab, you will be able to create a simple console-based
client application that calls a component.
Lab Setup
To complete this lab you will need the solution files from Lab 3.1 and a
Visual Basic component provided for you in the <install folder>\Labs\Lab03.2\
Starter\VB\Component folder. The solution files for this lab are located in
<install folder>\Labs\Lab03.2\Solution.
Scenario
This lab is based on a small client/server application scenario in which both the
client application and server component are written by using C#.
This lab focuses on building a simple console-based client application that calls
the component that was created in Lab 3.1, Creating a .NET Framework
Component. Also, another version of the component written in Visual Basic is
provided. The client will use both the C# and Visual Basic component versions
to show cross-language compatibility.
Estimated time to complete this lab: 15 minutes
Topic Objective
To introduce the lab.
Lead-in
In this lab, you will learn
how to create a simple
console-based client
application that calls a
component.
20 Module 3: Working with Components


Exercise 1
Creating a Client Application in C#
In this exercise, you will create a simple console application to call the C#
component that you created in Lab 3.1, Creating a .NET Framework
Component, and a Visual Basic component that is already provided. The
Visual Basic component provides the same StringComponent class as the C#
component.
! Specify namespace information
1. Open Notepad and import the System namespace.
2. Use the namespace of the C# component that you created in Lab 3.1,
Creating a .NET Framework Component.
3. Use the namespace of the Visual Basic component. The namespace of the
Visual Basic component is CompVB.
Because you are importing two libraries that contain the same type name but
have different namespaces, use an alias directive to allow for unambiguous
references to StringComponent classes.
4. Create a class in C# called MainApp.
5. Specify the program entry point.
The entry point takes no arguments and does not return a value.

! Write the code to call the C# and Visual Basic components
1. Assign to a local variable named myCSStringComp a new instance of the
C# version of StringComponent.
2. Write the code to print the following string to the console:
Strings from C# StringComponent

3. Iterate over all of the members of the C# string component and output the
strings to the console.
4. Repeat steps 1 through 3 for the Visual Basic version of StringComponent.
Name the local variable myVBStringComp.
5. Save the file as ClientCS.cs in the <install folder>\Labs\Lab03.2 folder.

Module 3: Working with Components 21


! Compile and run the program

To use Microsoft Visual Studio .NET tools within a command
prompt window, the command prompt window must have the proper
environment settings. The Visual Studio .NET Command Prompt window
provides such an environment. To run a Visual Studio .NET Command Prompt
window, click Start, All Programs, Microsoft Visual Studio .NET,
Visual Studio .NET Tools, and Visual Studio .NET Command Prompt.

1. From a Visual Studio .NET command prompt window, enter the command
to build the executable program ClientCS.exe from ClientCS.cs. You must
reference the assemblies that contain the C# and Visual Basic
StringComponent class. You should copy these assemblies into the same
directory as ClientCS.cs. The Visual Basic assembly is located in
<install folder>\Labs\Lab03.2\Starter\VB\Component. The C# assembly is
located in <install folder>\Labs\Lab03.1\Solution\C#\Component.
2. Run the resulting executable program.
Your C# program should generate the following output:
Strings from C# StringComponent
C# String 0
C# String 1
C# String 2
C# String 3

Strings from VB StringComponent
VB String 0
VB String 1
VB String 2
VB String 3


Important
22 Module 3: Working with Components


Demonstration: Creating a Windows Forms Client

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In previous sections of this course, all examples have been command-line
programs that wrote to the system console. In this demonstration, you will learn
how to rewrite the client application so it can use the new .NET Windows
Forms library, which is available to all .NET Framework runtime-compatible
languages. In this demonstration, the client displays in a list box on a form the
string output that was displayed in the system console in previous examples.
Topic Objective
To demonstrate how to
create a Windows Forms
client.
Lead-in
In this demonstration, you
will see how to create a
Windows Forms client.
Module 3: Working with Components 23


using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

using CSStringComp = CompCS.StringComponent;
using VBStringComp = CompVB.StringComponent;

namespace WinForm_Client
{
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.ListBox listBox1;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components =
null;

public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

(Code continued on the following page.)
24 Module 3: Working with Components


#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.listBox1 = new System.Windows.Forms.ListBox();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(24,
232);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(64, 24);
this.button1.TabIndex = 0;
this.button1.Text = "&Execute";
this.button1.Click += new
System.EventHandler(this.button1_Click);
//
// button2
//
this.button2.Location = new
System.Drawing.Point(192, 232);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(64, 24);
this.button2.TabIndex = 1;
this.button2.Text = "&Close";
this.button2.Click += new
System.EventHandler(this.button2_Click);
//
// listBox1
//
this.listBox1.Location = new System.Drawing.Point(8,
8);
this.listBox1.Name = "listBox1";
this.listBox1.Size = new System.Drawing.Size(264,
199);
this.listBox1.TabIndex = 2;
//
(Code continued on the following page.)
Module 3: Working with Components 25


// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5,
13);
this.ClientSize = new System.Drawing.Size(292, 273);
this.Controls.AddRange(new
System.Windows.Forms.Control[] {
this.listBox1,
this.button2,
this.button1});
this.Name = "Form1";
this.Text = "Client";
this.ResumeLayout(false);

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}

private void button1_Click(object sender,
System.EventArgs e)
{
//Local Variables
CSStringComp myCompCS = new CSStringComp();
VBStringComp myCompVB = new VBStringComp();
int stringCount=0;

//Display results from C# Component
for (stringCount=0;
stringCount<myCompCS.Count;stringCount++)
{
listBox1.Items.Add(
myCompCS.GetString(stringCount));
}

(Code continued on the following page.)
26 Module 3: Working with Components


//Display results from Visual Basic Component
for (stringCount=0;
stringCount<myCompVB.Count;stringCount++)
{
listBox1.Items.Add(
myCompVB.GetString(stringCount));
}
}

private void button2_Click(object sender,
System.EventArgs e)
{
Close();
}
}
}
Module 3: Working with Components 27


" "" " Creating an ASP.NET Client
! Writing the HTML for the ASP.NET Application
! Coding the Page_Load Event Handler
! Generating the HTML Response

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this module, you have learned how to create two stand-alone versions of a
client application that calls the simple string component: a console application
and a form based on the Windows Forms library.
Another important feature of the .NET Framework is the capability to execute
the component code that was used by such stand-alone client applications in a
Microsoft Internet Information Services (IIS) ASP.NET page.
In this section, you will learn how to use an ASP.NET page to obtain the string
data from the .NET Framework component, to format that data in HTML, and
to return this HTML to a Web browser for display.
Topic Objective
To provide an overview of
the section topics.
Lead-in
In this section, you will learn
how to use an ASP.NET
page to obtain the string
data from the .NET
Framework component, to
format the data in HTML,
and to return this HTML to a
Web browser for display.
28 Module 3: Working with Components


Multimedia: ASP.NET Execution Model

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The .NET Framework provides the capability to execute code that was used in
the preceding stand-alone client applications in an ASP.NET page. However,
there are some differences in the code execution processes.
An ASP.NET page generally produces HTML in response to a HTTP request;
and the page itself is compiled dynamically, unlike the stand-alone client
applications. Each ASP.NET page is individually parsed, and the syntax is
checked. Then, a .NET runtime class is produced, compiled, and invoked.
ASP.NET caches the compiled object, so subsequent requests do not perform
the parse and compile step and thus execute much faster.
In this animation, you will see how ASP.NET pages are processed on the
server. To view the animation, open the 2349B_mod3.htm file from the Media
folder.

Topic Objective
To describe the ASP.NET
execution model.
Lead-in
In this animation, you will
see how ASP.NET pages
are processed on the
server.
To launch the animation,
click the button in the lower-
left corner of the slide. To
play the animation, click the
Start button, and then click
the First Request, Second
Request, and Output Cache
buttons at the top of the
screen. There is no audio,
but explanatory text appears
in the Info column when you
click one of the buttons.
Tell students that they can
view the animation again
later for themselves by
opening the
2349B_mod3.htm file from
the Media folder.
Module 3: Working with Components 29


Writing the HTML for the ASP.NET Application
! Specify Page-Specific Attributes Within a Page Directive
! Import the Namespace and the Physical Assembly
! Specify Code Declaration Blocks
<%@ Page Language="C#" Description="ASP.NET Client" %>
<%@ Page Language="C#" Description="ASP.NET Client" %>
<%@ Import Namespace="CompCS"%>
<%@ Import Namespace="CompVB"%>
<%@ Import Namespace="CompCS"%>
<%@ Import Namespace="CompVB"%>
<script language="C#" runat=server>
...
</script>
<script language="C#" runat=server>
...
</script>

*****************************ILLEGAL FOR NON-TRAINER USE******************************
An ASP.NET file is a text file that contains markup syntax for coding server-
side page logic, dynamic output, and literal content. By default, ASP.NET files
have an .aspx extension or .ascx for user controls. However, ASP.NET will
parse and compile any file that is mapped to the aspnet_isapi.dll under IIS.
Specifying @ Page Directives
To create the ASP.NET page that calls the string component, you first specify
page-specific attributes within a page directive. Page directives specify optional
settings that are used by the page compiler when processing ASP.NET files, as
shown in the following example:
<%@ Page Language="C#" Description="ASP.NET Client" %>

The following table describes the two @ Page directive attributes that are used
in the sample client ASP.NET page.
Attribute Description

Language Language that is used when compiling all <% %> and <%= %> blocks
within a page. Can be Visual Basic, C#, or Microsoft JScript .NET.
Description Provides a text description of the page. Supports any string description.

Topic Objective
To describe how to write the
HTML for an ASP.NET
application.
Lead-in
An ASP.NET file is a text file
that contains markup syntax
for coding server-side page
logic, dynamic output, and
literal content.
30 Module 3: Working with Components


Specifying @ Import Directives
After specifying an @ Page directive, you include an @ Import directive,
which explicitly imports a namespace into a page, as in the following example:
<%@ Import Namespace="CompCS"%>
<%@ Import Namespace="CompVB"%>

When you use an @ Import directive to import a namespace into a page, all
classes and interfaces of the imported namespace are made available to the
page. The imported namespace can be part of the .NET Framework class library
or a user-defined namespace. In addition, the @ Import directive specifies the
name of the assembly that will be used. The assembly must be located in the
\Bin subdirectory of the applications starting point.
Specifying Code Declaration Blocks
After specifying @ Page directives and importing the required namespaces, you
add code declaration blocks in which you place the code that calls the string
component in the ASP.NET page.
You define code declaration blocks by using <script> tags that contain a runat
attribute, which tells the server to execute the code on the server, instead of
sending the code text back to the client as part of the HTML stream. The script
element may optionally use a language attribute to specify the language of its
inner code, as in the following example:
<html>
...
<script language="C#" runat=server>
...
</script>
...
</html>

If you do not specify a language, ASP.NET defaults to the language that was
configured for the base page, which is determined by the @ Page directive.
For more information about directives in ASP.NET, see Directive Syntax in
the .NET Framework SDK documentation.
Module 3: Working with Components 31


Coding the Page_Load Event Handler
void Page_Load(Object sender, EventArgs EvArgs)
{
StringBuilder Out = new StringBuilder("");
int Count = 0;
// Iterate over component's strings and concatenate
Out.Append("Strings from C# Component<br>");
CompCS.StringComponent myCSStringComp = new
CompCS.StringComponent();
for(int index = 0; index < myCSStringComp.Count;
index++)
{
Out.Append(myCSStringComp.GetString(index));
Out.Append("<br>");
}
//
Message.InnerHtml = Out.ToString();
}
void Page_Load(Object sender, EventArgs EvArgs)
{
StringBuilder Out = new StringBuilder("");
int Count = 0;
// Iterate over component's strings and concatenate
Out.Append("Strings from C# Component<br>");
CompCS.StringComponent myCSStringComp = new
CompCS.StringComponent();
for(int index = 0; index < myCSStringComp.Count;
index++)
{
Out.Append(myCSStringComp.GetString(index));
Out.Append("<br>");
}
//
Message.InnerHtml = Out.ToString();
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The Page_Load event contains most of the code for the sample client program.
The following example shows the Page_Load event-handling code:
void Page_Load(Object sender, EventArgs EvArgs)
{
StringBuilder Out = new StringBuilder("");
int Count = 0;

// Iterate over component's strings and concatenate
Out.Append("Strings from C# Component<br>");
CompCS.StringComponent myCSStringComp = new
CompCS.StringComponent();

for(int index = 0; index < myCSStringComp.Count; index++)
{
Out.Append(myCSStringComp.GetString(index));
Out.Append("<br>");
}
Out.Append("<br>");

// Iterate over component's strings and concatenate
Out.Append("Strings from VB Component<br>");
CompVB.StringComponent myVBStringComp = new
CompVB.StringComponent();

for(int index = 0; index < myVBStringComp.Count; index++)
{
Out.Append(myVBStringComp.GetString(index));
Out.Append("<br>");
}
Message.InnerHtml = Out.ToString();
}

Topic Objective
To explain how to code the
Page_Load event handler.
Lead-in
The Page_Load event
contains most of the code
for the sample client
program.
32 Module 3: Working with Components


Notice that the client code that calls the string component is very similar to the
code for the stand-alone console and Windows Forms-based applications. After
declaring the Page_Load event handler and initializing variables, you create a
new instance of the StringComponent class and iterate over the string array.
Each of the strings is appended to the variable Out. You then assign Out to a
property of an HTML server control, as in the following example:
Message.InnerHTML = Out.ToString();

Instead of assigning Out to a property of an HTML server control, you can use
Response.Write to write the stream directly into the HTML stream.

It is a recommended practice with ASP.NET forms to place the
executable code in a separate file from the declarative HTML. However, for the
sake of simplicity, the code for the ASP.NET page in this module has not been
placed in a separate file.

Note
Module 3: Working with Components 33


Generating the HTML Response
! Specify the Body of the HTML Response
<body>
<span id="Message" runat=server/>
</body>
<body>
<span id="Message" runat=server/>
</body>

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The following example shows how to create the HTML response by using the
Message object:
<body>
<span id="Message" runat=server/>
</body>

This code places the string output that was generated by the Page_Load event
handler in the HTTP response.
Topic Objective
To show how to generate
the HTML response by
using the Message object.
Lead-in
The following example
shows how to create the
HTML response by using
the Message object.
34 Module 3: Working with Components


The code for the final ASP.NET page is as follows:
<%@ Page Language="C#" Description="ASP.NET Component Test" %>
<%@ Import Namespace="CompCS"%>
<%@ Import Namespace="CompVB"%>
<%@ Import Namespace="System.Text"%>

<html>
<script language="C#" runat=server>
void Page_Load(Object sender, EventArgs EvArgs) {
StringBuilder Out = new StringBuilder();
int Count = 0;

// Iterate over component's strings and concatenate
Out.Append("Strings from C# Component<br>");
CompCS.StringComponent myCSStringComp = new
CompCS.StringComponent();
for (int index = 0; index < myCSStringComp.Count; index++){
Out.Append(myCSStringComp.GetString(index));
Out.Append("<br>");
}
Out.Append("<br>");

// Iterate over component's strings and concatenate
Out.Append("Strings from VB Component<br>");
CompVB.StringComponent myVBStringComp = new
CompVB.StringComponent();
for (int index = 0; index < myVBStringComp.Count; index++){
Out.Append(myVBStringComp.GetString(index));
Out.Append("<br>");
}

Message.InnerHtml = Out.ToString();
}
</script>
<body>
<span id="Message" runat=server/>
</body>
</html>

Module 3: Working with Components 35


Demonstration: Testing the ASP.NET Client

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This demonstration shows how to test the ASP.NET page that was created in
Creating an ASP.NET Client in this module. Before testing the page, you must
first configure a virtual directory that points to the directory that contains the
.aspx file. You can use the New Directory Wizard in the IIS snap-in to do this.
Topic Objective
To demonstrate how to test
the ASP.NET page.
Lead-in
In this demonstration, you
will see how to test the
ASP.NET page.
For Your Information
For instructions on running
this demonstration, see the
Instructor Notes. This exact
procedure is repeated in
Lab 3.3, Calling a
Component Through an
ASP.NET Page.
36 Module 3: Working with Components


Lab 3.3: Calling a Component Through an ASP.NET Page

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives
After completing this lab, you will be able to create an ASP.NET page that uses
a previously developed .NET Framework component to create an ASP.NET
application.
Lab Setup
Starter and solution files are associated with this lab. The starter files are in the
folder <install folder>\Labs\Lab03.3\Starter, and the solution files are in the
folder <install folder>\Labs\Lab03.3\Solution.
Scenario
The labs associated with Module 3 are based on a small client/server
application scenario in which a .NET Framework component may be used in
several different client scenarios. The component may be written in any .NET
Framework runtime-compatible language, but you will only create a C#
component.
Lab 3.2, Creating a Simple Console-Based Client, and Lab 3.3, Calling a
Component Through an ASP.NET Page, demonstrate how the same
component code runs unchanged in different client environments.
This lab focuses on using an ASP.NET page to call the component and display
the results in a Web browser.
Estimated time to complete this lab: 30 minutes
Topic Objective
To introduce the lab.
Lead-in
In this lab, you will learn
how to create an ASP.NET
page that uses a previously
developed .NET Framework
component to create an
ASP.NET application.
Module 3: Working with Components 37


Exercise 1
Writing the ASP.NET Page
In this exercise, you will create an ASP.NET page that uses the same
StringComponent component that was used in the stand-alone client
application that you created in Lab 3.2, Creating a Simple Console-Based
Client. You will build a string object that holds the results of processing the
string array in the StringComponent class and generates output that can be
displayed in a Web browser.
! Create an ASP.NET page
1. Open Notepad and enter an @ Page directive that specifies the following
page attributes:
a. Language = C#
b. Description = ASP.NET Component Test
2. Import the following namespaces into the page:
a. CompCS
b. CompVB
c. System.Text
3. Create HTML script that specifies C# as the script language and uses the
runat=server directive.

! Call the StringComponent class
1. Create a handler for the Page_Load event and initialize a local variable
called Out of type StringBuilder to represent the string array in the
StringComponent class.
StringBuilder Out = new StringBuilder("");

2. Declare a local variable called Count of type int to obtain the number of
string elements in the string array.
3. Call the Append method on the Out variable with the following string that
has as it final characters an HTML line break, "<br>":
"Strings from C# Component<br> "
4. Assign to a local variable called myCSStringComp a new instance of the
C# version of the StringComponent class.
5. Iterate over all the strings of the C# string component. In each iteration,
append the components string to the string variable named Out and then
append an HTML line break, "<br>".
6. Append the string "<br>Strings from the VB Component<br>" to the Out
string and repeat steps 4 and 5 for the Visual Basic version of the
StringComponent class. The local variable name is myVBStringComp.
7. Assign the result of processing the string array to an HTML server control
so that the output is dynamically generated on a Web page. Use the
ToString method to get the output string from the Out variable.
8. Save the file as ClientASP.NET.aspx in the <install folder>\Labs\
Lab03.3\Starter\ASP.NET_Client folder.

38 Module 3: Working with Components


Exercise 2
Building and Testing the ASP.NET Page
In this exercise, you will configure a virtual directory to point to a directory
where the ASP.NET page is located, so that you can test the ASP.NET
application that is a client of the previously created string component.
! Configure a virtual directory
1. Click Start, Control Panel, Performance and Maintenance,
Administrative Tools, and then click Internet Information Services.
2. Expand the computer icon, expand the Web Sites folder, and then expand
and select Default Web Site.
3. Click the Action menu, point to New, and then click Virtual Directory.
The Virtual Directory Creation Wizard starts.
4. Click Next to continue.
5. In the Alias text box, type Test and click Next.
6. Browse to the <install folder>\Labs\Lab03.3\Starter\ASP.NET_Client
folder that contains the file ClientASP.NET.aspx that you created in
Exercise 1, click OK, and then click Next.
7. On the Access Permissions page, accept the default selections, click Next,
and then click Finish.
The Test folder is added to the Default Web Site.

Module 3: Working with Components 39


! Test the ASP.NET Client
1. Ensure that the compiled component DLLs, CompCS.dll and CompVB.dll,
are in a \Bin subdirectory under the starting point for the application virtual
directory.
2. Open Microsoft Internet Explorer, in the Address bar, type
http://localhost/Test/ClientASP.NET.aspx and then press ENTER.
A dialog box that generates the correct string output should appear, as
shown in the following illustration:


40 Module 3: Working with Components


Review
! An Introduction to Key .NET Framework Development
Technologies
! Creating a Simple .NET Framework Component
! Creating a Simple Console Client
! Creating an ASP.NET Client

*****************************ILLEGAL FOR NON-TRAINER USE******************************
1. How do .NET methods indicate failure?
All .NET Framework methods indicate failure by throwing exceptions.


2. What namespace contains the classes to create a Windows Form
application?
The Windows Forms library is located in the System.Windows.Forms
namespace.


3. What is the default extension for ASP.NET pages?
By default, ASP.NET files have an .aspx extension or .ascx for user
controls.




Topic Objective
To reinforce module
objectives by reviewing
key points.
Lead-in
The review questions cover
some of the key concepts
taught in the module.








Contents
Overview 1
Introduction to Application Deployment 2
Application Deployment Scenarios 7
Related Topics and Tools 31
Lab 4: Packaging and Deployment 37
Review 42

Module 4: Deployment
and Versioning


Information in this document, including URL and other Internet Web site references, is subject to
change without notice. Unless otherwise noted, the example companies, organizations, products,
domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious,
and no association with any real company, organization, product, domain name, e-mail address,
logo, person, place or event is intended or should be inferred. Complying with all applicable
copyright laws is the responsibility of the user. Without limiting the rights under copyright, no
part of this document may be reproduced, stored in or introduced into a retrieval system, or
transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or
otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual
property rights covering subject matter in this document. Except as expressly provided in any
written license agreement from Microsoft, the furnishing of this document does not give you any
license to these patents, trademarks, copyrights, or other intellectual property.

2001-2002 Microsoft Corporation. All rights reserved.

Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint,
Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and
Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A.
and/or other countries.

The names of actual companies and products mentioned herein may be the trademarks of their
respective owners.



Module 4: Deployment and Versioning iii


Instructor Notes
After completing this module, students will be able to:
! Package and deploy simple and componentized applications.
! Create strong-named assemblies.
! Install and remove assemblies from the global assembly cache.
! Configure an application to control its binding to an assembly based on the
assemblys location and version data.

Materials and Preparation
This section provides the materials and preparation tasks that you need to teach
this module.
Required Materials
To teach this module, you need the Microsoft PowerPoint file 2349B_04.ppt.
Preparation Tasks
To prepare for this module, you should:
! Read all of the materials for this module.
! Complete the lab.

Presentation:
90 Minutes

Lab:
50 Minutes
iv Module 4: Deployment and Versioning


Module Strategy
Use the following strategy to present this module:
! Introduction to Application Deployment
Introduce common concepts, such as the Microsoft .NET Framework
hierarchy of namespaces, the organization of assemblies, and the role of the
assembly manifest. Describe simple and componentized applications, and
their configuration and distribution scenarios.
! Application Deployment Scenarios
The examples in this section show how to deploy a simple stand-alone
application, an application that uses a shared assembly, and an application
that makes use of assembly versioning.
Introduce compapp, a componentized application, which uses multiple
assemblies. Contrast compapp with the simple single assembly Hello
World application that was created in Module 2, Introduction to a
Managed Execution Environment, in Course 2349B, Programming with
the Microsoft .NET Framework (Microsoft Visual C#

.NET).
! Related Topics and Tools
Briefly introduce additional topics that are related to deployment and
versioning but are beyond the scope of this course. This section also
provides a list of tools that you can use to work with assemblies.
You should just inform the students of these topics and encourage them to
look for more information in the .NET Framework Software Development
Kit (SDK) documentation. In addition, Course 2350A, Securing and
Deploying Microsoft .NET Assemblies (Prerelease), covers code access
security and role-based security in greater detail.

Module 4: Deployment and Versioning 1


Overview
! Introduction to Application Deployment
! Application Deployment Scenarios
! Related Topics and Tools

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This module introduces the concepts of packaging and deployment of
Microsoft .NET Framework applications and assemblies. The .NET
Framework provides improved isolation of application assemblies, simplified
application deployment, and robust versioning.
This module walks you through the packaging and deployment of a simple
Hello World application, and a small, componentized application. These
applications are written in C#, the new language designed for the .NET
Framework. The steps that are necessary to construct, compile, and run C#
applications were explained in Module 2, Introduction to a Managed
Execution Environment, and Module 3, Working with Components, both in
Course 2349B, Programming with the Microsoft .NET Framework (Microsoft
Visual C#

.NET).
Because this course is an introduction to programming in the .NET Framework,
you should spend some time reading the .NET Framework Software
Development Kit (SDK) documentation. In fact, the labs, demonstrations, and
material for this module and other modules in this course are based on several
tutorials in the .NET Framework SDK.
After completing this module, you will be able to:
! Package and deploy simple and componentized applications.
! Create strong-named assemblies.
! Install and remove assemblies from the global assembly cache.
! Configure an application to control its binding to an assembly based on the
assemblys location and version data.

Topic Objective
To provide an overview of
the module topics and
objectives.
Lead-in
In this module, you will learn
about packaging and
deployment of Microsoft
.NET Framework
applications and
assemblies.
2 Module 4: Deployment and Versioning


" "" " Introduction to Application Deployment
! Common Concepts
! Simple Applications
! Componentized Applications
! Configuration and Distribution

*****************************ILLEGAL FOR NON-TRAINER USE******************************
You can deploy a .NET Framework application in several ways depending on
the following considerations:
! The complexity of the application
! The sharing of assemblies with other applications
! The applications security and protection requirements
! The applications method of distribution

An applications deployment is not affected by the .NET Framework common
language runtime-compatible language that is used to develop the application.
All applications that are written for use with the .NET Framework are compiled
to the same self-describing, Microsoft intermediate language (MSIL) code and
run with he same .NET Framework runtime.
This section introduces common concepts, such as the .NET Framework
hierarchy of namespaces, the organization of assemblies, and the role of the
assembly manifest. It then describes simple and componentized applications,
and their configuration and distribution.
Topic Objective
To introduce issues that
arise in application
deployment and to introduce
the topics in the section.
Lead-in
You can deploy a .NET
Framework application in
several ways.
Module 4: Deployment and Versioning 3


Common Concepts
! Classes and Types Used in .NET Framework Applications Are:
# Organized in a hierarchy of namespaces
# Stored in PE files, such as DLLs and EXEs
# Fully described by metadata
! Assemblies:
# Are made up of one or more PE files
# Contain a manifest that identifies the assembly and its files
# Specify exported and imported classes and types
# Are units of deployment, reuse, and versioning

*****************************ILLEGAL FOR NON-TRAINER USE******************************
As you learned in Module 2, Introduction to a Managed Execution
Environment, in Course 2349B, Programming with the Microsoft .NET
Framework (Microsoft Visual C# .NET), in addition to the common language
runtime, the .NET Framework provides a common class library that is
organized into a hierarchical tree of namespaces.
At the root of this hierarchy is the System namespace, which contains objects
for many other useful classes that can be used from any .NET-compatible
language. These objects include objects that are used for file I/O, messaging,
networking, and security.
The Organization of PE Files into Assemblies
The .NET Framework class library that you and others create are also organized
into hierarchical namespaces and stored in portable executable (PE) files, most
typically DLLs and EXEs. A single PE file can contain several namespaces,
including nested namespaces. You also can split a namespace across multiple
PE files. One or more PE files, and possibly non-PE files, such as resources, are
combined to create an assembly, which is a physical unit that can be deployed,
versioned, and reused.
The Role of the Assembly Manifest
In the .NET Framework, each class type is fully described through the types
metadata. Each assembly contains a manifest that includes the name of each
type that is exported from the assembly, along with information about the file
that contains that types metadata. The manifest also includes information about
the identity of the assembly, such as name, files that make up the assembly, and
version information, and full information about any dependencies on other
assemblies.
The .NET Framework runtime uses assembly manifests to locate and bind to
the referenced types.
Topic Objective
To review the organization
of the .NET Framework
class library and
assemblies, and the role of
the assembly manifest.
Lead-in
The .NET Framework
provides a common class
library that is organized into
a hierarchical tree of
namespaces.
For Your Information
This topic reviews
information that was
covered in Module 2. Do not
spend much time on this
topic. Present the material
as the starting point for a
discussion of deployment
and versioning.
4 Module 4: Deployment and Versioning


Simple Applications
! Require .NET Runtime Be Installed on Local Computer
! Can Be Run Directly from a File Server or Copied
Locally
! Make No Registry Entries
! Cannot Break Another Application
# Eliminate DLL Hell
! Can Be Uninstalled by Deleting Locally Copied File(s)

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In the simplest case, a .NET Framework application can be executed locally on
any computer on which the .NET runtime is already installed. The program can
run directly from a file server, or the files can be copied locally. Nothing else is
required. No registry entries are made, and no other applications are broken or
caused to stop running as a result.
The fact that no registry entries are made eliminates DLL versioning issues,
commonly referred to as DLL hell. Just deleting the executable file, if it was
copied locally, is sufficient to remove the application and leave no trace on the
computer.
Topic Objective
To describe how easily an
application can be executed
in the .NET Framework.
Lead-in
In the simplest case, a .NET
Framework application can
be executed locally on any
computer on which the .NET
runtime is already installed.
Module 4: Deployment and Versioning 5


Componentized Applications
! Assemblies Private to an Application
# Same as a simple application
! Assemblies Private to Related Applications
# Deployed into a common subdirectory
! Assemblies Shared with Other Unrelated Applications
# Require a strong name and version information
# Deployed into the global assembly cache

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Componentized applications are only slightly more complex than simple
applications. The complexity of componentized applications depends on
whether their components are:
! Contained in assemblies that are private to the application.
! Shared with other related applications.
! Shared with other potentially unknown applications.

If all of the component assemblies are private, the componentized application
can be treated in the same manner as the application. The application can run
from a file server, or the application files can be copied to a local volume.
Deleting all of a componentized applications files is sufficient to remove the
program.
Likewise, if several related applications use the same component assemblies,
those assemblies can be located in a common subdirectory. However, if the
application uses assemblies that are shared with other unrelated applications,
these assemblies can be installed in the global assembly cache and have certain
properties, such as a unique strong name that includes version information, that
enable the .NET runtime to ensure that the application binds to the appropriate
versions.
Topic Objective
To describe how assemblies
are handled in
componentized applications.
Lead-in
Componentized applications
are only slightly more
complex than applications.
6 Module 4: Deployment and Versioning


Configuration and Distribution
! Configuration
# Maintained in plain-text files
! Deployment
# Common distribution formats, such as a .CAB file
or a .MSI file
# Common distribution mechanisms, such as
Windows 2000 IntelliMirror or Microsoft Systems
Management Server

*****************************ILLEGAL FOR NON-TRAINER USE******************************
As a developer of .NET Framework applications, you should be aware of the
different ways in which applications may be configured and packaged for
distribution. Usually, individual organizations, or an administrator within an
organization, decide how an application is packaged for distribution and
configured for the organization.
Configuration
In the .NET Framework, you can maintain application configuration in plain
text files. This use of plain text XML-based application configuration files
allows administrators to tailor an applications behavior on a particular
computer without having to involve developers in the process.
The following section, Application Deployment Scenarios, presents several
common application scenarios. While this module does not cover ASP.NET
deployment, most of the concepts that are presented in this module apply to
ASP.NET deployment.
Deployment
Many client applications may be further packaged in a common distribution
format, such as a .CAB file or .MSI file. Client applications may also be
installed by using application distribution mechanisms, such as Microsoft
Windows 2000 IntelliMirror or Microsoft Systems Management Server
(SMS), which both use the Microsoft Windows Installer technology.
The Microsoft Windows Installer is an installation and configuration service
that is included in the Windows 2000 operating system. It is provided in a
service pack to Microsoft Windows 95, Microsoft Windows 98, and Microsoft
Windows NT version 4.0.
For more information about the Microsoft Windows Installer, see the Platform
SDK documentation.
Topic Objective
To introduce options for
configuring and deploying
applications in the .NET
Framework.
Lead-in
As a developer of .NET
Framework applications,
you should be aware of the
different ways in which
applications may be
configured and packaged for
distribution.
Module 4: Deployment and Versioning 7


" "" " Application Deployment Scenarios
! A Simple Application
! A Componentized Application
! Specifying a Path for Private Assemblies
! A Strong-Named Assembly
! Deploying Shared Components
! A Versioned Assembly
! Creating Multiple Versions of a Strong-Named Assembly
! Binding Policy
! Deploying Multiple Versions of a Strong-Named Assembly

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The examples in this section show how to deploy a simple application, an
application that uses a shared assembly, and an application that makes use of
assembly versioning.
Topic Objective
To introduce the topics in
the section.
Lead-in
This section shows how to
deploy a variety of
applications, from simple
applications to applications
that make use of assembly
versioning.
8 Module 4: Deployment and Versioning


A Simple Application
! Use the Microsoft Intermediate Language Disassembler
(Ildasm.exe) to Examine the Assembly Manifest
# Version information
# Imported types
# Exported types
! Deploy the Application by:
# Running the executable file directly from a file server, or
# Installing it locally by copying the file
# Uninstall the application by deleting the file

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In Module 2, Introduction to a Managed Execution Environment, in Course
2349B, Programming with the Microsoft .NET Framework (Microsoft
Visual C# .NET), you looked at the simplest .NET Framework application, the
traditional Hello World application written in C#. The C# source code for that
program, HelloDemoCS, is as follows:
using System;

class MainApp {
public static void Main() {
Console.WriteLine("Hello World using C#!");
}
}

This simple executable file prints a single line to the System.Console, a type
that is contained in the .NET Framework class library. It does not reference any
other libraries and does not itself produce a library.
Topic Objective
To explain how a simple
application is deployed.
Lead-in
In Module 2, you looked at
the simplest .NET
Framework application, the
traditional Hello World
application written in C#.
For Your Information
Point out that while Hello
World is simplistic, it is
extremely versatile in its
ability to easily demonstrate
many new concepts of the
.NET Framework, including
deployment and versioning.
Module 4: Deployment and Versioning 9


Using Microsoft Intermediate Language Disassembler to
Examine Hello World
Compiling this small application generates the HelloDemoCS.exe.

To use Microsoft Visual Studio .NET tools within a command
prompt window, the command prompt window must have the proper
environment settings. The Visual Studio .NET Command Prompt window
provides such an environment. To run a Visual Studio .NET Command Prompt
window, click Start, All Programs, Microsoft Visual Studio .NET,
Visual Studio .NET Tools, and Visual Studio .NET Command Prompt.

If you run the Microsoft intermediate language (MSIL) disassembler
(Ildasm.exe) against this executable file, a window appears, similar to the
following illustration.

The simple Hello World application highlights a particularly important concept
behind programming for the .NET Framework. The application is clearly self-
describing: all of the information that is needed to understand the application is
contained in the metadata.
The MSIL disassembler shows the classes or types that are created within the
application. In the case of the Hello World application, the only class is
MainApp. The MSIL disassembler also shows the methods Main and a default
constructor, indicated by .ctor. The program does not have any other members.
To save information about the assembly to a file, use the Dump command on
the File menu.
Important
10 Module 4: Deployment and Versioning


To see additional information about the application, double-click Manifest. The
following window appears.

The preceding illustration shows the manifest, which contains information
about the assembly, including the version (not yet set), external libraries, and
types within those libraries, which the application uses.
Deployment
Deployment to computers on which the .NET runtime is installed is a simple
process. The Hello World application can run directly from a file server. More
advanced programs may involve security issues.
In the simple Hello World case, no files are placed on the workstation, no
entries are made in the system registry, and, in effect, there is no affect on the
client workstation. There is also nothing to clean up because there is nothing to
remove on the client workstation.
As you would expect, HelloDemoCS.exe can also be copied to a local volume.
In this scenario, you can remove the program by deleting the file, and as before,
nothing would remain on the workstation.
Whether you run HelloDemoCS.exe from a file server or copy it to a local
volume, running this application will not break another program, and no other
application can cause HelloDemoCS.exe to stop functioning.
Module 4: Deployment and Versioning 11


A Componentized Application
! Assembly Component to Be Used by Application
# Assembly Stringer.dll is built from Stringer.cs as follows:
! Client Needs to Reference Assembly
! Deployment by File Server or Local Copy
csc /target:library Stringer.cs
csc /target:library Stringer.cs
csc /reference:Stringer.dll Client.cs
csc /reference:Stringer.dll Client.cs

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The Hello World application that is discussed in the preceding topic is
completely trivial and hardly representative of even the simplest real-world
application. This topic covers compapp, a componentized application, which
uses multiple assemblies.
Topic Objective
To introduce compapp, a
componentized application,
which uses multiple
assemblies.
Lead-in
The Hello World application
that is discussed in the
preceding topic is
completely trivial and hardly
representative of even the
simplest real-world
application.
12 Module 4: Deployment and Versioning


Creating Stringer.dll
The client, Client.exe, calls types that are contained in a component assembly
that is named Stringer (Stringer.dll). The source code for the Stringer
assembly, is located in Stringer.cs:
using System;

namespace org {
public class Stringer {
private string[] StringSet;

public Stringer() {
StringSet = new string[] {
"C# String 0",
"C# String 1",
"C# String 2",
"C# String 3"
};
}

public string GetString(int index) {
if ((index < 0) || (index >= StringSet.Length)) {
throw new IndexOutOfRangeException();
}
return StringSet[index];
}

public int Count {
get { return StringSet.Length; }
}
}
}

Module 4: Deployment and Versioning 13


A client application that uses this component could fully qualify a reference to
the Stringer class, for instance, as org.Stringer. Alternatively, a client
application could include a using statement to allow easy access to the types in
Stringer.dll by specifying the namespace, as shown in the Client.cs source code:
using System;
using org;

class MainApp {
public static void Main() {
Stringer myStringComp = new Stringer();
string[] StringsSet = new string[4];
// Iterate over component's strings
Console.WriteLine("Strings from StringComponent");
for (int index = 0; index < myStringComp.Count; index++)
{
StringsSet[index] = myStringComp.GetString(index);
Console.WriteLine(StringsSet[index]);
}
// ...
}
}

Building the Application
To build this componentized application, you first build the Stringer.dll
assembly component. Then, you build Client.exe, importing the Stringer
component by using the name of the file that contains the manifest, Stringer.dll,
rather than the namespace name, which is org in this case.
csc /target:library Stringer.cs
csc /reference:Stringer.dll Client.cs

The MSIL disassembler displays the Stringer.dll and shows all of the members,
as in the following illustration.

14 Module 4: Deployment and Versioning


Like HelloDemoCS.exe, Client.exe contains manifest information about itself,
the System library, and the types that the application uses. However, the
manifest now contains information about the Stringer assembly.
A MSIL disassembler display of the manifest for Client.exe shows the reference
to the Stringer assembly, as in the following illustration.

Deployment
Like the simple application, Client.exe can run directly from a file server on any
workstation that has the .NET runtime installed. Client.exe and Stringer.dll can
also be copied to a local volume. To remove the application, you need only
delete the two files.
Module 4: Deployment and Versioning 15


Specifying a Path for Private Assemblies
! Specifying a Directory From Which to Load Private
Assemblies.
# Client.exe.config file specifies a privatePath tag
! Configuration Files XML Tags Are Case-Sensitive
<configuration>
<runtime>
<assemblyBinding
xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath=MyStringer"/>
</assemblyBinding>
</runtime>
</configuration>
<configuration>
<runtime>
<assemblyBinding
xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath=MyStringer"/>
</assemblyBinding>
</runtime>
</configuration>

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The preceding Client.exe example has one important weakness: both Client.exe
and Stringer.dll reside in the same directory. In the real world, an administrator
may wish to use a directory structure to manage assemblies. The .NET
Framework provides a configuration mechanism that allows administrators to
specify a directory from which to load private assemblies.
Locating Stringer.dll and Client.exe in Separate
Directories
Using the preceding Client example, all of the source code is the same, but for
illustration purposes the build process has been modified so that the component
applications Stringer.dll is built in its own subdirectory named MyStringer.
cd \compapp
csc /target:library /out:MyStringer\Stringer.dll!
MyStringer\Stringer.cs
csc /reference:MyStringer\Stringer.dll Client.cs

Using a Configuration File to Locate Assemblies at Run
Time
Although the /reference: compile option locates an assembly in a directory
when compiling the program, a separate XML-based application configuration
file is required at run time to support assemblies that are located in other
directories. For client executable files like the examples that are covered in this
module, the configuration file resides in the same directory as the executable
file. The configuration file has the complete name of the executable file with an
additional extension of .config.
Topic Objective
To show how to use a
configuration file to specify a
path for private assemblies.
Lead-in
The .NET Framework
provides a configuration
mechanism that allows
administrators to specify a
directory from which to load
private assemblies.
16 Module 4: Deployment and Versioning


The Client.exe configuration file is called Client.exe.config. It specifies a
privatePath attribute, as shown in the following example:
<configuration>
<runtime>
<assemblyBinding
xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="MyStringer"/>
</assemblyBinding>
</runtime>
</configuration>

The configuration files XML tags are case-sensitive.

When this configuration file is placed in the same directory as the executable
file, at run time, the .NET Framework uses the privatePath attribute to
determine where to look for componentsin addition to looking in the
application directory.

When loading an assembly, the runtime also searches for a private path
that is equal to the name of the assembly. If the Stringer.dll assembly was
located in a subdirectory named Stringer, instead of MyStringer, then a
configuration file would be unnecessary.

Deployment
Like the simple application, the revised Client.exe can run directly from a file
server on any workstation that has the .NET runtime installed. Client.exe,
Stringer.dll, and the applications .config file can also be copied to a local
volume, using the same relative directory structure. Deleting the files and
directory effectively removes the application.
While they are not used in the preceding example, it is important to know that
in addition to application configuration files, the .NET Framework also
supports separate user and Computer Configuration Files for many common
configuration settings.
Important
Note
Module 4: Deployment and Versioning 17


A Strong-Named Assembly
! Global Assembly Cache
# Contains assemblies shared by unrelated applications
! Strong Names Are Required for Assemblies in the Cache
# Generate a public-private key pair:
# Add code to source file to specify version and key information:
# Compile:
#if STRONG
[assembly: System.Reflection.AssemblyVersion("1.0.0.0")]
[assembly: System.Reflection.AssemblyKeyFile("orgKey.snk")]
#endif
#if STRONG
[assembly: System.Reflection.AssemblyVersion("1.0.0.0")]
[assembly: System.Reflection.AssemblyKeyFile("orgKey.snk")]
#endif
csc /define:STRONG /target:library /out:AReverser.dll!
AReverser.cs
csc /define:STRONG /target:library /out:AReverser.dll!
AReverser.cs
sn k orgKey.snk
sn k orgKey.snk

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The client applications discussed in the preceding topics show the basics for
constructing a complex program, but they are limited in that they only illustrate
the use of assemblies that are private to the Client.exe and do not demonstrate
the use of assemblies that are shared by more than one application.
This topic discusses the problems that may occur when multiple applications
share components. It then introduces the use of strong names, the .NET
Framework solution to these problems.
Issues with Sharing Components
Many applications use assemblies that are shared by more than one application.
These shared assemblies, which are typically provided by third-party
developers, are installed in a common location on the system, the global
assembly cache. By default, the system looks for each applications assemblies
in the global assembly cache.
In classic COM and COM+ applications, the sharing mechanism depends
heavily on the Windows System Registry in which information about each
component, including its version and physical file location, is stored. This
mechanism allows multiple applications to share a single component, but it also
allows a component of a newly installed application to replace an existing
component and possibly cause other applications to break.
Such overwriting of existing components is often difficult to detect because the
application that is causing the problem appears to work properly.
Topic Objective
To discuss the problems
that may occur when
multiple applications share
components, and to
introduce the use of strong
names.
Lead-in
The client applications
discussed in the preceding
topics are limited in that they
only illustrate the use of
assemblies that are private
to the Client.exe and do not
demonstrate the use of
assemblies that are shared
by more than one
application.
18 Module 4: Deployment and Versioning


Strong Names
In the .NET Framework, you can solve the problems that are caused by the
sharing of components with multiple applications by more strongly associating
a distinct build of a component assembly with the client application. A distinct
build is indicated by a combination of a version number and a special value that
is called the publicKeyToken.

Strong names provide a strong integrity check. Passing the .NET
Framework security checks guarantees that the contents of the assembly have
not been changed since it was built. Note, however, that strong names in and of
themselves do not imply a level of trust, such as that provided by a digital
signature and supporting certificate.

When component assemblies are associated with a distinct build, the system can
isolate these component assemblies, thus allowing different versions to run at
the same time for different client applications. This system of protection is
sometimes called side-by-side execution. It differs from backward compatibility
because with side-by-side execution, applications can run alongside other
versions of the same applications without affecting their respective execution
environments.
You can facilitate side-by-side execution by assigning strong names to your
assemblies. A strong name consists of the assemblys identity, which includes
its simple text name, version number, and culture information, if provided, and
a public key.
To demonstrate these additional build attributes the client and component
scenario is extended by adding an assembly named AReverser. This assembly
contains a class with a method that is named Invert, which uses the
System.Array.Reverse method to reverse an array of strings.

The code details of AReverser are not relevant to deployment issues and
therefore are not shown. This modules lab contains the complete code details.

Building the Assembly Without a Strong Name
You begin by building the new component assembly without specifying any
options to make it have a strong name. First, you compile the new AReverser
assembly, as follows:
csc /target:library /out:AReverser.dll AReverser.cs

After compiling the new AReverser assembly, you can examine the metadata
by using the MSIL disassembler. The MSIL disassembler indicates that the
assembly does not have an established version number, as shown in the
following code:
.assembly AReverser
{ ...
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module AReverser.dll

Important
Note
Module 4: Deployment and Versioning 19


Generating a Public-Private Key Pair
To create an assembly with a strong name, you must compile the assembly by
using a private key. Public keys are used for verification. Therefore, before
compiling the assembly, you must first generate a public-private key pair. You
use the Strong Name tool (Sn.exe) to generate a new key pair and place them in
a file, as in the following example:
sn k orgKey.snk

Compiling a Strong-Named Assembly
Now that you have a private key, you are ready to compile the component,
specifying the key file and the version number to be assigned. You can do this
by specifying AssemblyVersion and AssemblyKeyFile attributes in the
AReverser.cs file using the STRONG conditional compilation symbol:
#if STRONG
[assembly: System.Reflection.AssemblyVersion("1.0.0.0")]
[assembly: System.Reflection.AssemblyKeyFile("orgKey.snk")]
#endif

You must then define STRONG in the compile process:
csc /define:STRONG /target:library /out:AReverser.dll!
AReverser.cs

If you run the MSIL disassembler again on AReverser.dll, you can verify that
the assembly is now strong-named from the presence of a .publickey property
and a non-default version, which is specified by the .ver property of 1:0:0:0.
The following example shows the disassembled code:
.assembly AReverser
{
...
.publickey = (00 ... 71 8A 7D 6A D7 )
.hash algorithm 0x00008004
.ver 1:0:0:0
}
.module AReverser.dll

For more information about the Strong Name tool, see Packaging and
Deployment Tools in this module.
20 Module 4: Deployment and Versioning


Deploying Shared Components
! Installing the Strong-Named Component in the Global
Assembly Cache
! Examining the Global Assembly Cache
! Removing a Shared Component File
gacutil /i AReverser.dll
gacutil /i AReverser.dll
gacutil /l
gacutil /l
gacutil /u AReverser
gacutil /u AReverser

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Deployment with strong-named assemblies is usually more complicated than
deployment with assemblies that are not strong-named. While components can
easily be shared by related applications by putting them in a common
subdirectory, shared assemblies that are used by many applications on the
system are often stored in the systems global assembly cache.
As with the previous examples of componentized applications, this revised
Client.exe can run directly from a file server on any workstation on which the
.NET runtime is installed. The Client.exe and Stringer.dll files can also be
copied to a local volume.
Installing a Strong-Named Assembly into the Global
Assembly Cache
Installing the strong-named component into the global assembly cache requires
the following additional command on the computer that will be running the
corresponding Client.exe program:
gacutil /i AReverser.dll

You must have Administrator privileges on a computer to install
assemblies into the global assembly cache. Assemblies that are installed in the
global assembly cache must have a strong name.

After installing the AReverser assembly, you can then examine the system
assembly cache by typing:
gacutil /l

Topic Objective
To explain the commands
that are used to install,
examine, and remove
assemblies from the global
assembly cache.
Lead-in
While components can
easily be shared by related
applications by putting them
in a common subdirectory,
shared assemblies that are
used by many applications
on the system are often
stored in the systems global
assembly cache.
Note
Module 4: Deployment and Versioning 21


The output of this command is:
...
AReverser, Version=1.0.0.0, Culture=neutral, PublicKeyToken=!
0588613cb04b772e, Custom=null
...

You can also examine the system assembly cache by navigating to the
\WindowsDirectory\Assembly directory and by using the cache shell extension.
Uninstalling a Strong-Named Assembly from the System
Cache
Cleaning up applications that use strong-named assemblies requires more work
than cleaning up the simple applications or componentized applications that are
described in the preceding topics. In addition to deleting the executable files,
you should remove the shared component file from the global assembly cache
since the global assembly cache is not automatically scavenged.
To remove a shared component file, an administrator can use the cache shell
extension, select the appropriate components, and delete them. Developers and
administrators who want to automate the process, however, should use the
command-line interface to the assembly cache manager as follows:
gacutil /u AReverser

To confirm the removal, you can view the contents of the global assembly
cache by using the following command:
gacutil /l

For more information about installing and uninstalling shared assemblies to the
global assembly cache, see Packaging and Deployment Tools in this module.
22 Module 4: Deployment and Versioning


A Versioned Assembly
! Applications Need to Bind to a Suitable Version of a
Shared Assembly
! The Version Number Is Represented by a 4-Part Number
! Applications Get the Versions of Assemblies with Which
They Were Built and Tested
# Unless overridden by explicit policy rules
<major version>.<minor version>.<revision>.<build number>
<major version>.<minor version>.<revision>.<build number>

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The final packaging and deployment example in this module involves updating
the shared assembly to a new version. In Creating Multiple Versions of a
Strong-Named Assembly in this module, the shared assembly is updated to
deliberately break compatibility with the client and to demonstrate how the
.NET Framework allows you to configure the client application to bind to the
desired version of the shared assembly.
Versioning
Each assembly has a specific compatibility version number as part of its
identity. As such, two assemblies that differ by compatibility version are
completely different assemblies as far as the .NET runtime class loader is
concerned.
This compatibility version number is physically represented as a four-part
number with the following format:
<major version>.<minor version>.<revision>.<build number>

Topic Objective
To discuss the physical
representation and logical
mapping of the compatibility
version number.
Lead-in
The final packaging and
deployment example in this
module involves updating
the shared assembly to a
new version.
Module 4: Deployment and Versioning 23


Each segment of this number has a specific meaning to the .NET runtime as it
decides which version of an assembly to load. Logically, the compatibility
version number has three parts, with the following meanings:
1. Incompatible
A change has been made to the assembly that is known to be incompatible
with previous versions.
Example: Major new release of the product
2. Maybe Compatible
A change has been made to the assembly that is thought to be compatible
and carries less risk than an incompatible change. However, backward
compatibility is not guaranteed.
Example: Service Pack or release of a new daily build
3. Quick Fix Engineering (QFE)
An engineering fix that customers may want to upgrade to.
Example: An emergency security fix

These three logical parts correspond to the physical four-part version number as
follows:

For example, an assembly with compatibility version number 2.0.0.0 is
considered incompatible with an assembly whose compatibility number is
1.0.0.0. Compatibility number 2.0.2.11 is considered a QFE to compatibility
number 2.0.2.1.
24 Module 4: Deployment and Versioning


Creating Multiple Versions of a Strong-Named Assembly
! Building Two New Versions of AReverser with a New Key Pair
# Specify version 2.0.0.0 for one and 2.0.1.0 for the other
For AReverser_v2.0.0.0\AReverser.cs:
! Build
! Use Ildasm.exe to Examine Different Versions
# Note the publickey and version numbers
#if STRONG
[assembly: System.Reflection.AssemblyVersion("2.0.0.0")]
[assembly: System.Reflection.AssemblyKeyFile("orgVerKey.snk")]
#endif
#if STRONG
[assembly: System.Reflection.AssemblyVersion("2.0.0.0")]
[assembly: System.Reflection.AssemblyKeyFile("orgVerKey.snk")]
#endif
csc /define:STRONG /target:library!
/out:AReverser_v2.0.0.0\AReverser.dll!
AReverser_v2.0.0.0\AReverser.cs
csc /define:STRONG /target:library!
/out:AReverser_v2.0.0.0\AReverser.dll!
AReverser_v2.0.0.0\AReverser.cs
.assembly AReverser
{ ...
.publickey = (00 24 ... 82 B1 F2 A0 )
.hash algorithm 0x00008004
.ver 2:0:1:0
.assembly AReverser
{ ...
.publickey = (00 24 ... 82 B1 F2 A0 )
.hash algorithm 0x00008004
.ver 2:0:1:0

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In the .NET Framework, you can solve the problems that are inherent in
working with multiple versions of a shared component by using strong-named
assemblies. This topic examines how multiple versions of an assembly are
created.
Building on the code in the preceding strong-named AReverser assembly, two
separate versions of the strong-named component are created, and additional
application configuration options are used to show how an application can be
made to run. A method in version 2.0.1.0 of AReverser.dll is deliberately made
incompatible with the same method in version 2.0.0.0 so that a client that
successfully calls the method that is using version 2.0.0.0 will fail with the later
revision.
Versioning keys can change from one version of an assembly to the next. To
illustrate this, you generate a new key pair using the Strong Name (Sn.exe) tool
and place that new key pair in a file, as follows:
sn k orgVerKey.snk

After creating a new private key, you add the following to the AReverser.cs file
in the AReverser_v2.0.0.0 subdirectory:
#if STRONG
[assembly: System.Reflection.AssemblyVersion("2.0.0.0")]
[assembly: System.Reflection.AssemblyKeyFile("orgVerKey.snk")]
#endif

Topic Objective
To explain how to create
multiple versions of a
shared component by using
strong-named assemblies.
Lead-in
In the .NET Framework, you
can solve the problems that
are inherent in working with
multiple versions of a
shared component by using
strong-named assemblies.
Module 4: Deployment and Versioning 25


You add the following to the AReverser.cs file in the AReverser_v2.0.1.0
subdirectory:
#if STRONG
[assembly: System.Reflection.AssemblyVersion("2.0.1.0")]
[assembly: System.Reflection.AssemblyKeyFile("orgVerKey.snk")]
#endif

You compile both of these versions by using the commands:
csc /define:STRONG /target:library!
/out:AReverser_v2.0.0.0\AReverser.dll!
AReverser_v2.0.0.0\AReverser.cs
csc /define:STRONG /target:library!
/out:AReverser_v2.0.1.0\AReverser.dll!
AReverser_v2.0.1.0\AReverser.cs

If you run the MSIL disassembler on these two updated files, you can verify
that the assemblies are strong-named as indicated by the version number,
2.0.0.0 or 2.0.1.0, depending on which one you inspect with the MSIL
disassembler.
Notice that these two assemblies have the same publickey property, but the
value of this property is different than the previous version 1.0.0.0
AReverser.dll. This property is different because a different key pair was used,
orgVerKey.snk, instead of orgKey.snk.
The following example shows the MSIL disassembler output for version 2.0.1.0
of the AReverser.dll:
.assembly AReverser
{
...
.publickey = (00 24 ... 82 B1 F2 A0 )
.hash algorithm 0x00008004
.ver 2:0:1:0
}

26 Module 4: Deployment and Versioning


Binding Policy
! Policy Resolution
# Allows an assembly reference, specified at compile time, to be
modified after the application has been deployed without recompiling
! Happens in the Following Stages:
1. Application-policy resolution
2. Publisher-policy resolution
3. Administrator-policy resolution
! In Each Stage, an XML Configuration File Is Read
# Note: XML is case-sensitive
! Version Numbers of Assemblies That Are Not Strong-Named Are
Not Checked
! Configuration File Tag Examples
# privatePath
# bindingRedirect

*****************************ILLEGAL FOR NON-TRAINER USE******************************
When the .NET Framework is asked to bind to a specific version of a shared
assembly, the version of the assembly reference may be altered at several
policy-resolution stages before the .NET Framework finally decides which
version to bind to. This policy-resolution process allows an assembly reference,
which is specified at compile time, to be modified after the application has been
deployed, without recompiling the assemblies involved.
Policy resolution happens in the following three stages:
1. Application-policy resolution
2. Publisher-policy resolution
3. Administrator-policy resolution

In each stage, an XML configuration file that describes the policy is read. The
privatePath attribute in the application-configuration file illustrates the simplest
form of application-policy resolution. In addition, the bindingRedirect tag can
be used to redirect the reference to a different version of a shared assembly.

The version numbers of assemblies that are not strong-named are not
checked.

Topic Objective
To discuss binding policy.
Lead-in
Whenever the .NET
Framework is asked to bind
to a specific version of a
shared assembly, the
version of the assembly
reference may be altered at
several policy-resolution
stages before the .NET
Framework finally decides to
which version to bind.
Note
Module 4: Deployment and Versioning 27


Publisher-policy resolution allows shared-component vendors to make
compatibility statements among different revisions of their software. These per-
assembly configuration files are distributed as strong-named assemblies and are
installed into the global assembly cache as part of a service pack-style update.
Any binding redirects that are specified by the publisher-policy file are then
applied to the reference, which is then subject to administrator-policy
resolution. Because publisher-policy assemblies affect all applications on the
system, it is important that these assemblies are installed separately from the
application.
Administrator-policy resolution is the finaland the strongeststage in the
binding policy-resolution process. The administrator-policy file is located in the
WindowsDirectory\Microsoft.NET\Framework\v1.0.FinalBuildNumber\
CONFIG directory and is called Machine.config. This file has the same XML-
based schema as the policy files that are used in the two previous stages of
policy resolution. Administrator policy affects all assembly bindings that occur
on the system and can never be bypassed.
28 Module 4: Deployment and Versioning


Deploying Multiple Versions of a Strong-Named Assembly
! Install Both Versions of AReverser.dll into the Global Assembly
Cache
! Compile the VerClient Executable and Specify Version 2.0.0.0 of the
AReverser Component
! Use Version Policies to Control Assembly Binding at Run Time
gacutil /i AReverser_v2.0.0.0\AReverser.dll
gacutil /i AReverser_v2.0.1.0\AReverser.dll
gacutil /i AReverser_v2.0.0.0\AReverser.dll
gacutil /i AReverser_v2.0.1.0\AReverser.dll
csc /reference:MyStringer\Stringer.dll!
/reference:AReverser_v2.0.0.0\AReverser.dll VerClient.cs
csc /reference:MyStringer\Stringer.dll!
/reference:AReverser_v2.0.0.0\AReverser.dll VerClient.cs

*****************************ILLEGAL FOR NON-TRAINER USE******************************
After you have configured both versions of the AReverser assembly to use
strong names, you can deploy those versions.
To configure client applications to use strong-named assemblies of a specific
version, you must first install both 2.0 versions of AReverser.dll into the global
assembly cache as follows:
gacutil /i AReverser_v2.0.0.0\AReverser.dll
gacutil /i AReverser_v2.0.1.0\AReverser.dll

After installing these AReverser assemblies, you can then examine the global
assembly cache by using:
gacutil /l

You can also examine the system assembly cache by navigating to the
\WindowsDirectory\Assembly directory, and using the cache shell extension.
You now compile the VerClient executable file, for which you specify the
version 2.0.0.0 of the AReverser component, as shown in the following
example:
csc /reference:MyStringer\Stringer.dll!
/reference:AReverser_v2.0.0.0\AReverser.dll VerClient.cs

Topic Objective
To explain how to deploy
multiple versions of a
strong-named assembly.
Lead-in
After you have configured
both versions of the
AReverser assembly to use
strong names, you can
deploy those versions.
Module 4: Deployment and Versioning 29


Version Policies
All versioning of assemblies that use the common language runtime takes place
at the assembly level. The specific version of an assembly and the versions of
dependent assemblies are recorded in the assemblys manifest. The rules that
specify the acceptable versions of an assembly are called version policies.
Version policies are expressed in configuration files.
Using Configuration Files to Override Version Policy
According to the default version policy for the runtime, applications run only
with the versions that they were built and tested with unless these versions were
overridden by explicit version policy in configuration files. These configuration
files, which include the application configuration file, a publisher policy file,
and the machines administrator configuration file, provide the means for
overriding the version policy that is recorded in the assembly manifest.
Binding to a Specific Assembly Version
In a configuration file, you can specify a version policy that instructs the
runtime to bind to a specific version of an assembly that you want the
application to use. This specific version policy allows you to bind to a different
version of an assembly than the version that is recorded in the assembly
manifest of the calling assembly. In particular, the bindingRedirect tag can be
used to redirect the reference to a different version of a shared assembly, by
overriding the version in the original reference with this newer version.
The following option says that for any assembly reference from version 2.0.0.0
through 2.0.0.9, the version that should instead be used at run time is 2.0.1.0:
<bindingRedirect
oldVersion="2.0.0.0-2.0.0.9" newVersion="2.0.1.0"
/>

This option allows an administrator to reconfigure an application without
having to have it recompiled.
30 Module 4: Deployment and Versioning


The following sample VerClient.exe.config file instructs the runtime to look in
subdirectory MyStringer for assembly references and to bind to version 2.0.0.0
of AReverser:
<configuration>
<runtime>
<assemblyBinding
xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="MyStringer"/>
<publisherPolicy apply="no"/>
<dependentAssembly>
<assemblyIdentity name="AReverser"
publicKeyToken="52398B7804D8A95C"
culture=""/>
<publisherPolicy apply="no"/>
<bindingRedirect oldVersion="2.0.0.0"
newVersion="2.0.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

The publicKeyToken tags value can be obtained using the Sn.exe tool or
by examining the global assembly cache by using gacutil /l or the cache shell
extension.


The Sn.exe switches to obtain the publicKeyToken do not work
correctly in some of the earlier .NET Framework versions, such as build 9188.

Because a method of a type in version 2.0.1.0 of AReverser.dll was deliberately
made incompatible with the same method in version 2.0.0.0, a version 2.0.0.0-
compatible client that attempted to call this later revision would fail. If
VerClient.exe.config changed the line:
newVersion="2.0.0.0"/>

to:
newVersion="2.0.1.0"/>

The resulting error message would be similar to:
Unhandled Exception: System.MissingMethodException:!
Method not found: at MainApp.Main()

More typically, the configuration file mechanism allows an administrator to
repair an application so that it will continue to run even if it is broken by a
subsequent install of another application that used a different version of the
same shared component.
Finally, when you need to clean up the application, remove the shared
component files from the global assembly cache, as shown in the following
example:
gacutil /u AReverser
Note
Important
Key Points
Reinforce the fact that the
configuration files XML tags
are case-sensitive.
Module 4: Deployment and Versioning 31


" "" " Related Topics and Tools
! Related Topics
! Packaging and Deployment Tools

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This section briefly introduces additional topics that are related to deployment
and versioning, but are beyond the scope of this course. This section also
provides a list of tools that you can use to work with assemblies.
Topic Objective
To introduce the topics in
the section.
Lead-in
This section briefly
introduces additional topics
that are related to
deployment and versioning.
32 Module 4: Deployment and Versioning


Related Topics
! ASP.NET
! Assemblies
! Security
! Localization

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This module introduces the concepts of packaging and deployment of .NET
Framework applications and assemblies. Several related topics are worth
mentioning, and, when appropriate, references to additional information are
provided.
ASP.NET
This module focuses on packaging and deploying traditional client applications.
For more information about deploying ASP.NET applications to a Web server,
see the .NET Framework SDK documentation.
Assemblies
For more information about how the .NET Framework locates assemblies when
they are referenced at run time, see How the Runtime Locates Assemblies in
the .NET Framework SDK documentation. This topic covers the Assembly
Resolver, strong-named assemblies, application and administrator version
policies, codebase locations, and QFE updates.
Security
The.NET Framework offers code access security and role-based security to
address mobile code security concerns and to provide support that enables
components to determine what users are authorized to do. These security
mechanisms use a simple, consistent model so that developers who are familiar
with code access security can easily use role-based security and developers who
are familiar with role-based security can easily use code access security. Code
access security and role-based security are implemented by using a common
infrastructure that is supplied by the common language runtime.
Topic Objective
To briefly introduce several
related topics and provide
references for students to
follow up on.
Lead-in
This module introduces the
concepts of packaging and
deployment of .NET
Framework applications and
assemblies. Several related
topics are worth mentioning,
and, when appropriate,
references to additional
information are provided.
For Your Information
Do not spend much time on
this topic. The subject
matter is mostly beyond the
scope of the course. You
should just inform the
students of these topics and
encourage them to look for
more information in the
provided references.
Module 4: Deployment and Versioning 33


Code Access Security and Role-based Security
Code access security uses permissions to control the access that code has to
protected resources and operations. It helps to protect computer systems from
malicious mobile code and provides a way to allow mobile code to run safely.
Role-based security provides information that is needed to make decisions
about what a user is allowed to do. These decisions can be based on the users
identity, role membership, or both.
A more detailed discussion of security is outside the scope of this course. For
more information about security, see Securing Your Application in the .NET
Framework SDK documentation.
Additionally, Course 2350A, Securing and Deploying Microsoft .NET
Assemblies (Prerelease) covers code access security and role-based security in
greater detail.
Localization
To localize an application, you typically perform the following steps:
1. Separate your default resources from the code, and specify the localized text
in a text file.
2. Compile the text file into a .resources file.
3. Package your default resources in the main assembly by using the C#
compiler.
4. Create satellite resource assemblies, including satellites for .NET
Framework cultures, by using the Alink tool.
5. Deploy the satellites to a directory structure underneath the main
application.
6. Write the code to access the resources at run time.

For more information about localization, see the .NET Framework SDK
documentation.
34 Module 4: Deployment and Versioning


Packaging and Deployment Tools
! Assembly Linker (Al.exe)
! Global Assembly Cache tool (Gacutil.exe)
! MSIL Disassembler (Ildasm.exe)
! Strong Name (Sn.exe)
! Native Image Generator (Ngen.exe)
! Assembly Binding Log Viewer (Fuslogvw.exe)
! .NET Framework Configuration Tool (Mscorcfg.msc)
! Code Access Security Policy Tool (Caspol.exe)

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The .NET Framework SDK includes several useful tools for examining
assemblies and working with the global assembly cache. For more information
about the tools that are introduced in this topic, see .NET Framework Tools
and Debugger in the .NET Framework SDK documentation.
Assembly Linker (Al.exe)
The Assembly Linker is most useful to developers who need to create a single
assembly from multiple components files, such as those that may be produced
with mixed-language development.
Global Assembly Cache Tool (Gacutil.exe)
The global assembly cache tool (Gacutil.exe) allows you to view and
manipulate the contents of the global assembly cache. This tool provides much
of the same cache-viewing functionality as the Windows Shell Extension
(Shfusion.dll), but the global assembly cache tool is more usable from build
scripts, makefiles, and batch files.
Specifically, Gacutil.exe allows you to install assemblies into the cache, remove
them from the cache, and list the contents of the cache. You must have
Administrator privileges on a computer to install assemblies into the global
assembly cache.
To avoid removing more than one assembly from the global assembly cache,
you can use a command with the name of your specific assembly, as follows:
gacutil /u!
hello,ver=1.0.0.1,Culture=en,PublicKeyToken=874e23ab874e23ab

The preceding command removes only the hello assembly that matches the
fully specified version number, culture, and public key.
Topic Objective
To provide a brief overview
of packaging and
deployment tools that are
included in the .NET
Framework SDK.
Lead-in
The .NET Framework SDK
includes several useful tools
for examining assemblies
and working with the global
assembly cache.
Module 4: Deployment and Versioning 35


The following command lists the contents of the global assembly cache:
gacutil /l

MSIL Disassembler (Ildasm.exe)
You can also explore the namespaces in files that come with the runtime or in
files that you or others create by using the command-line MSIL disassembler.
To open a window that displays information about System.Data.dll, use the
following command:
>ildasm System.Data.dll

Each of the namespace nodes represents a separate namespace, which can be
expanded to explore the class objects and their methods and properties.
The MSIL disassembler also features a number of command-line options,
which are particularly useful when you want to redirect output to the console or
to a text file for subsequent analysis.

Place a shortcut to Ildasm.exe in your SendTo folder.

Strong Name Tool (Sn.exe)
When working with shared components, you can use the Strong Name (Sn.exe)
command-line tool for several purposes. For example, you can use the tool to
generate a new public-private key pair and write that pair to an output file, as
follows:
sn -k <outfile>

Native Image Generator (Ngen.exe)
The Native Image Generator creates a native image from a managed assembly
and installs it into the native image cache on the local computer. Running
Ngen.exe on an assembly allows the assembly to load faster, because it restores
code and data structures from the native image cache rather than generating
them dynamically.
Pre-compiling assemblies with Ngen.exe can improve the startup time for
applications, because much of the work required to execute code has been done
in advance. Therefore, it is more appropriate to use Ngen.exe for client-side
applications where you have determined that the CPU cycles consumed by just-
in-time (JIT) compilation cause slower performance.
Because there are many factors that affect the startup time of an application,
you should carefully determine which applications would benefit from the use
of Ngen.exe. Experiment by running both a JIT-compiled and a pre-compiled
version of a candidate assembly in the environment in which it will be used.
This will allow you to compare the startup times for the same assembly
executing under different compilation schemes.
Tip
36 Module 4: Deployment and Versioning


Assembly Binding Log Viewer (Fuslogvw.exe)
The Assembly Binding Log Viewer can be used to display details for assembly
binds. This information helps you diagnose why the .NET Framework cannot
locate an assembly at run time. These failures are usually the result of an
assembly deployed to the wrong location or a mismatch in version numbers or
cultures.
.NET Framework Configuration Tool (Mscorcfg.msc)
The .NET Framework Configuration Tool provides a graphical interface for
managing .NET Framework security policy and applications that use remoting
services. This tool also allows you to manage and configure assemblies in the
global assembly cache. The .NET Framework Configuration Tool is a
Microsoft Management Console (MMC) snap-in.
Code Access Security Policy Tool (Caspol.exe)
The Code Access Security Policy Tool allows you to examine and modify
machine, user, and enterprise-level code access security policies. For more
information about Caspol, see Course 2350A, Securing and Deploying
Microsoft .NET Assemblies and the .NET SDK.
For Your Information
The Assembly Binding Log
Viewer tool is used in the
lab.
Module 4: Deployment and Versioning 37


Lab 4: Packaging and Deployment

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives
After completing this lab, you will be able to:
! Create an application by using private assemblies in multiple directories.
! Install and remove a strong-named assembly from the global assembly
cache.
! Configure an application to control its binding to an assembly based on the
assemblys location and version data.

Lab Setup
Starter and solution files are associated with this lab. The starter files are in the
folder <install folder>\Labs\Lab04\Starter. The solution files are in the folder
<install folder>\Labs\Lab04\Solution.
Scenario
You will build an application that is named client, which retrieves an array of
strings from an instance of the Stringer class and reverses the order of these
strings by using an instance of the AReverser class. These classes are packaged
in two separate assembly libraries that are named Stringer.dll and
AReverser.dll.
Estimated time to complete this lab: 50 minutes
Topic Objective
To introduce the lab.
Lead-in
In this lab, you will build an
application that is named
client, which retrieves an
array of strings from an
instance of the Stringer
class and reverses the order
of these strings by using an
instance of the AReverser
class. These classes are
packaged in two separate
assembly libraries that are
named Stringer.dll and
AReverser.dll.
38 Module 4: Deployment and Versioning


Exercise 1
Locating Private Assemblies in Multiple Directories
In this exercise, you will build the client application by using private assemblies
that are located in multiple directories.
! Examine the application
Open Notepad and examine the source code for the files that are listed in the
following table.
Filename Directory location

Client.cs <install folder>\Labs\Lab04\Starter\Compapp
Stringer.cs <install folder>\Labs\Lab04\Starter\Compapp\MyStringer
AReverser.cs <install folder>\Labs\Lab04\Starter\Compapp\AReverser


! Compile and build the application in a command window

To use Microsoft Visual Studio .NET tools within a command
prompt window, the command prompt window must have the proper
environment settings. The Visual Studio .NET Command Prompt window
provides such an environment. To run a Visual Studio .NET Command Prompt
window, click Start, All Programs, Microsoft Visual Studio .NET,
Visual Studio .NET Tools, and Visual Studio .NET Command Prompt.

1. Go to the MyStringer directory and build the private assembly library
named Stringer.dll in that directory.
2. Go to the AReverser directory and make the private assembly named
AReverser.dll in that directory.
3. Go to the Compapp directory and build the client application, referencing
the private assemblies from steps 1 and 2. Reference the DLLs in their own
directories. Do not copy them to the Compapp directory.

Important
Module 4: Deployment and Versioning 39


! Execute the application
1. Run the Assembly Binding Log Viewer by executing Fuslogvw.exe. Click
on the right hand check box labeled Log Failures.
2. Execute the Client.exe application and note the exception in the space that is
provided.
3. In the Assembly Binding Log Viewer click on Refresh. Then double click
on the Client.exe application, and view the log entry.
4. Create a configuration file in the Client directory to specify the appropriate
privatePath so that the application can successfully bind and load the
necessary classes.
The Client.exe application should generate the following output:
Strings from StringComponent
C# String 0
C# String 1
C# String 2
C# String 3

Reversed Array Strings
C# String 3
C# String 2
C# String 1
C# String 0


40 Module 4: Deployment and Versioning


Exercise 2
Using Strong-Named Assemblies and Versioning
In this exercise, you will build the client application by using strong-named
assemblies that are located in the global assembly cache. You will then
configure an application to control its binding to an assembly based on the
assemblys location and version data.
! Build a compatible strong-named version of AReverser
1. From a command prompt window, navigate to the <install folder>\Labs\
Lab04\Starter\Compapp directory, and use the Strong Name tool to create a
public-private key pair file that is named OrgVerKey.snk.
2. Open Notepad. On the File menu, click Open, browse to <install folder>\
Labs\Lab04\Starter\ Compapp\AReverser_v2.0.0.0, and open AReverser.cs.
3. Examine the source code and note that it is the same code that was used in
the preceding exercise.
In particular, note that the signature of the Invert method is:
public string[] Invert(string[] myString)

4. Modify the source code to specify the key pair file from step 1 and to
specify a version number of 2.0.0.0.
5. From a command prompt window, navigate to the <install folder>\Labs\
Lab04\Starter\Compapp directory and build the 2.0.0.0 strong-named
version of AReverser.cs. The AReverser.dll should be output into the
AReverser_v2.0.0.0 subdirectory.
6. Use the global assembly cache tool (Gacutil.exe) to install this assembly in
the global assembly cache.
7. Examine the global assembly cache by using Gacutil.exe or Windows
Explorer, and note the presence of AReverser and its properties.

! Build an incompatible strong-named version of AReverser
1. In Notepad, on the File menu, click Open, browse to <install folder>\
Labs\Lab04\Starter\Compapp\AReverser_v2.0.1.0, and open AReverser.cs.
2. Examine the source code and note that it is different than the code that was
used in the preceding exercise.
In particular, note that the signature of the Invert method is:
public string[] Invert(string[] myString, int myCount)

3. Modify the source code to specify the key pair file OrgVerKey.snk and to
specify a version number of 2.0.1.0.
4. From a command prompt window, navigate to the <install folder>\Labs\
Lab04\Starter\Compapp directory and build the 2.0.1.0 strong-named
version of AReverser.cs. The AReverser.dll should be output into the
AReverser_v2.0.1.0 subdirectory.
5. Install this assembly in the global assembly cache.
6. Examine the global assembly cache by using Gacutil.exe or Windows
Explorer, and note the presence of AReverser and its properties.

Module 4: Deployment and Versioning 41


! Compile, build, and execute the application
1. From a command prompt window, navigate to the Client directory and
build the client application. Reference the private assembly
MyStringer\Stringer.dll and the strong-named assembly
AReverser_v2.0.0.0\AReverser.dll.
2. Execute the Client.exe application, which should generate the following
output:
Strings from StringComponent
C# String 0
C# String 1
C# String 2
C# String 3

Reversed Array Strings
C# String 3
C# String 2
C# String 1
C# String 0


! Configure to bind to the latest version of AReverser

You can obtain the publicKeyToken tags value by examining the global
assembly cache using gacutil /l or the cache shell extension.

1. Modify the configuration file to bind to the incompatible later version
2.0.1.0 of AReverser.
2. Execute Client.exe and note the exception.
3. Use the global assembly cache tool to remove the two strong-named
versions of AReverser.
4. Confirm the removal of the two strong-named versions of AReverser by
viewing the global assembly cache with Gacutil.exe and Windows Explorer.

Tip
42 Module 4: Deployment and Versioning


Review
! Introduction to Application Deployment
! Application Deployment Scenarios
! Related Topics and Tools

*****************************ILLEGAL FOR NON-TRAINER USE******************************
1. What part of the assembly identifies its imported and exported types and its
version information?
The manifest.


2. What software does a computer require to run a .NET application locally?
The .NET Framework common language runtime.


3. Name two simple ways to run a .NET Framework application.
Copy the executable file and referenced assemblies to the local
computer, or access them on a file server.


Topic Objective
To reinforce module
objectives by reviewing
key points.
Lead-in
The review questions cover
some of the key concepts
taught in the module.
Module 4: Deployment and Versioning 43


4. Describe how an application can use an assembly that is located in an
applications subdirectory.
Create a configuration file in the applications directory that specifies a
PrivatePath as follows:
<configuration>
<runtime>
<assemblyBinding
xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="MyStringer"/>
</assemblyBinding>
</runtime>
</configuration>


5. What kind of assembly can be placed in the global assembly cache and be
versioned?
A strong-named assembly.


6. What command is used to generate public-private key pairs?
The Strong Name (Sn.exe) tool is used to generate a new key pair and
place them in a file:
sn k orgKey.snk


7. What command is used to install a strong-named assembly into the global
assembly cache?
> gacutil -i <filename>







THIS PAGE INTENTIONALLY LEFT BLANK









Contents
Overview 1
An Introduction to the Common Type
System 2
Elements of the Common Type System 8
Object-Oriented Characteristics 25
Lab 5: Building Simple Types 39
Review 44

Module 5: Common
Type System


Information in this document, including URL and other Internet Web site references, is subject to
change without notice. Unless otherwise noted, the example companies, organizations, products,
domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious,
and no association with any real company, organization, product, domain name, e-mail address,
logo, person, place or event is intended or should be inferred. Complying with all applicable
copyright laws is the responsibility of the user. Without limiting the rights under copyright, no
part of this document may be reproduced, stored in or introduced into a retrieval system, or
transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or
otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual
property rights covering subject matter in this document. Except as expressly provided in any
written license agreement from Microsoft, the furnishing of this document does not give you any
license to these patents, trademarks, copyrights, or other intellectual property.

2001-2002 Microsoft Corporation. All rights reserved.

Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint,
Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and
Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A.
and/or other countries.

The names of actual companies and products mentioned herein may be the trademarks of their
respective owners.



Module 5: Common Type System iii


Instructor Notes
After completing this module, students will be able to:
! Describe the difference between value types and reference types.
! Explain the purpose of each element in the type system, including values,
objects, and interfaces.
! Explain how object-oriented programming concepts, such as abstraction,
encapsulation, inheritance, and polymorphism, are implemented in the
Common Type System.

Materials and Preparation
This section provides the materials and preparation tasks that you need to teach
this module.
Required Materials
To teach this module, you need the Microsoft PowerPoint file 2349B_05.ppt.
Preparation Tasks
To prepare for this module, you should:
! Read all of the materials for this module.
! Complete the lab.

Module Strategy
Use the following strategy to present this module:
! An Introduction to the Common Type System
Cover the basic architecture of the Common Type System. Introduce
System.Object as the root type for all types in the Microsoft .NET
Framework common language runtime. Explain how all types in the
Common Type System are either reference types or value types.
! Elements of the Common Type System
In this section, cover the primitive types, objects, properties, and other
elements of the Common Type System. The information will not be new to
students with a C++ object-oriented background, so you can cover these
topics quickly or omit most of the material in this section. This decision will
depend on the students experience.
! Object-Oriented Characteristics
Discuss how the object-oriented characteristics of the .NET Framework
common language runtime are supported. If the class is already experienced
in object-oriented techniques, you can omit this section.

Presentation:
90 Minutes

Lab:
45 Minutes
Module 5: Common Type System 1


Overview
! An Introduction to the Common Type System
! Elements of the Common Type System
! Object-Oriented Characteristics

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this module, you will learn about the Common Type System. Specifically,
you will learn to differentiate between value types and reference types. You will
also examine how classes, interfaces, properties, methods, events, and values
are represented in the Microsoft .NET Framework.
After completing this module, you will be able to:
! Describe the difference between value types and reference types.
! Explain the purpose of each element in the type system, including values,
objects, and interfaces.
! Explain how object-oriented programming concepts, such as abstraction,
encapsulation, inheritance, and polymorphism, are implemented in the
Common Type System.

Topic Objective
To provide an overview of
the module topics and
objectives.
Lead-in
In this module, you will learn
about the Common Type
System and object-oriented
programming.
2 Module 5: Common Type System


" "" " An Introduction to the Common Type System
! Common Type System Architecture
! Value Types vs. Reference Types

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this section, you will learn about the Common Type System architecture.
Specifically, you will learn how types are specified and represented in the .NET
Framework common language runtime, and you will learn the difference
between value types and reference types.
Topic Objective
To provide an overview of
the section topics.
Lead-in
In this section, you will learn
about the architecture of the
Common Type System.
Module 5: Common Type System 3


Common Type System Architecture

*****************************ILLEGAL FOR NON-TRAINER USE******************************
A type defines characteristics for a set of values. For example, the set of whole
numbers between negative 32768 and positive 32767 is called the short type in
C#, or System.Int16 in the .NET Framework class library. The short type has a
physical representation, which, in this case, is a 16-bit value. The short type
also defines operations allowed on the type, such as addition and subtraction.
System.Object
System.Object is the root type for all types in the .NET Framework common
language runtime. For this reason, any type you use will have System.Object
as its base class. System.Object has the following methods.
Methods Description

Public Equals Overloaded. Determines whether two Object
instances are equal.
Public GetHashCode Serves as a hash function for a particular type;
suitable for use in hashing algorithms and data
structures, such as a hash table.
Public GetType Gets the type of the object.
Public ReferenceEquals Determines whether the specified Object instances
are the same instance.
Public ToString Returns a string that represents the current object.
Protected Finalize Overridden. Allows an Object to attempt to free
resources and perform other cleanup operations
before the Object is reclaimed by garbage collection.
Protected MemberwiseClone Creates a shallow copy of the current Object.

For more information about the methods of System.Object, see Module 6,
Working with Types, in Course 2349B, Programming with the
Microsoft .NET Framework (Microsoft Visual C#

.NET).
Topic Objective
To describe the basic
architecture of the
Common Type System.
Lead-in
A type defines
characteristics for a set of
values.
4 Module 5: Common Type System


Value Types
Value types are any type that inherits from System.ValueType. Value types are
typically simple types, such as integer values or long values. Value types are
special because they are allocated on the stack.
New value types are defined by using the struct or enum keywords. You
cannot create value types by using the class keyword. System.ValueType
inherits from System.Object, which means that all value types can behave like
System.Object.
The type system defines primitive types, which are Byte, Int16, Int32, Int64,
Single, Double, Boolean, Char, Decimal, IntPtr, and String. There are also
built-in types such as System.Array available.
Reference Types
Reference types are any type that inherits from System.Object and not
System.ValueType. Reference types are analogous to pointers in C++, but
there are subtle differences in the way reference types and pointers work.
Reference types are allocated on the heap. Reference types also must be
garbage collected. One example of a reference type is the System.IO file class,
which represents a file in the system.
Fields, Methods, and Properties
The Common Type System defines fields, methods, and properties that are
available to all value types and reference types. A field is a data value stored in
a class. For example, Name and Age could be fields describing an employee
stored in an Employee structure.
A method is an operation that can be performed on a class. For example, a
Tenure method could calculate how long an employee has been employed.
Properties are a convenient way to expose data values to external objects and
code. For example, the Name and Age fields could be exposed as properties,
which would allow the Employee structure to control how the name and age
are stored and retrieved.
Interfaces
Interfaces are the only types that do not inherit from System.Object. Interfaces
have no implementation; they merely provide a description of methods,
properties, and events. Any class that inherits from an interface must implement
all of the methods, properties, and events in that interface. Interfaces provide a
mechanism for specifying contractual relationships between classes. Interfaces
also provide a means of implementing multiple inheritance. For more
information about inheritance, see Module 6, Working with Types, in Course
2349B, Programming with the Microsoft .NET Framework (Microsoft
Visual C# .NET).
Module 5: Common Type System 5


Value Types vs. Reference Types
! Value Types Are Primitive or User-Defined Structures
# Allocated on stack
# Assigned as copies
# Default behavior is pass by value
! Reference Types Are Objects That Are:
# Allocated on heap using the new keyword
# Assigned as references
# Passed by reference

*****************************ILLEGAL FOR NON-TRAINER USE******************************
With the exception of interfaces, all types in the Common Type System are
divided into two categories: value types and reference types.
Value Types
Value types represent primitive or user-defined structures. Value types are
allocated on the stack and are removed from the stack when the method call in
which they were declared returns. When a value type is passed as an argument
to a method, it is passed by value, unless the ref keyword is used. All value
types must inherit from System.ValueType.
Topic Objective
To explain the difference
between value types and
reference types.
Lead-in
With the exception of
interfaces, all types in the
Common Type System are
divided into two categories:
value types and reference
types.
6 Module 5: Common Type System


An integer is an example of a value type. The following example shows an
integer value type and its effect on method calls and assignment operations.
using System;

public class MainClass
{
public static void Main()
{
// Create a new integer with value 5
int a = 5;
// Create a new uninitialized integer
int b;
// Initialize b by copying the value 5 into b
b = a;
//Increase the value of b by 5
b += 5;
Console.WriteLine("{0}, {1}", a, b);
Add5(b);
Console.WriteLine("{0}, {1}", a, b);
}

public static void Add5(int num)
{
num += 5;
}
}

This code generates the following output:
5, 10
5, 10

In the preceding example, the variables a and b represent two separate instances
of the integer type. The Assignment operator (=) copies the value from one
variable to another. When the Add5 method is called, the = operator updates a
copy of the integer, not the original integer, because a copy of the value is
pushed onto the stack when passing value types.
Module 5: Common Type System 7


Reference Types
Reference types are a reference to a value. Reference types are allocated on the
heap and will remain on the heap until they are garbage collected. Reference
types must be allocated by using the new keyword. When you pass reference
types as parameters, the reference is pushed onto the stack, and the method call
works with the actual value, not with a copy of the value. All reference types
inherit from System.Object.
The following example shows the effect of assignments and method calls when
you use the StringBuilder class, which is a reference type:
using System;
using System.Text;

public class MainClass
{
public static void Main()
{
//Create a new StringBuild instance
//with the string "Bob"
StringBuilder name1 = new StringBuilder("Bob");
StringBuilder name2;
//Make name2 reference the same StringBuilder instance
name2 = name1;
name2.Append("by");
Console.WriteLine("Values are {0}, {1}", name1, name2);
Possessive(name2);
Console.WriteLine("Values are {0}, {1}", name1, name2);
}
public static void Possessive(StringBuilder name)
{
name.Append("'s");
}
}

This code generates the following output:
Values are Bobby, Bobby
Values are Bobbys, Bobbys

In this example, name1 and name2 always refer to the same StringBuilder
instance on the heap. Thus, whenever a change is made to the underlying
instance, both variables are affected. Also, name2 is passed by reference to the
Possessive method, so that when the Possessive method makes a change to the
instance, both name2 and name1 are affected.
8 Module 5: Common Type System


" "" " Elements of the Common Type System
! Primitive Types
! Objects
! Constructors
! Properties
! Custom Types
! Enumerations
! Interfaces

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this section, you will learn about primitive types, objects, properties, and
other elements of the Common Type System.
Topic Objective
To provide an overview of
the section topics and
objectives.
Lead-in
In this section, you will learn
about primitive types,
objects, properties, and
other elements of the
Common Type System.
Module 5: Common Type System 9


Primitive Types
! Simple Small Types Common in Most Languages
! Naming Differences
! C# Support
! Conversions

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Primitive types in the .NET Framework common language runtime are simple
value types, which are commonly found in most programming languages.
Primitive types are simple small types, such as Boolean types or character
types.
The following table identifies all of the primitive types available in the .NET
Framework common language runtime. The table lists the Microsoft
intermediate language (MSIL) assembler name of the type, which you can see
when you view the disassembly of managed code in the MSIL Disassembler,
the name of the type in C#, the name of the type in the .NET Framework class
library, and the description of the type.
Name in
MSIL Assembler

Name in C#
Name in
class library

Description

bool bool System.Boolean True/false value
char char System.Char Unicode 16-bit char
float32 float System.Single IEEE 32-bit float
float64 double System.Double IEEE 64-bit float
int8 sbyte System.SByte Signed 8-bit integer
int16 short System.Int16 Signed 16-bit integer
int32 int System.Int32 Signed 32-bit integer
int64 long System.Int64 Signed 64-bit integer
unsigned int8 byte System.Byte Unsigned 8-bit integer
unsigned int16 ushort System.UInt16 Unsigned 16-bit integer
unsigned int32 uint System.UInt32 Unsigned 32-bit integer
unsigned int64 ulong System.UInt64 Unsigned 64-bit integer

Topic Objective
To explain how primitive
types are used in the
common language runtime.
Lead-in
Primitive types in the .NET
Framework common
language runtime are simple
value types, commonly
found in most programming
languages.
10 Module 5: Common Type System


The following example shows how to create and initialize the primitive
System.UInt16 type in C# to store an employees age:
System.UInt16 age = new System.UInt16();
age = 5;

Primitive types are inherently supported in C#, allowing you to use simpler
syntax, as in the following example:
ushort age = 5;

You can convert primitive types by using the Convert class. The Convert class
defines static methods to convert from one primitive type to another. The
following example shows how to convert a long type to an unsigned short type:
long longAge = 32;
ushort ushortAge;
ushortAge = Convert.ToUInt16(longAge);

C# provides a more convenient conversion mechanism: casting. The following
example shows how to explicitly cast a long type to an unsigned short type:
long longAge = 32;
ushort ushortAge;
ushortAge = (ushort) longAge;

Casting in C# will automatically compile to conversion methods, such as
ToUInt16. Also, you can use casting to avoid memorizing the class library
names for primitive types.
Module 5: Common Type System 11


Objects
! Every Class Inherits from System.Object
! Objects Specify Data and Behavior
! Fields Define the Data
! Methods Define the Behavior
class Accumulator
{
public float Result;
public void Add(float amount)
{
Result += amount;
}
}
class Accumulator
{
public float Result;
public void Add(float amount)
{
Result += amount;
}
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Everything in the Common Type System is an object and inherits from
System.Object. An object type declaration specifies data and behavior. The
Common Type System defines data in terms of fields and defines behavior in
terms of methods. Each field or method in an object is called a member of that
object.
An object is an instance of a class. The class specifies the types that are
contained in a class and the classs operations; an object is an instance
containing a set of data values.
In C#, you can use the class keyword to declare an object type that is a
reference type. The following example shows how to declare a class called
Accumulator.
class Accumulator
{
// Fields and methods defined here
}

Fields
Fields describe the data in an object. In the preceding example of the
Accumulator class, a field is needed to store the current result that has been
accumulated.
class Accumulator
{
public float Result;
}

Topic Objective
To explain the role of object
types in the Common Type
System.
Lead-in
Everything in the Common
Type System is an object
and inherits from
System.Object.
12 Module 5: Common Type System


Methods
Methods describe the operations in an object. A method can have arguments
and can also have a return type. Because the Common Type System requires
strong typing, you must define a specific type for every parameter and return
value.
The following example shows how to specify an Add method for the
Accumulator class that takes an amount of type float and adds it to the Result
field.
class Accumulator
{
public float Result;
public void Add(float amount)
{
Result += amount;
}
}

The following code is an example of client code:
Accumulator calc = new Accumulator();
calc.Result = 0;
calc.Add(5);
Console.WriteLine(calc.Result);

This code generates the following output:
5

Module 5: Common Type System 13


Constructors
! Constructors Are Used to Initialize Classes
! Constructors Can Have Zero or More Parameters
class Rectangle
{
private int x1,y1,x2,y2;
public Rectangle(int x1, int y1, int x2,int y2)
{
this.x1 = x1;
this.x2 = x2;
this.y1 = y1;
this.y2 = y2;
}
}
class Rectangle
{
private int x1,y1,x2,y2;
public Rectangle(int x1, int y1, int x2,int y2)
{
this.x1 = x1;
this.x2 = x2;
this.y1 = y1;
this.y2 = y2;
}
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
If you design a class that requires special initialization code, you can use a
constructor to initialize the class properly when objects are created. A
constructor is a method with the same name as the class. The constructor runs
when a new instance of the class is created.
The following example shows how a constructor is used to initialize a class:
public class Rectangle
{
//Rectangle coordinates
private int x1,y1,x2,y2;

//Initialize coordinates in constructor
public Rectangle()
{
x1 = 0;
x2 = 0;
y1 = 0;
y2 = 0;
}
}

public class MainClass
{
public static void Main()
{
//Creating a new Rectangle
//will call constructor
Rectangle r = new Rectangle();
}
}
Topic Objective
To explain how to use
constructors to initialize
classes.
Lead-in
If you design a class that
requires special initialization
code, you can use a
constructor to initialize the
class properly when objects
are created.
14 Module 5: Common Type System


Default Constructor
If you do not specify a constructor, the C# compiler will automatically provide
a default constructor. The default constructor takes no parameters and calls the
base class constructor.
Parameterized Constructor
You can also create a constructor with parameters as follows:
class Rectangle
{
//Rectangle coordinates
private int x1,y1,x2,y2;

//Initialize coordinates in constructor
public Rectangle(int x1, int y1, int x2,int y2)
{
this.x1 = x1;
this.x2 = x2;
this.y1 = y1;
this.y2 = y2;
}
}

public class MainClass
{
public static void Main()
{
//Must specify parameters
//for this constructor
Rectangle r = new Rectangle(2,2,10,10);
}
}

Module 5: Common Type System 15


Properties
! Properties Are Similar to Fields
! Properties Use get and set Accessor Methods for
Managing Data Values
public float Start
{
get
{
return start;
}
set
{
if (start >= 0) start = value;
}
}
public float Start
{
get
{
return start;
}
set
{
if (start >= 0) start = value;
}
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Properties are values that can be stored or retrieved on a class. Like fields and
methods, properties are also considered members of an object. Properties are
different than fields because access to properties is controlled through get and
set accessor methods. Also, properties are not necessarily stored in a single
variable. They may be calculated on the fly or stored in multiple variables.
get and set Accessors
Property storage and property retrieval are defined by the get and set accessor
methods. You write code in the get accessor method to determine how to
retrieve a property value. You write code in the set accessor method to
determine how to store a property value. By omitting the get or the set accessor,
you can make a property read-only or write-only.
Topic Objective
To describe how to use
properties.
Lead-in
Properties are values that
can be stored or retrieved
on a class.
16 Module 5: Common Type System


The following example shows how to implement a Distance class, which can
calculate the length of a trip from start to finish. The class contains three
properties: Start, Finish, and Length. The Length property is read-only and is
calculated on the fly.
class Distance
{
float start, finish;
public float Start
{
get
{
return start;
}
set
{
if (start >= 0) start = value;
}
}
public float Finish
{
get
{
return finish;
}
set
{
if (finish >= start) finish = value;
}
}
public float Length
{
get
{
return (finish - start);
}
}
}

public class MainClass
{
public static void Main()
{
Distance d = new Distance();
d.Start = 5;
d.Finish = 10;
float length = d.Length;
}
}

Module 5: Common Type System 17


In the preceding example, verification code is used in the set statements to
control how the properties are set. Negative numbers are not allowed.
Additional code could be written to throw an exception if invalid values are
used.
The client code in MainClass does not have to use method syntax with
parentheses to refer to properties.
In general, you should always use properties to expose data items to external
classes. Properties allow you to encapsulate fields, so that you can change them
in the future, if necessary.
For more information about encapsulation, see Encapsulation in this module.
18 Module 5: Common Type System


Custom Types
! Inherit from System.ValueType
! Are Defined with the struct Keyword in C#
! Can Have Methods, Properties, and Fields
struct Employee
{
public string Name;
public ushort Age;
public DateTime HireDate;
public float Tenure()
{
TimeSpan ts = DateTime.Now HireDate;
return ts.Days/(float)365;
}
}
struct Employee
{
public string Name;
public ushort Age;
public DateTime HireDate;
public float Tenure()
{
TimeSpan ts = DateTime.Now HireDate;
return ts.Days/(float)365;
}
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
You can build your own data types by using the C# struct keyword. The struct
keyword will define a custom type that inherits from System.ValueType,
which in turn inherits from System.Object. All value types are sealed;
additional classes cannot be derived from them.
The following example shows how the C# struct keyword is used to build a
simple custom type that describes an individual employee:
struct Employee
{
public string Name;
public ushort Age;
public DateTime HireDate;
}

In C#, structures can also have methods as in the following example:
struct Employee
{
public string Name;
public ushort Age;
public DateTime HireDate;

public float Tenure()
{
TimeSpan ts = DateTime.Now HireDate;
return ts.Days/(float)365;
}
}

Structures can also have properties. For example, the Employee structure could
be modified so that employee age is a property. Then the set accessor could be
used to verify that the age is a valid age.
Topic Objective
To describe how to build
data types that use the C#
struct keyword.
Lead-in
You can build your own data
type by using the C# struct
keyword.
Module 5: Common Type System 19


Enumerations
! .NET Framework Enumerations
# Inherit from System.Enum
# Use the enum keyword
! Bit Flags
enum SeatPreference : ushort
{
Window, //Assigned value 0
Center, //Assigned value 1
Aisle //Assigned value 2
}
enum SeatPreference : ushort
{
Window, //Assigned value 0
Center, //Assigned value 1
Aisle //Assigned value 2
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In programming, it is common for a variable to take on only one of a small
number of values. For example, the variable for an airline passengers seating
preference has only three values: Window, Center, or Aisle. It is much easier
and safer for a developer to reference such values with text, rather than
numbers. The .NET Framework common language runtime supports
enumerations, which allow a developer to specify and represent these simple
types.
.NET Framework Enumerations
In the .NET Framework, enumerations inherit from the class System.Enum,
which inherits from System.ValueType. The following example shows how to
create an enumeration:
enum SeatPreference : ushort
{
Window, //Assigned value 0
Center, //Assigned value 1
Aisle //Assigned value 2
}

In the preceding example, the base type of the enumeration is defined as
ushort. You can use any simple type for a base type, except System.Char. An
enumeration will be of type int, unless otherwise specified.
The literals are specified in a comma-delimited list. Unless you specify
otherwise, the first literal is assigned a value of 0, the second a value of 1, and
so on.
Topic Objective
To describe how to create
enumerations, which allow
developers to specify and
represent simple types.
Lead-in
It is common for a variable
to take on only one of a
small number of values.
20 Module 5: Common Type System


To use enumeration literals, you must fully specify the literal name as in the
following example:
SeatPreference sp;

//Assign enumerated window type seat to sp variable
sp = SeatPreference.Window;
Console.WriteLine(sp.GetHashCode());
Console.WriteLine(sp.ToString());

This code generates the following output:
0
Window

The GetHashCode method will return the literal value. By using the ToString
method, you can obtain the human-readable string name of the literal from a
variable. This feature is useful when you need to print or save a human-readable
form of the enumerations literals.
Also, you can use the Parse method to obtain an enumerated value from a
string. This feature is useful when receiving input in a text form that requires
conversion to the appropriate enumerated value. The following example shows
how to convert the string center into an enumerated value for the
SeatPreference enumeration.
sp = (SeatPreference)
System.Enum.Parse(typeof(SeatPreference), Center);

Module 5: Common Type System 21


Bit Flags
Enumerations in the .NET Framework are also useful for storing bit flags that
often involve bitwise operations, such as the bitwise AND operator (&), and the
bitwise OR operator (|).
Use the Flags attribute to create an enumeration of bit flags. The following
example refers to a computer-controlled weather display that uses icons to
represent weather conditions. The enumeration would allow you to combine the
icons in various ways to represent different conditions:
[Flags] enum WeatherDisplay : ushort
{
Sunny = 1,
Cloudy = 2,
Rain = 4,
Snow = 8,
Fog = 16
}

WeatherDisplay wd;
//Assign both Sunny and Cloudy attributes
//to create partly sunny forecast
wd = WeatherDisplay.Sunny | WeatherDisplay.Cloudy;
Console.WriteLine(wd.ToString());
wd = (WeatherDisplay)System.Enum.Parse(typeof(WeatherDisplay),
"Rain, Snow");
Console.WriteLine(wd.GetHashCode());

This code generates the following output:
Sunny, Cloudy
12

When using the Flags attribute, you must specify the values for the literals
manually.
22 Module 5: Common Type System


Interfaces
! An Interface Is a Contractual Description of Methods
and Properties
! An Interface Has No Implementation
! Use Casting in Client Code to Use an Interface
interface ICDPlayer
{
void Play(short playTrackNum);
void Pause();
void Skip(short numTracks);
short CurrentTrack
{
get;
set;
}
}
interface ICDPlayer
{
void Play(short playTrackNum);
void Pause();
void Skip(short numTracks);
short CurrentTrack
{
get;
set;
}
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
An interface is a contractual description of a set of related methods and
properties. An interface has a name, and it has methods and properties. For
example, an interface named ICDPlayer may have methods, such as Play,
Pause, and Skip. It may also have properties, such as CurrentTrack and
Time.
The following example shows how to define an interface called ICDPlayer that
has the methods Play, Pause, and Skip, and has the property CurrentTrack:
interface ICDPlayer
{
void Play(short playTrackNum);
void Pause();
void Skip(short numTracks);
short CurrentTrack
{
get;
set;
}
}

An interface has no implementation. Any class can inherit from any interface.
To inherit from an interface, a class must implement all methods, properties,
and events on that interface. Thus an interface serves as a contract specifying to
any user of the class that the class has implemented all methods properties, and
events defined in the interface.
Topic Objective
To describe the uses of
interfaces.
Lead-in
An interface is a contractual
description of a set of
related methods and
properties.
Module 5: Common Type System 23


The following example shows how to implement the ICDPlayer interface in a
class called Device.
public class Device : ICDPlayer
{
// Internal property values
protected string deviceName;
protected short currentTrack;

//Constructor
public Device()
{
deviceName = "Default";
currentTrack = 1;
}

//Properties
public string DeviceName
{
get
{
return deviceName;
}
set
{
deviceName = value;
}
}
public short CurrentTrack
{
get
{
return currentTrack;
}
set
{
currentTrack = value;
}
}

//Methods
public void Play(short playTrackNum)
{
Console.WriteLine("Now Playing Track: {0}",
playTrackNum);
currentTrack = playTrackNum;
}
public void Pause()
{
Console.WriteLine("Now Paused");
}
public void Skip(short numTracks)
{
Console.WriteLine("Skipped {0} Tracks",numTracks);
}
}
24 Module 5: Common Type System


In the preceding example, all of the methods and properties of ICDPlayer were
implemented. A class may implement additional properties and methods, such
as the DeviceName property in the Device class.
Client code uses an interface by casting to the interface name. The following
example shows how a client can create an instance of the Device class and then
use the ICDPlayer interface:
public class MainClass
{
public static void Main()
{
Device Device1 = new Device();
ICDPlayer CD1 = (ICDPlayer) Device1;
//Call Play method on ICDPlayer interface
CD1.Play(1);
//Get CurrentTrack property of ICDPlayer interface
Console.WriteLine("Current Track = {0}",
CD1.CurrentTrack);
//Get DeviceName property of Device object
Console.WriteLine("Device Name = {0}",
Device1.DeviceName);
}
}

In the preceding example, the Device1 variable also could have been used to
access methods and properties of the ICDPlayer interface.
For more information about how to separate interface methods and properties
from class methods and properties, see Module 6, Working with Types, in
Course 2349B, Programming with the Microsoft .NET Framework (Microsoft
Visual C# .NET).
Module 5: Common Type System 25


" "" " Object-Oriented Characteristics
! Abstraction
! Encapsulation
! Inheritance
! Polymorphism

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this section, you will learn how the object-oriented characteristics of the
.NET Framework common language runtime are supported.
Everything in the common language runtime is an object that encompasses
fields, methods, properties, and events. Object-oriented characteristics, such as
abstraction, encapsulation, inheritance, and polymorphism, make it easier to
work with code in the .NET Framework.
Topic Objective
To provide an overview of
the topics covered in this
section.
Lead-in
In this section, you will learn
how the object-oriented
characteristics of the .NET
Framework common
language runtime are
supported.
26 Module 5: Common Type System


Abstraction
! Abstraction Works from the Specific to the General
! Grouping Elements Makes It Easier to Work with
Complex Data Types
! Abstraction Is Supported Through Classes

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In object-oriented programming, the term abstraction refers to the process of
reducing an object to its essence so that only essential elements are represented.
For programmers, abstraction means working from the specific to the general.
An example of abstraction would be a phone number, such as (206) 555-1212.
At the specific level, this phone number is a sequence of numbers: 2065551212.
However, most people do not memorize phone numbers at this specific level.
Instead they memorize such information by grouping the numbers into
meaningful chunks. In this example, the phone number becomes an area code
(206), prefix (555), and number (1212). This abstraction makes it easier for a
person to remember the number. The number is reduced to three chunks to
memorize, instead of 10 individual numbers.
Abstraction is a powerful process that allows people to group and communicate
information in simpler and more meaningful ways. In the Common Type
System and other object-oriented systems, the fundamental unit of abstraction is
the class. Class types allow you to group different types of information, and
they provide the functionality to help you operate on that information. From a
design perspective, abstraction allows you to treat information and functionality
as complete functional units, rather than as disparate pieces of information and
operations.
Topic Objective
To describe the use and
advantages of abstraction
in object-oriented
programming.
Lead-in
In object-oriented
programming, the term
abstraction refers to the
process of reducing an
object to its essence so that
only essential elements are
represented.
Module 5: Common Type System 27


Encapsulation
! Encapsulation Is the Process of Hiding Internal Details
of a Class
! Encapsulation Keywords
# public
# protected
# internal
# private
! Type-Level Accessibility
! Nested Classes

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In object-oriented programming, encapsulation, or information hiding, is the
process of packaging attributes and functionality to prevent other objects from
manipulating data or procedures directly. Encapsulation also allows the object
that is requesting service to ignore the details of how the service is provided. By
hiding the internal unnecessary data and functionality of a class from other
classes, encapsulation provides greater flexibility in the design of classes. For
example, when a sorting class performs sorts on data items, the internal data
structure of the class will probably store the data items, such as a linked list.
The class will also have internal functions that operate on the linked list.
If clients that use the sorting class have direct access to the linked list, they will
incorporate code that relies on the linked list. In fact, if no accessor functions
are written for the linked list, the clients must use the linked list to store or
retrieve data items.
If the sorting class changes in the future, it may change the linked list to an
array for optimization, and the clients will stop functioning correctly. At that
time, the clients would require rewriting to use the array syntax. If the sorting
class hid the internals of its implementation and provided accessor functions
that mapped to the internal data structure, this problem could be avoided.
C# provides a set of access modifiers that vary the degree of accessibility to
members of any type. The following table lists all of the access modifiers
supported in C#.
Access
modifier

Description

public Makes a member available to all other classes in all assemblies
protected Makes a member available only to classes that inherit from this class
internal Makes a member available only to other classes in the same assembly
private Makes a member available only to the same class

Topic Objective
To explain the process of
encapsulation in object-
oriented programming.
Lead-in
Encapsulation, or
information hiding, is the
process of packaging
attributes and functionality
to prevent other objects
from manipulating data or
procedures directly.
28 Module 5: Common Type System


If no access modifier is specified, a default modifier is applied. The following
table shows which modifiers are applied to different types by default.
Members of Default member accessibility

enum public
class private
interface public
struct private

Type-Level Accessibility
You can specify accessibility at the type level and at the member level. Access
modifiers can be applied to enumerations, classes, interfaces, and structs.
However, for top-level types, internal and public are the only allowed
modifiers. You cannot create a private class or a protected interface at the top
level in a namespace.
The access modifier applied at the type level will supercede member-level
access modifiers if the type-level access modifier is more restrictive. For
example, an internal class can have a public method, but the internal modifier
will make the public method internal as well.
The following example shows the effect of applying access modifiers at the
class level and the member level:
public class COuter1
{
public static short MyPublicShort = 0;
internal static short MyInternalShort = 0;
private static short MyPrivateShort = 0;
}

public class MainClass
{
public static void Main()
{
COuter1.MyPublicShort = 1;
//Success because publicly available
COuter1.MyInternalShort = 2;
//Success because in same assembly
COuter1.MyPrivateShort = 3;
//Failure because private only available to COuter1 class
}
}

Module 5: Common Type System 29


Nested Classes
When classes are nested, the nested classes cannot exceed the accessibility of
the containing class. For example, if a nested class is marked as public, but the
containing class is marked as internal, the nested class must also be internal.
The following example shows the effect of access modifiers on nested classes:
internal class COuter1
{
public class CInner1
{
public static short MyPublicShort = 0;
internal static short MyInternalShort = 0;
private static short MyPrivateShort = 0;
}
internal class CInner2
{
public static short MyPublicShort = 0;
internal static short MyInternalShort = 0;
private static short MyPrivateShort = 0;
}
private class CInner3
{
public static short MyPublicShort = 0;
internal static short MyInternalShort = 0;
private static short MyPrivateShort = 0;
}
}

public class MainClass
{
public static void Main()
{
COuter1.CInner1.MyPublicShort = 1;
//Success because internal at class level
//and in same assembly

COuter1.CInner2.MyPublicShort = 2;
//Success because internal and in same assembly

COuter1.CInner3.MyPublicShort = 3;
//Failure because class is private, and all members are
private
}
}

30 Module 5: Common Type System


Inheritance
! Inheritance Is the Reuse of Class Members in
Other Classes
! The Common Type System Only Supports Single
Inheritance for Classes
! Member Hiding
# Redefine the same method in the derived class
# Use the new keyword
! Abstract Members
! Sealed Classes

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In object-oriented programming, inheritance is a method of reuse that enables
one class to reuse, or inherit, the fields, properties, and methods of another
class. Inheritance represents an is a relationship between classes. For
example, if a square class inherits from a shape class, the square is a kind of
shape.
Inheritance is useful when multiple classes share the same methods or data. The
common methods and data can be abstracted into a separate class that the other
classes inherit from.
Inheritance is pervasive in the Common Type System. Every class must inherit
from System.Object, so all classes will have the members of System.Object.
Single Inheritance
The Common Type System only supports single inheritance of class types.
Single inheritance means a class can inherit directly from only one other class.
Some object-oriented systems, such as C++, allow multiple inheritance, which
means that one class can inherit from many other classes.
The Common Type System does support multiple inheritance through
interfaces. In that case, one class can inherit from one other class and from zero
or more interfaces. For more information about multiple inheritance through
interfaces, see Module 6, Working with Types, in Course 2349B,
Programming with the Microsoft .NET Framework (Microsoft Visual C#
.NET).
In the simplest form of inheritance, a class inherits its members from the base
class and gains all the functionality of the base class, and at the same time it
adds additional functionality of its own.
Topic Objective
To explain how inheritance
works in the Common Type
System.
Lead-in
Inheritance is a method of
reuse that enables one
class to reuse, or inherit, the
fields, properties, and
methods of another class.
Module 5: Common Type System 31


In the following example, three classes are defined. The base class is called
Animal; it implements an Age property and a Move method. The second class
is called Dog; it inherits from Animal and implements an additional method
called Bark. The third class is called Cat; it also inherits from Animal and
implements an additional method called Meow.
public class Animal
{
protected short age = 0;
public short Age
{
get
{
return age;
}
set
{
if (value > 0) age = value;
}
}
public void Move()
{
Console.WriteLine("Animal is Moving");
}
}

public class Dog : Animal
{
public void Bark()
{
Console.WriteLine("Bark!");
}
}

public class Cat : Animal
{
public void Meow()
{
Console.WriteLine("Meow!");
}
}

public class MainClass
{
public static void Main()
{
Dog d = new Dog();
Cat c = new Cat();
d.Age = 3;
d.Move();
d.Bark();
c.Age = 2;
c.Move();
c.Meow();
}
}
32 Module 5: Common Type System


This code generates the following output:
Animal is Moving
Bark!
Animal is Moving
Meow!

In the Main method, a Dog object and Cat object are created. Methods and
properties of the Animal class are called on the Dog object and the Cat object.
In this way, the Animal class functionality for storing the age of an animal and
implementing movement in an animal can be written once and reused in
multiple classes.
Member Hiding
Sometimes you will want a class to inherit from a base class, but you will want
to modify some of the base classs functionality. For example, you may want a
new kind of animal class called Slug to inherit from the Animal class, but you
will want to use the Move method to make the slug move slowly. You can
accomplish this effect by creating a method with the same name and parameter
list as the base class method.
The following example shows how you can customize the Move method for
slugs by making the Slug class replace the Move method of the Animal class:
public class Slug : Animal
{
public new void Move()
{
Console.WriteLine("Moving very slowly");
}
}

public class MainClass
{
public static void Main()
{
Slug s = new Slug();
s.Move();
}
}

This code generates the following output:
Moving very slowly

In the preceding example, the new keyword is used to signal that a method was
replaced. In this context, the new keyword denotes that a method was hidden.
This use of the new keyword should not be confused with the use of the new
keyword to allocate a new object. While the new keyword is not required for
method hiding, it enhances readability. The C# compiler will issue a warning if
the new keyword is not used on replaced methods.
Module 5: Common Type System 33


Abstract Members
Methods, fields, and properties can also be abstract. A method, field, or
property is abstract when the base class does not implement the member, and
the derived class must implement the member.
Abstract members are marked with the abstract keyword. When derived
classes implement abstract methods, they must use the override keyword to
indicate that they are overriding the base-class functionality.
The following example shows an abstract Shape class that has one abstract
method called Draw. The Shape class implements a Move method, which,
when called, will call the derived class Draw method. Thus the Shape class has
information about how to move but does not have information about how to
draw after it moves. The abstract method forces the derived class to implement
the Draw method so that the Shape class can move properly.
using System;

abstract class Shape
{
protected int x, y;

//Derived class must implement next method
public abstract void Draw();
public void Move(int x, int y)
{
this.x = x;
this.y = y;
//Call derived class implementation
Draw();
}
}

class Square : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a Square at {0},{1}",x,y);
}
}

class MainClass
{
public static void Main()
{
Square s = new Square();
//Call base class Move method, which in turn calls
//derived class Draw method
s.Move(1,1);
}
}

34 Module 5: Common Type System


This code generates the following output:
Drawing a Square at 1,1

If a class contains any abstract members, the entire class becomes an abstract
class. As a result, an instance of the abstract class cannot be created because it
is missing functionality for a method. For this reason, the Shape class in the
preceding example was marked as abstract.
Sealed Classes
You can prevent other classes from deriving from a specific class by sealing
that class. Use the sealed modifier to make a sealed class.
sealed class SealedClass
{
public static void Foo()
{
}
}

The sealed modifier is primarily used to prevent unintended derivation, but it
also enables certain run time optimizations. In particular, because a sealed class
is known to never have any derived classes, it is possible to transform virtual
function member invocations on sealed class instances into non-virtual
invocations.
Module 5: Common Type System 35


Polymorphism
! Polymorphism Allows a Reference Variable to Call the
Correct Method
! Virtual Methods Enable Polymorphism in the Common
Type System
# Use the virtual keyword in the base class
# Use the override keyword in the derived class
! Sealed Methods

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Polymorphism derives from the Greek for many forms. In object-oriented
programming, polymorphism allows you to define a base class that includes
routines that perform standard operations on groups of related objects, without
regard to the exact type of each object. For example, in the preceding animal
scenario, you could add a method called MakeNoise. When this method is
called, Animal objects should print Animal is making noise. A dog object
should print Bark!, and a cat object should print Meow!
In the following example, the code does not work as expected; it prints Animal
is making noise, instead of the desired specific animal noise, such as Bark or
Meow:
public abstract class Animal
{
//..Other properties and methods
public void MakeNoise()
{
Console.WriteLine("Animal is making noise");
}
}

public class Dog : Animal
{
//..Other properties and methods
public new void MakeNoise()
{
Console.WriteLine("Bark!");
}
}

(Code continued on the following page.)
Topic Objective
To explain the use of
polymorphism to define a
base class.
Lead-in
In object-oriented
programming, polymorphism
allows you to define a base
class that includes routines
that perform standard
operations on groups of
related objects, without
regard to the exact type of
each object.
36 Module 5: Common Type System


public class Cat : Animal
{
//..Other properties and methods
public new void MakeNoise()
{
Console.WriteLine("Meow!");
}
}

public class MainClass
{
public static void Main()
{
Dog d = new Dog();
Cat c = new Cat();
d.Age = 3;
c.Age = 2;
WorkWithAnimal(d);
WorkWithAnimal(c);
}
public static void WorkWithAnimal(Animal a)
{
Console.WriteLine("Working with animal age {0}",a.Age);
a.MakeNoise();
}
}

This code generates the following output:
Working with animal age 3
Animal is making noise
Working with animal age 2
Animal is making noise

In the preceding example, because the reference in the WorkWithAnimal
method is type Animal, the Animal base class MakeNoise always gets called.
The desired behavior would be a dog object making a noise like a dog, not an
animal.
Module 5: Common Type System 37


To resolve this problem, use the virtual keyword to mark a method as virtual.
Using the virtual keyword will ensure that the correct method call gets called
on the basis of the object type, not the reference type. The derived class must
then use the override keyword on the same method, as in the following
example:
public abstract class Animal
{
//..Other properties and methods
public virtual void MakeNoise()
{
Console.WriteLine("Animal is making noise");
}
}

public class Dog : Animal
{
//..Other properties and methods
public override void MakeNoise()
{
Console.WriteLine("Bark!");
}
}

public class Cat : Animal
{
//..Other properties and methods
public override void MakeNoise()
{
Console.WriteLine("Meow!");
}
}

public class MainClass
{
public static void Main()
{
Dog d = new Dog();
Cat c = new Cat();
d.Age = 3;
c.Age = 2;
WorkWithAnimal(d);
WorkWithAnimal(c);
}
public static void WorkWithAnimal(Animal a)
{
Console.WriteLine("Working with animal age {0}",a.Age);
a.MakeNoise();
}
}

38 Module 5: Common Type System


This code generates the following output:
Working with animal age 3
Bark!
Working with animal age 2
Meow!

Sealed Methods
You can use the sealed modifier to prevent derived classes from overriding
specific methods. Methods marked with the sealed modifier are called sealed
methods. The sealed modifier can only be used in conjunction with the
override modifier.
class A
{
public virtual void Foo()
{/*...*/
}
}
class B : A
{
//Any class derived from B will
//not be able to override Foo
public sealed override void Foo()
{/*..*/
}
}

Module 5: Common Type System 39


Lab 5: Building Simple Types

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives
After completing this lab, you will be able to:
! Create enumerations.
! Create custom value types as structures.
! Inherit from a base class and override properties and methods.
! Create constructors.
! Use appropriate levels of encapsulation through the private and public
access modifiers.

Prerequisites
Before working on this lab, you must have:
! Knowledge about how to use Microsoft Visual Studio .NET for creating
and working with console applications.
! Knowledge about the C# language.
! Knowledge about the System.Console namespace for interacting with the
console.

Estimated time to complete this lab: 45 minutes
Topic Objective
To introduce the lab.
Lead-in
In this lab, you will create
simple types and
enumerations, and work
with inheritance.
40 Module 5: Common Type System


Exercise 1
Creating an Enumeration
In this exercise, you will create a simple program that uses an enumeration to
reference weekdays. You will enumerate the weekdays, Monday through
Friday, as a type. Then you will write code to accept a day from the user, match
the day to the enumerated type, and write a response back to the user.
! Create an enumeration
1. Open the Days project located in the folder <install folder>\Labs\Lab05\
Starter\Days.
2. Open the Main.cs file.
3. Create an enumeration called Weekdays. The enumeration should be of
type int and should enumerate the literals Monday, Tuesday, Wednesday,
Thursday, and Friday.

! Use an enumeration
1. Locate the Main method of the MainClass class.
2. In the Main method, write code to perform the following tasks:
a. Write a question to the console that reads, What day of the week is it?
b. Use the Console.ReadLine method to get the users response.
c. Determine which day the user selected by parsing the Weekdays
enumeration. Store the correct Weekdays literal in a variable of type
Weekdays.
d. Use a switch statement on the mapped response to determine which day
of the week it is. Based on the response, write a string to the user. For
example, for Monday, write Monday is the first weekday. For
Tuesday, write Tuesday is the second weekday, and so on.
3. Compile and test the code. Run the program and type in a weekday. You
should get an appropriate response based on the day you entered.

Module 5: Common Type System 41


Exercise 2
Creating a Custom Value Type
In this exercise, you will create a custom value type or structure to represent a
complex number. Complex numbers have a real part and an imaginary part.
You will design the structure to store the real and imaginary parts, and you will
perform addition on complex numbers.
! Create a structure
1. Open the Complex project located in the folder <install folder>\Labs\
Lab05\Starter\Complex.
2. Open the Main.cs file.
3. Create a new structure called Complex.
a. Create a public member variable called Real of type int.
b. Create a public member variable called Imaginary of type int.
4. Create an overloaded Addition operator (+) for the Complex structure. The
overloaded operator should look as follows:
public static Complex operator +(Complex c1, Complex c2)
{
}

a. In the Addition operator method, create a variable called result of type
Complex to hold the result of the addition.
b. Add c1.Real to c2.Real and store the result in result.Real.
c. Add c1.Imaginary to c2.Imaginary and store the result in
result.Imaginary.
d. Return the result.

! Use the structure
1. Locate the Main method in the MainClass class.
2. In the Main method, write code to create a variable called num1 of type
Complex.
3. Create a second variable called num2 of type Complex.
4. Initialize num1 and num2 with real and imaginary parts. For example,
num1 could have values of 2 and 3 for the real and imaginary parts. num2
could have values of 3 and 4 for the real and imaginary parts.
5. Add the two numbers together, and print both the real and imaginary parts.
6. Compile the program and run it. Verify that the result is correct. Using the
values in step 4, you should see the results of 5 and 7 for the real and
imaginary parts.

42 Module 5: Common Type System


Exercise 3
Creating Objects
In this exercise, you will derive new classes from base classes and then use the
derived classes. You will work with a base class called Polygon. The class
represents the geometric characteristics of polygons.
Polygons have a number of sides. Thus the Polygon class defines a NumSides
property but does not implement it. The derived classes implement the
NumSides property. For example, a Triangle class could be created that
overrides the NumSides property and initializes it to 3. A Pentagon class could
be created that overrides the NumSides property and initializes it to 5.
The Polygon class also implements a Degrees property that returns the sum of
all the angles in the polygon. For example, the angles in a triangle always add
up to 180 degrees. This simple calculation is based on the number of sides in
the polygon, and so the derived class must implement the NumSides property.
The Polygon class also defines an abstract method called Area that will return
the area of the polygon. The derived class must override this method to
calculate the correct area.
! Derive a new class
1. Open the Shapes project located in the folder <install folder>\Labs\Lab05\
Starter\Shapes.
2. Open the Polygon.cs file and study the code for the Polygon class. Notice
that it is an abstract class that has an abstract property called NumSides, a
property called Degrees, and an abstract method called Area that returns the
area inside the polygon.
3. Open the Main.cs file.
4. Create a new class called Square that inherits from Polygon.
a. Create private member variables called x1, y1, and size of type int to
store the Cartesian coordinates of the upper-left corner and the size of
the square, which is the length of one side.
b. Create a private member variable called numSides of type ushort to
store the number of sides that the square has.
c. Create a constructor that accepts three integer parameters (x1, y1, size) to
construct a new square. Initialize the private member variables to the
values passed as parameters. Initialize numSides to 4.
d. Override the NumSides property in the base class to return the
numSides variable.
e. Create read/write properties called X1, and Y1 respectively that map to
the private variables x1, and y1.
f. Create two read-only properties called X2 and Y2. These represent the
lower-right coordinate of the square and are calculated by adding size to
x1 or y1 respectively.
g. Override the Area method to return the area of the square. The area can
be calculated with the following formula: (size)
2
.

Module 5: Common Type System 43


! Use the derived class
1. Locate the Main method in the MainClass class.
2. In the Main method, write code to create a new Square object. Initialize the
square with values for the upper-left corner and size, such as 1, 1, and 4.
3. Write the number of degrees in the square to the console by printing the
Degrees property.
4. Write the area to the console by calling the Area method.
5. Write the x1, y1, x2, y2 coordinates to the console by using the properties of
the square object.
6. Compile the program and test it. Verify that all the methods and properties
work as expected. Given a square with upper-left coordinates of 1, 1, and a
size of 4, the number of degrees is 360, the area is 16, and the lower-right
coordinates are 5, 5.

44 Module 5: Common Type System


Review
! An Introduction to the Common Type System
! Elements of the Common Type System
! Object-Oriented Characteristics

*****************************ILLEGAL FOR NON-TRAINER USE******************************
1. What are the differences between value types and reference types?
Value types are allocated on the stack, assigned as copies, and passed by
value.
Reference types are allocated on the heap, assigned as references, and
passed by reference.


2. What are the differences between fields and properties?
A field is a data value in a class that can be directly accessed and
manipulated by other classes.
A property is a value in a class that is accessed through get and set
accessor methods. The actual data value of a property may be stored in
the class instance, or calculated when accessed.


3. How do you create an enumeration in C#?
Use the enum keyword:
enum name : type
{
literals
}


Topic Objective
To reinforce module
objectives by reviewing
key points.
Lead-in
The review questions cover
some of the key concepts
taught in the module.
Module 5: Common Type System 45


4. What is an interface?
An interface is a contractual description of a set of related methods and
properties.


5. How is encapsulation supported by the .NET Framework?
Encapsulation is supported through access modifiers, such as public,
protected, internal, and private.






THIS PAGE INTENTIONALLY LEFT BLANK








Contents
Overview 1
System.Object Class Functionality 2
Specialized Constructors 12
Type Operations 18
Interfaces 28
Managing External Types 34
Lab 6: Working with Types 38
Review 43

Module 6:
Working with Types



Information in this document, including URL and other Internet Web site references, is subject to
change without notice. Unless otherwise noted, the example companies, organizations, products,
domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious,
and no association with any real company, organization, product, domain name, e-mail address,
logo, person, place or event is intended or should be inferred. Complying with all applicable
copyright laws is the responsibility of the user. Without limiting the rights under copyright, no
part of this document may be reproduced, stored in or introduced into a retrieval system, or
transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or
otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual
property rights covering subject matter in this document. Except as expressly provided in any
written license agreement from Microsoft, the furnishing of this document does not give you any
license to these patents, trademarks, copyrights, or other intellectual property.

2001-2002 Microsoft Corporation. All rights reserved.

Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint,
Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and
Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A.
and/or other countries.

The names of actual companies and products mentioned herein may be the trademarks of their
respective owners.



Module 6: Working with Types iii


Instructor Notes
After completing this module, students will be able to:
! Apply attributes to control visibility and inheritance in classes and
interfaces.
! Create and use interfaces that define methods and properties.
! Explain how boxing and unboxing works and when it occurs.
! Use operators to determine types at run time and cast values to different
types.
! Explain what features are available to work with unmanaged types, such as
COM types.

Materials and Preparation
This section provides the materials and preparation tasks that you need to teach
this module.
Required Materials
To teach this module, you need the Microsoft PowerPoint file 2349B_06.ppt.
Preparation Tasks
To prepare for this module, you should:
! Read all of the materials for this module.
! Complete the lab.

Presentation:
75 Minutes

Lab:
45 Minutes
iv Module 6: Working with Types


Module Strategy
Use the following strategy to present this module:
! System.Object Class Functionality
In this section, discuss how System.Object provides classes to generate
hash functions, represent strings, and compare objects for identity and
equality.
Explain that this section does not cover all of the classes in System.Object,
and that other classes are covered elsewhere in the course. For example,
finalization and the Finalize method are covered in detail in Module 9,
Memory and Resource Management, in Course 2349B, Programming
with the Microsoft .NET Framework (Microsoft Visual C#

.NET).
! Specialized Constructors
This section covers more advanced types of constructors. Explain how static
constructors work, when to use them, and when to use private constructors.
If the students in your class already have experience in C++, you may not
need to spend much time on these topics.
! Type Operations
The Microsoft .NET Framework common language runtime supports a
variety of type operations for working with types. Discuss conversions and
conversion operators for determining and converting the type of an object.
Also cover how to cast types for conversion and for treating a type as a
different type.
For experienced C++ programmers, you may be able to cover type
conversion and casting quickly. C++ programmers may find it useful that
the as operator in C# is similar to dynamic_cast in C++.
Spend most of this section discussing boxing and unboxing. Students will
need to be aware of the performance consequences of boxing. Explain how
you can avoid or minimize these consequences if you must use boxing.
! Interfaces
Discuss how multiple inheritance works through interfaces and explain how
to explicitly implement interfaces.
As with the other topics in this module, you may be able to cover this
section quickly if your audience is already familiar with object-oriented
programming techniques.
For experienced C++ programmers, consider mentioning that explicit
interface implementation was not possible in C++.
! Managing External Types
Briefly introduce Platform Invocation Services and COM interoperability.
Be aware that more information about these topics is available in Module
15, Interoperating Between Managed and Unmanaged Code, in Course
2349B, Programming with the Microsoft .NET Framework (Microsoft
Visual C# .NET) and Interoperating with Unmanaged Code in the .NET
Framework SDK documentation.

Module 6: Working with Types 1


Overview
! System.Object Class Functionality
! Specialized Constructors
! Type Operations
! Interfaces
! Managing External Types

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this module, you will learn how to apply your knowledge of the Common
Type System to various programming scenarios. This module will help you
understand how to use types efficiently when developing Microsoft .NET
Framework applications. You should understand that many nuances in the type
system can affect program clarity and performance if ignored.
This module covers the use of attributes to control visibility and inheritance on
types and explains how to work with various type operations, such as boxing
and unboxing, and type operators. The module then explores how to work with
types programmatically by using operators to coerce, cast, or discover types at
run time.
In addition, this module discusses how to build an interface that supports
methods and properties and how to make interface designs more efficient.
The module also highlights features that are designed to help you work with
unmanaged types, such as COM types.
After completing this module, you will be able to:
! Apply attributes to control visibility and inheritance in classes and
interfaces.
! Create and use interfaces that define methods and properties.
! Explain how boxing and unboxing works and when it occurs.
! Use operators to determine types at run time and cast values to different
types.
! Explain what features are available to work with unmanaged types, such as
COM types.

Topic Objective
To provide an overview of
the module topics and
objectives.
Lead-in
In this module, you will learn
how to apply your
knowledge of the Common
Type System to various
programming scenarios.
2 Module 6: Working with Types


" "" " System.Object Class Functionality
! Hash Codes
! Identity
! Equality
! String Representation

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this section, you will learn about the common methods that you need to
override on the System.Object class.
This section does not cover finalization and the Finalize method, which are
covered in detail in Module 9, Memory and Resource Management, in
Course 2349B, Programming with the Microsoft .NET Framework (Microsoft
Visual C#

.NET). Also, this section does not cover the MemberwiseClone


method.
Topic Objective
To provide an overview of
the topics covered in this
section.
Lead-in
In this section, you will learn
about the common methods
that you need to override on
the System.Object class.
Module 6: Working with Types 3


Hash Codes
! Hash Code Used to Perform Quick Lookups
! Override GetHashCode Method on System.Object
! Should Return Same Hash Code for Objects of
Same Value
! Should Implement Efficient Algorithm
struct Student {
string name;
int ID; //Unique for each instance
public override int GetHashCode() {
return ID;
}
}
struct Student {
string name;
int ID; //Unique for each instance
public override int GetHashCode() {
return ID;
}
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
A hash function is used to quickly generate a number, or hash code, that
corresponds to the value of an object. Hash codes are useful for performing
quick lookups in tables, such as the HashTable class, and other kinds of
collections.
A hash table uses the hash code to drastically limit the number of objects that
must be searched to find a specific object in a collection of objects. The hash
table does this by getting the hash value of the object and eliminating all objects
with a different hash code. This preliminary search leaves only those objects
with the same hash code to be searched. Because there are few instances with
that hash code, searches are much quicker.
System.Object provides a GetHashCode method, which returns an int type.
You should override this method to return a hash code on any custom classes or
structures that you create. One reason for overriding this method is that when
two objects are equal in value, you should get the same hash code for each
object if you call GetHashCode. In the case of custom objects, the default
implementation of GetHashCode does not give you the same hash code for two
objects that are equal in value.
Topic Objective
To explain how to use hash
codes to perform quick
lookups in tables and other
types of collections.
Lead-in
A hash function is used to
quickly generate a number,
or hash code, that
corresponds to the value of
an object.
4 Module 6: Working with Types


A good hash code algorithm will support the best performance by generating a
random distribution for all input. You should base your hash code algorithm on
one of the unique fields in the class. Also, you should never throw an exception
from the GetHashCode method because GetHashCode can be called
frequently and should always work reliably.
The following example shows how to implement GetHashCode for a Student
structure that stores a students name and ID.
struct Student
{
string name;
int ID; //Unique for each instance
public override int GetHashCode()
{
return ID;
}
}

Module 6: Working with Types 5


Identity
! Compare to Determine If Two References Are Actually
the Same Object
! Use the Object.ReferenceEquals Method to Test Identity

*****************************ILLEGAL FOR NON-TRAINER USE******************************
There are two kinds of comparison for objects: identity and equality. This topic
covers object identity.
Determining Identity
Two objects are identical if they are, in fact, the same object. Every object in
the .NET Framework common language runtime has an identity that makes it
unique in the system.
In C++, an objects identity is determined by its address. Thus, if two pointers
are compared and contain the same address, they point to the same object.
In COM, an objects identity is determined by the IUnknown interface. Thus, if
two IUnknown interface pointers are compared and contain the same address,
they are the same COM object.
In the .NET Framework common language runtime, you can use the
Object.ReferenceEquals method to compare for identity. Internally,
ReferenceEquals compares the addresses of the objects in memory to
determine if they are the same object. If they are the same object,
ReferenceEquals returns true.
Topic Objective
To explain how identity is
determined in the .NET
Framework common
language runtime.
Lead-in
There are two kinds of
comparison for objects:
identity and equality. This
topic covers object identity.
6 Module 6: Working with Types


Using the Object.ReferenceEquals Method
In the following example, a value type variable called x is created and passed in
two parameters to the Test method. The Test method compares the two
parameters to determine if they are identical.
class MyObject
{
public int X;
}

class MainClass
{
public static void Main()
{
MyObject obj1 = new MyObject();
obj1.X = 5;
Test(obj1, obj1);
MyObject obj2 = new MyObject();
obj2.X = 5;
Test(obj1, obj2);
}
public static void Test(MyObject a, MyObject b)
{
if (Object.ReferenceEquals(a,b))
Console.WriteLine("Identical");
else
Console.WriteLine("Not Identical");
}
}

This code generates the following output:
Identical
Not Identical

Module 6: Working with Types 7


Equality
! Comparing Two Objects to Determine If They Are Equal
! Override the Equals Method
! Supply == and != Operators
! Guidelines
# If overriding Equals, override GetHashCode
# If overloading ==, override Equals to use same
algorithm
# If implementing IComparable, implement Equals
# Equals, GetHashCode, and == operator should never
throw exceptions

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objects can also be compared for equality. The Object.Equals method,
equality operator (==), or inequality operator (!=) are used to test equality.
Equals Method
The Equals method is part of the Object class. You should override this
method in your classes and structures to perform appropriate behavior when
comparing objects of certain types. The default Object.Equals method calls
Object.ReferenceEquals, which results in an identity comparison instead of a
value comparison. In general, you should also override the == and != operators
to allow easier syntax to compare objects.
Topic Objective
To explain how to override
the Equals method and to
introduce guidelines for
implementing code to
provide equality comparison
for types.
Lead-in
Objects can also be
compared for equality.
8 Module 6: Working with Types


The following example shows how to override the Equals method and the ==
and != operators to test user-defined Rectangle objects for equality.
class Rectangle
{
//Rectangle coordinates
public int x1,y1,x2,y2;
public Rectangle(int x1, int y1, int x2, int y2)
{
this.x1 = x1;
this.x2 = x2;
this.y1 = y1;
this.y2 = y2;
}
public override int GetHashCode()
{
return x1;
}
public override bool Equals (Object obj)
{
//Check for null and compare run-time types.
if (obj == null || GetType() != obj.GetType()) return
false;
Rectangle r = (Rectangle)obj;
return (x1 == r.x1) && (y1 == r.y1) && (x2 == r.x2) &&
(y2 == r.y2);
}
static public bool operator == (Rectangle r1, Rectangle r2)
{
//Check for null parameters
//Cast to object to avoid recursive call
if ((object)r1 == null) return false;
//Let Equals method handle comparison
return r1.Equals(r2);
}
static public bool operator != (Rectangle r1, Rectangle r2)
{
//Check for null parameters
//Cast to object to avoid recursive call
if ((object)r1 == null) return true;
//Let Equals method handle comparison
return !r1.Equals(r2);
}
}

class MainClass
{
public static void Main()
{
Rectangle r1 = new Rectangle(5,5,50,55);
Rectangle r2 = new Rectangle(5,5,50,55);
Console.WriteLine(r1.Equals(r2));
Console.WriteLine(r1 == r2);
Console.WriteLine(null == r1);
}
}
Module 6: Working with Types 9


This code generates the following output:
True
True
False

Guidelines for Equality Comparison
Use the following guidelines when implementing code to provide equality
comparison for types.
! Anytime you override the Equals method, also override the GetHashCode
method. If two objects are equal, they must return the same hash code. The
default implementation of GetHashCode does not return the same value.
! Anytime you overload the == operator, also override the Equals method
and the != operator to use the same algorithm. This technique allows
infrastructure code, such as HashTable and ArrayList classes, that uses the
Equals method, to behave in the same manner as user code that is written
with the == operator.
! Anytime you implement the IComparable interface, also implement the
Equals method, and ensure that both elements use the same algorithm for
comparisons. You should also consider overloading the comparison
operators because any client code that uses the IComparable interface is
also likely to use these operators.
! The Equals method, GetHashCode method, and comparison operators
should never throw an exception. Exceptions in comparison operators can
cause confusion because most programmers do not anticipate these types of
exceptions. Also, these methods are called frequently and need to be
efficient and clean to avoid writing additional error-handling code for what
are considered simplistic methods.

10 Module 6: Working with Types


String Representation
! Override ToString to Customize String Form of a Class
! Use IFormattable Interface and ToString Method for
Localized Strings
struct President
{
public string FirstName;
public string LastName;
public override string ToString()
{
return FirstName + " " + LastName;
}
}
struct President
{
public string FirstName;
public string LastName;
public override string ToString()
{
return FirstName + " " + LastName;
}
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
One characteristic of all objects is the ability to represent themselves in a string
form. The ToString method provides this ability for all objects. Methods in the
Microsoft .NET Framework common language runtime, such as
Console.WriteLine, frequently use ToString. The ToString method also is
useful for debugging.
The default behavior of the Object.ToString method is to return the name of
the class. The following example shows what happens when ToString is called
on a President structure.
struct President
{
public string FirstName;
public string LastName;
}
class MainClass
{
public static void Main()
{
President firstPres;
firstPres.FirstName = "George";
firstPres.LastName = "Washington";
Console.WriteLine(firstPres.ToString());
}
}

This code generates the following output:
President

Topic Objective
To explain how the
ToString method is used in
the .NET Framework
common language runtime.
Lead-in
All objects have the ability to
represent themselves in a
string form.
Module 6: Working with Types 11


You can override the ToString method to provide custom behavior, as shown
in the following example:
struct President
{
public string FirstName;
public string LastName;
public override string ToString()
{
return FirstName + " " + LastName;
}
}
class MainClass
{
public static void Main()
{
President firstPres;
firstPres.FirstName = "George";
firstPres.LastName = "Washington";
Console.WriteLine(firstPres.ToString());
}
}

This code generates the following output:
George Washington

If an object needs to be represented in localized string forms, you should not
override Object.ToString. Instead, you should implement the IFormattable
interface and its ToString method. The IFormattable interface can take into
account different locales.
12 Module 6: Working with Types


" "" " Specialized Constructors
! Static Constructors
! Private Constructors

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This section covers more advanced types of constructors. It explains how static
constructors work, when to use them, and when to use private constructors.
Topic Objective
To provide an overview of
the topics covered in this
section.
Lead-in
This section covers more
advanced types of
constructors. It explains how
static constructors work,
when to use them, and
when to use private
constructors.
Module 6: Working with Types 13


Static Constructors
! Used to Initialize Static Members
! .cctor in Disassembly
class DeviceConnection
{ public static uint ConnectionCount;
public void OpenConnection(string connectionName)
{ ConnectionCount++;
//Other work to open device }
static DeviceConnection()
{ //Initialize static members
ConnectionCount = 0; }
}
class DeviceConnection
{ public static uint ConnectionCount;
public void OpenConnection(string connectionName)
{ ConnectionCount++;
//Other work to open device }
static DeviceConnection()
{ //Initialize static members
ConnectionCount = 0; }
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Static constructors are used to initialize static fields. Static constructors are also
known as class constructors and type constructors. Static constructors are called
after a program begins running but before the first instance of the class is
created.
Topic Objective
To explain how static
constructors work.
Lead-in
Static constructors are used
to initialize static fields.
14 Module 6: Working with Types


The following example shows a DeviceConnection class that represents a
generic connection to a device. The class maintains a static field that holds the
current connection count. The static constructor is created with the same name
as the class but has the static attribute, rather than the public or private
attribute.
class DeviceConnection
{
public static uint ConnectionCount;
public void OpenConnection(string connectionName)
{
ConnectionCount++;
//Other work to open device
}
static DeviceConnection()
{
//Initialize static members
ConnectionCount = 0;
}
}

class MainClass
{
public static void Main()
{
// At some point before next line,
// static constructor is called
DeviceConnection d = new DeviceConnection();
d.OpenConnection("GameConsole:Joy1/3");
// Next line prints 1
Console.WriteLine(DeviceConnection.ConnectionCount);
}
}

A static constructor has no access modifiers, such as private or public. Inside a
static constructor, only static fields can be used. Instance fields must be
initialized in an instance constructor.
When the static constructor is viewed in disassembly, it has a different name,
.cctor, which stands for class constructor. In disassembly, instance constructors
are called .ctor. You can have both types of constructors in the same class.
Module 6: Working with Types 15


If you initialize a static field inline with a value, a static constructor is created
automatically. The following example shows how the DeviceConnection class
can be rewritten to use an implicit static constructor. The presence of the static
constructor can be verified by viewing the disassembly of the code.
class DeviceConnection
{
//Next line automatically creates static constructor
public static uint ConnectionCount = 0;
public void OpenConnection(string connectionName)
{
ConnectionCount++;
//Other work to open device
}
}

In the preceding example, the static field ConnectionCount is initialized inline
to a value of 0. When you initialize static fields inline, a static constructor is
implicitly created in which the initialization occurs. If you also provide an
explicit static constructor, the inline initialization is compiled into the explicit
static constructors. Inside the static constructor, the code for the inline
initializations runs first, and then the code that you wrote in the static
constructor runs.
16 Module 6: Working with Types


Private Constructors
! Prevent a Class from Being Instantiated
! Use Them on Classes with All Static Members
! Use a Protected Constructor to Inherit from the Class
class Trig
{
public static double Sin (double x)
{ //Calculate and return sin(x) }
public static double Cos (double x)
{ //Calculate and return cos(x) }
public static double Tan (double x)
{ //Calculate and return tan(x) }
private Trig(){}
}
class Trig
{
public static double Sin (double x)
{ //Calculate and return sin(x) }
public static double Cos (double x)
{ //Calculate and return cos(x) }
public static double Tan (double x)
{ //Calculate and return tan(x) }
private Trig(){}
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
A private constructor can never be called. Therefore any class with a private
constructor cannot be instantiated. The only type of class that uses a private
constructor is a class with all static members.
The following example shows how a private constructor is used to prevent a
class from being instantiated.
class Trig
{
public static double Sin (double x)
{
//Calculate and return sin(x)
}
public static double Cos (double x)
{
//Calculate and return cos(x)
}
public static double Tan (double x)
{
//Calculate and return tan(x)
}
private Trig(){}
}

Topic Objective
To explain when to use
private constructors.
Lead-in
A private constructor can
never be called. Therefore
any class with a private
constructor cannot be
instantiated.
Module 6: Working with Types 17


Classes with all static members can be used to maintain global algorithms, as
shown in the preceding example. They can also be used as a singleton class,
which has only one set of values and methods that is available while a program
is running.
To derive from a class with static members, you should mark the constructor as
protected. Marking the constructor as protected will prevent programmers from
creating instances of the class, but allow other classes to derive from it.

A class with static members is different than an interface. The static
members can contain implementations, a class can have static fields, but an
interface cannot.

Note
18 Module 6: Working with Types


" "" " Type Operations
! Conversions
! Casting
! Boxing

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The .NET Framework common language runtime supports a variety of type
operations for working with types. This section discusses conversions and
conversion operators for determining and converting the type of an object. This
section also discusses how to cast types for conversion and for treating a type as
a different type. It also describes boxing and unboxing value types to treat them
as reference types.
Topic Objective
To provide an overview of
the topics covered in this
section.
Lead-in
The .NET Framework
common language runtime
supports a variety of type
operations for working with
types.
Module 6: Working with Types 19


Conversions
! Explicit Conversions
! Implicit Conversions
! Conversion Operators
int x = 5;
double y = (double) x;
int x = 5;
double y = (double) x;
int x = 5;
double y = x;
int x = 5;
double y = x;
public static implicit operator byte(Digit d)
public static explicit operator Digit(byte b)
public static implicit operator byte(Digit d)
public static explicit operator Digit(byte b)

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Conversion is the process of changing a type to another type. Conversions are
necessary when a value of one type must be assigned to a variable of a different
type during assignment or when passing arguments to a method call.
Explicit and Implicit Conversions
In explicit conversions, you do not need to specify the name of the type in a cast
for assignment conversions or operand conversions.
The following example shows how to convert an int value to a double value by
using an explicit conversion.
public static bool BiggerThanFive(double value)
{
if (value > 5) return true;
else return false;
}
public static void Main()
{
int x = 5;
//Use an explicit conversion for assignment
double y = (double) x;
//Use an explicit conversion for operand
bool answer = BiggerThanFive((double) x);
}

The preceding example is an example of a widening conversion. The bit size of
an int is 32 bits, and the bit size of a double is 64 bits. Therefore, the type was
widened during the conversion.
Topic Objective
To explain when and how
to use explicit and implicit
conversions.
Lead-in
Conversion is the process
of changing a type to
another type.
20 Module 6: Working with Types


In implicit conversions, you do not need to specify the name of the type during
the conversion. Widening conversions can occur implicitly, and so they allow
for simpler syntax. The preceding example could be written more simply as
follows:
public static void Main()
{
int x = 5;
//Use an implicit conversion for assignment
double y = x;
//Use an implicit conversion for operand
bool answer = BiggerThanFive(x);
}

The following table shows the allowable implicit conversions for numeric types
in the common language runtime.
From To

sbyte short, int, long, float, double, or decimal
byte short , ushort, int, uint, long, ulong, float, double, or decimal
short int, long, float, double, or decimal
ushort int, uint, long, ulong, float, double, or decimal
int long, float, double, or decimal
uint long, ulong, float, double, or decimal
long float, double, or decimal
char ushort, int, uint, long, ulong, float, double, or decimal
float double
ulong float, double, or decimal

Some conversions can result in a loss of precision. For example, converting an
int to a float is an allowable implicit conversion, but a float has only seven
digits of precision. Depending on the value of the int, some digits of precision
may be lost.
The following example shows a narrowing conversion in which a double is
converted to an integer.
double y = 4.56;
int x = (int) y;

In this case, narrowing conversions must be explicit because there is almost
always a loss of precision in the value that is being converted. In the preceding
example, the value 4.56 will be truncated to 4 when it is assigned to x.
Module 6: Working with Types 21


Conversion Operators
You can create user-defined conversions by providing conversion operators in
your class or struct.
Use the following syntax for specifying a conversion operator:
public static [implicit | explicit] operator conv-type-out (conv-type-in
operand)
in which:
! conv-type-out is the name of the type to convert to.
! conv-type-in is the name of the type to convert from.
! operand is the name of the parameter holding the value being converted.

The following example shows how a structure that is called Digit, which
represents a value from 0 to 9, can convert implicitly to a byte and explicitly to
a Digit.
struct Digit
{
byte value;
public Digit(byte value)
{
if (value < 0 || value > 9)
throw new ArgumentException();
this.value = value;
}
public static implicit operator byte(Digit d)
{
//Implicitly convert from Digit to short
return d.value;
}
public static explicit operator Digit(byte b)
{
//Explicitly convert from short to Digit
return new Digit(b);
}
}
class MainClass
{
public static void Main()
{
Digit dig = new Digit(3);
byte b = dig; //Implicit conversion operator invoked
Console.WriteLine(b); //Prints 3
Digit dig2 = (Digit) b; //Explicit conversion invoked
Console.WriteLine(dig2); //Implicit conversion prints 3
}
}

22 Module 6: Working with Types


In the preceding example, the conversion from Digit to byte is implicit, while
the conversion from byte to Digit is explicit. You should use the following
guidelines to determine whether a conversion operator should be implicit or
explicit.
! A conversion operator should be implicit to make code easier to read.
Implicit conversions should never throw an error.
! A conversion operator should be explicit whenever information could be
lost in the conversion or if the conversion could throw an exception.

Module 6: Working with Types 23


Casting
! Casting Up from Derived Class to Base Class
! Casting Down from Base Class to Derived Class
! Type Operators
# is
# as
# typeof
! Casting Interfaces

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Casting is used for explicit conversions. However, casting is also used for
changing the type that is used to reference an object.
Casting in an Inheritance Hierarchy
Casting on objects is frequently used to change the reference type of an object
to a base class reference or a derived class reference. When casting a derived
class to a base class, you do not need to explicitly cast the object. The following
example shows how a Shape type reference can be used to work with a Square
object.
//Shape is a base class
class Shape {...}
//Square is a derived class
class Square : Shape {...}

Square sq = new Square();
Shape sh = sq;
sh.ShapeMethod();

When casting from a base class to a derived class, you must explicitly cast the
object. If you cast to a derived type that does not match the underlying object,
an InvalidCastException error will be thrown.
Topic Objective
To explain how casting is
used in the .NET
Framework.
Lead-in
Casting is used for explicit
conversions. However,
casting is also used for
changing the type that is
used to reference an object.
24 Module 6: Working with Types


The following example shows how to cast from a base class type to a derived
class type.
Square sq = new Square();
Shape sh = sq;
Square sq2 = (Square) sh;//Cast down to Square
sh = new Shape();
sq2 = (Square) sh;//InvalidCastException

Type Operators
C# provides several operators to help cast object types appropriately. These
operators help you avoid InvalidCastException errors.
The is operator compares an object to a type. If the type matches the object,
true is returned. If the type does not match the object, false is returned.
Shape sh = new Shape();
Square sq;
if (sh is Square) sq = (Square) sh;

The as operator attempts to convert an object to a type. If the conversion is
successful, a reference of the specified type is assigned. Otherwise, null is
assigned.
Shape sh = new Shape();
Square sq = sh as Square;
if (sq == null)
Console.WriteLine("Shape was not a square");
else
Console.WriteLine("Shape was a square");

You can use the typeof operator for more advanced type operations. The typeof
operator will return a System.Type instance that describes the type through
reflection. If you have an instance of a class, you can call GetType to obtain the
same information.
For more information about reflection, the mechanism for obtaining
information about loaded assemblies and the types defined within them,
including classes, interfaces, and value types, see Discovering Type
Information at Run time, in the .NET Framework Software Developers Kit
(SDK) documentation and Module 17, Attributes, in Course 2349B,
Programming with the Microsoft .NET Framework (Microsoft Visual C#
.NET).
Casting Interfaces
Casting is also used to obtain interface references to an object. The following
example shows how the interface ICDPlayer can be obtained through casting
from the Device class, which implements the ICDPlayer interface.
ICDPlayer player;
Device d = new Device();
player = (ICDPlayer) d;//Cast to interface
player.Play();

Module 6: Working with Types 25


Boxing
! Boxing Occurs to Convert a Value Type to a
Reference Type
# Instance of System.Object is allocated on heap
# Value type is copied to new object
! Unboxing Occurs to Retrieve Value Type from Object
! Boxing Can Be Expensive If Inside Loops
int x = 5; //Value type
Object o = x; //Boxed
Console.WriteLine("The answer is : {0} ", x);//Boxed
int x = 5; //Value type
Object o = x; //Boxed
Console.WriteLine("The answer is : {0} ", x);//Boxed
int y = (int) o; //unbox
int y = (int) o; //unbox

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Boxing and unboxing are conversions that occur automatically when a value
type is converted to an object or when an object is converted to a value type.
Boxing and unboxing occur implicitly and can affect the performance of your
application code.
When Boxing Occurs
Boxing occurs when a value type must be converted to a reference type. Boxing
only occurs when converting to the type System.Object. Any other reference
type must have implicit or explicit conversion operators to convert from value
types.
The following example shows how an integer value type is converted to an
object reference type.
int x = 5; //Value type
Object o = x; //Implicit boxing operation occurs

In the preceding example, a boxing operation occurs when the object o is
assigned the value in x.
Boxing frequently occurs when calling methods that accept parameters of type
Object. For example, Console.WriteLine can accept a string and an object
parameter as follows.
int x = 5;
Console.WriteLine("The answer is: {0}",x);//Boxing occurs

In the preceding example, a boxing operation occurs on the call to
Console.WriteLine. Because Console.WriteLine requires an Object type as
the second parameter, x is implicitly boxed as an Object type.
Topic Objective
To explain when and how
boxing occurs.
Lead-in
Boxing and unboxing are
conversions that occur
automatically when a value
type is converted to an
object or when an object is
converted to a value type.
26 Module 6: Working with Types


You can determine where boxing operations occur in your code by examining
the disassembly. For example, if you use the Microsoft intermediate language
(MSIL) Disassembler (Ildasm.exe), you will see an operation code called box,
which is followed by the type that is being boxed.
How Boxing Works
A value type must be copied from the stack to the heap if the value is going to
be treated as a reference type. The boxing operation allocates memory on the
heap for the value type and creates a reference to the new memory location.
Then it copies the value type into the new memory location.
Unboxing
To work with the value inside a boxed type, it must first be unboxed. Unboxing
a type will copy it from the heap into a variable on the stack.
The following example shows how to unbox an integer.
int x = 5;
Object o = x; //box
int y = (int) o; //unbox

If the underlying value type that is being unboxed is incompatible with the type
of the variable that it is assigned to, you will get an InvalidCastException.
int x = 5;
short s;
Object o = x; //box
s = (short) o; //Will throw InvalidCastException

Consequences
Boxing can cause performance problems if you do not minimize its occurrence.
The following example shows the consequences of boxing.
int age = 5;
int count = 1;
for (count = 1;count < 10;count++)
{
Console.WriteLine("Current Age = {0}",age);//1 Box
Console.WriteLine("Age in {0} year(s) will be {1}",
count, age+count);//2 box operations
}

In the preceding example, three boxing operations will occur for every iteration
through the loop. This kind of scenario can be very costly in terms of
performance. One solution is to box value types before entering a loop, as in the
following example.
int age = 5;
Object oAge = age; //1 box operation
int count = 1;
for (count = 1;count < 10;count++)
{
Console.WriteLine("Current Age = {0}",oAge);
Console.WriteLine("Age in {0} year(s) will be {1}",
count, age+count);//2 box operations
}
Module 6: Working with Types 27


In cases when the need for a higher level of performance is necessary, custom
classes can be created to encapsulate values and work with values as reference
types.
class RefInt32
{
public int Value;
public RefInt32(int value)
{
this.Value = value;
}
public override string ToString()
{
return Value.ToString();
}
public static implicit operator int (RefInt32 refInt)
{
return refInt.Value;
}
}

int age = 5;
RefInt32 refAge = new RefInt32(age); //Convert to ref type
int count = 1;
RefInt32 refCount = new RefInt32(count); //Convert to ref type
RefInt32 refTotal = new RefInt32(0);
for (refCount.Value = 1;refCount.Value < 10;refCount.Value++)
{
refTotal.Value = refAge.Value + refCount.Value;
Console.WriteLine("Current Age = {0}",refAge);
Console.WriteLine("Age in {0} year(s) will be {1}",
refCount,refTotal);//0 box operations
}

The preceding example demonstrates the importance of overriding appropriate
Object methods to get expected behavior. If the recipient of your reference type
calls one of the methods that are expecting appropriate behavior, it may cause
problems. In the preceding example, you must override ToString.
28 Module 6: Working with Types


" "" " Interfaces
! Inheritance Considerations
! Explicit Interface Implementation

*****************************ILLEGAL FOR NON-TRAINER USE******************************
When designing and using interfaces, you should consider a number of factors.
This section discusses how multiple inheritance works through interfaces. This
section also explains how to explicitly implement interfaces.
Topic Objective
To provide an overview of
the topics covered in this
section.
Lead-in
When designing and using
interfaces, you should
consider a number of
factors.
Module 6: Working with Types 29


Inheritance Considerations
! Multiple Inheritance
! Deriving New Interfaces from Existing Ones
! Base Class Interfaces
interface IFoo {void DoSomething1();}
interface IBar {void DoSomething2();}
class MyObject : IFoo, IBar {...}
interface IFoo {void DoSomething1();}
interface IBar {void DoSomething2();}
class MyObject : IFoo, IBar {...}
interface INewInterface : IFoo, IBar
{void DoSomething3();}
interface INewInterface : IFoo, IBar
{void DoSomething3();}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Interfaces are used only through inheritance. There are several ways to group or
partition functionality when you are using interfaces. You can use multiple
inheritance to combine functionality from multiple interfaces. Interfaces also
can inherit from other interfaces, allowing functionality to be partitioned into
multiple groups.
Multiple Inheritance
In the .NET Framework common language runtime, a class must inherit from
only one class. However, a class can inherit from zero or more interfaces in
addition to inheriting from one class. Interfaces are the only type to allow
multiple inheritance in the common language runtime. The following example
shows how a class can inherit from two interfaces.
interface IFoo
{
void DoSomething1();
}
interface IBar
{
void DoSomething2();
}
class MyObject : IFoo, IBar
{
public void DoSomething1()
{
Console.WriteLine("DoSomething1 called");
}
public void DoSomething2()
{
Console.WriteLine("DoSomething2 called");
}
}
Topic Objective
To explain the use of
inheritance and the use of
interfaces.
Lead-in
Interfaces are used only
through inheritance.
30 Module 6: Working with Types


Multiple inheritance is an important design consideration when you are
choosing between abstract classes and interfaces. If two sets of functionality are
designed into two abstract classes, a single class cannot inherit from both
classes. However, if interfaces are used, a single class can inherit from both
interfaces. If a specification of functions, properties, or events is likely to be
used by many different kinds of classes, you should use an interface instead of
an abstract class.
Deriving New Interfaces from Existing Ones
You can also create new interfaces by deriving from other interfaces. The
following example shows how a new interface that is called INewInterface is
created from the IFoo and IBar interfaces.
interface IFoo
{
void DoSomething1();
}
interface IBar
{
void DoSomething2();
}
interface INewInterface : IFoo, IBar
{
void DoSomething3();
}
class MyObject : INewInterface
{
public void DoSomething1()
{
Console.WriteLine("DoSomething1 called");
}
public void DoSomething2()
{
Console.WriteLine("DoSomething2 called");
}
public void DoSomething3()
{
Console.WriteLine("DoSomething3 called");
}
}

Module 6: Working with Types 31


Base Class Interfaces
If you derive a new class from a base class that implements an interface, you
must decide if the derived class will also implement the interface. When an
instance of the derived class is converted to the interface, the inheritance
hierarchy is searched until a class that implements the interface is found. Then
the interface methods are called on that class.
The following example shows what happens when interface methods are called
on a derived class that does not implement the interface.
interface IFoo
{
void DoSomething1();
}
class Base : IFoo
{
public void DoSomething1()
{
Console.WriteLine("Base DoSomething1 called");
}
}
class Derived : Base
{
public new void DoSomething1()
{
Console.WriteLine("Derived DoSomething1 called");
}
}
class MainClass
{
public static void Main()
{
Derived d = new Derived();
IFoo i = (IFoo) d;
d.DoSomething1();
i.DoSomething1();
}
}

This code generates the following output:
Derived DoSomething1 called
Base DoSomething1 called

In the preceding example, the base class DoSomething1 method is called when
an IFoo variable is used. If this behavior is not the desired behavior, then the
derived class must be modified to also inherit from the IFoo interface. Then,
the derived class DoSomething1 method will be called when the IFoo variable
is used.
32 Module 6: Working with Types


Explicit Interface Implementation
! Class vs. Interface Accessibility
! Use Explicit Interface Implementation
# When interface member names that are combined with
class names are confusing
# When inheriting from multiple interfaces with same
members
interface IFoo {void DoSomething();}
interface IBar {void DoSomething();}
class MyObject : IFoo, IBar {
void IFoo.DoSomething()
{Console.WriteLine("IFoo DoSomething called");}
void IBar.DoSomething()
{Console.WriteLine("IBar DoSomething called");}
}
interface IFoo {void DoSomething();}
interface IBar {void DoSomething();}
class MyObject : IFoo, IBar {
void IFoo.DoSomething()
{Console.WriteLine("IFoo DoSomething called");}
void IBar.DoSomething()
{Console.WriteLine("IBar DoSomething called");}
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
When you implement an interface in a class, the interface methods are exposed
through the interface type and the class type.
The following example shows how an interface method can be called by both
the class type and the interface type.
interface IFoo
{
void DoSomething();
}
class MyObject : IFoo
{
public void DoSomething()
{
Console.WriteLine("DoSomething called");
}
}
class MainClass
{
public static void Main()
{
MyObject o = new MyObject();
//Call method through class type
o.DoSomething();
IFoo foo = (IFoo) o;
//Call method through interface type
foo.DoSomething();
}
}

Topic Objective
To distinguish class
accessibility and interface
accessibility, and to explain
when to use explicit
interface implementation.
Lead-in
When you implement an
interface in a class, the
interface methods are
exposed through the
interface type, and the class
type.
Module 6: Working with Types 33


You can use explicit interface implementation to prevent the class type from
accessing the method. This technique is useful if the interface member names
are likely to confuse a user of the class. Also, explicit interface implementation
is mandatory if you inherit from multiple interfaces that have the same
members.
To use explicit interface implementation, you prepend the name of the interface
to the name of the member in your class definition. You should not use any
access modifiers in the member definition. Access modifiers are not allowed
because the explicit interface implementation is only accessible through the
interface type.
The following example shows how explicit interface implementation can avoid
conflicts between multiple interfaces with the same members.
interface IFoo
{
void DoSomething();
}
interface IBar
{
void DoSomething();
}

class MyObject : IFoo, IBar
{
void IFoo.DoSomething()
{
Console.WriteLine("IFoo DoSomething called");
}
void IBar.DoSomething()
{
Console.WriteLine("IBar DoSomething called");
}
}

class MainClass
{
public static void Main()
{
MyObject o = new MyObject();
IFoo foo = (IFoo) o;
IBar bar = (IBar) o;
foo.DoSomething();
bar.DoSomething();
}
}

This code generates the following output:
IFoo DoSomething called
IBar DoSomething called

34 Module 6: Working with Types


" "" " Managing External Types
! Platform Invocation Services
! COM Interoperability

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The .NET Framework common language runtime is not an isolated system.
Most programs will require some level of interaction with outside applications.
The common language runtime provides Platform Invocation Services for
interacting with code in DLLs. Also, the common language runtime provides
integration services to interact with COM objects.

Using unmanaged external types is risky because the common
language runtime cannot enforce security, manage memory, or provide code
type safety verification checking for unmanaged code.

This module does not cover all aspects of Platform Invocation Services or COM
Integration Services. For more information, see Module 15, Interoperating
Between Managed and Unmanaged Code, in Course 2349B, Programming
with the Microsoft .NET Framework (Microsoft Visual C# .NET) and
Interoperating with Unmanaged Code in the .NET Framework SDK
documentation.
Topic Objective
To provide an overview of
the topics covered in this
section.
Lead-in
The .NET Framework
common language runtime
is not an isolated system.
Most programs will require
some level of interaction
with outside applications.
Warning
Module 6: Working with Types 35


Platform Invocation Services
! Also Known as PInvoke
! Use DllImport Attribute to Import an API Function from
an External DLL
[DllImport("user32.dll", CharSet=CharSet.Ansi)]
public static extern int MessageBox(int h, string m,
string c, int type);
public static void Main()
{ string pText = "Hello World!";
string pCaption = "PInvoke Test";
MessageBox(0, pText, pCaption, 0); }
[DllImport("user32.dll", CharSet=CharSet.Ansi)]
public static extern int MessageBox(int h, string m,
string c, int type);
public static void Main()
{ string pText = "Hello World!";
string pCaption = "PInvoke Test";
MessageBox(0, pText, pCaption, 0); }

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Platform Invocation Services, also known as PInvoke, allow managed code to
interact with unmanaged code in existing DLLs. PInvoke provides the code that
is needed to locate and invoke a function and to marshal parameters to and from
the function.
To call an external function, you use the DllImport attribute to provide the
name of the DLL and any other characteristics, such as the use of ANSI or
Unicode strings by the DLL. After the function has been declared, you can call
it from managed code.
The following example shows how to call the MessageBox API located in
User32.dll.
using System;
using System.Runtime.InteropServices;

class MainClass
{
[DllImport("user32.dll", CharSet=CharSet.Ansi)]
public static extern int MessageBox(int h, string m,
string c, int type);

public static void Main()
{
string pText = "Hello World!";
string pCaption = "PInvoke Test";
MessageBox(0, pText, pCaption, 0);
}
}

For more information on PInvoke, see Module 15, Interoperating Between
Managed and Unmanaged Code, in Course 2349B, Programming with the
Microsoft .NET Framework (Microsoft Visual C# .NET) and Interoperating
with Unmanaged Code in the .NET Framework SDK documentation.
Topic Objective
To introduce the use of
Platform Invocation Services
to call external functions.
Lead-in
Platform Invocation
Services, also known as
PInvoke, allow managed
code to interact with
unmanaged code in existing
DLLs.
36 Module 6: Working with Types


COM Interoperability
! Exposing .NET Framework Classes to COM
# Must create COM Callable Wrapper
# Create CCWs by using Tlbexp.exe
# Register by using Regasm.exe
! Exposing COM Classes to the .NET Framework
# Must create Runtime Callable Wrapper
# Create RCWs by using Tlbimp.exe

*****************************ILLEGAL FOR NON-TRAINER USE******************************
COM interoperability allows managed code to create and call COM objects. It
also allows COM objects to create and call .NET Framework objects.
Exposing .NET Framework Classes to COM
You can expose .NET Framework classes to COM by creating COM callable
wrappers (CCW) for each .NET Framework class in your application. The
CCW creates and manages standard COM elements, such as IUknown and
other standard interfaces, GUIDs, IIDs, and reference counting. These elements
allow COM objects to interoperate with your .NET Framework classes as if the
.NET Framework classes were real COM classes.
The .NET Framework classes that you write do not need to be aware of any
COM elements. However, you can directly control some COM elements when
necessary, by such means as specifying manual IIDs for your interfaces.
You can create CCWs for your .NET Framework classes by running the Type
Library Exporter (Tlbexp.exe). You can also register your .NET Framework
classes as COM classes by using the Assembly Registration Tool (Regasm.exe).
Topic Objective
To introduce COM
interoperability.
Lead-in
COM interoperability allows
managed code to create
and call COM objects. It
also allows COM objects to
create and call .NET
Framework objects.
Module 6: Working with Types 37


Exposing COM Classes to the .NET Framework
Managed code can also create and call existing COM objects. You can
accomplish this by creating a runtime callable wrapper (RCW) for each COM
class that you want to access from managed code. Similar to the CCW, the
RCW handles COM specific semantics, such as reference counting,
QueryInterface for different IIDs, and marshaling. Your managed code can
use COM interfaces as if they were managed interfaces and COM classes as if
they were managed classes.
You can create RCWs by using the Type Library Importer (Tlbimp.exe).
For more information on COM Interopability, see Module 15, Interoperating
Between Managed and Unmanaged Code, in Course 2349B, Programming
with the Microsoft .NET Framework (Microsoft Visual C# .NET) and
Interoperating with Unmanaged Code in the .NET Framework SDK
documentation.
38 Module 6: Working with Types


Lab 6: Working with Types

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives
After completing this lab, you will be able to:
! Create classes that override the GetHashCode, and equality methods and
operators.
! Create classes that provide conversion operators.
! Implement an explicit interface in a class and use it from a client.

Lab Setup
Starter and solution files are associated with this lab. The starter files are in the
folder <install folder>\Labs\Lab06\Starter, and the solution files are in the
folder <install folder>\Labs\Lab06\Solution.
Prerequisites
Before working on this lab, you must have:
! Knowledge about how to use Microsoft Visual Studio .NET for creating
and working with console applications.
! Knowledge about the C# language.
! Knowledge about the System.Console namespace for interacting with the
console.

Estimated time to complete this lab: 45 minutes
Topic Objective
To introduce the lab.
Lead-in
In this lab, you will create a
class that overrides
methods in System.Object.
You will also create an
interface that must be
implemented explicitly.
Module 6: Working with Types 39


Exercise 1
Overriding System.Object Methods
In this exercise, you will create a structure that represents an office in a
building. The Office structure stores the size, building number, and room
number of an office. You will override appropriate methods and operators to
support retrieving hash codes and comparing different offices.
You will also create a structure called OfficeUnit that is similar to the Office
structure. The only difference between the OfficeUnit structure and the Office
structure is that OfficeUnit represents size as an enumerated type with the
values small, medium, or large. The Office structure represents size as square
footage. You will write an explicit conversion operator to convert values of
type Office to type OfficeUnit.
! Implement a hash code
1. Open the Offices project located in the <install folder>\Labs\
Lab06\Starter\Offices folder.
2. Open the Office.cs file.
3. Locate the Office structure.
4. Override the GetHashCode method. Generate and return a hash code by
using the following algorithm:
HashCode = Building * 10000 + Room

! Provide a string representation
1. Override the ToString method.
2. Generate and return a string that contains the building number, followed by
a forward slash, followed by the room number. You can use the following
code to build and return the string.
return String.Concat(Building,"/",Room);


! Implement equality
1. Override the Equals method.
2. Cast the object parameter to type Office.
3. Compare the size, room, and building numbers of the parameter to this. If
the values are all equal, return true. If they are not equal, return false.
4. Override the == and != operators. Implement them by calling the Equals
method.

40 Module 6: Working with Types


! Implement conversion
1. Define an explicit conversion operator to convert from type Office to
OfficeUnit.
2. Convert the size from square footage to an OfficeSizes value according to
the following table. The OfficeSizes enumeration is already defined in the
start code for you.
Square Footage OfficeSizes

<= 64 Small
>64 and <100 Medium
>=100 Large

3. Assign the Building and Room values to the new type and return an
instance of the new type.

! Test
1. Open the Main.cs file.
2. Create two offices called o1 and o2. Assign different sizes, room numbers,
and building numbers to each office variable.
3. Use the == operator to compare the two offices. Print the results to the
console.
4. Call GetHashCode on both offices and print the results.
5. Call ToString on both offices and print the results.
6. Create an OfficeUnit and assign it one of the offices. Use explicit
conversion to assign the office.
7. Print all of the values of the OfficeUnit.
8. Compile and run the program.
The two offices should not be equal. They should have different hash codes.
The ToString method should print the building and room numbers, not the
class names. Finally, the printed enumerated size should match the square
footage appropriately.

Module 6: Working with Types 41


Exercise 2
Implementing an Explicit Interface
In this exercise, you will create an interface called IRange, which provides
properties and methods to select and print a range. You will also modify a class
called TextEntry to inherit from IRange. This combination allows text in the
TextEntry class to be selected as a range and printed.
The TextEntry class already implements a Print method. Because the IRange
interface also has a Print method, you will need to explicitly implement
IRange in the TextEntry class to avoid a name conflict.
! Create an explicit interface
1. Open the Interfaces project located in the <install folder>\Labs\
Lab06\Starter\Interfaces folder.
2. Open the TextEntry.cs file.
3. Define an interface called IRange that has the following properties.
Property Name Type

RangeBegin uint
RangeEnd uint

4. Define the following methods for IRange.
Method Name Return Type Parameters

Select void uint rb, uint re
Print void none

5. Modify the TextEntry class to inherit from IRange.
6. In the TextEntry class, create private fields named rangeBegin and
rangeEnd to store the RangeBegin and RangeEnd data values. Use the
uint type for both fields.
7. Implement the IRange RangeBegin property.
a. Implement the get accessor by returning the value of rangeBegin.
b. Implement the set accessor by storing the value in rangeBegin. Check to
ensure that the new value is less than the length of the text field, and that
the value of rangeEnd is not less than rangeBegin.
8. Implement the IRange RangeEnd property.
a. Implement the get accessor by returning the value of rangeEnd.
b. Implement the set accessor by storing the value in rangeEnd. Check to
ensure that the new value does not exceed the length of the text field,
and that the value of rangeEnd is not less than rangeBegin.
9. Implement the IRange Select method. Ensure that the re parameter is not
less than the rb parameter, and that the new values are not greater than the
length of the text field. Assign rb to rangeBegin and re to rangeEnd.
10. Implement the IRange Print method. Using the rangeBegin and rangeEnd
fields, create a text range from the text field by using the SubString
method. Then print the resulting string to the console.

42 Module 6: Working with Types


! Use the interface
1. Open the Main.cs file.
2. In the MainClass class, create an instance of the TextEntry class called
myEntry.
3. Create an interface of type IRange named r and cast myEntry to r.
4. Initialize the Text property on myEntry with a value, such as The quick
brown fox jumped over the gate.
5. Select a range by using the interface variable r. For example, the values 4
and 10 will select the word quick.
6. Call the Print method on myEntry.
7. Call the Print method on r.
8. Compile and run the program. You should see the entire Text property
printed to the console, followed by the range of text you selected.

Module 6: Working with Types 43


Review
! System.Object Class Functionality
! Specialized Constructors
! Type Operations
! Interfaces
! Managing External Types

*****************************ILLEGAL FOR NON-TRAINER USE******************************
1. What kind of algorithm should you use to implement the GetHashCode
method?
The algorithm should be efficient and return a unique number.


2. What is the default behavior of the ToString method?
The default behavior of the ToString method is to return the name of
the class.


3. How can you determine if two separate object references are the same
object?
Use the ReferenceEquals method to compare the two references.


4. If you override the Equals method, what else should you override?
You should override the == and != operators.


5. When should you use a private constructor?
Use a private constructor to prevent a class from being instantiated. An
example of such a class is a singleton class with static methods and
fields.


Topic Objective
To reinforce module
objectives by reviewing
key points.
Lead-in
The review questions cover
some of the key concepts
taught in the module.
44 Module 6: Working with Types


6. When should you use implicit conversions, and when should you use
explicit conversions?
Use implicit conversions for improved readability and use. Use explicit
conversions when the conversion could cause a loss of data or throw an
exception.


7. When do boxing operations occur?
Boxing occurs when a value type is converted to an Object type.
Unboxing occurs when the value is retrieved from an Object type.


8. How do you explicitly implement an interface?
Do not use the public access modifier. Use the name of the interface in
front of the field or method names. For example, void
IFoo.DoSomething().













Contents
Overview 1
Strings 2
Terminology Collections 20
.NET Framework Arrays 21
.NET Framework Collections 39
Lab 7: Working with Strings, Enumerators,
and Collections 57
Review 63

Module 7: Strings,
Arrays, and Collections


Information in this document, including URL and other Internet Web site references, is subject to
change without notice. Unless otherwise noted, the example companies, organizations, products,
domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious,
and no association with any real company, organization, product, domain name, e-mail address,
logo, person, place or event is intended or should be inferred. Complying with all applicable
copyright laws is the responsibility of the user. Without limiting the rights under copyright, no
part of this document may be reproduced, stored in or introduced into a retrieval system, or
transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or
otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual
property rights covering subject matter in this document. Except as expressly provided in any
written license agreement from Microsoft, the furnishing of this document does not give you any
license to these patents, trademarks, copyrights, or other intellectual property.

2001-2002 Microsoft Corporation. All rights reserved.

Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint,
Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and
Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A.
and/or other countries.

The names of actual companies and products mentioned herein may be the trademarks of their
respective owners.



Module 7: Strings, Arrays, and Collections iii


Instructor Notes
After completing this module, students will be able to:
! Parse, format, manipulate, and compare strings.
! Use the classes in the System.Array and System.Collections namespaces.
! Improve the type safety and performance of collections by using specialized
collections and class-specific code.

Materials and Preparation
This section provides the materials and preparation tasks that you need to teach
this module.
Required Materials
To teach this module, you need the Microsoft PowerPoint file 2349B_07.ppt.
Preparation Tasks
To prepare for this module, you should:
! Read all of the materials for this module.
! Practice the demonstrations.
! Complete the lab.

Presentation:
120 Minutes

Lab:
60 Minutes
iv Module 7: Strings, Arrays, and Collections


Demonstrations
This section provides demonstration procedures that will not fit in the margin
notes or are not appropriate for the student notes.
Sorting and Enumerating an Array
In this demonstration, you will show students how to sort and enumerate an
array.
The code for this demonstration is contained in one project and is located in
<install folder>\Democode\Mod07\Demo07.1. In addition, the code for the
individual demonstration is provided in the student notes.
ArrayList
In this demonstration, you will show students how ArrayList implements the
IList interface by using an array whose size is dynamically increased as
required.
The code for this demonstration is contained in one project and is located in
<install folder>\Democode\Mod07\Demo07.2. In addition, the code for the
individual demonstration is provided in the student notes.
Hashtable
In this demonstration, you will show students how to create a hash table that is
used for searches.
The code for this demonstration is contained in one project and is located in
<install folder>\Democode\Mod07\Demo07.3. In addition, the code for the
individual demonstration is provided in the student notes.
In all of the preceding demonstrations, use the debugger to step through the
code while you point out features.
Module 7: Strings, Arrays, and Collections v


Module Strategy
Use the following strategy to present this module:
! Strings
Discuss how to work with strings in the Microsoft .NET Framework,
including common operations, such as parsing, formatting, manipulating,
and comparing strings.
! Terminology Collections
Define the term collection as it is used in this module and identify where
collections are found in the .NET Framework. Be sure that students
understand that the term collection is used in its broader sense: to describe a
group of items.
! .NET Framework Arrays
Introduce the System.Array class as the base class of all array types that
contains methods for creating, manipulating, searching, and sorting arrays.
Discuss features of arrays that are specific to C#. Explain the role of the
IEnumerable and IEnumerator interfaces in System.Array and
System.Collections classes.
Use the Sorting and Enumerating an Array demonstration to show how to
sort and enumerate an array.
! .NET Framework Collections
Briefly introduce some commonly used classes in the System.Collections
namespace.
Discuss the IList interface with regards to classes that represent an ordered
collection of objects that can be individually indexed. Use the ArrayList
demonstration to reinforce this concept.
Discuss the IDictionary interface and the classes that it implements. Use
the Hashtable demonstration to show how to use the IDictionary interface.
Provide guidelines to help students distinguish between collections and
arrays, and explain when collections are used.
Discuss runtime casting for type safety and the effects of runtime casting,
and boxing and unboxing on performance. Discuss techniques for handling
boxing and unboxing to optimize performance.

Module 7: Strings, Arrays, and Collections 1


Overview
! Strings
! Terminology Collections
! .NET Framework Arrays
! .NET Framework Collections

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this module, you will learn about some of the key classes in the Microsoft
.NET Framework class library. Specifically, you will learn how to work with
strings, arrays, collections, and enumerators.
After completing this module, you will be able to:
! Parse, format, manipulate, and compare strings.
! Use the classes in the System.Array and System.Collections namespaces.
! Improve the type safety and performance of collections by using specialized
collections and class-specific code.

Topic Objective
To provide an overview of
the module topics and
objectives.
Lead-in
In this module, you will learn
about some of the key
classes in the .NET
Framework class library.
2 Module 7: Strings, Arrays, and Collections


" "" " Strings
! Parse
! Format
! Format Examples
! Changing Case
! Compare
! Trim and Pad
! Split and Join
! StringBuilder
! C# Specifics
! Regular Expressions

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In the C# language, string is an alias for System.String in the .NET
Framework. The System.String type represents a string of Unicode characters.
Working with strings is an everyday task in software development, and includes
operations, such as parsing, formatting, manipulating, and comparing strings.
The String object is immutable. Therefore, every time you use one of the
methods in the System.String class, you create a new string object. When you
want to perform repeated modifications to a string, the overhead that is
associated with creating a new String object can be costly. As an alternative,
you can use the System.Text.StringBuilder class to modify a string without
creating a new object.
In this section, you will learn how to work with strings in the .NET Framework.
Topic Objective
To introduce the topics in
the section.
Lead-in
In this section, you will learn
how to work with strings in
the .NET Framework.
Module 7: Strings, Arrays, and Collections 3


Parse
! Parse Method Converts a Numeric String to a Numeric
! To Ignore Commas, Use the
NumberStyles.AllowThousands Flag
string MyString = "12345";
int MyInt = int.Parse(MyString);
MyInt++;
Console.WriteLine(MyInt);
// The output to the console is "12346".
string MyString = "12345";
int MyInt = int.Parse(MyString);
MyInt++;
Console.WriteLine(MyInt);
// The output to the console is "12346".
string MyString = "123,456";
int MyInt = int.Parse(MyString,
System.Globalization.NumberStyles.AllowThousands);
Console.WriteLine(MyInt);
// The output to the console is "123456".
string MyString = "123,456";
int MyInt = int.Parse(MyString,
System.Globalization.NumberStyles.AllowThousands);
Console.WriteLine(MyInt);
// The output to the console is "123456".

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The Parse method converts a string that represents a .NET Framework numeric
base type to an actual .NET Framework numeric base type.
The Parse method takes a combination of up to three parameters, as follows:
! The string to be converted
! One or more values from the System.Globalization.NumberStyles
enumeration
! A NumberFormatInfo class

Because the Parse method assumes that all string input represents a base-10
value, non-base-10 values are not parsable. The Parse method also does not
parse strings that represent the values NaN (Not A Number), PositiveInfinity,
or NegativeInfinity of the Single and Double classes because they are not real
numbers.
The following code example converts a string to an int value, increments that
value, and displays the result:
string MyString = "12345";
int MyInt = int.Parse(MyString);
MyInt++;
Console.WriteLine(MyInt);
// The output to the console is "12346".

Topic Objective
To explain how the Parse
method is used to convert
numeric strings to a .NET
Framework numeric base
type.
Lead-in
The Parse method converts
a string that represents a
.NET Framework numeric
base type to an actual .NET
Framework numeric base
type.
4 Module 7: Strings, Arrays, and Collections


Handling Nonnumeric Characters
The NumberStyles enumeration is useful if you have a string that contains
nonnumeric characters that you want to convert into a .NET Framework
numeric base type. You must use this enumeration to parse a string with a
currency symbol, decimal point, exponent, parentheses, and so on.
For example, a string that contains a comma cannot be converted to an int
value by using the Parse method unless you pass the
System.Globalization.NumberStyles enumeration.
Incorrect Way to Parse a String with Nonnumeric Characters
The following code example is invalid and raises an exception. It illustrates the
incorrect way to parse a string that contains nonnumeric characters.
string MyString = "123,456";
// the following line raises a System.Format exception
int MyInt = int.Parse(MyString);
Console.WriteLine(MyInt);

Correct Way to Parse a String with Nonnumeric Characters
When you apply the System.Globalization.NumberStyles enumeration with
the AllowThousands flag, the Parse method ignores the comma that raised the
exception in the preceding example.
The following code example uses the same string as the preceding example but
does not raise an exception.
string MyString = "123,456";
int MyInt = int.Parse(MyString,
System.Globalization.NumberStyles.AllowThousands);
Console.WriteLine(MyInt);
// The output to the console is "123456"

Module 7: Strings, Arrays, and Collections 5


Format
! Format Strings Are Used in Methods That Create String
Representations of a .NET Framework Data Type
# To display $100.00 to the console on computers on
which U.S. English is the current culture
# Alternatively
int MyInt = 100;
string MyString = MyInt.ToString("C");
Console.WriteLine(MyString);
int MyInt = 100;
string MyString = MyInt.ToString("C");
Console.WriteLine(MyString);
int MyInt = 100;
Console.WriteLine("{0:C}", MyInt);
int MyInt = 100;
Console.WriteLine("{0:C}", MyInt);

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The .NET Framework provides several format strings, or specifiers, that you
can use to format the appearance of strings that derive from other objects.
There are several advantages to converting base data types to strings before
displaying them to users. Strings are easily displayed and can be appended to
the messages and dialog boxes of your application. You can also use format
specifiers to display the same numeric value in scientific format, monetary
format, hexadecimal format, and so on.
When to Use Format Strings
You can use format specifiers in situations where your application stores
information in a format that is designed for use by the application, and not by
the user. For example, a business application may keep track of the current date
and time in a DateTime object to log when transactions are completed. The
DateTime object stores information in which the user is not necessarily
interested, such as the number of milliseconds that have elapsed since the
creation of the object.
You can also use format specifiers to display only information that is of interest
to the user, such as the date and hour of the transaction. Additionally, you can
dynamically modify strings that are created by using format specifiers to
represent monetary, date, and time conventions for the current culture. For
example, your application can display the date and time in the notation that is
specific to the users current culture.
Topic Objective
To explain how to use
format strings, or specifiers,
to format the appearance of
your application.
Lead-in
The .NET Framework
provides several format
strings that you can use to
format the appearance of
strings that derive from
other objects.
6 Module 7: Strings, Arrays, and Collections


Methods Used with Format Strings
Format strings are used with any method that creates a string representation of a
.NET Framework data type, such as Int32, Int64, Single, Double,
Enumeration, DateTime, and so on. Format strings are also used with
Console.Writeline, String.Format, and several methods in the System.IO
namespace.
Additionally, every base data type contains a ToString method that returns a
string representation of the data types value and accepts a string format
specifier. You can control the layout and design of strings that are created by
any of these methods by using one of several format strings defined by the
.NET Framework.
Using the ToString Method
The ToString method is useful if you want to convert one of the standard .NET
Framework data types to a string that represents that type in some other format.
For example, if you have an integer value of 100 that you want to represent to
the user as a currency value, you can easily use the ToString method and the
currency format string ("C") to produce a string of "$100.00". The original
value that is contained in the data type is not converted, but a new string is
returned that represents the resulting value. This new string cannot be used for
calculation until it is converted back to a .NET base data type. The original
value, however, can be calculated at any time.

Computers that do not have U.S. English specified as the current culture
will display whatever currency notation is used by the current culture.

In the following code example, the ToString method displays the value of 100
as a currency-formatted string in a console window:
int MyInt = 100;
string MyString = MyInt.ToString("C");
Console.WriteLine(MyString);

The preceding code displays $100.00 to the console on a computer on which
U.S. English is the current culture.
Using Console.Writeline
The Console.WriteLine method also accepts a format string specifier as an
argument and can produce the same value as the preceding example.
Console.Writeline accepts string format specifiers in the form, where the
characters inside the curly brackets specify the formatting to apply to the
variable.
The following code example uses the Console.WriteLine method to format the
value of MyInt to a currency value.
int MyInt = 100;
Console.WriteLine("{0:C}", MyInt);

In the preceding example, the 0 character specifies the variable or value on
which to apply formatting. In this example, it is the first and only variable. The
characters that follow the colon are interpreted as string format specifiers.
Note
Module 7: Strings, Arrays, and Collections 7


Format Examples
! Currency Format
# C - $XX,XXX.XX
! Date Time Format
# D - dd MMMM yyyy
# d - MM/dd/yyyy
int MyInt = 12345;
string MyString = MyInt.ToString("C" );
// In the U.S. English culture: "$12,345.00"
int MyInt = 12345;
string MyString = MyInt.ToString("C" );
// In the U.S. English culture: "$12,345.00"
DateTime MyDate = new DateTime(2000, 1, 10, 0, 0, 0);
string MyString = MyDate.ToString( "d" );
// In the U.S. English culture: "1/10/2000"
DateTime MyDate = new DateTime(2000, 1, 10, 0, 0, 0);
string MyString = MyDate.ToString( "d" );
// In the U.S. English culture: "1/10/2000"

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The standard numeric, picture numeric, date and time, and enumeration format
strings are described in detail in the .NET Framework Software Development
Kit (SDK). This topic provides examples of these format strings.
The following examples show the use of the format string that returns the value
of MyInt in the currency format:
int MyInt = 12345;

string MyString = MyInt.ToString( "C" );
// In the U.S. English culture, MyString has the value:
// "$12,345.00".

The following table lists some format characters for standard patterns that are
used to format DateTime types.

Format Character
Associated
Property/Description
Example Format Pattern
(en-US)

d ShortDate Pattern MM/dd/yyyy
D LongDatePattern Dd MMMM yyyy
f Full date and time (long date
and short time)
Dd MMMM yyyy
HH:mm
F FullDateTimePattern (long
date and long time)
Dd MMMM yyyy
HH:mm:ss

Topic Objective
To provide examples of
format strings that return
common numeric string
types.
Lead-in
Lets look at some examples
of format strings that return
the value of MyString in the
currency and date time
formats.
8 Module 7: Strings, Arrays, and Collections


The following example shows the use of the format string that returns the value
of MyDate in the short date pattern format:
DateTime MyDate = new DateTime(2000, 1, 10, 0, 0, 0);

string MyString = MyDate.ToString( "d" );
// In the U.S. English culture, MyString has the value:
// "1/10/2000".

Module 7: Strings, Arrays, and Collections 9


Changing Case
! You Can Easily Change the Case of a String
# String.ToUpper converts to upper case
# String.ToLower converts to lower case
string MyString = "hello world!";
// outputs: HELLO WORLD!
Console.WriteLine(MyString.ToUpper());
string MyString = "hello world!";
// outputs: HELLO WORLD!
Console.WriteLine(MyString.ToUpper());
string MyString = "HELLO WORLD!";
// outputs: hello world!
Console.WriteLine(MyString.ToLower());
string MyString = "HELLO WORLD!";
// outputs: hello world!
Console.WriteLine(MyString.ToLower());

*****************************ILLEGAL FOR NON-TRAINER USE******************************
You can easily change the case of a string by using the two methods that are
described in the following table.
Method Name Use

String.ToUpper Converts all characters in a string to uppercase.
String.ToLower Converts all characters in a string to lowercase.

String.ToUpper and String.ToLower provide an override that accepts a
culture.
The String.ToUpper method changes all of the characters in a string to
uppercase.
The following code example converts the string "hello world!" from
lowercase to uppercase:
string MyString = "hello world!";
Console.WriteLine(MyString.ToUpper());

The preceding example displays HELLO WORLD! to the console.
The String.ToLower method is similar to the String.ToUpper method, but it
converts all of the characters in a string to lowercase.
The following code example converts the string "HELLO WORLD!" to lowercase.
string MyString = "HELLO WORLD!";
Console.WriteLine(MyString.ToLower());

The preceding example displays hello world! to the console.
Topic Objective
To explain how to use the
String.ToUpper and
String.ToLower methods to
change the case of a string.
Lead-in
You can easily change the
case of a string by using the
String.ToUpper and
String.ToLower methods.
10 Module 7: Strings, Arrays, and Collections


Compare
! The .NET Framework Has Methods to Compare Strings
# For example, the Compare method compares the
current string object to another string or object, returning:
- Negative if first string is less than second string
- 0 if the two strings are equal
- Positive if first string is greater than second string
string MyString = "Hello World!";
Console.WriteLine(
String.Compare(MyString,"Hello World!"));
// outputs: 0
string MyString = "Hello World!";
Console.WriteLine(
String.Compare(MyString,"Hello World!"));
// outputs: 0

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The .NET Framework provides several methods to compare the values of
strings. The following table describes some of the value-comparison methods.
Method Name Use

String.Compare Compares the values of two strings. Returns an integer value.
String.StartsWith Determines if a string begins with the string passed. Returns a
boolean value.
String.IndexOf Returns the index of the first occurrence of a String, or one or
more characters, within this instance.

For more information about value comparison methods and for a complete list
of these methods, see Comparing Strings in the .NET Framework SDK
documentation.
For example, the String.Compare method provides a thorough way to compare
the current string object to another string or object. You can use this function to
compare two strings or substrings of two strings.
Additionally, overloads are provided that regard or disregard case and cultural
variance.
Topic Objective
To introduce some of the
value-comparison methods
that are used to compare
the values of strings.
Lead-in
The .NET Framework
provides several value-
comparison methods to
compare the values of
strings.
Module 7: Strings, Arrays, and Collections 11


The following table shows the three integer values that are returned by the
Compare(string strA, string strB) method.
Value Type Condition

A negative integer strA is less than strB
0 strA equals strB
A positive integer strA is greater than strB

The following code example uses the Compare method to determine whether
two strings are the same.
string MyString = "Hello World!";
Console.WriteLine(String.Compare(MyString, "Hello World!"));

The preceding example displays 0 to the console.
12 Module 7: Strings, Arrays, and Collections


Trim and Pad
! Trim Methods Remove Spaces
! Pad Methods Expand a Specific Number of Characters
string MyString = " Big ";
Console.WriteLine("Hello{0}World!", MyString );
string TrimString = MyString.Trim();
Console.WriteLine("Hello{0}World!", TrimString );
// outputs the following lines to the console:
//Hello Big World!
//HelloBigWorld!
string MyString = " Big ";
Console.WriteLine("Hello{0}World!", MyString );
string TrimString = MyString.Trim();
Console.WriteLine("Hello{0}World!", TrimString );
// outputs the following lines to the console:
//Hello Big World!
//HelloBigWorld!
string MyString = "Hello World!";
Console.WriteLine(MyString.PadLeft(20, '-'));
// outputs the following line to the console:
//--------Hello World! to the console.
string MyString = "Hello World!";
Console.WriteLine(MyString.PadLeft(20, '-'));
// outputs the following line to the console:
//--------Hello World! to the console.

*****************************ILLEGAL FOR NON-TRAINER USE******************************
When you want to remove or extend the spaces around strings, the
System.String class provides methods for trimming and padding strings.
Trimming
When you are parsing a sentence into individual words, you might have white
spaces on either end of a word. You can use one of the trim methods in the
System.String class to remove any number of spaces from the beginning or end
of the string.
The following table describes two of the available trim methods.
Method Name Use

String.Trim Removes white spaces from the beginning and end of a string.
String.Remove Removes a specified number of characters from a specified index
position in a string.

For example, you can easily remove white spaces from both ends of a string by
using the String.Trim method, as shown in the following code example.
string MyString = " Big ";
Console.WriteLine("Hello{0}World!", MyString );
string TrimString = MyString.Trim();
Console.WriteLine("Hello{0}World!", TrimString );

This code outputs the following lines to the console:
Hello Big World!
HelloBigWorld!

For a complete list of trim methods in the System.String class, see Trimming
and Removing Characters in the .NET Framework SDK documentation.
Topic Objective
To explain how to use
methods of the
System.String class to trim
and pad strings.
Lead-in
You can remove or extend
the spaces around strings
by using methods of the
System.String class.
Module 7: Strings, Arrays, and Collections 13


Padding
System.String also provides methods that you can use to create a new version
of an existing string that is expanded by a specific number of characters.
The following table describes the available pad methods.
Method Name Use

String.PadLeft Right aligns and pads a string, so its rightmost character is a
specified distance from the beginning of the string.
String.PadRight Left aligns and pads a string, so its rightmost character is a
specified distance from the beginning of the string.

For example, the String.PadLeft method creates a new string that moves an
existing string to the right, so its last character is a specified number of spaces
from the first index of the string. White spaces are inserted if you do not use an
override that allows you to specify your own custom padding character.
The following code example uses the PadLeft method to create a new string
with a total length of 20 spaces.
string MyString = "Hello World!";
Console.WriteLine(MyString.PadLeft(20, '-'));

This example displays the following text to the console.
--------Hello World!

14 Module 7: Strings, Arrays, and Collections


Split and Join
! Split Method Is Used to Break Up a String Into an Array of
Substrings
# String is broken at positions indicated by the specified separator
characters parameter
# If the separator parameter is null, the whitespace characters are
assumed to be the separator
! Join Method Is Used to Concatenate Strings
# A specified separator string is placed between each element of a
string array
string Line = "Hello World";
string[] Words = Line.Split(null);
// Words[0] = "Hello" and Words[1] = "World"
string Line = "Hello World";
string[] Words = Line.Split(null);
// Words[0] = "Hello" and Words[1] = "World"

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The System.String class provides the Split method to break up strings and the
Join method to concatenate strings.
The Split Method
You use the Split method to break up a string instance into an array of
substrings at the positions that are specified by separator characters. If separator
characters are omitted, that is to say, if the parameter is null, the whitespace
characters are assumed to be the separator. If the separator is a zero-length
string, a single-element array that contains the entire expression string is
returned.
The following example shows how to break up a string into a string array of
words:
string Line = "Hello World";
string[] Words = Line.Split(null);
// Words[0] = "Hello" and Words[1] = "World"

The Join Method
You use the Join method to concatenate a specified separator string between
each element of a specified String array, which yields a single concatenated
string. If the separator is omitted, that is to say null, the space character (" ")
is used. If the separator is a zero-length string (""), all of the items in the list
are concatenated with no delimiters.
Topic Objective
To explain how to use the
Split and Join methods to
break up and concatenate
strings.
Lead-in
The System.String class
provides the Split method to
break up strings and the
Join method to concatenate
strings.
For Your Information
You should cover the
String.Split method
because it is used in the lab.
Module 7: Strings, Arrays, and Collections 15


StringBuilder
! The String Object is Immutable
! System.Text.StringBuilder Allows You to Modify a
String Without Creating a New Object
! You Can Specify the Maximum Number of Characters
! Methods Include:
# Append, AppendFormat, Insert, Remove,
and Replace
StringBuilder MyStringBuilder = new StringBuilder("Hello");
StringBuilder MyStringBuilder = new StringBuilder("Hello");
// MyStringBuilder can hold a maximum of 25 characters
StringBuilder MyStringBuilder =
new StringBuilder("Hello World!", 25);
// MyStringBuilder can hold a maximum of 25 characters
StringBuilder MyStringBuilder =
new StringBuilder("Hello World!", 25);

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The String object is immutable. Therefore, every time you use one of the
methods in the System.String class, you create a new string object. When you
want to perform repeated modifications to a string, the overhead that is
associated with creating a new String object can be costly. As an alternative,
you can use the System.Text.StringBuilder class to modify a string without
creating a new object.
Creating a StringBuilder Object
You can create a new instance of the StringBuilder object by initializing your
variable with one of the overloaded constructor methods, as shown in the
following code example:
StringBuilder MyStringBuilder = new StringBuilder("Hello");

Although the StringBuilder object is a dynamic object that allows you to
expand the number of characters in the string that it encapsulates, you can
specify a value for the maximum number of characters that it can hold. This
value is called the capacity of the object and must not be confused with the
length of the string that the current StringBuilder object holds. Any attempt to
expand the StringBuilder class beyond the maximum range causes an
ArgumentOutOfRangeException to be thrown.
The following code example specifies that the MyStringBuilder object can be
expanded to a maximum of 25 spaces.
StringBuilder MyStringBuilder =
new StringBuilder("Hello World!", 25);

Topic Objective
To explain how to use the
StringBuilder method to
modify a string without
creating a new object.
Lead-in
When you want to perform
repeated modifications to a
string, use the
System.Text.StringBuilder
class to modify a string
without creating a new
object.
16 Module 7: Strings, Arrays, and Collections


StringBuilder Methods
The following table describes the methods that you can use to modify the
contents of the StringBuilder object.
Method Name Use

StringBuilder.Append Appends information to the end of the current
StringBuilder object.
StringBuilder.AppendFormat Replaces zero or more format specifications with
the appropriately formatted value of an object.
StringBuilder.Insert Inserts a string or object into the specified index of
the current StringBuilder object.
StringBuilder.Remove Removes a specified number of indexes from the
current StringBuilder object.
StringBuilder.Replace Replaces a specified index or character with the
passed character.

Module 7: Strings, Arrays, and Collections 17


C# Specifics
! C# string Type Is a String of Unicode Characters
# Alias for System.String
# Equality operators (== and !=) compare the values of string objects,
not references
# The + operator concatenates strings
# The [ ] operator accesses individual characters of a string
# With @-quoting, escape sequences are not processed
string a = "\u0068ello ";
string b = "world";
Console.WriteLine( a + b == "hello world" );//True
string a = "\u0068ello ";
string b = "world";
Console.WriteLine( a + b == "hello world" );//True
char x = "test"[2]; // x = 's';
char x = "test"[2]; // x = 's';
@"c:\Docs\Source\a.txt"
// rather than "c:\\Docs\\Source\\a.txt"
@"c:\Docs\Source\a.txt"
// rather than "c:\\Docs\\Source\\a.txt"

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The string type represents a string of Unicode characters; string is an alias for
System.String in the .NET Framework.
Although string is a reference type, the equality (==) operator and the
inequality (!=) operator are defined to compare the values of string objects, not
the references. Comparing the values of string objects makes testing for string
equality more intuitive, as in the following example:
string a = "hello";
string b = "hello";
Console.WriteLine( a == b ); // output: True -- same value

The + operator concatenates strings, as in the following example:
string a = "good " + "morning";

The [ ] operator accesses individual characters of a string, as in the following
example:
char x = "test"[2]; // x = 's';

String literals are of type string and can be written in two forms: quoted and
@-quoted. Quoted string literals are enclosed in quotation marks ("), as in the
following example:
"good morning" // a string literal

Quoted string literals can also contain any character literal, including escape
sequences, as in the following example:
string a = "\\\u0066\n"; // backslash, letter f, new line

Topic Objective
To explain what the string
type is in the .NET
Framework and to describe
the functions of the +, [ ],
and != operators, and @-
quoting.
Lead-in
The string type represents
a string of Unicode
characters; string is an
alias for System.String in
the .NET Framework.
18 Module 7: Strings, Arrays, and Collections


@-quoted string literals start with @ and are enclosed in quotation marks, as in
the following example:
@"good morning" // a string literal

The advantage of using @-quoted string literals is that escape sequences are not
processed. This makes it easy to write a fully qualified file name, as in the
following example:
@"c:\Docs\Source\a.txt"
// rather than "c:\\Docs\\Source\\a.txt"

To include a quoted phrase in an @-quoted string, use two pairs of double
quotation marks, as in the following example:
@"""Ahoy!"" cried the captain." // "Ahoy!" cried the captain.

The following code example uses the C# features that are discussed in this
topic:
using System;
class test
{
public static void Main( String[] args )
{
string a = "\u0068ello ";
string b = "world";
Console.WriteLine( a + b );
Console.WriteLine( a + b == "hello world" );
}
}

The preceding code example displays the following output to the console:
hello world
True

Module 7: Strings, Arrays, and Collections 19


Regular Expressions
! Regular Expressions Powerful Text Processing
! Pattern-Matching Notation Allows You to:
# Find specific character patterns
# Extract, edit, replace, or delete text substrings
# Add the extracted strings to a collection to generate
a report
! Designed to be Compatible With Perl 5

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Regular expressions provide a powerful, flexible, and efficient method to
process text. The extensive pattern-matching notation of regular expressions
allows you to quickly parse large amounts of text to find specific character
patterns; to extract, edit, replace, or delete text substrings; and to add the
extracted strings to a collection in order to generate a report.
For many applications that deal with strings, such as HTML processing, log file
parsing, and HTTP header parsing, regular expressions are an essential tool.
The .NET Framework regular expressions incorporate the most popular features
of other regular expression implementations, such as those used in Perl and
awk. Designed to be compatible with Perl 5 regular expressions, .NET
Framework regular expressions include features that are not yet available in
other implementations, such as right-to-left matching and dynamic compilation.
The .NET Framework regular expression classes are part of the .NET
Framework class library and can be used with any language or tool that targets
the common language runtime, including ASP.NET and Microsoft
Visual Studio .NET.
A detailed explanation of how to use the regular expression classes is beyond
the scope of this course. For more information about using regular expression
classes, see the .NET Framework SDK documentation.
Topic Objective
To briefly describe how
regular expressions can be
used in the .NET
Framework.
Lead-in
Regular expressions allow
you to quickly parse large
amounts of text in order to
find specific character
patterns; to extract, edit,
replace, or delete text
substrings; and to add the
extracted strings to a
collection to generate a
report.
This topic provides a brief
summary of regular
expressions. Do not spend
much time on this slide, but
encourage students to refer
to the .NET Framework
SDK, especially for details
about using the regular
expression classes.
20 Module 7: Strings, Arrays, and Collections


Terminology Collections
! In This Module, the Term Collection Is Used in Its
Broader Sense to Refer to a Group of Items
! In the .NET Framework, Collections Are Found in
the Namespaces
# System.Array
# System.Collections

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this module, the term collection is used in its broader sense to refer to a
group of items. In the .NET Framework, examples of collections are found in
the namespaces System.Array, and System.Collections.
Topic Objective
To define the term collection
as it is used in this module
and to identify where
collections are found in the
.NET Framework.
Lead-in
In this module, the term
collection is used in its
broader sense: to describe a
group of items.
Do not spend much time on
this slide. The objective of
this slide is to define the
term collection clearly,
before discussing the
collections that are found in
the .NET Framework.
Module 7: Strings, Arrays, and Collections 21


" "" " .NET Framework Arrays
! System.Array
! C# Specifics
! Iterating Over
! Comparing
! Sorting

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The System.Array class is the base class of all array types and contains
methods for creating, manipulating, searching, and sorting arrays. The Array
class is not part of the Collections namespace. However, it is still a collection
because it is based on the IList interface.
In an array style of collection, an item in the collection is referred to by the term
element. Specific elements are identified by their array index. The low bound or
lower bound of an Array is the index of its first element.
To understand the functionality of the various collection classes, you must
understand their key interfaces. In this section, you will learn about the
interfaces that are used by the methods of System.Array.
Topic Objective
To introduce the topics in
the section.
Lead-in
The System.Array class is
the base class of all array
types and contains methods
for creating, manipulating,
searching, and sorting
arrays.
22 Module 7: Strings, Arrays, and Collections


System.Array
! System.Array Is the Base Class of All Array Types
! Arrays Implement the Following Interfaces
# ICloneable, IList, ICollection, and IEnumerable
! System.Array Has Methods For
# Creating, manipulating, searching, and sorting
! Null, Empty String, and Empty (0 item) Arrays Should Be
Treated the Same
# Therefore, return an Empty array, instead of a
null reference

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The System.Array class is the base class of all array types and contains
methods for creating, manipulating, searching, and sorting arrays. Arrays are
always allocated on the garbage-collected heap. Arrays can be single
dimensional or multidimensional. You can also create arrays of arrays, called
jagged arrays.
For optimum performance, it is highly recommended that each rank of an array
be zero-based. Additionally, each rank of an array must be zero-based when
passing arrays between programming languages.
Defining an Array Type
You define an array type by specifying the element type of the array, the rank,
or number of dimensions, of the array, and the upper and lower bounds of each
dimension of the array. All of these details are included in any signature of an
array type.
The runtime automatically creates exact array types as they are required. No
separate definition of the array type is needed. Arrays of a particular type can
only hold elements of that type. If you need to manipulate a set of unlike
objects or value types, consider using one of the collection types that are
defined in the System.Collections namespace.
Topic Objective
To explain how array types
are used and defined.
Lead-in
The System.Array class is
the base class of all array
types and contains methods
for creating, manipulating,
searching, and sorting
arrays.
Module 7: Strings, Arrays, and Collections 23


Methods and Properties of System.Array
Some of the methods and properties for the System.Array class are described
in this topic. Many of these methods are used to implement the classs
interfaces.
System.Array implements the interfaces that are described in the following
table.
Interface Use

ICloneable Supports cloning, which creates a new instance of a class with the
same value as an existing instance.
IList Represents a collection of objects that can be individually
indexed.
ICollection Defines size, enumerators, and synchronization methods for all
collections.
IEnumerable Exposes the enumerator, which supports a simple iteration over a
collection.

The interfaces that are listed in the preceding table are supported not only by
arrays but also by many of the System.Collections classes. Subsequent topics
in this module discuss specific interfaces and the classes that implement them.
The following tables describe some of the public members that are available
through the System.Array class.
Static Method Use

BinarySearch Overloaded. Searches a one-dimensional sorted Array for a
value, using a binary search algorithm.
CreateInstance Overloaded. Initializes a new instance of the Array class.
Sort Overloaded. Sorts the elements in one-dimensional Array
objects.

Property Use

IsFixedSize Gets a value that indicates whether the Array has a fixed size.
IsReadOnly Gets a value that indicates whether the Array is read-only.
Length Gets the total number of elements in all of the dimensions of the
Array.
Rank Gets the rank (number of dimensions) of the Array.


The IsFixedSize and IsReadOnly properties are always false unless they
are overridden by a derived class.

Note
24 Module 7: Strings, Arrays, and Collections


The following table describes some of the public instance methods that are
available through the System.Array class.
Instance Method Use

Clone Creates a shallow copy of the Array.
GetEnumerator Returns an IEnumerator for the Array.
GetLength Gets the number of elements in the specified dimension of the
Array.
GetLowerBound Gets the lower bound of the specified dimension in the Array.
GetUpperBound Gets the upper bound of the specified dimension in the Array.
GetValue Overloaded. Gets the values of the Array elements at the
specified indexes.
SetValue Overloaded. Sets the specified Array elements to the specified
value.

For complete lists of public members of the System.Array class, see Array
Members in the .NET Framework SDK documentation.
Empty Arrays
Nulls should only be returned by reference properties that refer to another
object or component. String and Array properties should never return null,
because a programmer typically does not expect null in this context. For
example, a programmer typically would assume that the following code works:
public void DoSomething() {
string[] sa = SomeOtherFunc();
// The following line assumes sa is never null
if (sa.Length > 0) {
// do something else
}
}

Generally, null, empty string, and empty (0 item) arrays should be treated in the
same way. Therefore, return an Empty array, instead of a null reference.
Module 7: Strings, Arrays, and Collections 25


C# Specifics
! C# Array Indexes Start at Zero
! Declaring an Array Size Is Not Part of Its Type
! Creating an Array
! Initializing an Array
! Using System.Array Members
int[] numbers; // declare numbers as
// an int array of any size
int[] numbers; // declare numbers as
// an int array of any size
int[] numbers = new int[5]; // declare and create
int[] numbers = new int[5]; // declare and create
int[] numbers = {1, 2, 3, 4, 5};
int LengthOfNumbers = numbers.Length;
int[] numbers = {1, 2, 3, 4, 5};
int LengthOfNumbers = numbers.Length;
int[] numbers = new int[5] {1, 2, 3, 4, 5};
int[] numbers = new int[5] {1, 2, 3, 4, 5};

*****************************ILLEGAL FOR NON-TRAINER USE******************************
C# arrays are zero-indexed. That means the array indexes start at zero. Arrays
in C# and arrays in most other popular languages work similarly, but there are a
few differences that you should be aware of.
When declaring an array, the brackets ([]) must come after the type, and not
after the identifier. Placing the brackets after the identifier is not legal syntax in
C#, as shown in the following example:
int[] table; // not int table[];

In addition, and unlike the C language, the size of the array is not part of its
type. This allows you to declare and assign to an array any array of int objects,
regardless of the arrays length, as in the following examples.
int[] numbers; // declare numbers as an int array of any size

numbers = new int[10]; // numbers is a 10-element array

numbers = new int[20]; // now it's a 20-element array

Topic Objective
To explain features that are
unique to C# arrays.
Lead-in
C# arrays are similar to
arrays in most other popular
languages, but you should
be aware of the differences
that are presented in this
topic.
26 Module 7: Strings, Arrays, and Collections


Declaring an Array
C# supports single-dimensional arrays and multidimensional arrays, which are
also know as rectangular arrays, and jagged arrays.
The following code examples show how to declare each of these arrays:
int[] numbers; // single-dimensional array

string[,] names; // multidimensional array

byte[][] scores; //Array-of-arrays

Instantiating an Array
Declaring arrays, as shown in the preceding examples, does not actually create
the arrays. As discussed later in this topic, arrays in C# are objects and must be
instantiated.
The following code examples show the syntax to create an array for each of the
arrays already discussed:
int[] numbers = new int[5]; // Single-dimensional array

string[,] names = new string[5,4]; // Multi-dimensional array

byte[][] scores = new byte[5][]; // Array-of-arrays
for (int x = 0; x < scores.Length; x++)
{
scores[x] = new byte[4];
}

Module 7: Strings, Arrays, and Collections 27


The following example shows a complete C# program that declares and
instantiates arrays as discussed in this topic.
using System;
class DeclareArraysSample
{
public static void Main()
{
// Single-dimensional array
int[] numbers = new int[5];

// Multidimensional array
string[,] names = new string[5,4];

// Array-of-arrays (jagged array)
byte[][] scores = new byte[5][];

// Create the jagged array
for (int i = 0; i < scores.Length; i++)
{
scores[i] = new byte[i+3];
}

// Print length of each row
for (int i = 0; i < scores.Length; i++)
{
Console.WriteLine("Length of row {0} is {1}",
i, scores[i].Length);
}
}
}

The preceding program displays the following output:
Length of row 0 is 3
Length of row 1 is 4
Length of row 2 is 5
Length of row 3 is 6
Length of row 4 is 7

Initializing Arrays
C# provides simple and straightforward ways to initialize arrays when they are
declared, by enclosing the initial values in curly braces ( {} ).

If an array is not initialized when it is declared, array members are
automatically initialized to the default initial value for the array type.

Important
28 Module 7: Strings, Arrays, and Collections


Single-Dimensional Array
The following examples show different ways to initialize single-dimensional
arrays:
int[] numbers = new int[5] {1, 2, 3, 4, 5};

string[] names = new string[3] {"Matt", "Joanne", "Robert"};

You can omit the size of the array, as in the following examples:
int[] numbers = new int[] {1, 2, 3, 4, 5};

string[] names = new string[] {"Matt", "Joanne", "Robert"};

If an initializer is provided, you can also omit the new statement, as in the
following examples:
int[] numbers = {1, 2, 3, 4, 5};

string[] names = {"Matt", "Joanne", "Robert"};

Multidimensional Array
The following examples show different ways to initialize multidimensional
arrays:
int[,] numbers = new int[3, 2] { {1, 2}, {3, 4}, {5, 6} };

string[,] siblings = new string[2, 2] { {"Mike","Amy"},
{"Mary","Albert"} };

You can omit the size of the array, as in the following examples:
int[,] numbers = new int[,] { {1, 2}, {3, 4}, {5, 6} };

string[,] siblings = new string[,] { {"Mike","Amy"},
{"Mary","Ray"} };

If an initializer is provided, you can also omit the new statement, as in the
following examples:
int[,] numbers = { {1, 2}, {3, 4}, {5, 6} };

string[,] siblings = { {"Mike", "Amy"}, {"Mary", "Albert"} };

Module 7: Strings, Arrays, and Collections 29


Jagged Array (Array-of-Arrays)
The following examples show different ways to initialize jagged arrays. You
can initialize jagged arrays by using the following style:
int[][] numbers = new int[2][] { new int[] {2,3,4},
new int[] {5,6,7,8,9} };

You can omit the size of the first array, as in the following example:
int[][] numbers = new int[][] { new int[] {2,3,4},
new int[] {5,6,7,8,9} };

Accessing Array Members
Accessing array members in C# is straightforward and similar to how you
access array members in C and C++. For example, the following code creates
an array called numbers and then assigns 5 to the fifth element of the array:
int[] numbers = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
numbers[4] = 5;

The following code example declares a multidimensional array and assigns 5 to
the member located at [1, 1]:
int[,] numbers = { {1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10} };
numbers[1, 1] = 5;

The following code example shows how to access a member of a jagged array:
int[][] numbers = new int[][]
{
new int[] {1, 2},
new int[] {3, 4}
};
numbers[1][1] = 5;

Arrays Are Objects
Arrays in C# are actually objects. System.Array is the abstract base type of all
array types. Therefore, you can use the properties and other class members of
System.Array.
For example, you can use the Length property to get the length of an array. The
following code example assigns the length of the numbers array, which is 5, to
a variable called LengthOfNumbers:
int[] numbers = {1, 2, 3, 4, 5};
int LengthOfNumbers = numbers.Length;

The System.Array class provides many other useful methods and properties,
such as methods for sorting, searching, and copying arrays.
30 Module 7: Strings, Arrays, and Collections


Iterating Over
! System.Array and System.Collections Classes
Implement IEnumerable Interface and its
GetEnumerator Method
! Enumerator Classes Implement IEnumerator
# Members: MoveNext, Reset, and Current
int[] numbers = new int[5] {1, 2, 3, 4, 5};
IEnumerator e = numbers.GetEnumerator();
while (e.MoveNext()) {
Console.WriteLine("Number: {0}", (int)e.Current);
}
// alternatively
foreach (int i in numbers)
{
Console.WriteLine("Number: {0}", i);
}
int[] numbers = new int[5] {1, 2, 3, 4, 5};
IEnumerator e = numbers.GetEnumerator();
while (e.MoveNext()) {
Console.WriteLine("Number: {0}", (int)e.Current);
}
// alternatively
foreach (int i in numbers)
{
Console.WriteLine("Number: {0}", i);
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
To understand the functionality of the various collection classes, you must
understand the key interfaces of the collection classes. A .NET Framework
interface provides a way to group a set of related members that can be used to
perform a particular action.
For example, a client of an object of any class that implements the
IEnumerable interface can obtain an enumerator object for that class. The
System.Array class and all of the System.Collections classes support the
IEnumerable interface.

It is easy to confuse the term enumerator with enumeration. However,
enumerator and enumeration are very different concepts. Enumeration, an
enum type in C#, is a distinct type with named constants, as in the following
example:
enum Color {Red,Green,Blue}


Support for IEnumerable and the GetEnumerator Method
These classes inherit from IEnumerable and implement its single method
GetEnumerator. The GetEnumerator method returns an enumerator that
implements the IEnumerator interface.
Enumerators are intended to be used only to read data in the collection. You
cannot use enumerators to modify the underlying collection. The enumerator is
required to be safe. In other words, enumerators must have a fixed view of the
items in a collection that remain the same, even if the collection is modified.
For example, if you call GetEnumerator on a collection at the point in time
when the collection contains the elements 1, 2, and 3, the enumerator object that
is returned by this method must always produce 1, 2, and 3 when it is iterated
over, even if the collection is later changed.
Topic Objective
To explain the role of the
IEnumerable and
IEnumerator interfaces in
collections.
Lead-in
A .NET Framework interface
provides a way to group a
set of related members that
can be used to perform a
particular action.
Emphasize the difference
between enumeration and
an enumerator.
Note
Module 7: Strings, Arrays, and Collections 31


Using Enumerators with Collections
When working with collections, you typically implement an enumerator in one
of the following ways:
! The enumerator makes a copy of all of the items in the collection.
Making a copy of a large collection guarantees safety but imposes a severe
penalty in performance and memory utilization for large collections and
therefore is usually not done.
! The enumerator has a reference to the collection.
If the enumerator implementation uses a reference to the collection, the
copy penalty is avoided. However, safety must be guaranteed by another
means.
If the collection is static or unchanging, you need take no additional action
to ensure safety. The .NET Framework collections are typically not static.
Instead, they implement a versioning mechanism. Every time a .NET
Framework collection changes, the collections version number is
incremented. An enumerator that detects that the collections version
number has changed after the enumerator was created throws an
InvalidOperationException that should be caught and handled by the
enumerators client.

The IEnumerator interface has the Current public instance property. The
Current property gets the current element in the collection.
An enumerator maintains a reference to the item in the collection that is
currently being enumerated. The enumerator is in an invalid state if it is
positioned before the first element in the collection or after the last element in
the collection. When the enumerator is in an invalid state, calling Current
throws an exception.
The IEnumerator interface also requires the following public instance
methods.
Method Use

MoveNext Advances the enumerator to the next element of the collection.
Reset Sets the enumerator to its initial position, which is before the first
element in the collection.

Initially, the enumerator is positioned before the first element in the collection.
Reset also brings the enumerator back to this position. Therefore, after an
enumerator is created or after a Reset, you must call MoveNext to advance the
enumerator to the first element of the collection before reading the value of
Current.
Current returns the same object until MoveNext or Reset is called.
After the end of the collection is passed, the enumerator returns to an invalid
state. At this time, calling MoveNext returns false. Calling Current throws an
exception if the last call to MoveNext returned false.
32 Module 7: Strings, Arrays, and Collections


The following code example shows how to iterate over a collection. In this
example, the collection is an instance of System.Array.
int[] numbers = new int[5] {1, 2, 3, 4, 5};
IEnumerator e = numbers.GetEnumerator();
while (e.MoveNext()) {
Console.WriteLine("Number: {0}", (int)e.Current);
}

This code outputs:
Number: 1
Number: 2
Number: 3
Number: 4
Number: 5

Using foreach to Iterate Through an Array
C# provides the foreach statement, which is a less verbose way to iterate
through the elements of an array. For example, the following code example
creates an array called numbers and iterates through it with the foreach
statement:
int[] numbers = {4, 5, 6, 1, 2, 3, -2, -1, 0};
foreach (int i in numbers) {
System.Console.WriteLine("Number: {0}", i);
}

Module 7: Strings, Arrays, and Collections 33


Comparing
! To Sort and Search, Collections Must Be Able to Compare Items
! IComparers Compare Method Compares Two Objects of Any Type
# Comparer class is the default implementation of IComparer
Its Compare method uses IComparable.CompareTo
! IComparables CompareTo Method Compares the Current Instance
to an Object of the Same Type
! CompareTo Returns
int CompareTo( object anObject );
int CompareTo( object anObject );
int Compare( object x, object y );
int Compare( object x, object y );
Value Meaning
Less than zero Instance is less than object
Zero Instance is equal to object
Greater than zero Instance is greater than object

*****************************ILLEGAL FOR NON-TRAINER USE******************************
To sort a collection, you must be able to compare and order the items of the
collection. The IComparer and IComparable interfaces are used to sort and
order a collections items.
The IComparer interfaces Compare method compares two objects of any
type and returns a value that indicates whether one object is less than, equal to,
or greater than the other.
The Compare method provides the sort order of a collection and is also used in
conjunction with the Array.BinarySearch method.
Default Implementation of IComparer
The Comparer class provides the default implementation of the IComparer
interface.
The Comparer classs Compare(object a, object b) method is implemented as
follows:
! If a implements IComparable, then a.CompareTo(b) is returned.
! Otherwise, if b implements IComparable, then b.CompareTo(a) is
returned.

The IComparable interface has a single CompareTo method that compares
the current instance with another object of the same type.
Topic Objective
To explain how the
IComparer and
IComparable interfaces are
used to sort and order a
collections items.
Lead-in
To sort a collection, you
must be able to compare
and order the items of the
collection.
34 Module 7: Strings, Arrays, and Collections


Return Values of CompareTo
The value returned by the CompareTo method is a 32-bit signed integer that
indicates the relative order of the instance and the object that is passed as a
parameter, as in the following example:
int CompareTo(object anObject);

The following table describes the possible meanings of the return value.
Value Meaning

Less than zero This instance is less than anObject.
Zero This instance is equal to anObject.
Greater than zero This instance is greater than anObject.

By definition, any object compares greater than a null reference, and two null
references compare equal to each other.
Issues with Using IComparable.CompareTo
The parameter, anObject, must be the same type as the class or value type that
implements this interface. Otherwise, ArgumentException is thrown.
The default comparison procedures use the Thread.CurrentCulture of the
current thread unless it is otherwise specified. String comparisons may have
different results depending on the culture.
To perform case-insensitive comparisons on strings, you can use the
CaseInsensitiveComparer classs implementation of the IComparer
interface.
Module 7: Strings, Arrays, and Collections 35


Sorting
! Sort Method Using Elements IComparable.CompareTo
! IComparable.CompareTo Design Pattern
Array.Sort( anArray );
Array.Sort( anArray );
public int CompareTo(Object anObject) {
if ( anObject == null) return 1;
if ( !(anObject is <classname>) ) {
throw new ArgumentException(); }
// Do comparison and return a
// negative integer if instance < anObject
// 0 if instance == anObject
// positive integer if instance > anObject
}
public int CompareTo(Object anObject) {
if ( anObject == null) return 1;
if ( !(anObject is <classname>) ) {
throw new ArgumentException(); }
// Do comparison and return a
// negative integer if instance < anObject
// 0 if instance == anObject
// positive integer if instance > anObject
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Some collections, such as Array, sort their items when their Sort method is
called. Overloaded versions of the Sort method allow you to supply an
IComparer implementation in the method call that is used to perform the
ordering of the elements. These overloaded versions of the Sort method provide
you with the flexibility to resort the same collection of items by using different
IComparer implementations.
If you use the arrays public static void Sort(Array) method, the default
Comparer class implementation of IComparer is used, and the arrays
elements are sorted by using the elements implementation of the CompareTo
method of the IComparable interface.
Other collection classes, such as SortedList, have constructors whose
parameters determine the sort order. These classes are less flexible than classes
that implement a sort method because after they are constructed their sort
ordering cannot be changed.
Topic Objective
To explain how to compare
and order the items of a
collection by using the
IComparer and
IComparable interfaces.
Knowledge of this topic is
required for the lab.
Lead-in
Some collections, such as
Array, sort their items when
their Sort method is called.
36 Module 7: Strings, Arrays, and Collections


Demonstration: Sorting and Enumerating an Array

*****************************ILLEGAL FOR NON-TRAINER USE******************************
As stated in Sorting in this module, you can sort an array by using the
CompareTo method when the value type or class of the items in the collection
implements the IComparable interface.
In this demonstration, an Employee class contains an employees name, level
number, and hiring date. To sort an array of Employees based on increasing
level numbers where equal level numbers are then ordered by increasing hiring
date, implement the following:
using System;

namespace ArraySorting
{
public class Employee : IComparable
{
public string name;
public int level;
public DateTime hiringDate;

public Employee(
string name,int level,DateTime hiringDate) {
this.name = name;
this.level=level;
this.hiringDate=hiringDate;
}

public int CompareTo(Object anObject) {
if (anObject == null) return 1;
if ( !(anObject is Employee) ) {
throw new ArgumentException();
}

(Code continued on the following page.)
Topic Objective
To demonstrate how to sort
and enumerate an array.
Lead-in
In this demonstration, we
will sort an array by using
the CompareTo method
when the value type or class
of the items in the collection
implements the
IComparable interface.
For Your Information
You should carefully cover
the CompareTo method
because it is used in the lab.
Module 7: Strings, Arrays, and Collections 37


Employee anEmployee = (Employee)anObject;
if ( level < anEmployee.level ) return -1;
else {
if ( level == anEmployee.level ) {
if (hiringDate <
anEmployee.hiringDate)
return -1;
else {
if ( hiringDate ==
anEmployee.hiringDate)
return 0;
else return 1;
}
}
else return 1;
}
}
}

public class ArraySort {

public static void Main() {

// Create and initialize a new Array instance.
Employee[] myEmployees = new Employee[10];
myEmployees[0] = new Employee(
"a",2,new DateTime(1990,1,1));
myEmployees[1] = new Employee(
"b",2,new DateTime(2000,1,1));
myEmployees[2] = new Employee(
"c",2,new DateTime(1990,1,1));
myEmployees[3] = new Employee(
"d",4,new DateTime(2000,1,1));
myEmployees[4] = new Employee(
"e",4,new DateTime(1990,1,1));
myEmployees[5] = new Employee(
"f",4,new DateTime(2000,1,1));
myEmployees[6] = new Employee(
"g",1,new DateTime(1990,2,5));
myEmployees[7] = new Employee(
"h",1,new DateTime(2000,1,1));
myEmployees[8] = new Employee(
"i",1,new DateTime(1990,1,1));
myEmployees[9] = new Employee(
"j",0,new DateTime(2001,1,1));

// Display the values of the Array.
Console.WriteLine(
"The Array instance initially contains values:"
);
PrintIndexAndValues( myEmployees );

// Sort the values of the Array.
Array.Sort( myEmployees );

(Code continued the following page.)
38 Module 7: Strings, Arrays, and Collections


// Display the values of the Array.
Console.WriteLine( "After sorting:" );
PrintIndexAndValues( myEmployees );
}

public static void PrintIndexAndValues(
Array myEmployees ) {

foreach ( Employee e in myEmployees ) {
Console.WriteLine(
"name: {0} \tlevel: {1} \tdate:{2:d}",
e.name, e.level, e.hiringDate);
}

}
}
}

The preceding code displays the following output to the console:
The Array instance initially contains values:
name: a level: 2 date:1/1/1990
name: b level: 2 date:1/1/2000
name: c level: 2 date:1/1/1990
name: d level: 4 date:1/1/2000
name: e level: 4 date:1/1/1990
name: f level: 4 date:1/1/2000
name: g level: 1 date:2/5/1990
name: h level: 1 date:1/1/2000
name: i level: 1 date:1/1/1990
name: j level: 0 date:1/1/2001
After sorting:
name: j level: 0 date:1/1/2001
name: i level: 1 date:1/1/1990
name: g level: 1 date:2/5/1990
name: h level: 1 date:1/1/2000
name: c level: 2 date:1/1/1990
name: a level: 2 date:1/1/1990
name: b level: 2 date:1/1/2000
name: e level: 4 date:1/1/1990
name: d level: 4 date:1/1/2000
name: f level: 4 date:1/1/2000

Module 7: Strings, Arrays, and Collections 39


" "" " .NET Framework Collections
! Examples of System.Collections Classes
! Lists
! Dictionaries
! SortedList
! Collection Usage Guidelines
! Type Safety and Performance

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The System.Collections namespace contains interfaces and classes that define
various collections of objects, such as lists, queues, arrays, hashtables, and
dictionaries.
Topic Objective
To introduce the topics in
the section.
Lead-in
The System.Collections
namespace contains
interfaces and classes that
define various collections of
objects, such as lists,
queues, arrays, hashtables,
and dictionaries.
40 Module 7: Strings, Arrays, and Collections


Examples of System.Collections Classes
! ArrayList
# Implements IList by using a dynamically-sized array
! DictionaryBase
# Provides abstract base class for strongly-typed collection of
associated keys and values
! Hashtable
# Represents a collection of keys and values that are organized
around the keys hash code
! SortedList
# Represents the collection of keys and values, sorted by keys and
accessible by key and index
! BitArray, Queue, Stack, CollectionBase, ReadOnlyCollectionBase

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The following table shows some of the collection classes in the
System.Collections namespace.
Class Description

ArrayList Implements the IList interface by using an array whose
size is dynamically increased as required.
BitArray Manages a compact array of bit values, which are
represented as Booleans, where true indicates that the bit
is on (1) and false indicates the bit is off (0).
CollectionBase Provides the abstract base class (MustInherit in
Microsoft Visual Basic) for a strongly-typed collection.
DictionaryBase Provides the abstract base class (MustInherit in
Visual Basic) for a strongly-typed collection of
associated keys and values.
Hashtable Represents a collection of associated keys and values that
are organized around the hash code of the key.
Queue Represents a first-in, first-out collection of objects.
ReadOnlyCollectionBase Provides the abstract base class (MustInherit in
Visual Basic) for a strongly-typed read-only collection.
SortedList Represents a collection of associated keys and values that
are sorted by the keys and are accessible by key and by
index.
Stack Represents a simple last-in-first-out collection of type
Object.

Topic Objective
To show some of the
collection classes in the
System.Collections
namespace.
Lead-in
Lets look briefly at some of
the collection classes in the
System.Collections
namespace.
Do not spend a lot of time
on this slide. Just introduce
students to some commonly
used collection classes and
encourage them to refer to
the .NET Framework SDK
for more information.
Module 7: Strings, Arrays, and Collections 41


Lists
! IList Interface for Classes That Represent an Ordered
Collection of Objects That Can Be Individually Indexed
! Some Classes That Implement IList
# Array, ArrayList, StringCollection, and
TreeNodeCollection
! Methods Include:
# Add, Clear, Contains, Insert, IndexOf, Remove, and
RemoveAt

*****************************ILLEGAL FOR NON-TRAINER USE******************************
IList is an interface for classes that represent an ordered collection of objects
that can be individually indexed. Array, ArrayList, StringCollection, and
TreeNodeCollection are some of the classes that implement IList.
The following tables describe the public instance properties that the IList
interface implements.
Property Use

IsFixedSize When implemented by a class, gets a value indicating whether the IList
has a fixed size.
IsReadOnly When implemented by a class, gets a value indicating whether the IList
is read-only.
Item When implemented by a class, gets or sets the element at the specified
index.
In C#, this property is the indexer for the IList class.

Topic Objective
To describe the public
instance members that the
IList interface implements.
Lead-in
IList is an interface for
classes that represent an
ordered collection of objects
that can be individually
indexed. Array, ArrayList,
StringCollection, and
TreeNodeCollection are
some of the classes that
implement IList.
42 Module 7: Strings, Arrays, and Collections


The following table describes the public instance methods that the IList
interface defines.
Method Use

Add When implemented by a class, adds an item to the IList.
Clear When implemented by a class, removes all items from the IList.
Contains When implemented by a class, determines whether the IList contains a
specific value.
IndexOf When implemented by a class, determines the index of a specific item in
the IList.
Insert When implemented by a class, inserts an item to the IList at the
specified position.
Remove When implemented by a class, removes the first occurrence of a specific
object from the IList.
RemoveAt When implemented by a class, removes the IList item at the specified
index.

Module 7: Strings, Arrays, and Collections 43


Demonstration: ArrayList

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this demonstration, ArrayList implements the IList interface by using an
array whose size is dynamically increased as required.
using System;
using System.Collections;

class listSample
{

public static void Main(string[] args) {
ArrayList fruit = new ArrayList();
fruit.Add("Apple");
fruit.Add("Pear");
fruit.Add("Orange");
fruit.Add("Banana");
Console.WriteLine ("\nList Contains:");
foreach (string item in fruit) {
Console.WriteLine(item);
}

Console.WriteLine (
"\nResult of Contains method for Kiwi: {0}",
fruit.Contains("Kiwi"));

Console.WriteLine (
"\nAdding Kiwi at Orange:");

fruit.Insert(fruit.IndexOf("Orange"),"Kiwi");
Console.WriteLine ("\nList Contains:");
foreach (string item in fruit) {
Console.WriteLine(item);
}
(Code continued the following page.)
Topic Objective
To demonstrate how
ArrayList implements the
IList interface by using an
array whose size is
dynamically increased as
required.
Lead-in
In this demonstration,
ArrayList implements the
IList interface by using an
array whose size is
dynamically increased as
required.
44 Module 7: Strings, Arrays, and Collections


Console.WriteLine (
"\nResult of Contains method for Kiwi: {0}",
fruit.Contains("Kiwi"));

Console.WriteLine (
"\r\nPress Return to exit.");
Console.Read();
}
}

The preceding code example displays the following output to the console:
List Contains:
Apple
Pear
Orange
Banana

Result of Contains method for Kiwi: False

Adding Kiwi at Orange:

List Contains:
Apple
Pear
Kiwi
Orange
Banana

Result of Contains method for Kiwi: True

Press Return to exit.

Module 7: Strings, Arrays, and Collections 45


Dictionaries
! IDictionary is an Interface for Collections of Associated
Keys and Values
# Each association must have a unique non-null key, but
the value of an association can be any object reference,
including a null reference
! Collection Classes That Implement IDictionary Include
# Hashtable, DictionaryBase, and SortedList
! Methods Include:
# Add, Clear, Contains, GetEnumerator, and Remove

*****************************ILLEGAL FOR NON-TRAINER USE******************************
IDictionary is an interface for collections of associated keys and values. Each
association must have a unique non-null key, but the value of an association can
be any object reference, including a null reference.
Hashtable, DictionaryBase, and SortedList are examples of collection classes
that implement IDictionary.
The IDictionary interface allows the contained keys and values in the items in
a collection to be enumerated, but it does not imply any particular sort order.
The IDictionaryEnumerator interface inherits from IEnumerator and adds
members to return the objects Key and Value fields individually or in a
DictionaryEntry structure.
IDictionary implementations can be divided into the following categories:
! Read-only
You cannot modify a read-only IDictionary.
! Fixed-size
You cannot add or remove elements from a fixed-size IDictionary, but you
can modify existing elements.
! Variable-size
You can add, remove, or modify elements in a variable-size IDictionary.

Topic Objective
To describe the IDictionary
interface and the classes
that it implements.
Lead-in
IDictionary is an interface
for collections of associated
keys and values.
46 Module 7: Strings, Arrays, and Collections


The following table describes some of the public instance properties that the
IDictionary interface implements.
Property Use

IsFixedSize When implemented by a class, gets a value indicating whether the
IDictionary has a fixed size.
IsReadOnly When implemented by a class, gets a value indicating whether the
IDictionary is read-only.
Item When implemented by a class, gets or sets the element at the
specified index.
In C#, this property is the indexer for the class.
Keys When implemented by a class, gets an ICollection containing the
keys of the IDictionary.
Values When implemented by a class, gets an ICollection containing the
values in the IDictionary.

The following table describes some of the public instance methods that the
IDictionary interface implements.
Method Use

Add When implemented by a class, adds an entry with the provided
key and value to the IDictionary.
Clear When implemented by a class, removes all elements from the
IDictionary.
Contains When implemented by a class, determines whether the
IDictionary contains an entry with the specified key.
GetEnumerator When implemented by a class, returns an
IDictionaryEnumerator for the IDictionary.
Remove When implemented by a class, removes the entry with the
specified key from the IDictionary.

For a complete list of the members of the IDictionary interface, see
IDictionary Members in the .NET Framework SDK documentation.
Module 7: Strings, Arrays, and Collections 47


Demonstration: Hashtable

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The Hashtable class represents a collection of associated keys and values that
are organized on the basis of the keys hash code. The objects that are used as
keys in a Hashtable must implement or inherit the Object.GetHashCode and
Object.Equals methods. If key equality were simply reference equality, the
inherited implementation of these methods would suffice.
Furthermore, these methods must produce the same results when they are called
with the same parameters while the key exists in the Hashtable. Key objects
must be immutable as long as they are used as keys in the Hashtable.
The foreach statement of the C# language requires the type of the elements in
the collection. Because each element of the Hashtable is a key-and-value pair,
the element type is not the type of the key or the type of the value. Instead, the
element type is DictionaryEntry, as in the following example:
foreach (DictionaryEntry myEntry in myHashtable) {...}

Topic Objective
To demonstrate how to
create a hash table that is
used for searches.
Lead-in
The Hashtable class
represents a collection of
associated keys and values
that are organized on the
basis of the keys hash
code.
48 Module 7: Strings, Arrays, and Collections


The code in this demonstration creates a hash table of employee numbers and
names, and searches the table for an employee by number and by name.
using System;
using System.Collections;

class HashTableSample
{
public static void Main(String[] args)
{
//create hash table of employee numbers and names
Hashtable table = new Hashtable();
table.Add("0123","Jay");
table.Add("0569","Brad");
table.Add("1254","Brian");
table.Add("6839","Seth");
table.Add("3948","Rajesh");
table.Add("1930","Lakshan");
table.Add("9341","Kristian");
printTable(table);

//now we'll look to see if an employee is in the table
//by key
Console.Write(
"Search for employee by key, enter ID ==-> ");
string input = Console.ReadLine();
if (table.Contains(input)) {
Console.WriteLine("Found {0} in the list.",input);
}
else {
Console.WriteLine("Employee {0} not found.",input);
}
//by value
Console.Write(
"Search for employee by value, enter name ==-> ");
input = Console.ReadLine();
if (table.ContainsValue(input)) {
Console.WriteLine("Found {0} in the list.",input);
}
else {
Console.WriteLine("Employee {0} not found.",input);
}
printTable(table);
// remove an employee by key
Console.Write("Remove employee by key, enter ID ==-> ");
input = Console.ReadLine();
table.Remove(input);
printTable(table);
Console.WriteLine ("\r\nPress Return to exit.");
Console.Read();
}

(Code continued on the following page.)
Module 7: Strings, Arrays, and Collections 49


public static void printTable(Hashtable table) {
Console.WriteLine ("Current list of employees:\n");
Console.WriteLine ("ID\tName");
Console.WriteLine ("--\t----");
foreach (DictionaryEntry d in table) {
Console.WriteLine ("{0}\t{1}", d.Key, d.Value);
}
}
}

The preceding code example displays the following output or similar output to
the console:
Current list of employees:

ID Name
-- ----
1254 Brian
6839 Seth
3948 Rajesh
1930 Lakshan
0123 Jay
0569 Brad
9341 Kristian
Search for employee by key, enter ID ==-> 111
Employee 111 not found.
Search for employee by value, enter name ==-> Jay
Found Jay in the list.
Current list of employees:

ID Name
-- ----
1254 Brian
6839 Seth
3948 Rajesh
1930 Lakshan
0123 Jay
0569 Brad
9341 Kristian
Remove employee by key, enter ID ==-> 9341
Current list of employees:

ID Name
-- ----
1254 Brian
6839 Seth
3948 Rajesh
1930 Lakshan
0123 Jay
0569 Brad

Press Return to exit.

50 Module 7: Strings, Arrays, and Collections


SortedList
! SortedList Maintains Two Arrays for Entries
# One array for the keys and another array for the associated values
! Count Property Number of Elements in the SortedList
! Sorted Using a Specific IComparer Implementation or According to
the Key's IComparable Implementation
! Printing the Keys and Values of a SortedList
SortedList mySL = new SortedList();
// Add an entry with a key = "First" and a value = 1
mySL.Add("First", 1);
// Increment the value of the entry whose key = "First"
mySL["First"] = (Int32)mySL["First"] + 1;
SortedList mySL = new SortedList();
// Add an entry with a key = "First" and a value = 1
mySL.Add("First", 1);
// Increment the value of the entry whose key = "First"
mySL["First"] = (Int32)mySL["First"] + 1;
for ( int i = 0; i < myList.Count; i++ ) {
Console.WriteLine( "\t{0}:\t{1}",
myList.GetKey(i), myList.GetByIndex(i) ); }
for ( int i = 0; i < myList.Count; i++ ) {
Console.WriteLine( "\t{0}:\t{1}",
myList.GetKey(i), myList.GetByIndex(i) ); }

*****************************ILLEGAL FOR NON-TRAINER USE******************************
A SortedList maintains two arrays internally to store entries to the list: one
array for the keys, and another array for the associated values. An entry is a
key-and-value pair. A SortedList implements the IDictionary, IEnumerable,
ICollection, and ICloneable interfaces.
The Count property gets the number of elements that are contained in the
SortedList. The Add method is used to add an entry to the SortedList. The [ ]
Operator is used to modify the value of an entry with the specified key.
You can sort the keys of a SortedList according to an IComparer
implementation that is specified when the SortedList is instantiated or
according to the IComparable implementation that is provided by the keys
themselves. In either case, a SortedList does not allow duplicate keys.
Operations on a SortedList tend to be slower than operations on a Hashtable
because of the sorting. However, the SortedList offers more flexibility by
allowing access to the values through the associated keys or through the
indexes.
A key cannot be a null reference, but a value can be a null reference. Indexes in
the SortedList collection are zero-based.
Topic Objective
To explain how arrays are
used in a SortedList and
how to create, initialize, and
access a SortedList. This
topic is very important
because its contents are
required for the lab.
Lead-in
A SortedList maintains two
arrays internally to store
entries to the list: one array
for the keys, and another
array for the associated
values.
For Your Information
You should carefully cover
the SortedList class, the
Add method, and the [ ]
Operator because they are
used in the lab.
Module 7: Strings, Arrays, and Collections 51


The following example shows how to create a SortedList, add an entry, modify
an entrys value, and print out the SortedLists keys and values.
using System;
using System.Collections;
public class SamplesSortedList {

public static void Main() {

// Create and initialize a new SortedList.
SortedList mySL = new SortedList();
mySL.Add("First", 1);
mySL.Add("Second", 2);
mySL.Add("Third", 3);

// Display the properties and values of the SortedList.
Console.WriteLine( "mySL" );
Console.WriteLine( " Count: {0}", mySL.Count );
Console.WriteLine( " Capacity: {0}", mySL.Capacity );
Console.WriteLine( " Keys and Values:" );
PrintKeysAndValues( mySL );

// increment the value of the entry whose key is "Third"
mySL["Third"] = (Int32)mySL["Third"] + 1;
PrintKeysAndValues( mySL );
}


public static void PrintKeysAndValues( SortedList myList ){
Console.WriteLine( "\t-KEY-\t-VALUE-" );
for ( int i = 0; i < myList.Count; i++ ) {
Console.WriteLine( "\t{0}:\t{1}",
myList.GetKey(i), myList.GetByIndex(i) );
}
Console.WriteLine();
}
}

The preceding code example displays the following output to the console:
mySL
Count: 3
Capacity: 16
Keys and Values:
-KEY- -VALUE-
First: 1
Second: 2
Third: 3

-KEY- -VALUE-
First: 1
Second: 2
Third: 4

52 Module 7: Strings, Arrays, and Collections


Collection Usage Guidelines
! Use a Collection instead of an Array:
# When Add, Remove, or other methods for manipulating the set
of objects are supported. This scopes all related methods to
the collection
# When you want to provide a read-only set of objects. System.Array
objects are always writable
- You can add read-only wrappers around internal arrays
! Use Collections to Avoid Inefficiencies
# In the following code, each call to the myObj property creates a
copy of the array. As a result, 2n+1 copies of the array are created:
for (int i = 0; i < obj.myObj.Count; i++)
DoSomething(obj.myObj[i]);
for (int i = 0; i < obj.myObj.Count; i++)
DoSomething(obj.myObj[i]);

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Class library designers sometimes need to decide when to use an array and
when to use a collection. Arrays and collections have similar usage models, but
they differ somewhat in performance.
Collections vs. Arrays
When Add, Remove, or other methods for manipulating the collection are
supported, use a collection, instead of an array. Using a collection scopes all
related methods to the collection.
Also, use collections to add read-only wrappers around internal arrays.
System.Array objects are always writable.
Array Valued Properties
Use collections to avoid code inefficiencies. In the following code example,
each call to the myObj property creates a copy of the array. As a result, 2n+1
copies of the array will be created in the following loop:
for (int i = 0; i < obj.myObj.Count; i++)
DoSomething(obj.myObj[i]);

Topic Objective
To distinguish between
collections and arrays and
explain when collections are
used.
Lead-in
Arrays and collections are
used similarly, but they
perform differently.
Module 7: Strings, Arrays, and Collections 53


Choosing A Collection Class
Choosing the right Collections class must be done carefully. Using the wrong
collection can unnecessarily restrict how you use it.
Consider the following questions:
! Do you need temporary storage?
If yes, consider Queue or Stack.
If no, consider the other collections.
! Do you need to access the elements in a certain order, such as first-in-first-
out, last-in-first-out, or randomly?
The Queue offers first-in, first-out access.
The Stack offers last-in, first-out access.
The rest of the collections offer random access.
! Do you need to access each element by index?
ArrayList and StringCollection offer access to their elements by the
zero-based index.
Hashtable, SortedList, ListDictionary, and StringDictionary offer
access to their elements by the key of the element.
NameObjectCollectionBase and NameValueCollection offer access to
their elements either by the zero-based index or by the key of the
element.
! Will each element contain just one value or a key-singlevalue pair or a key-
multiplevalues combination?
One value: Use any of the collections based on IList.
Key-singlevalue pair: Use any of the collections based on IDictionary.
Key-multiplevalues combination: Consider using or deriving from the
NameValueCollection class in the Collections.Specialized namespace.
! Do you need to sort the elements differently from how they were entered?
Hashtable sorts the elements by the hash code of the key.
SortedList sorts the elements by the key, based on an IComparer
implementation.
ArrayList provides a Sort method that takes an IComparer
implementation as a parameter.
! Do you need fast searches and retrieval of information?
ListDictionary is faster than Hashtable for small collections of ten
items or less.
! Do you need collections that accept only strings?
StringCollection (based on IList) and StringDictionary (based on
IDictionary) are in the Collections.Specialized namespace.

54 Module 7: Strings, Arrays, and Collections


Type Safety and Performance
! System.Collections Items Require a Runtime Object Cast
! Drawbacks of Runtime Casting
# Type errors found at runtime, rather than at compile time
# Runtime overhead of casting
# Runtime overhead of boxing/unboxing operations for value types
! Type-Specific Collection Classes Can Eliminate Runtime Casting
# System.Collections.Specialized namespace contains string-
specific collections
# Create your own type-specific collection class by inheriting from a
System.Collections class and adding type-specific members
! Ways to Reduce Boxing and Unboxing Operations of Value Types
# Encapsulate value types in a class
# Manipulate value types through interfaces

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The System.Collections classes are generic collections, which ignore their
items true types. Generic collections allow classes to be used for items of any
type.
Drawbacks of Runtime Casting
The System.Collections classes have properties and methods that manipulate
the collections items through the items System.Object base type. The classs
members take and return parameters of type System.Object. For example,
IEnumerator.Current returns a System.Object instance.
This use of an objects base class requires casting a runtime object to obtain the
true type of the object. Casting System.Object instances at run time has two
drawbacks:
! Collection operations with invalid parameter or return types are not flagged
at compile time.
If the cast is invalid, the problem is only detected at run time when an
InvalidCastException is thrown.
! Such casting hurts runtime performance because the .NET runtime must
perform type-checking.

In addition, in the case of a collection of value types, such as an int, casting to
and from System.Object results in boxing and unboxing operations that further
impair performance.
The ideal solution to these two drawbacks would be a language mechanism that
automatically generates a type-specific version of the System.Collections
classes that is similar to the C++ template class mechanism. For example, if you
declared an ArrayList<int>, this object would store int values directly, without
casting and without boxing. This feature is not implemented in the first version
of C#; however, it may be implemented in a later version.
Topic Objective
To discuss runtime casting
for type safety and the
effects of runtime casting
and boxing and unboxing on
performance.
Lead-in
The System.Collections
classes are generic
collections, which ignore
their items true types.
Generic collections allow
classes to be used for items
of any type.
Module 7: Strings, Arrays, and Collections 55


The System.Collections classes provide a simple and powerful way to
implement collections. Carefully evaluate whether the use of these classes in
your programs imposes a significant enough performance penalty to warrant
taking the further action that is described in the following sections.
Strongly-Typed Collections
One way to provide compile-time type-checking and improve the performance
of collection code is to use strongly-typed collections. The
System.Collections.Specialized namespace contains string-specific collection
classes, as described in the following table.
Class Description

NameValueCollection Represents a sorted collection of associated String keys
and String values that can be accessed with the hash
code of the key or with the index.
StringCollection Represents a collection of strings.
StringDictionary Implements a hash table with the key strongly-typed to
be a string, rather than an object.
StringEnumerator Supports a simple iteration over a StringCollections.

Creating Custom Collection Classes
You can also create your own strongly-typed collection by creating your own
custom collection class. Your class can reuse System.Collections functionality
by inheriting from the appropriate System.Collections class. You create the
type-specific class with methods and properties whose parameters and return
types are type-specific.
By using C#s explicit interface implementation mechanism, which is discussed
in Module 6, Working with Types, in Course 2349B, Programming with the
Microsoft .NET Framework (Microsoft Visual C#

.NET), a class can


implement both non-interface and interface members of the same name.
Explicit interface method implementations are frequently used when you
implement such interfaces as IEnumerable, IEnumerator, ICloneable,
IComparable, ICollection, IList, and IDictionary. By having both sets of
members, clients that expect type-specific or System.Object parameters and
return values can use the custom class.
Techniques for Handling Boxing and Unboxing
Instead of creating a type-specific collection class, you can remove the
overhead of boxing and unboxing by using a reference type, rather than a value
type, for the items in a collection. To accomplish this, you can wrap a value
type in a class, as shown in Module 6, Course 2349B, Programming with the
Microsoft .NET Framework (Microsoft Visual C# .NET).
In some cases, you can reduce boxing and unboxing by using interfaces. Value
types can implement interfaces, but because interfaces are referenced types, you
can only have an interface reference to a boxed value type.
You can define an interface for your value type that has the methods and
properties that are needed to manipulate the value type. The value type instance
must still be boxed when it is inserted into a generic System.Collections object,
but your code will be able to manipulate the items value through its interface
without unboxing the item.
56 Module 7: Strings, Arrays, and Collections


For example, in the case of a collection of integer values where you want to be
able to add an integer to an elements value, you can have your value type
implement an interface, as in the following code:
interface IAdd
{
void Add(int amount);
}


struct IntStruct: IAdd
{
int number;

public IntStruct(int number) {
this.number = number;
}

public int Number {
get { return(number);}
}

public void Add(int amount) {
number += amount;
}

public override string ToString() {
return(number.ToString());
}
}

Module 7: Strings, Arrays, and Collections 57


Lab 7: Working with Strings, Enumerators, and
Collections

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives
After completing this lab, you will be able to:
! Create an application that parses and formats strings.
! Create an application that uses SortedList collection objects.

Lab Setup
Starter and solution files are associated with this lab. The starter files are in the
folder <install folder>\Labs\Lab07\Starter, and the solution files are in the
folder <install folder>\Labs\Lab07\Solution.
Scenario
In this lab, you are provided with a Visual Studio .NET console application as a
starting point. The application, named WordCount, parses a test string into
words and then determines the number of unique words and the number of
times each word occurs. The results of this analysis are formatted and displayed
on the console.
The application is a modified version of the .NET Framework SDK sample,
Word Count.
Estimated time to complete this lab: 60 minutes
58 Module 7: Strings, Arrays, and Collections


Exercise 1
Sorting Words Alphabetically
In this exercise, you will create a class that breaks up the test string into words
and then stores each word and the number of its occurrences in a SortedList.
By default, the sort order will be based on the alphabetical ordering of the word
keys.
! Modify the WordCount class to sort words alphabetically
1. Open the WordCount project in the starter directory by using Visual Studio
.NET.
2. Open the WordCount.cs file.
3. In the WordCounter class, create a member named wordCounter that is
an instance of the SortedList class.
4. Create a read-only property named UniqueWords that returns the number
of items in wordCounter.
5. Create a public method named GetWordsAlphabeticallyEnumerator that
takes no arguments and returns a wordCounter enumerator object of type
IDictionaryEnumerator.
6. Create a public method named CountStats that takes two out parameters of
type Int64 named numWords and numChars and returns a Boolean.
7. Implement the CountStats method to break up the test string, create the
alphabetically sorted list, and return the word count statistics.
a. Initialize the CountStats methods out parameters to 0.
b. Use the String.Split method to break up testString into individual
words that are stored in an array of type string named Words.
c. Assign to the out parameter numWords the number of words that are
obtained in step 7.b.
d. For each non-empty string in the array Words:
i. Add the number of characters in the word string to numChars.
ii. If wordCounter does not already contain the string, add a new entry
whose key is the string and whose value is 1. The value represents
the number of occurrences of the word. Otherwise, increment by one
the value of the existing entry.

For information about and examples of the IDictionary methods
named Add and Contains, see Dictionaries and Demonstration: Hashtable
in this module. For an example of how to modify the value of an entry by
using the [] Operator, see SortedList in this module.

iii. Return true.

Tip
Module 7: Strings, Arrays, and Collections 59


! Modify the Application class
1. In the Application class's Main method, create an instance of
WordCounter named wc.
2. Create two variables of type Int64 named NumWords and NumChars.
3. Call the wc objects CountStats method to assign the word and character
counts to NumWord and NumChars respectively.
4. Output to the console the test string WordCounter.testString.
5. Output to the console a two-column header labeled Words and Chars. Use
a tab to separate the columns.
6. Output to the console the number of words, and format this output to occupy
five characters, followed by a tab and the number of characters.

! Test the WordCount application
Build and run the application. You should see output that is similar to the
following:
For string
Hello world
hello here i am where are you hello you

Words Chars
11 41


60 Module 7: Strings, Arrays, and Collections


Exercise 2
Sorting Words by Number of Occurrences
In this exercise, you will create a nested class that implements the
IComparable interface and whose CompareTo method will result in a
SortedList that is ordered on the basis of the number of occurrences of a word.
! Modify the WordCount class to sort words by occurrences
1. Add a nested class named WordOccurrence that inherits from
IComparable.
2. Add a private member of type int named occurrences.
3. Add a private member of type String named word.
4. Add a constructor that takes two parameters. The first parameter is of type
int, and the second parameter is of type String. The constructor assigns the
first value to occurrences and the second value to word.
5. Add a public method CompareTo that takes an Object parameter and
returns an int.
The code should follow the CompareTo design pattern and return a value
that results in a sort of words that is ordered by the occurrences of each
word in ascending order. Less frequently occurring words should be
followed by more frequently occurring words.
In the case in which the number of occurrences of the instance and the
Object parameter are the same, the sort should be alphabetical by using the
String.Compare method.

For an example of implementing the CompareTo method, see
Demonstration: Sorting and Enumerating an Array in this module.

6. Add two public read-only properties named Occurrences and Word that
return the fields occurrences and word respectively. End the nested class
definition.
7. Back in the WordCounter class itself, add a public method named
GetWordsByOccurrenceEnumerator that returns an object of type
IDictionaryEnumerator. Implement
GetWordsByOccurrenceEnumerator as follows:
a. Create a new SortedList named sl.
b. Iterate through the alphabetically-sorted list named wordCounter by
using an enumerator that is obtained by calling the
GetWordsAlphabeticallyEnumerator method.
i. For each alphabetical list entry, create a new object of type
WordOccurence that is initialized with the alphabetically sorted list
entrys Value and Key.
ii. Add to sl a new entry whose key field is the new WordOccurence
object and whose value field is set to null.
c. Return an enumerator of sl of type IDictionaryEnumerator.

Tip
Module 7: Strings, Arrays, and Collections 61


! Modify the Application class
1. Output the words to be sorted alphabetically by adding code to the
Application classs Main method to:
a. Obtain an IDictionaryEnumerator object named de by calling the wc
objects GetWordsAlphabeticallyEnumerator method.
b. Display a message to inform the user that the output that is generated
will display word usage sorted alphabetically. Also inform the user of
the number of unique words.
c. Iterate over the collection and output each entrys value and key,
formatting them so that the value is displayed in the first column and the
key is displayed in the second column.
2. Output the words sorted by occurrence by adding code to:
a. Assign to de an IDictionaryEnumerator object by calling the wc
objects GetWordsByOccurrenceEnumerator method.
b. Display a message to inform the user that the output that is generated
will display word usage sorted by occurrence. Also inform the user of
the number of unique words.
c. Iterate over the collection. For each entry, obtain the key fields
WordCounter.WordOccurrence object and output to the console the
Occurrences property of the WordCounter.WordOccurrence object
in the first column and the objects Word property in the second
column.

62 Module 7: Strings, Arrays, and Collections


! Test the WordCount application
Build and run the application. You should see output that is similar to the
following:
For string
Hello world
hello here i am where are you hello you

Words Chars
11 41
Word usage sorted alphabetically (9 unique words)
1: "am"
1: "are"
2: "hello"
1: "Hello"
1: "here"
1: "i"
1: "where"
1: "world"
2: "you"
Word usage sorted by occurrence (9 unique words)
1: am
1: are
1: Hello
1: here
1: i
1: where
1: world
2: hello
2: you


Module 7: Strings, Arrays, and Collections 63


Review
! Strings
! Terminology Collections
! .NET Framework Arrays
! .NET Framework Collections

*****************************ILLEGAL FOR NON-TRAINER USE******************************
1. Enter the code to read an integer from the console and assign it to a variable
named aNumber.
int MyInt = int.Parse(Console.ReadLine());


2. What class should you use to improve performance when you want to
perform repeated modifications to a string?
System.Text.StringBuilder


3. Name and briefly describe the interfaces implemented by System.Array.
ICloneable: Supports cloning, which creates a new instance of a class
with the same value as an existing instance.
IList: Represents a collection of objects that can be individually
indexed.
ICollection: Defines size, enumerators, and synchronization methods
for all collections.
IEnumerable: Exposes the enumerator, which supports a simple
iteration over a collection.


4. What does it mean to say that an enumerator is required to be safe?
The enumerator must have a fixed view of the items in a collection that
remains the same, even if the collection is modified.


Topic Objective
To reinforce module
objectives by reviewing
key points.
Lead-in
The review questions cover
some of the key concepts
taught in the module.
64 Module 7: Strings, Arrays, and Collections


5. Create an array that contains the integers 1, 2, and 3. Then use the C#
foreach statement to iterate over the array and output the numbers to the
console.
int[ ] numbers = {1, 2, 3};
foreach (int i in numbers) {
System.Console.WriteLine("Number: {0}", i);
}


6. What is the name of the interface that is implemented by classes that contain
an ordered collection of objects that can be individually indexed? Name the
System.Collections classes that implement this interface.
The IList interface is implemented by Array, ArrayList, StringCollection,
and TreeNodeCollection.


7. What is the name of the interface for collections of associated keys and
values? Name the System.Collections classes that implement this interface.
The IDictionary interface is implemented by Hashtable, DictionaryBase,
and SortedList.


8. Generic collection classes require runtime type-casting of their items to
obtain the true type of the items in the collection classes. Name the issues
raised by runtime casting.
Type-checking cannot be done at compile time.
Performance overhead of casting.
In the case of collection of value types, boxing and unboxing operations.











Contents
Overview 1
Delegates 2
Multicast Delegates 12
Events 21
When to Use Delegates, Events, and
Interfaces 31
Lab 8: Creating a Simple Chat Server 32
Review 42

Module 8:
Delegates and Events


Information in this document, including URL and other Internet Web site references, is subject to
change without notice. Unless otherwise noted, the example companies, organizations, products,
domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious,
and no association with any real company, organization, product, domain name, e-mail address,
logo, person, place or event is intended or should be inferred. Complying with all applicable
copyright laws is the responsibility of the user. Without limiting the rights under copyright, no
part of this document may be reproduced, stored in or introduced into a retrieval system, or
transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or
otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual
property rights covering subject matter in this document. Except as expressly provided in any
written license agreement from Microsoft, the furnishing of this document does not give you any
license to these patents, trademarks, copyrights, or other intellectual property.

2001-2002 Microsoft Corporation. All rights reserved.

Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint,
Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and
Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A.
and/or other countries.

The names of actual companies and products mentioned herein may be the trademarks of their
respective owners.



Module 8: Delegates and Events iii


Instructor Notes
After completing this module, students will be able to:
! Use the delegate class to create type-safe callback functions and event-
handling methods.
! Use the event keyword to simplify and improve the implementation of a
class that raises events.
! Implement events that conform to the Microsoft .NET Framework
guidelines.

Materials and Preparation
This section provides the materials and preparation tasks that you need to teach
this module.
Required Materials
To teach this module, you need the Microsoft PowerPoint file 2349B_08.ppt.
Preparation Tasks
To prepare for this module, you should:
! Read all of the materials for this module.
! Practice the demonstrations.
! Complete the lab.

Presentation:
75 Minutes

Lab:
75 Minutes
iv Module 8: Delegates and Events


Demonstrations
This section provides demonstration procedures that will not fit in the margin
notes or are not appropriate for the student notes.
Using Delegates
In this demonstration, you will use Microsoft Visual Studio .NET to run code
in which a delegate to a light objects method is passed to a switch object.
When the switch objects state changes, the switch object calls the light
objects method and passes its new state.
The code for this demonstration is provided in the student notes. The
demonstration files are located in <install folder>\Democode\Mod08\
Demo08.1\Using Delegates.
Multicast Delegates
In this demonstration, you will show students how to add and remove methods
from the invocation list of multicast delegates by using the plus operator (+)
and the minus operator (-).
The code for this demonstration is provided in the student notes. The
demonstration files are located in <install folder>\Democode\Mod08\
Demo08.2\MULTICAST DELEGATES.
Module Strategy
Use the following strategy to present this module:
! Delegates
Use the Delegate Scenario to help students to visualize and comprehend the
concept of delegates. Some students may question the validity of this
scenario. In the real world, a house would never be rewired dynamically, but
you can explain that software programs often need to rebind dynamically.
! Multicast Delegates
Contrast typical multicast delegate use with that of the single delegate
example by using the common scenario of a switch that controls two light
bulbs. Explain how to create and invoke multicast delegates and show how
to use the + and operators in C# to add and remove methods from the
invocation list of multicast delegates.
! Events
Use the Event Scenario to help students to visualize and comprehend the
concept of events. Emphasize that events are an important building block
for creating classes that can be reused in many different programs, and that
delegates are particularly suited for event handling. Explain how to declare,
connect to, and raise an event.
Introduce the .NET Framework guidelines for delegates and events.
! When to Use Delegates, Events, and Interfaces
Contrast and compare the specific usage characteristics of delegates,
interfaces, and events for providing callback functionality in particular
situations.

Module 8: Delegates and Events 1


Overview
! Delegates
! Multicast Delegates
! Events
! When to Use Delegates, Events, and Interfaces

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In the Microsoft .NET Framework, delegates are the object-oriented
equivalents of function pointers. However, unlike function pointers, delegates
are type-safe and secure. The common language runtime supports the use of
delegates in callback and event-handling scenarios.
An event is raised by an object or event source in response to an action
performed by a user or some sort of program logic. The event receiver must
then respond to the raised event and perform an action. The event keyword lets
you specify delegates that will be called upon the occurrence of an event.
After completing this module, you will be able to:
! Use the delegate class to create type-safe callback functions and event-
handling methods.
! Use the event keyword to simplify and improve the implementation of a
class that raises events.
! Implement events that conform to the .NET Framework guidelines.

Topic Objective
To provide an overview of
the module topics and
objectives.
Lead-in
Delegates are the object-
oriented equivalents of
function pointers.
2 Module 8: Delegates and Events


" "" " Delegates
! Delegate Scenario
! Declaring a Delegate
! Instantiating a Delegate
! Calling a Delegate

*****************************ILLEGAL FOR NON-TRAINER USE******************************
You can use delegates to encapsulate a reference to a method inside a delegate
object. Because delegates are type-safe, secure, managed objects, they offer all
of the advantages of pointers without any of the disadvantages of pointers. For
example, delegates will always point to a valid object and cannot corrupt the
memory of other objects.
Topic Objective
To provide an overview of
the topics in this section.
Lead-in
In this section, you will learn
about how delegates are
used in the .NET
Framework.
Module 8: Delegates and Events 3


Delegate Scenario
1 - Change in
switch position
invokes switchs
OnFlip method
2 - OnFlip Method
invokes delegate
3 - Delegate invokes lights
OnFlipCallback method
4 - OnFlipCallback method
changes lights state
OnFlip method
Switch Object
OnFlipCallback
method
Light Object
Delegate object
Delegate object
OnFlip method
Switch Object

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This scenario of a switch that controls a light illustrates one use of delegates.
The switch object models an electric switch, and the light object models an
electric light. The delegate object encapsulates a reference to a light objects
OnFlipCallback method.
The switch object code could be written without a delegate by referring directly
to the specific light objects method. However, this approach does not offer the
flexibility to dynamically connect, disconnect, and reconnect various light
object methods to the switch object. You can use a delegate object that
connects the switch object and the light object to achieve this flexibility.
In real life, the preceding scenario would probably not occur as described.
While a house would never be rewired dynamically, software programs often
need to dynamically rebind.
When the switch is flipped in the light switch scenario shown in the slide:
1. The switch objects OnFlip method is invoked.
2. The OnFlip method invokes the delegate object.
3. The delegate object invokes the light objects OnFlipCallback method.
4. The OnFlipCallback method changes the state of the light.


Topic Objective
To illustrate one use of
delegates through the
scenario of a switch and a
light bulb.
Lead-in
This scenario of a switch
that controls a light
illustrates one use of
delegates.
To run the build slide, click
through the lower-left button
on the slide.
For Your Information
Stress that this scenario
reflects a common scenario
to which everyone can
relate. While you may not
use the same light switch to
turn different lights on and
off at different times, this
scenario helps you to
visualize and comprehend
the concepts behind
delegates.
Delivery Tip
You should briefly preview
the Demonstration: Using
Delegates to provide
students with an overview of
the code that can be used to
implement this scenario.
Defer discussion of delegate
specific code until after the
following topics are covered.
4 Module 8: Delegates and Events


Declaring a Delegate
! A Delegate Declaration Defines a Type That
Encapsulates a Method with a Particular Set of
Arguments and Return Type
// declares a delegate for a method that takes a single
// argument of type string and has a void return type
delegate void MyDelegate1(string s);
// declares a delegate for a method that takes a single
// argument of type string and has a void return type
delegate void MyDelegate1(string s);

*****************************ILLEGAL FOR NON-TRAINER USE******************************
A delegate declaration defines a type that encapsulates a method with a
particular set of arguments and return type. The delegate declaration is
sufficient to define a delegate class whose actual implementation is provided by
the common language runtime.
The following example shows how to declare a delegate for a method that takes
a single argument of type string and has a void return type:
delegate void MyDelegate1(string s);

Topic Objective
To explain how to declare a
delegate.
Lead-in
A delegate declaration
defines a type that
encapsulates a method with
a particular set of arguments
and return type.
For Your Information
The details of how the
common language runtime
creates delegates are
presented later in this
module.
Module 8: Delegates and Events 5


Instantiating a Delegate
! A Delegate Object Is Created with the new Operator
! Delegate Objects Are Immutable
// instantiating a delegate to a static method Hello
// in the class MyClass
MyDelegate1 a = new MyDelegate1(MyClass.Hello);
// instantiating a delegate to an instance method
// AMethod in object p
MyClass p = new MyClass();
MyDelegate1 b = new MyDelegate1(p.AMethod);
// instantiating a delegate to a static method Hello
// in the class MyClass
MyDelegate1 a = new MyDelegate1(MyClass.Hello);
// instantiating a delegate to an instance method
// AMethod in object p
MyClass p = new MyClass();
MyDelegate1 b = new MyDelegate1(p.AMethod);

*****************************ILLEGAL FOR NON-TRAINER USE******************************
After you have declared a delegate type, you can create a delegate object and
associate it with a particular method. This topic explains the code used to
instantiate delegates.
Creating a New Delegate Object
Like all objects, a new delegate object is created with the new operator. When
creating a delegate, however, the argument passed to the new operator is
special: it is written like a method call, but without the arguments to the
method. After a delegate is created, the method to which it is associated never
changes: delegate objects are immutable.
When referencing an object, an interesting and useful feature of a delegate is
that the delegate does not know or care about the class of the object that it
references. It can reference any object as long as the methods signature
matches the delegates signature.
A delegate can reference static or instance methods. When referencing instance
methods, the delegate stores not only a reference to the methods entry point,
but also a reference to the object instance on which the method is invoked.
Topic Objective
To explain how to instantiate
delegates.
Lead-in
After you have declared a
delegate type, you can
create a delegate object and
associate it with a particular
method.
Delivery Tip
To place the delegate
instantiation code in a fuller
context, refer to the
following code example.
6 Module 8: Delegates and Events


The following example shows how to declare a delegate named MyDelegate1.
The code illustrates how this delegate can be instantiated to a static or an
instance method. The signature of the method and MyDelegate1 must match;
the method must have a void return and take a single argument of type string.
delegate void MyDelegate1(string s);
...
public class MyClass
{
public static void Hello(string s) {
//...
}
public void AMethod(string s) {
//...
}
}
...

// instantiating a delegate to a static method
MyDelegate1 a = new MyDelegate1(MyClass.Hello);

// instantiating a delegate to object p's AMethod method
MyClass p = new MyClass();
MyDelegate1 b = new MyDelegate1(p.AMethod);

Module 8: Delegates and Events 7


Calling a Delegate
! Use a Statement Containing:
# The name of the delegate object
# Followed by the parenthesized arguments to be passed
to the delegate
// given the previous delegate declaration and
// instantiation, the following invokes MyClass'
// static method Hello with the parameter "World"
a("World");
// given the previous delegate declaration and
// instantiation, the following invokes MyClass'
// static method Hello with the parameter "World"
a("World");

*****************************ILLEGAL FOR NON-TRAINER USE******************************
After a delegate object is created, it can be passed to other code that will call the
delegate. For example, a server object may provide a method that a client
object calls to register a callback method for a specific event. The server will
invoke this callback method when that event occurs. Typically, the client
instantiates a delegate that refers to its callback method. The client passes the
callback delegate object as a parameter.
You can call a delegate object by using the name of the delegate, followed by
the parenthesized arguments to be passed to the delegate. For example, using
the declaration and instantiations of delegates a and b shown in the previous
slide, the following lines invoke the static Hello method of the class MyClass
and the AMethod of the MyClass object p with the argument "World":
a("World");
b("World");

Topic Objective
To explain how to call
delegates.
Lead-in
After a delegate is created,
it can be passed to other
code that will call the
delegate.
For Your Information
Refer to the previous slide
on delegate declaration and
instantiations.
8 Module 8: Delegates and Events


Demonstration: Using Delegates

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this demonstration, you will use Microsoft Visual Studio .NET to run code
in which a delegate to a light objects method is passed to a switch object.
When the switch objects state changes, the switch object calls the light
objects method and passes its new state.
Topic Objective
To demonstrate using a
callback delegate.
Lead-in
In this demonstration, you
will use Microsoft Visual
Studio .NET to run code in
which a delegate to a light
objects method is passed to
a switch object. When the
switch objects state
changes, the switch object
calls the light objects
method and passes its new
state.
Delivery Tip
Use Visual Studio .NET to
run the following code. Set
breakpoints or single step to
illustrate the sequence of
events.
Module 8: Delegates and Events 9


using System;

namespace SwitchAndLight
{
public enum SwitchPosition {Up, Down};

delegate void SwitchFlipped(SwitchPosition switchState);

class Light
{
private string name;

public Light(string s)
{
name = s;
}

public void OnFlipCallback(SwitchPosition switchState)
{
if (switchState == SwitchPosition.Up)
{
Console.WriteLine("... {0} light is on",name);
}
else
{
Console.WriteLine("... {0} light is off",name);
}
}
}

class Switch
{
private SwitchPosition switchState =
SwitchPosition.Down;
private SwitchFlipped switchFlippedHandler = null;

public void ConnectToLight(SwitchFlipped lightHandler)
{
switchFlippedHandler = lightHandler;
}

public SwitchPosition SwitchState
{
get {return switchState;}
}

(Code continued on the following page.)
For Your Information
As noted in C# Language-
Specific Syntax in this
module, delegate types with
a void return value in C#
cause the compiler to derive
the delegate class from the
MulticastDelegate class.
Therefore, the IL for this
delegate demonstration
code will show the
SwitchFlipped delegate
inheriting from the
System.MulticastDelegate
class.
10 Module 8: Delegates and Events


public void OnFlip()
{
if (switchState == SwitchPosition.Down)
{
switchState = SwitchPosition.Up;
}
else
{
switchState = SwitchPosition.Down;
}
if (switchFlippedHandler != null)
{
switchFlippedHandler(switchState);
}
}
}

class TheApp
{
static void OnFlip(Switch aSwitch)
{
Console.WriteLine(
"Before flipping, the switch is: {0}",
aSwitch.SwitchState);
Console.WriteLine("Flipping switch ... ");
aSwitch.OnFlip();
Console.WriteLine(
"After flipping, the switch is: {0}\n\n",
aSwitch.SwitchState);
}

static void Main(string[] args)
{
Switch s = new Switch();
Light light1 = new Light("bathroom");
Light light2 = new Light("bedroom");

// connect switch and bathroom light by passing a
// delegate to the bathroom light's
// OnFlipCallback method to s
s.ConnectToLight(new
SwitchFlipped(light1.OnFlipCallback));
OnFlip(s);
OnFlip(s);

// connect switch and bedroom light by passing a
// delegate to the bedroom's light's
// OnFlipCallback method to s
s.ConnectToLight(new
SwitchFlipped(light2.OnFlipCallback));
OnFlip(s);
OnFlip(s);

}
}
}
Module 8: Delegates and Events 11


This code generates the following output:
Before flipping, the switch is: Down
Flipping switch ...
... bathroom light is on
After flipping, the switch is: Up


Before flipping, the switch is: Up
Flipping switch ...
... bathroom light is off
After flipping, the switch is: Down


Before flipping, the switch is: Down
Flipping switch ...
... bedroom light is on
After flipping, the switch is: Up


Before flipping, the switch is: Up
Flipping switch ...
... bedroom light is off
After flipping, the switch is: Down

12 Module 8: Delegates and Events


" "" " Multicast Delegates
! Multicast Delegate Scenario
! Single vs. Multicast Delegates
! Creating and Invoking Multicast Delegates
! C# Language-Specific Syntax
! Delegate Details

*****************************ILLEGAL FOR NON-TRAINER USE******************************
If you change the state in one object, you may need to broadcast that change to
multiple objects. You can use multicast delegate objects to provide this
functionality; they can be composed together so that a single invocation invokes
all of their methods, just as a single light switch can turn on or off all lights
connected to that switch.

Topic Objective
To provide an overview of
the topics in this section.
Lead-in
If you change the state in
one object, you may need to
broadcast that change to
multiple objects.
For Your Information
As noted in the C#
Language-Specific Syntax in
this module, delegate types
with a void return value in
C# cause the compiler to
derive the delegate class
from the MulticastDelegate
class.
Module 8: Delegates and Events 13


Multicast Delegate Scenario
2 - OnFlip method
invokes multicast
delegate1
4 - OnFlipCallback method
changes light1s state
3 - delegate1 invokes
light1s OnFlipCallback
7 - OnFlipCallback
method changes
light2s state
6 - delegate2 invokes
light2s OnFlipCallback
OnFlip method
Switch Object
OnFlipCallback
method
Light1 Object
OnFlipCallback
method
Light2 Object
Multicast delegate1 object
Multicast delegate1 object
Multicast delegate2 object
Multicast delegate2 object
Invocation list
5 - delegate2
is invoked
1 - Change in
switch position
invokes switchs
OnFlip method
OnFlip method
Switch Object

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In the light scenario, multicast delegate1 refers to the light1 objects
OnFlipCallback method, and multicast delegate2 refers to the light2 objects
OnFlipCallback method. Their composition logically represents an invocation
list, a list of methods that are executed when the delegate is invoked. In this
case, the invocation list consists of these two methods.
In the light switch scenario shown in the slide, the multicast delegate objects
encapsulate references to two objects and two methods. When the switch is
flipped:
1. The switch objects OnFlip method is invoked.
2. The OnFlip method invokes the multicast delegate1 object.
3. The multicast delegate1 object first invokes the light1 objects
OnFlipCallback method.
4. The light1 objects OnFlipCallback method changes the state of the light1
object.
5. The multicast delegate1 object next invokes the multicast delegate2
object.
6. The multicast delegate2 object invokes the light2 objects
OnFlipCallback method.
7. The light2 objects OnFlipCallback method changes the state of the light2
object.

You can compose additional multicast delegates to allow the switch to control
additional lights.
Topic Objective
To illustrate the typical
multicast delegate use by
using the common scenario
of a switch that controls two
light bulbs.
Lead-in
The following scenario of a
switch that controls two
lights illustrates the use of
multicast delegates.
To run the build slide, click
through the lower-left button
on the slide.
The following scenario of a
switch that controls two
lights illustrates the use of
multicast delegates. The
switch object models the
light switch, and the two
light objects model the two
electric lights. To provide
the capability to dynamically
connect, disconnect, and
reconnect light object
methods to the switch
object, the switch object is
dynamically connected to
the light objects by
instantiating and composing
two multicast delegate
objects.
14 Module 8: Delegates and Events


Single vs. Multicast Delegates
! All Delegates Have an Invocation List of Methods That Are
Executed When Their Invoke Method is Called
! Single-Cast Delegates: Derived Directly From
System.MulticastDelegate
# Invocation list contains only one method
! Multicast Delegates: Derived from System.MulticastDelegate
# Invocation list may contain multiple methods
# Multicast delegates contain two static methods to add and remove
references from invocation list: Combine and Remove
! Use GetInvocationList to Obtain an Invocation List as an Array of
Delegate References
! Use a Delegates Target and Method Properties to Determine:
# Which object will receive the callback
# Which method will be called

*****************************ILLEGAL FOR NON-TRAINER USE******************************
When delegate declarations are compiled, the compiler, in effect, generates a
new class. The new delegate class derives from the System.MulticastDelegate
class that is provided by the .NET Framework.
All delegates have an invocation list, or linked list of delegates that are
executed when the invoke method is called. A delegate that derives from the
MulticastDelegate class may contain an invocation list with multiple delegates.
The MulticastDelegate class contains two static methods to add and remove
method references from an invocation list: Combine and Remove.
Combine is declared as follows:
public static Delegate Combine(
Delegate a,
Delegate b);

The method returns a new multicast Delegate object with an invocation list that
concatenates the invocation lists of a and b in that order.
Remove is declared as follows:
public static Delegate Remove(
Delegate source,
Delegate value);

The method returns a new Delegate object with an invocation list formed by
taking the invocation list of source and removing the last occurrence of value,
if value is found in the invocation list of source. If value is null or if value is
not found, then source is returned.
You can use the method GetInvocationList to obtain the invocation list as an
array of delegate references. You can also use the delegates Target and
Method properties to determine which object is to receive the callback and
which method is to be called. In the case of a static method, Target is null.
Topic Objective
To explain the difference
between single-cast and
multicast delegates.
Lead-in
When delegate declarations
are compiled, the compiler
generates a new class,
which derives from two
delegate classes provided
by the .NET Framework.
Module 8: Delegates and Events 15


Creating and Invoking Multicast Delegates
// assign to c the composition of delegates a and b
c = (MyDelegate2)Delegate.Combine(a, b);
// assign to d the result of removing a from c
d = (MyDelegate2)Delegate.Remove(c, a);
// Iterate through c's invocation list
// and invoke all delegates except a
Delegate[] DelegateList = c.GetInvocationList();
for (int i = 0; i < DelegateList.Length; i++) {
if (DelegateList[i].Target != aFoo1) {
((MyDelegate2) DelegateList[i])();
}
}
// assign to c the composition of delegates a and b
c = (MyDelegate2)Delegate.Combine(a, b);
// assign to d the result of removing a from c
d = (MyDelegate2)Delegate.Remove(c, a);
// Iterate through c's invocation list
// and invoke all delegates except a
Delegate[] DelegateList = c.GetInvocationList();
for (int i = 0; i < DelegateList.Length; i++) {
if (DelegateList[i].Target != aFoo1) {
((MyDelegate2) DelegateList[i])();
}
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The following sample code shows:
! How to create multicast delegate objects.
! How to write code to invoke delegates Combine and Remove methods.
! How to write code to iterate through an invocation list invoking all
delegates, except those delegates whose target is a specific object.

Topic Objective
To provide specific
examples of how to create
and invoke multicast
delegates, and how to
iterate though an invocation
list to invoke specific
delegates.
Lead-in
This sample code shows
how to create and invoke
multicast delegates, and
how to iterate through an
invocation list to invoke
specific delegates.
16 Module 8: Delegates and Events


using System;

public delegate void MyDelegate2();

public class Foo
{
public void Bar() {
Console.WriteLine("Bar invoked");
}
}

class Application {
public static void Main() {
Foo aFoo1 = new Foo();
Foo aFoo2 = new Foo();

MyDelegate2 a, b, c, d;

a = new MyDelegate2(aFoo1.Bar);
b = new MyDelegate2(aFoo2.Bar);

// assign to delegate c the composition of delegates a and b
c = (MyDelegate2)Delegate.Combine(a , b);

// assign to d the result of removing a from c
d = (MyDelegate2)Delegate.Remove(c , a);

// iterate through c's invocation list
// and invoke all delegates except those that target aFoo1
Delegate[] DelegateList = c.GetInvocationList();
for (int i = 0; i < DelegateList.Length; i++) {
if (DelegateList[i].Target != aFoo1) {
((MyDelegate2) DelegateList[i])();
}
}
}
}

The preceding code invokes the delegate that invokes object aFoo2s Bar
method and outputs:
Bar invoked

Module 8: Delegates and Events 17


C# Language-Specific Syntax
! C# Delegates That Return Void Are Multicast Delegates
! In C#, Use the + and - Operators to Add and Remove
Invocation List Entries
# Less verbose than Combine and Remove methods
MyDelegate a, b, c, d;
a = new MyDelegate(Foo);
b = new MyDelegate(Bar);
c = a + b; // Compose two delegates to make another
d = c - a; // Remove a from the composed delegate
a += b; // Add delegate b to a's invocation list
a -= b; // Remove delegate b from a's list
MyDelegate a, b, c, d;
a = new MyDelegate(Foo);
b = new MyDelegate(Bar);
c = a + b; // Compose two delegates to make another
d = c - a; // Remove a from the composed delegate
a += b; // Add delegate b to a's invocation list
a -= b; // Remove delegate b from a's list

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Delegate types with a void return value in C# cause the compiler to derive the
delegate class from the MulticastDelegate class. You can also use these
delegates addition operator (+) and the subtraction operator (-) to invoke the
Combine and Remove methods for delegate composition and decomposition.
Topic Objective
To present helpful
alternative C# syntax.
Lead-in
Delegate types with a void
return value in C# cause the
compiler to derive the
delegate class from the
MulticastDelegate class.
18 Module 8: Delegates and Events


Demonstration: Multicast Delegates

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this demonstration, you will learn how to add and remove methods from the
invocation list of multicast delegates by using the + and operators.
using System;

namespace Multicast_Delegates
{

delegate void MyDelegate3(string s);

class MyClass
{
public static void Hello(string s) {
Console.WriteLine(" Hello, {0}!", s);
}

public static void Goodbye(string s) {
Console.WriteLine(" Goodbye, {0}!", s);
}

(Code continued on the following page.)
Topic Objective
To demonstrate how to use
the + and operators to add
and remove methods.
Lead-in
You can add and remove
methods from the invocation
list of multicast delegates by
using the + and operators.
Delivery Tip
Use Visual Studio .NET to
run this code. Set
breakpoints or single-step to
illustrate the sequence of
events.
Module 8: Delegates and Events 19


public static void Main() {
MyDelegate3 a, b, c, d;
a = new MyDelegate3(MyClass.Hello);
b = new MyDelegate3(MyClass.Goodbye);
c = a + b; // Compose two delegates to make another
d = c - a; // Remove a from c to make another

Console.WriteLine("Invoking delegate a:");
a("A");
Console.WriteLine("Invoking delegate b:");
b("B");
Console.WriteLine("Invoking delegate c:");
c("C");
Console.WriteLine("Invoking delegate d:");
d("D");

Console.WriteLine("Adding b to a and invoking:");
a += b; // Add the b delegate to a
a("E");
Console.WriteLine("Removing b from a and invoking:");
a -= b; // Remove the b delegate from a
a("F");
}
}

}

This code generates the following output:
Invoking delegate a:
Hello, A!
Invoking delegate b:
Goodbye, B!
Invoking delegate c:
Hello, C!
Goodbye, C!
Invoking delegate d:
Goodbye, D!
Adding b to a and invoking:
Hello, E!
Goodbye, E!
Removing b from a and invoking:
Hello, F!

20 Module 8: Delegates and Events


Delegate Details
! A Delegate Declaration Causes the Compiler to
Generate a New Class
// delegate void MyDelegate3(string val);
class MyDelegate3 : System.MulticastDelegate {
public MyDelegate3(object obj, methodref mref)
: base (obj, mref) { //...
}
public void virtual Invoke(string val) { //...
}
};
// delegate void MyDelegate3(string val);
class MyDelegate3 : System.MulticastDelegate {
public MyDelegate3(object obj, methodref mref)
: base (obj, mref) { //...
}
public void virtual Invoke(string val) { //...
}
};

*****************************ILLEGAL FOR NON-TRAINER USE******************************
As previously noted in this module, when delegate declarations are compiled,
the compiler, in effect, generates a new class that derives from
System.Delegate. The new delegate class has two members: a constructor and
an Invoke method. The following class declaration resembles what the
compiler actually does:
// delegate void MyDelegate3(string val);

class MyDelegate3 : System.MulticastDelegate {
public MyDelegate3(object obj, methodref mref)
: base (obj, mref) {//...
}
public void virtual Invoke(string val) {//...
}
}

The first member is a constructor: its first parameter is the delegates target
object, and its second parameter is a reference to a method. When a delegate
refers to a static method, the target object is null.
The Invoke method for the class MyDelegate3 indicates that delegate instances
of this class encapsulate methods that have a void return and a single string
parameter. When a delegate is invoked, the Invoke method is called with the
specified parameters.
Topic Objective
To show how a delegate
declaration is handled by
the compiler.
Lead-in
When delegate declarations
are compiled, the compiler
generates a new class that
derives from
System.Delegate.
For Your Information
There is no actual type
named methodref, but
having an instance of a type
that encapsulates a
reference to a class method
resembles what the
compiler actually does.
Module 8: Delegates and Events 21


" "" " Events
! Event Scenario
! Declaring an Event
! Connecting to an Event
! Raising an Event
! .NET Framework Guidelines

*****************************ILLEGAL FOR NON-TRAINER USE******************************
An event is a way for a class to notify clients of that class when some
interesting thing happens to an object. The most familiar use for events is in
graphical user interfaces. Typically the classes that represent controls in the
graphical user interface have events that are notified when a user manipulates
the control, as when a user clicks a button. However, the use of events is not
limited to graphical user interfaces. Events also allow an object to signal state
changes that may be useful to clients of that object. Events are an important
building block for creating classes that can be reused in many different
programs.
Delegates are particularly suited for implementing events. You can use the
.NET event mechanism to easily provide an object with type safe methods that
clients can call to register and deregister delegates to event handler methods.
When an event is raised, the event handler methods are called.
Topic Objective
To provide an overview of
the topics in this section.
Lead-in
An event is a way for a class
to notify clients of that class
of a change in an object.
22 Module 8: Delegates and Events


Event Scenario
Mouse Object
MouseClicked field
Invocation List:
remove_MouseClicked method
add_MouseClicked method
OnMouseClicked
method
add_MouseClicked method
SoundMaker Object
MouseClicked method
MouseClicked method
multicast delegate object multicast delegate object
OnMouseClicked
method
stopButton object
MouseClicked method
MouseClicked method
multicast delegate object multicast delegate object
MouseClicked method
MouseClicked method
MouseClicked method
MouseClicked method
multicast delegate object
remove_MouseClicked method

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This scenario of a mouse object that notifies interested client objects when a
mouse click occurs illustrates one use of the .NET event mechanism. Client
objects add and remove multicast delegate objects to the mouse objects
invocation list. These delegates specify the event handler methods to be called
when a mouse click occurs. The mouse object code could be written to call a
predefined list of event handler methods. However, this approach does not offer
the flexibility to dynamically connect, disconnect, and reconnect methods to be
called when the mouse click event occurs. By using multicast delegate objects,
you can provide this flexibility.
In the mouse clicked event scenario that is shown in the slide, a mouse object
encapsulates code to monitor a hardware mouse:
1. The mouse objects class uses the Microsoft Visual C#

compiler event
keyword to have the compiler automatically create a private field named
MouseClicked that is used to store a reference to the invocation list of
delegates, and two public methods named add_MouseClicked and
remove_MouseClicked that are used to add and remove delegates to this
list. You can call these methods in Visual C# by using the += and -=
operators.
2. The mouse objects class declares an OnMousedClicked method that is
called when a mouse click is detected. The OnMouseClicked method raises
the MouseClicked event by invoking the invocation lists first delegate
object.
3. A user issues a command that causes sound to be generated whenever the
mouse is clicked. This command causes the mouse objects
add_MouseClicked method to be called with a delegate object that points
to the soundMaker objects MouseClicked method. This delegate is added
to the mouse objects MouseClicked invocation list.
4. The mouse is clicked and the mouse objects OnMouseClicked method is
called to raise the mouse clicked event.
Topic Objective
To illustrate the .NET event
mechanism through the
scenario of a mouse clicked
event.
Lead-in
This scenario of a mouse
object that notifies
interested objects when a
mouse clicked event occurs
illustrates one use of
delegates and events.
To run the build slide, click
through the lower-left button
on the slide.
Module 8: Delegates and Events 23


5. The OnMouseClicked method invokes the MouseClicked invocation lists
delegate.
6. The delegate calls the SoundMaker objects MouseClicked method and a
sound is generated.
7. A user then starts an application that operates a piece of machinery. The
application displays a Stop button on the screen that the user clicks to stop
the operation of the machinery. The application then instantiates a
stopButton object and calls the mouse objects add_MouseClicked
method, passing a delegate object that refers to the stopButton objects
MouseClicked method. This delegate is added to the mouse objects
MouseClicked invocation list. The application then starts the machinery.
8. The mouse is clicked and the mouse objects OnMouseClicked method is
called to raise the MouseClicked event.
9. The OnMouseClicked method invokes the MouseClicked invocation lists
first delegate object. The delegate calls the SoundMaker objects
MouseClicked method and a sound is generated.
10. The first delegate object invokes the second delegate object. The second
delegate object calls the stopButton objects MouseClicked method. This
method checks the location of the mouse cursor and after determining that it
is located over the Stop button, halts the machinery.
11. The user then issues a command to terminate the application. The
application calls the mouse objects remove_MouseClicked method,
passing a delegate object that refers to the stopButton objects
MouseClicked method. This delegate is removed from the mouse objects
MouseClicked invocation list.

24 Module 8: Delegates and Events


Declaring an Event
! Declare the Delegate Type for the Event
! Declare the Event
# Like the field of delegate type preceded by an event
keyword
// MouseClicked delegate declared
public delegate void MouseClickedEventHandler();
public class Mouse
{
// MouseClicked event declared
public static event MouseClickedEventHandler
MouseClicked;
//...
}
// MouseClicked delegate declared
public delegate void MouseClickedEventHandler();
public class Mouse
{
// MouseClicked event declared
public static event MouseClickedEventHandler
MouseClicked;
//...
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
To declare an event inside a class, you first must declare a delegate type for the
event. The delegate type defines the set of arguments that are passed to the
method that handles the event.
Multiple events can share a delegate type, so you need only declare a delegate
type for an event if no suitable delegate type has already been declared. For
example, in the case of a MouseClicked event in which the method that
handles the event takes no arguments and returns void, the delegate is declared
as follows:
public delegate void MouseClickedEventHandler();

Next, the event itself is declared. You declare an event as you would declare a
field of delegate type, except that the keyword event follows the modifiers and
precedes the delegate type. Events usually are declared public, but any
accessibility modifier is allowed. The following code declares a class Mouse
with an event named MouseClicked:
public class Mouse
{
public static event MouseClickedEventHandler
MouseClicked;
//...
}

Topic Objective
To explain how to declare
events.
Lead-in
To declare an event inside a
class, you first must declare
a delegate type for the
event.
Module 8: Delegates and Events 25


When you declare an event, the compiler generates a private field that
references the end of a delegate invocation list. In the preceding example, a
private field named MouseClicked, which refers to delegates of type
MouseClickedEventHandler, is created.
The compiler also generates two public methods for clients to call to compose
and remove their delegate objects. In the preceding example, these public
methods would be named add_MouseClicked and remove_MouseClicked.
You can call these methods in C# by using the += and -= operators.
Because client access to the delegate invocation list is restricted to these
methods, the use of the event keyword not only makes the implementation of
events easier, it also prevents clients from accessing or raising the delegates of
other clients.
26 Module 8: Delegates and Events


Connecting to an Event
! Connect by Combining Delegates
! Disconnect by Removing Delegates
// Clients method to handle the MouseClick event
private void MouseClicked() { //...
}
//...
// Client code to connect to MouseClicked event
Mouse.MouseClicked += new
MouseClickedEventHandler(MouseClicked);
// Client code to break connection to MouseClick event
Mouse.MouseClicked -= new
MouseClickedEventHandler(MouseClicked);
// Clients method to handle the MouseClick event
private void MouseClicked() { //...
}
//...
// Client code to connect to MouseClicked event
Mouse.MouseClicked += new
MouseClickedEventHandler(MouseClicked);
// Client code to break connection to MouseClick event
Mouse.MouseClicked -= new
MouseClickedEventHandler(MouseClicked);

*****************************ILLEGAL FOR NON-TRAINER USE******************************
From outside the class that declared it, an event looks like a field, but access to
that field is restricted. You can only add a delegate to an event or remove a
delegate from an event. In C#, you add and remove delegates by using the +=
and -= operators. To begin receiving event invocations, client code first creates
a delegate of the event type that refers to the method that should be invoked
from the event. Then, the client code uses the + and operators to compose that
delegate with any other delegates to which the event might be connected. When
the client code is finished receiving event invocations, it removes its delegate
from the event by using the -= operator.
The following example shows how to connect to and disconnect from a
MouseClicked event:
public class aClient
{
// Client's method to handle the MouseClick event
private void MouseClicked() {
//...
}

//...

// code to connect to Mouse class' MouseClicked event
Mouse.MouseClicked += new
MouseClickedEventHandler(MouseClicked);

// code to break the connection to MouseClicked event
Mouse.MouseClicked -= new
MouseClickedEventHandler(MouseClicked);
//...
}

Topic Objective
To describe how to connect
to and disconnect from an
event.
Lead-in
From outside the class that
declared it, an event looks
like a field, but access to
that field is restricted.
Module 8: Delegates and Events 27


Raising an Event
! Check Whether Any Clients Have Connected to
This Event
# If the event field is null, there are no clients
! Raise the Event by Invoking the Events Delegate
if (MouseClicked != null)
MouseClicked();
if (MouseClicked != null)
MouseClicked();

*****************************ILLEGAL FOR NON-TRAINER USE******************************
After a class has declared an event, it can treat that event like a field of the
indicated delegate type. The field will be null if no client has connected a
delegate to the event, or it will refer to a delegate that should be called when the
event is raised. Therefore, when an event is raised, you should first check for
null and then call the event. You can only raise an event from within the class
that declared the event.
if (MouseClicked != null) MouseClicked();

Topic Objective
To show when and how to
raise an event.
Lead-in
After a class has declared
an event, it can treat that
event like a field of the
indicated delegate type.
28 Module 8: Delegates and Events


.NET Framework Guidelines
! Name Events with a Verb and Use Pascal Casing
! Use Raise for Events, Instead of Fire
! Event Argument Classes Extend System.EventArgs
! Event Delegates Return Void and Have Two Arguments
! Use a Protected Virtual Method to Raise Each Event
public class SwitchFlippedEventArgs : EventArgs { //...
}
public delegate void SwitchFlippedEventHandler(
object sender, SwitchFlippedEventArgs e);
public event SwitchFlippedEventHandler SwitchFlipped;
public class SwitchFlippedEventArgs : EventArgs { //...
}
public delegate void SwitchFlippedEventHandler(
object sender, SwitchFlippedEventArgs e);
public event SwitchFlippedEventHandler SwitchFlipped;

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The .NET Framework includes guidelines on the delegate types that should be
used for events. If you are creating a component for use with the .NET
Framework, you should follow these guidelines:
! Consider naming events with a verb and use Pascal Casing. The Pascal
Casing convention capitalizes the first character of each word. For example:
SwitchFlipped
Prefix pre- and post-event names by using the present and past tense. Do not
use the BeforeXxx\AfterXxx pattern. For example, a close event that could
be canceled should have a Closing and Closed event. An event that
indicates that a control has been added should be declared as follows:
public event ControlEventHandler ControlAdded;

! Use raise for events, rather than fired.
When referring to events in documentation, use an event was raised,
instead of an event was fired.
! For the events delegate declaration, use a return type of void, two
parameters, an object source parameter named sender, and an e parameter
that encapsulates any additional information about the event. Delegates
should return void and have two arguments: the object that raised the event
and the event data object. Use names ending in EventHandler for the
events delegate, as in the following example:
public delegate void SwitchFlippedEventHandler(
object sender, SwitchFlippedEventArgs e);

The sender parameter represents the object that raised the event and called
the delegate. The sender parameter is always a parameter of type object,
even if you can employ a more specific type.
The state associated with the event is encapsulated in an instance of an event
class named e. Use an appropriate and specific event class for its type.
Topic Objective
To introduce .NET
Framework guidelines for
delegates and events.
Lead-in
You should follow .NET
Framework guidelines if you
intend to use your
component in the .NET
Framework.
For Your Information
The Pascal Casing
convention capitalizes the
first character of each word.
For example: BackColor

The camelCasing
convention capitalizes the
first character of each word,
except the first word. For
example: backColor
Module 8: Delegates and Events 29


! When necessary, event classes extend System.EventArgs and end in
EventArgs, as in the following example:
public class SwitchFlippedEventArgs : EventArgs {//...
}

For events that do not use any additional information, the .NET Framework
has already defined an appropriate delegate type: EventHandler, whose
argument is of the event base type EventArgs, as in the following example:
public delegate void EventHandler(
object sender, EventArgs e);

! Create an invoking method for the event.
Because events can only be raised from within the class that declared them,
derived classes cannot directly raise events that are declared within the base
class. Often it is appropriate to let the derived class raise the event. You can
do this by creating a protected invoking method for the event. By calling
this invoking method, derived classes can raise the event.
protected virtual void OnSwitchFlipped (//...) { //... }

For even more flexibility, you can declare the invoking method as virtual,
which allows the derived class to override it. This approach allows the
derived class to intercept the events that the base class is raising, possibly
doing its own processing of them.
For this reason, the .NET Framework guideline suggests that you create an
invoking method to raise an event that is a protected (family) virtual
method. However, if the class is sealed, the method should not be virtual
because the class cannot be derived from. Name the method OnEventName,
where EventName is the event being raised.

30 Module 8: Delegates and Events


The switch and light delegate scenarios in the preceding topics help to put these
guidelines in context. When a switch is flipped, the switch object raises a
SwitchFlipped event for connected light objects. In this case, the additional
SwitchFlipped event data indicates whether the switch is in the up or down
position:
public enum SwitchPosition {Up, Down};

// class to encapsulate switch data
public class SwitchFlippedEventArgs : EventArgs
{
private SwitchPosition position;

public SwitchFlippedEventArgs(SwitchPosition position) {
this.position = position;
}

public SwitchPosition Position {
get { return position; }
}
}

//delegate declaration
public delegate void SwitchFlippedEventHandler(
object sender, SwitchFlippedEventArgs e);

public class Switch
{
//event declaration
public event SwitchFlippedEventHandler SwitchFlipped;

// ProcessSwitchFlippedUp called when switch flipped up
// to create an event argument object and raise the event
public void ProcessSwitchFlippedUp() {
SwitchFlippedEventArgs e =
new SwitchFlippedEventArgs(SwitchPosition.Up);
OnSwitchFlipped(e);
}

// ProcessSwitchFlippedDown when switch flipped down
public void ProcessSwitchFlippedDown() {
SwitchFlippedEventArgs e =
new SwitchFlippedEventArgs(SwitchPosition.Down);
OnSwitchFlipped(e);
}

protected virtual void OnSwitchFlipped (
SwitchFlippedEventArgs e) {
if (SwitchFlipped != null) {
// call the delegate if non-null
SwitchFlipped(this, e);
}
}
//...
}

Module 8: Delegates and Events 31


When to Use Delegates, Events, and Interfaces
! Use a Delegate If:
# You basically want a C-style function pointer
# You want single callback invocation
# The callback should be registered in the call or at construction time, not
through an add method call
! Use Events If:
# Client signs up for the callback function through an add method call
# More than one object will care
# You want end users to be able to easily add a listener to the notification in
the visual designer
! Use an Interface If:
# The callback function entails complex behavior,
such as multiple methods

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Delegates, interfaces, and events all provide callback functionality, but each has
specific usage characteristics that make it better suited to particular situations.
Use a delegate if:
! You basically want a C-style function pointer.
! You want single callback invocation.
! You want the callback function registered in the call or at construction time,
not through an add method call.

Use events if:
! Client code signs up for the callback prior to the occurrence of events,
typically through an add method call.
! More than one client object will care.
! You want end users to be able to easily add a listener to the notification in
the visual designer.

Use an interface if:
The callback function entails complex behavior, as when more than one
callback method is invoked.

Topic Objective
To explain when to use
delegates, events, and
interfaces.
Lead-in
Delegates, interfaces, and
events all provide callback
functionality, but each has
specific usage
characteristics that make it
better suited to particular
situations.
32 Module 8: Delegates and Events


Lab 8: Creating a Simple Chat Server

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives
After completing this lab, you will be able to:
! Write code that uses the delegate class to create type-safe callbacks.
! Write code that uses the event keyword to simplify and improve the
implementation of a class that raises events.
! Write event code that conforms to the .NET Framework guidelines.

Lab Setup
Only solution files are associated with this lab. The solution files for this lab are
in the folder <install folder>\Labs\Lab08\Solution.
Scenario
In this lab, you will create a simple chat-style server to which multiple clients
can connect. When one client sends a string message to the server, the server
forwards the message to all registered clients that have not been specifically
excluded.
You should note that a real chat application would be based on a more scalable
and flexible design to connect client and servers, such as a publish-and-
subscribe design.
Estimated time to complete this lab: 60 minutes
Topic Objective
To introduce the lab.
Lead-in
In this lab, you will create
use delegates and events
with a simple chat server
application.
Module 8: Delegates and Events 33


Exercise 1
Creating a Simple Chat Server Using Delegates
In this exercise, you will implement a simple chat server and clients by using
delegates.
Scenario
Each client will register a delegate with the server. Then, when a client sends a
message to the server, the server forwards the message to all specified clients.
This implementation uses delegates as a callback mechanism.
! Create a Visual Studio .NET project
Open Visual Studio .NET and create a new C# Console Application project
named chat1 in <install folder>\Labs\Lab08.

! Create the chat server class
1. Create the chat server class and name it DChatServer.
2. Declare within the DChatServer class a delegate that will be used to invoke
the callback function in the client when a chat message arrives.
a. Name the delegate OnMsgArrived.
b. Declare this delegate public and reflect the callback functions
signature. The callbacks signature is a void return value with a single
argument of type string.
3. Define a field to store the connected clients delegates by performing the
following steps.
a. Name the field onMsgArrived.
b. Make the field a private static reference to the type of the delegate,
OnMsgArrived.
4. Define the method that will be called by a client to connect that client to the
server by performing the following steps.
a. Name the method ClientConnect.
b. Declare the method public static void.
c. Include an argument of type OnMsgArrived.
d. Create the methods implementation by using the delegates Combine
method to add this new delegate to the delegate stored in the
onMsgArrived invocation list.
As an alternative to the Combine method, you can use the += operator.
5. Define a method that will be called by a client to disconnect that client from
the server by performing the following steps.
a. Name the method ClientDisconnect.
b. Declare the method public static void.
c. Declare the method so that it has the same signature as ClientConnect.
d. Create the methods implementation by using the delegates Remove
method to remove the delegate from the onMsgArrived invocation list.
As an alternative to the Remove method, you can use the -= operator.
34 Module 8: Delegates and Events


6. Define a method that will send a specified message to the connected clients,
with the possible exception of one client, by performing the following steps.
a. Name the method SendMsg.
b. Declare the method public static void.
c. Declare the method so that it takes two arguments: a string argument for
the message and an object argument for the excluded client.
d. Create the methods implementation.
i. If the excluded client argument is null, invoke the multicast delegate
to send the message to all the clients.
ii. If the excluded client argument is not null, iterate through the
onMsgArrived delegates invocation list and invoke only those
delegates that do not match the excluded client argument.

! Create the chat client class
1. Create the chat client class and name it DChatClient.
2. Implement within the DChatClient class a callback method that the server
will invoke by a delegate when the server receives a chat message.
a. Name the method onMsgArrived.
b. Declare the method private void.
c. Declare the method so that it takes a single string argument.
d. Create the methods implementation so that it prints the name of the
client along with the string argument to the console.
3. Add a constructor that will receive the name of the client as a parameter and
connect the client with the server.
a. Declare the constructor so that it takes a string argument that represents
the clients name.
b. Create the constructors implementation.
i. Store the clients name in a private field.
ii. Connect the client to the server by calling the servers
ClientConnect method.
The calls argument should be an instance of the delegate type
DChatServer.OnMsgArrived that was instantiated with the clients
callback method: onMsgArrived.

Module 8: Delegates and Events 35


! Create the programs main entry point
1. Name the class that contains the programs standard Main entry point,
Application.
Typically, a Visual Studio .NET project creates the class with the Main
entry point for you. You may only need to rename this class.
2. Write to the console a line of text to indicate that the program is starting.
3. Instantiate three DChatClient objects, passing the client names 1, 2,
and 3 to the constructor.
4. Send a message to the server by invoking the static method SendMsg of the
DChatServer. The message should be sent to all clients.
5. Send a second message to all clients, except Client 2.
6. Write to the console a line of text to indicate that the program is finished.

! Compile and test the program
Build the program and run it.
The output should resemble the following:
Demo start: Delegate Chat Server.
Msg arrived (Client 1): Hi to all clients
Msg arrived (Client 2): Hi to all clients
Msg arrived (Client 3): Hi to all clients
Msg arrived (Client 1): Hi to all clients except client 2
Msg arrived (Client 3): Hi to all clients except client 2
Demo stop: Delegate Chat Server.


36 Module 8: Delegates and Events


Exercise 2
Creating a Simple Chat Server Using Events
In this exercise, you will implement a simple chat server and clients by using
events and delegates. This implementation uses the event keyword, which hides
some of the low-level code details associated with delegates and prevents
clients from accessing or invoking the delegates of other clients.
Scenario
Each client will connect with the servers on message arrived event. Then,
when one client sends a string message to the server, the server forwards the
message to all specified connected clients.
! Access the chat1 Visual Studio .NET project
Open Visual Studio .NET and open the project named chat1, which you
created in Exercise 1, in <install folder>\Labs\Lab08. Open the previously
created C# source file and perform the following procedures.

! Create the event-based chat server class
1. Create a new chat server class and name it EChatServer.
2. Declare within the EChatServer class a delegate that will be used to invoke
the callback function in the client when a chat message arrives.
a. Name the delegate OnMsgArrived.
b. This delegate should be declared public and reflect the callback
functions signature.
The callbacks signature is a void return value with a single argument of
type string.
3. Declare an event for the OnMsgArrived delegate type.
a. Name the event onMsgArrived.
b. Declare the event public static.
4. Define a method that will send a specified message to the connected clients,
with the possible exception of one client.
a. Name the method SendMsg.
b. Declare the method public static void.
c. Declare the method so that it takes two arguments: a string argument for
the message and an object argument for the excluded client.
d. Create the methods implementation.
i. If the excluded client argument is null, invoke the multicast delegate
to send the message to all of the clients.
ii. If the excluded client argument is not null, iterate through the
onMsgArrived delegates invocation list and invoke only those
delegates that do not match the excluded client argument.

Module 8: Delegates and Events 37


! Create the event-based chat client class
1. Create a new chat client class and name it EChatClient.
2. Implement within the EChatClient class a callback method that the server
will invoke by a delegate when the server receives a chat message.
a. Name the method onMsgArrived.
b. Declare the method private void.
c. Declare the method so that it takes a single string argument.
d. Implement the method so that it prints the name of the client, together
with the string argument, to the console.
3. Add a constructor that will receive the name of the client as a parameter and
connect the client with the server.
a. Declare the constructor so that it takes a string argument that represents
the clients name.
b. Create the constructors implementation.
i. Store the clients name in a private field.
ii. Connect the client to the server by using the += operator to add a
new delegate instance to the EChatServers event.
The += operators right operand should be an instance of the delegate
type EChatServer.OnMsgArrived that was instantiated with the
clients callback method, onMsgArrived.

! Extend the programs main entry point by performing the following
procedures
1. Write to the console a line of text to indicate that the event chat portion of
the program is starting.
2. Instantiate three EChatClient objects, passing the client names 1, 2,
and 3 to the constructor.
3. Send a message to the server by invoking the static method SendMsg of the
EchatServer. You should send the message to all clients.
4. Send a second message to all clients except client 2.
5. Write to the console a line of text to indicate that event chat is finished.

! Compile and test the program
Build the program and run it.
The output should resemble the following:
...

Demo start: Event Chat Server.
Msg arrived (Client 1): Hi to all clients
Msg arrived (Client 2): Hi to all clients
Msg arrived (Client 3): Hi to all clients
Msg arrived (Client 1): Hi to all clients except client 2
Msg arrived (Client 3): Hi to all clients except client 2
Demo stop: Event Chat Server.


38 Module 8: Delegates and Events


Exercise 3 (As Time Permits)
Conforming to the .NET Framework Guidelines
In this exercise, you will implement the simple chat program with events that
conform to the .NET Framework guidelines. Although the C# language allows
events to use any delegate type, the .NET Framework has stricter guidelines on
the delegate types that should be used for events. If you intend for your
component to be used with the .NET Framework, follow these guidelines.
Scenario
As in Exercise 2, each client connects with the servers event. Then, when one
client sends a string message to the server, the server forwards the message to
all specified clients.
In this exercise, the delegate type will conform to the .NET Framework
guidelines. The delegate type will take two parameters, an object source
parameter, which indicates the source of the event, and an e parameter, which
encapsulates information about the event and, in this case, includes the string
message that generated the event.
In this exercise, you will also instantiate the chat server as an object.
Instantiating the chat server will allow you to invoke the connected clients
delegates with a non-null object source parameter.
! Create the event class that will encapsulate the events state
1. Open Visual Studio .NET and open the project named chat1, which you
created in Exercise 1, in location <install folder>\Labs\Lab08. Open the
previously created C# source file and add the following code.
2. Create a new public class named MsgArrivedEventArgs that inherits from
EventArgs.
3. Declare within MsgArrivedEventArgs a private readonly field of type
string named message.
4. Declare a public constructor that takes a single string argument and stores it
in the field named message.
5. Add a property named Message that returns the value of message.

Module 8: Delegates and Events 39


! Create the guidelines-based chat server class
1. Create a new chat server class and name it GChatServer.
2. Declare within the GChatServer class a delegate that will be used to invoke
the function in the client when a chat message arrives.
a. Name the delegate MsgArrivedEventHandler.
b. This delegate should be declared public and reflect the guidelines
signature of a void return value and two arguments of type object and
MsgArrivedEventArgs.
3. Declare an event for the MsgArrivedEventHandler delegate type.
a. Name the event MsgArrivedHandler.
b. Make the event public. Do not make it static.
4. Define a method that will send a specified message to the connected clients,
with the possible exception of one client.
a. Name the method SendMsg.
b. Declare the method public void.
c. Declare the method so that it takes two arguments: a string argument for
the message and an object argument for the excluded client.
d. Create the methods implementation.
i. Instantiate an instance of the event class MsgArrivedEventArgs
with the message string.
ii. Invoke the OnMsgArrived method, which is implemented in the
next step.
5. Define the OnEvent method to raise the event.
a. Name the method OnMsgArrived. In accordance with the .NET
Framework guidelines declare the method protected virtual void with
two arguments of type MsgArrivedEventArgs and object.
b. Create the methods implementation.
i. If the events field contains a non-null delegate list and the excluded
client argument is null, invoke the multicast delegate with the
appropriate arguments.
ii. If the events field contains a non-null delegate list and the excluded
client argument is not null, iterate through the MsgArrivedHandler
delegates invocation list and invoke only those delegates that do not
match the excluded client argument.
iii. If the events field contains a null delegate list, do nothing.

40 Module 8: Delegates and Events


! Create the guidelines-based chat client class
1. Create a new chat client class and name it GChatClient.
2. Implement within the GChatClient class a callback method that the server
will invoke by a delegate when the server receives a chat message.
a. Name the method onMsgArrived.
b. Declare the method private void.
c. Declare the method so that it takes an object and
MsgArrivedEventArgs arguments.
d. Create the methods implementation so that it prints the name of the
client, the Message property of MsgArrivedEventArgs, and the server
object as a string to the console.
3. Add a constructor that will receive the name of the client and a reference to
the server, and will connect the client with the server.
a. Declare the constructor so that it takes a string argument that represents
the clients name and a GChatServer argument that specifies the server
object.
b. Create the constructors implementation.
i. Store the clients name in a private field.
ii. Store the servers reference in a private field.
iii. Connect the client to the server by using the += operator to add a
new delegate instance to the GChatServers event.
The += operators right operand should be an instance of the delegate
type GChatServer.MsgArrivedEventHandler that was
instantiated with the clients callback method: onMsgArrived.

! Extend the programs main entry point by performing the following
procedures
1. Create an instance of the GChatServer object.
2. Write to the console a line of text to indicate that the guidelines-based chat
server class portion of the program is starting.
3. Instantiate three GChatClient objects, passing the client names 1, 2,
and 3 to the constructor.
4. Send a message to the server by invoking the SendMsg method of
GChatServer. The message should be sent to all clients.
5. Send a second message to all clients except client 2.
6. Write to the console a line of text to indicate that the guidelines-based event
chat is finished.

Module 8: Delegates and Events 41


! Compile and test the program
Build the program and run it.
The guidelines-based event chat output should resemble the following:
...
Demo start: Guidelines Based Event Chat Server. Server:
GChatServer
Msg arrived (Client 1): Hi to all clients Server:
GChatServer
Msg arrived (Client 2): Hi to all clients Server:
GChatServer
Msg arrived (Client 3): Hi to all clients Server:
GChatServer
Msg arrived (Client 1): Hi to all clients except client 2
Server: GChatServer
Msg arrived (Client 3): Hi to all clients except client 2
Server: GChatServer
Demo stop: Guidelines Based Event Chat Server.



42 Module 8: Delegates and Events


Review
! Delegates
! Multicast Delegates
! Events
! When to Use Delegates, Events, and Interfaces

*****************************ILLEGAL FOR NON-TRAINER USE******************************
1. Write the code to declare a delegate that is named ProcessOrderCallback
for the following method:
static public bool ProcessOrder(int Quantity, string Item)
{ //...
}

public delegate bool ProcessOrderCallback (int Quantity,
string, string Item);


2. Write the code to call the following EnterOrder method, instantiating and
passing an instance of the ProcessOrderCallback delegate that refers to a
public static method that is named Foo in the public class Bar.
static public void EnterOrder(
ProcessOrderCallback processOrderCallback) { //...
};

EnterOrder( new ProcessOrderCallback(Bar.Foo) );


Topic Objective
To reinforce module
objectives by reviewing
key points.
Lead-in
The review questions cover
some of the key concepts
taught in this module.
Module 8: Delegates and Events 43


3. Write the body of the static method EnterOrder that takes as an argument
an instance of the delegate that is declared in question 1. EnterOrder
outputs to the console strings to prompt for an item name and quantity and
reads in the users input. EnterOrder should invoke the callback delegate
with this information.
static public void EnterOrder(
ProcessOrderCallback processOrderCallback) {
Console.WriteLine("Enter Item Name:");
string name = Console.ReadLine();
Console.WriteLine("Enter Item Quanity:");
int quantity = Int32.Parse( Console.ReadLine() );
processOrderCallback(quantity, name);
}


4. Using the following declarations, write the code to add delegate b to as
invocation list.
delegate void MyDelegate();
MyDelegate a, b;
a = new MyDelegate(Bar1.Foo1);
b = new MyDelegate(Bar2.Foo2);
a += b;


5. Use the event keyword to write the code to declare a public static event for
a delegate type ProcessOrderEventHandler.
public static event ProcessOrderEventHandler
processOrderHandler;


6. Describe when you should use a delegate and when you should use an
event.
Use a delegate when:
You want a C-style function pointer.
You want single callback invocation.
You want the callback function to be registered in the call or at
construction time, not in a separate add method.

Use an event when:
Client code signs up for the callback prior to the occurrence of
events, typically through a separate add method.
More than one client object will be affected.




THIS PAGE INTENTIONALLY LEFT BLANK









Contents
Overview 1
Memory Management Basics 2
Non-Memory Resource Management 12
Implicit Resource Management 13
Explicit Resource Management 26
Optimizing Garbage Collection 36
Lab 9: Memory and Resource Management 48
Review 55

Module 9: Memory and
Resource Management


Information in this document, including URL and other Internet Web site references, is subject to
change without notice. Unless otherwise noted, the example companies, organizations, products,
domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious,
and no association with any real company, organization, product, domain name, e-mail address,
logo, person, place or event is intended or should be inferred. Complying with all applicable
copyright laws is the responsibility of the user. Without limiting the rights under copyright, no
part of this document may be reproduced, stored in or introduced into a retrieval system, or
transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or
otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual
property rights covering subject matter in this document. Except as expressly provided in any
written license agreement from Microsoft, the furnishing of this document does not give you any
license to these patents, trademarks, copyrights, or other intellectual property.

2001-2002 Microsoft Corporation. All rights reserved.

Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint,
Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and
Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A.
and/or other countries.

The names of actual companies and products mentioned herein may be the trademarks of their
respective owners.



Module 9: Memory and Resource Management iii


Instructor Notes
After completing this module, students will be able to:
! Describe how garbage collection manages object memory.
! Implicitly manage non-memory resources by using a destructors finalize
code.
! Explicitly manage non-memory resources by using client-controlled
deterministic release of resources.
! Write code by using the temporary resource usage design pattern.
! Programmatically control the behavior of the garbage collection.
! Describe advanced garbage collection features.

Materials and Preparation
This section provides the materials and preparation tasks that you need to teach
this module.
Required Materials
To teach this module, you need the Microsoft PowerPoint file 2349B_09.ppt.
Preparation Tasks
To prepare for this module, you should:
! Read all of the materials for this module.
! Review the animation.
! Practice the demonstrations.
! Complete the lab.

Presentation:
125 Minutes

Lab:
60 Minutes
iv Module 9: Memory and Resource Management


Demonstrations
This section provides demonstration procedures that will not fit in the margin
notes or are not appropriate for the student notes.
The code for each of the following demonstrations is contained in one project
and is located in <install folder>\Democode\Mod09\
GARBAGE COLLECTION. In addition, the code for the individual
demonstrations is provided in the student notes.
Use the debugger to step through the code while you point out features and ask
students what they think will happen next.
Finalization
In this demonstration, you will show students how garbage collection handles
finalization and resurrection. In this demonstration, run the Introduction and
ResurrectionDemo methods.
The IDisposable Interface
In this demonstration, you will show students how to perform explicit resource
management by using the IDisposable interface. In this demonstration, run the
DisposeDemo method.
Weak References
In this demonstration, you will show students how garbage collection handles
weak references. In this demonstration, run the WeakRefDemo method.
Generations
In this demonstration, you will show students how garbage collection handles
generations. In this demonstration, run the GenerationDemo method.
Multimedia
This section lists the multimedia items that are part of this module. Instructions
for launching and playing the multimedia are included with the relevant slides.
Simple Garbage Collection
This animation will show students the Microsoft .NET Framework common
language runtimes garbage collection process without finalization.
Garbage Collection
This animation will show students the .NET Framework common language
runtime garbage collection process, including finalization.
Module 9: Memory and Resource Management v


Module Strategy
Use the following strategy to present this module:
! Memory Management Basics
Students in your classes will probably use different approaches to memory
management. Begin with a brief review of different memory management
techniques that you or the students may have learned from experience.
Because students will need to adapt their programming practices to the
automatic memory management that is provided by the common language
runtime, it is important to mention other memory management techniques.
Compare and contrast manual memory management with the automatic
memory management that is provided by the common language runtime.
Outline the simple garbage collection process without the finalization details
and use the Simple Garbage Collection animation to help the students
understand the concept of the garbage collection process more easily.
Instructions for running the animations in this module are included in
Instructor Margin Notes.
! Non-Memory Resource Management
This topic is an introduction to handling non-memory resources implicitly
and explicitly. Tell students that the next two sections cover these areas in
detail. You should not spend much time on this slide.
! Implicit Resource Management
Introduce the finalization phase of the garbage collection process.
Emphasize that in C#, a destructor must be used for the finalization code.
The second animation, Garbage Collection, is more complex than the first.
It shows the garbage collection process with the finalization details.
Spend time discussing the drawbacks that are associated with finalization
and what to do if finalization is required. Show students how to deal with an
object that has been resurrected.
Use the Finalization demonstration to highlight how garbage collection
deals with finalization and resurrection.
! Explicit Resource Management
Show students how to perform explicit resource management by using the
IDisposable interface and Dispose method.
Discuss the temporary resource usage design pattern as an example of how
to allocate resources for temporary use.
! Optimizing Garbage Collection
Use the demonstrations that are provided to show how to optimize garbage
collection through weak references and generations.
In addition to discussing the programmatic optimizations that can be made
to the garbage collection process, briefly mention the use of performance
counters to monitor memory activity and the use of a multiprocessor system
to scale applications where there are garbage collection bottlenecks.

Module 9: Memory and Resource Management 1


Overview
! Memory Management Basics
! Non-Memory Resource Management
! Implicit Resource Management
! Explicit Resource Management
! Optimizing Garbage Collection

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objects in the Microsoft .NET Framework use memory resources and may use
other resources, such as file handles. For software to run properly, these
resources must be well managed. In other words, they must be properly
allocated and released.
After completing this module, you will be able to:
! Describe how garbage collection manages object memory.
! Implicitly manage non-memory resources by using a destructors finalize
code.
! Explicitly manage non-memory resources by using client-controlled
deterministic release of resources.
! Write code by using the temporary resource usage design pattern.
! Programmatically control the behavior of the garbage collection.
! Describe advanced garbage collection features.

Topic Objective
To provide an overview of
the module topics and
objectives.
Lead-in
Objects in the Microsoft
.NET Framework use
memory resources and may
use other resources, such
as file handles. For software
to run properly, these
resources must be well
managed.
2 Module 9: Memory and Resource Management


" "" " Memory Management Basics
! Developer Backgrounds
! Manual vs. Automatic Memory Management
! Memory Management of .NET Framework Types
! Simple Garbage Collection

*****************************ILLEGAL FOR NON-TRAINER USE******************************
A major feature of the .NET Framework common language runtime is that the
runtime automatically handles the allocation and release of an objects memory
resources. In most cases, automatic memory management enhances code quality
and developer productivity without negatively impacting expressiveness or
performance.
Understanding how the .NET Framework facilitates resource management is
essential for writing correct and efficient code.
In this section, you will learn about memory management in the .NET
Framework, including simple garbage collection.
Topic Objective
To provide an overview of
the section topics.
Lead-in
A major feature of the .NET
Framework common
language runtime is that the
runtime automatically
handles the allocation and
release of an objects
memory resources.
Module 9: Memory and Resource Management 3


Developer Backgrounds
! COM
# Manually implement reference counting and handle
circular references
! C++
# Manually use the new operator and delete operator
! Visual Basic
# Accustomed to automatic memory management

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Your experience with memory management will vary depending upon your
development background. In certain situations, you will need to adapt your
programming practices to the automatic memory management that is provided
by the common language runtime.
COM Developers
COM developers are accustomed to implementing reference counting as a
manual memory management technique. Each time an object is referenced, a
counter is incremented. When a reference to an object goes out of scope, the
counter is decremented. When an objects reference count reaches zero, the
object is terminated and its memory is freed.
The reference counting scheme is the source of many bugs. If the reference
counting rules are not followed precisely, objects may be freed prematurely or
unreferenced objects may accumulate in memory.
Circular references are also a common source of bugs. A circular reference
occurs when a child object has a reference to a parent object, and the parent
object has a reference to the child object. Circular references prevent either
object from being released or destroyed. The only solution is for the parent and
child objects to agree on a fixed pattern of usage and destruction, such as where
the parent always deletes the child first.
When you develop applications in a managed language, the runtimes garbage
collector eliminates the need for reference counting and, as a result, the bugs
that can arise from this manual memory management scheme.
Topic Objective
To discuss various
developer backgrounds with
regard to memory
management.
Lead-in
Your experience with
memory management will
vary depending upon your
development background.
4 Module 9: Memory and Resource Management


C++ Developers
C++ developers are accustomed to the tasks that are related to manual memory
management. In C++, when you allocate memory for an object by using the
new operator, you must release the objects memory by using the delete
operator. This can lead to errors such as forgetting to release an object and
causing a memory leak, or attempting to access memory for an object that has
already been released.
When you develop applications by using the Managed Extensions for C++, or
another managed language, you do not have to use the delete operator to release
an object. The garbage collector does this for you automatically when the object
is no longer being used by the application.
C++ developers may be accustomed to avoiding the use of short-term objects
because of the associated cost of manually managing the memory for these
objects. For managed short-term objects that are created and then go out of
scope between collections, the cost of allocating and releasing memory is
extremely low.
In the .NET Framework, the garbage collector is actually optimized to manage
objects with short lifetimes. When you develop managed applications, it is
appropriate to use short-term objects in situations where they simplify your
code.
Visual Basic Developers
Microsoft Visual Basic developers are accustomed to automatic memory
management. If you are a Visual Basic developer, the programming practices
with which you are familiar apply to the majority of the managed objects that
you create in the .NET Framework. However, you should take special note of
the suggested design pattern for a Dispose method to use when you create or
use objects that encapsulate unmanaged resources.
Module 9: Memory and Resource Management 5


Manual vs. Automatic Memory Management
! Manual Memory Management
# Programmer manages memory
! Common Problems
# Failure to release memory
# Invalid references to freed memory
! .NET Runtime Provides Automatic Memory Management
# Eases programming task
# Eliminates a potential source of bugs

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Manual memory management requires that you manage the allocation and
deallocation of blocks of memory. The NET Framework common language
runtime provides automatic memory management so that you are freed from
this time-consuming and difficult task.
Manual Memory Management
The following table provides examples of manual memory management in
different programming languages, C and C++, and in COM.
Language or
environment

Example of manual memory management

C malloc and free functions
C++ new and delete operators
COM AddRef and Release reference counting methods

Automatic Memory Management in the .NET Framework
The .NET Framework common language runtime automatically handles
managed object memory and manages references to these objects, releasing
managed objects when they are no longer being used. This automatic memory
management eliminates the possibility of programming errors that can cause
memory leaks and the use of memory that has already been freed. With
automatic memory management, you no longer have to deal with complex bugs
that are associated with reference counting, circular reference leaks, or dangling
references.
Topic Objective
To introduce the advantages
of automatic memory
management in the .NET
Framework.
Lead-in
Manual memory
management requires that
you manage the allocation
and deallocation of blocks of
memory.
6 Module 9: Memory and Resource Management


Memory Management of .NET Framework Types
! Instances of Value Types Use Stack Memory
# Allocation and deallocation are automatic and safe
! Managed Objects Are Reference Types and Use Heap
Memory
# Created by calls to the new operator
# Freed by garbage collection

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In the .NET Framework, all values have a type, which may be a value type or a
reference type. Each values type affects how that value is managed in memory.
Instances of Value Types
Instances of value types are stored in memory that is allocated on the stack.
Allocation and deallocation of memory occur automatically as follows:
! Allocation
Memory for an instance of a value type is created when the activation record
for its scope is pushed on to the stack.
! Deallocation
Memory is deallocated when the scopes activation record, which contains
the value type instance, is popped from the stack.

Value types are always accessed directly. You cannot create a reference to a
value type, and therefore you cannot refer to a value instance that has been
deallocated. As a result, there is no danger of creating a dangling reference to a
value type.
Topic Objective
To explain how a values
type affects how the value is
managed in memory.
Lead-in
In .the NET Framework, all
values have a type, which
may be a value type or a
reference type.
Module 9: Memory and Resource Management 7


Instances of Reference Types
Managed objects are reference types, which you create by calls to the new
operator. The memory of these objects is allocated in the common language
runtime managed heap. You can access reference types only through a
reference to that storage. The use of references enables garbage collection to
track outstanding references to a particular instance and to free that objects
heap memory when appropriate.
A managed objects heap memory is only released through garbage collection
when there are no reachable references to that object. This mechanism ensures
that there will be no invalid references to the objects freed memory and thus no
dangling references.
8 Module 9: Memory and Resource Management


Simple Garbage Collection
! Simple Garbage Collection Algorithm
# Wait until managed code threads are in a safe state
# Build a graph of all reachable objects
# Move reachable objects to compact heap
- Unreachable objects memory is reclaimed
# Update references to all moved objects
! Reference Cycles Are Handled Automatically

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Typically, garbage collection is triggered when an application creates an object,
and there is not enough space left in the heap to provide memory for the object.
Alternatively, you can invoke garbage collection programmatically. This
process is discussed in Optimizing Garbage Collection in this module.
The Garbage Collection Algorithm
Whether garbage collection occurs automatically or you invoke it
programmatically, the garbage collection algorithm is used to find any objects
in the heap whose memory can be reclaimed. Such objects include objects that
are no longer being used by the application. If garbage collection can reclaim
enough objects to free sufficient memory, memory for the new objects can be
allocated. Otherwise, an OutOfMemoryException is thrown.
For the sake of simplicity, this topic describes the simple garbage collection
process. The finalization phase and optimization details are discussed in
Implicit Resource Management in this module.
Simple Garbage Collection Process
Simple garbage collection uses the following process:
1. Waits until other managed threads reach a safe state, for example,
suspended.
The garbage collection process modifies managed objects and their
references. Therefore it must first wait until other managed threads are
suspended.
2. Builds a graph of all reachable objects.
3. Compacts the heap by moving reachable objects.
By moving reachable objects down in the heap, garbage collection reclaims
the space in the heap that was used by unreachable objects.
4. Updates all application references to moved objects.

Topic Objective
To explain how simple
garbage collection works in
the .NET Framework.
Lead-in
Garbage collection is
triggered when an
application creates an
object, and there is not
enough space left in the
heap to provide memory for
the object.
Module 9: Memory and Resource Management 9


Building the Graph of Reachable Objects
Garbage collection accesses the collection of root references that are maintained
by the runtime. Each application has a logical collection of root references. The
collection contains all of the managed object references from global and static
objects and local variables that are currently on the stack and in CPU registers.
To build the graph of reachable objects, garbage collection performs the
following actions.
1. It adds all of the objects that are referenced by each root reference.
2. It recursively adds objects that are referenced by any added object.

Before an object is added to the graph, garbage collection checks to ensure that
the object is not already in the graph. This check prevents garbage collection
from entering an infinite loop that is caused by circular references.
At the end of the process, any object that is not in the reachable object graph is
considered unreachable and therefore garbage.
Reference Cycles Handled Automatically
An object is reachable only if there is a path from a root reference to that object.
Therefore, reference cycles between unreachable objects will not prevent the
objects memory from being released by the garbage collection process. For
example, if A references B and B references A, then both objects will be
garbage collected when they are no longer reachable from a root reference.
10 Module 9: Memory and Resource Management


Multimedia: Simple Garbage Collection

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This animation illustrates the .NET Framework common language runtime
garbage collection process.

Compiler optimization is disabled in this scenario to prevent an object
from becoming eligible for garbage collection earlier than would be expected.
Otherwise, the compiler could optimize away assignments to local variables
that are never observed by a later read. This optimization could result in an
object being subject to garbage collection before its reference is assigned to
null.

! The common language runtime allocates memory resources for reference
type objects in the section of the applications memory that is called the
managed heap.
! When there is insufficient space in the managed heap, the common language
runtime executes the garbage collection algorithm to remove objects that are
no longer being used by the application.

Topic Objective
To illustrate the .NET
Framework garbage
collection process.
Lead-in
This animation illustrates the
.NET Framework common
language runtimes garbage
collection process.
To launch the animation,
click the button in the lower
left corner of the slide. To
play the animation, click the
Simplified Garbage
Collection button at the top
of the screen, and then click
the play button in the lower
left corner of the screen.
Note
Module 9: Memory and Resource Management 11


The steps of the algorithm are as follows:
1. After all other managed threads reach a safe state, for example suspended,
garbage collection builds a graph of all of the objects that are reachable
from the root references.
2. Before an item is added to the graph, a check is made to ensure that the
object is not already in the graph.
This check ensures that circular references are handled without garbage
collection entering an infinite loop.
3. After all of the root references and added objects have been processed, any
objects that are not in the graph are not reachable by the application.
The memory for these objects can be reclaimed when the heap is
compacted.
4. The remaining objects are moved down in the heap to fill the gaps.
5. The garbage collection process must then update all references to the moved
objects.

12 Module 9: Memory and Resource Management


Non-Memory Resource Management
! Implicit Resource Management
! Explicit Resource Management

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Managed objects sometimes encapsulate control over resources that are not
managed by the runtime. Examples of these non-memory resources include
window handles, file handles, and database connections.
You need implicit and explicit ways to free these resources. Garbage collection
provides implicit resource management of an object by calling the objects
finalize code.
The client of an object provides explicit resource management by calling the
Dispose method on the IDisposable interface of the object when the client is
finished using the object.
Topic Objective
To provide an overview of
non-memory resource
management, which is
discussed in the following
topics.
Lead-in
Managed objects
sometimes encapsulate
control over resources that
are not managed by the
runtime.
For Your Information
This topic is an introduction
to handling non-memory
resources implicitly and
explicitly. Tell students that
the next two sections cover
these areas in detail. You
should not spend much time
on this slide.
Module 9: Memory and Resource Management 13


" "" " Implicit Resource Management
! Finalization
! Garbage Collection with Finalization
! Finalization Guidelines
! Controlling Garbage Collection

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The .NET Framework common language runtime provides the means for
notifying an object before it is destroyed so that it can clean up and release non-
memory resources.
In this section, you will learn how to take advantage of this feature.
Topic Objective
To provide an overview of
the section topics.
Lead-in
The .NET Framework
common language runtime
provides the means for
notifying an object before it
is destroyed so that it can
clean up and release non-
memory resources.
14 Module 9: Memory and Resource Management


Finalization
! Finalize Code Called by Garbage Collection
! In C#, the Finalize Code Is Provided by a Destructor
! Use C# Destructor to Implicitly Close a FileStream
class Foo {
private System.IO.FileStream fs;
//...
public Foo() {
fs = new System.IO.FileStream(
"bar", FileMode.CreateNew);
}
~Foo() { fs.Close(); }
}
class Foo {
private System.IO.FileStream fs;
//...
public Foo() {
fs = new System.IO.FileStream(
"bar", FileMode.CreateNew);
}
~Foo() { fs.Close(); }
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Implicit management of resources ensures that an object can properly clean up
its resources at some time in the future when there are no longer any valid
references to the object.
If an object provides finalize code in its destructor, the garbage collection
process calls this code when there are no longer any valid references to the
object. This phase of garbage collection is referred to as finalization.
Finalization allows an object to properly cleanup before its memory resources
are freed.
Using a Destructor for Finalization
In C#, the Finalize method is not directly accessible, and you cannot call or
override the Finalize method. You must place code to be executed during
finalization inside a C# destructor. The syntax for a C# destructor is the tilde
operator (~), followed by the class name and a block of statements that will be
executed during finalization. The following example shows the C# destructor
syntax for a class named Foo.
~Foo() {
// ... perform some cleanup operation here
}

Topic Objective
To define and describe
finalization in garbage
collection.
Lead-in
Implicit management of
resources ensures that an
object can properly clean up
its resources at some time
in the future when there are
no longer any valid
references to the object.
Module 9: Memory and Resource Management 15


This code implicitly translates to the following:
protected override void Finalize() {
try {
// ... do something
}
finally {
base.Finalize();
}
}

Destructors are not inherited. When the finalization code of an object is
executed and the object is destructed, the destructors in the inheritance chain of
that object are called in order, from the most derived destructor to the least
derived destructor.
Differences Between C# and C++ Destructors
C# destructors and C++ destructors differ in several important ways, as shown
in the following table.
Destructor Characteristics

C# destructors Execute non-deterministically
Automatically invoked at any time after an object becomes eligible
for garbage collection
Not guaranteed to run in any specific order, even if one object
contains or refers to another
Cannot be invoked explicitly
C++ destructors Execute deterministically
Run in the order they are called
Can be invoked explicitly

An Example of Implicit Resource Management
A class Foo has a constructor that creates a FileStream. To ensure that the
buffer of the FileStream object is properly flushed, the class Foo provides
implicit management of the resource through destructor code that closes the
FileStream, as shown in the following code:
class Foo {
private System.IO.FileStream fs;
//...
public Foo() {
fs = new System.IO.FileStream("bar", FileMode.CreateNew);
}
~Foo() { fs.Close(); }
}

Implicit management of resources may not be adequate in all circumstances. In
the preceding example, full access by other objects to the file that is opened by
a Foo object may be delayed for an indeterminate amount of time after the Foo
object no longer needs the resource. Therefore, you typically need to use an
explicit form of resource management. For more information about explicit
resource management, see Explicit Resource Management in this module.
16 Module 9: Memory and Resource Management


Garbage Collection with Finalization
! Runtime Maintains a List of Objects That Require Finalization
# Finalization queue
! Garbage Collection Process Invoked
! Unreachable Objects Requiring Finalization
# References added to freachable queue
# Objects are now reachable and not garbage
! Move Reachable Objects to Compact the Heap
# Unreachable objects' memory is reclaimed
! Update References to All Moved Objects

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Finalization adds to the complexity and increases performance overhead of the
basic garbage collection process, as it was described in Simple Garbage
Collection in this module.
The .NET Framework common language runtime maintains a list of managed
objects that require finalization. This list is known as the finalization queue and
is used during garbage collection to provide implicit resource management.
Garbage collection is typically invoked when the creation of a new object
requires more space in the managed heap than is currently available.
After waiting for all other managed threads to be suspended, garbage collection
with finalization proceeds as follows:
1. Garbage collection builds a graph of all reachable objects, as described in
Simple Garbage Collection in this module.
Any managed object that is not in the graph is unreachable.
2. Garbage collection checks the finalization queue to see if an unreachable
object requires finalization.
If an unreachable object requires finalization, it is removed from the
finalization queue, and a reference to the object is placed in the freachable
(pronounced F-reachable) queue. The freachable queue is part of the root
references of an application. The object is therefore now considered
reachable and is no longer garbage.
3. Garbage collection compacts the heap and updates references to all moved
objects.
At this point, the memory resources for the unreachable objects have been
freed.
4. Garbage collection allows the application to continue normal operation.

At this point, the finalization phase of the garbage collection process can
commence on a separate thread.
Topic Objective
To describe the initial
process of garbage
collection with finalization.
Lead-in
Finalization adds to the
complexity and increases
performance overhead of
the basic garbage collection
process, as it was described
in Simple Garbage
Collection in this module.
Module 9: Memory and Resource Management 17


Garbage Collection with Finalization (continued)
! Finalize Thread Runs
# Executes freachable objects' Finalize methods
# References removed from freachable queue
# Unless resurrected, objects are now garbage
# May be reclaimed next time garbage collection occurs

*****************************ILLEGAL FOR NON-TRAINER USE******************************
After the memory resources for the unreachable objects are freed and the
application has continued normal operation, the finalization phase of garbage
collection commences on a separate thread.
The Finalization Phase
As the application runs, a special runtime thread removes references to objects
from the freachable queue and calls the finalize code of those objects.
Therefore, it is important that this code does not depend upon the identity of the
thread. For example, the method should not depend on thread local storage.
After an object has been finalized and removed from the freachable queue, it
becomes unreachable again as long as that object has not been resurrected. An
object is resurrected if it becomes reachable from an application root after
previously being unreachable.
However, the objects memory resources are not freed at this time. The
reclamation of the objects memory resources must wait until garbage
collection occurs next.
Topic Objective
To describe the latter part of
the process of garbage
collection with finalization.
Lead-in
After the memory resources
for the unreachable objects
are freed and the application
has continued normal
operation, the finalization
phase of garbage collection
commences on a separate
thread.
18 Module 9: Memory and Resource Management


Resurrection
Resurrection of an object occurs when a previously unreachable object becomes
reachable from an application root during finalization. For example, the finalize
code for an object may assign to a global or static variable a reference to the
object itself. The object is now reachable and is not subject to garbage
collection.
Issues with Resurrection
You should avoid resurrection when possible because the objects finalize code
has been called and may have released resources that are required for the
objects proper operation, even if the objects memory is valid.
For example, when the destructor of the Foo class that was shown in An
Example of Implicit Resource Management in this module executes its
finalization code, the file will close, and the other methods of Foo that require
an open FileStream may not be able to successfully complete.
In addition, when a resurrected object becomes unreachable sometime in the
future, its finalize code will not be called unless the object has called the
GC.ReRegisterForFinalize method.
You should also note that even if the finalize code of a class does not resurrect
an object, that object may still be resurrected, as when another object that refers
to the object is resurrected. Thus, all objects should be able to handle
resurrection. For more information about resurrection and finalization, see
Controlling Garbage Collection in this module.
Module 9: Memory and Resource Management 19


Multimedia: Garbage Collection

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This animation illustrates the .NET Framework common language runtime
garbage collection process, including finalization.

Compiler optimization is disabled in this scenario to prevent an object
from becoming eligible for garbage collection earlier than would be expected.
Otherwise, the compiler could optimize away assignments to local variables
that are never observed by a later read. This optimization could result in an
object being subject to garbage collection before its reference is assigned to
null.

In this animation, you will learn that:
! The .NET Framework common language runtime allocates memory
resources for objects in the section of the applications memory that is
called the managed heap.
! The finalization queue holds references to objects whose classes require
finalization.
! The freachable queue is a special kind of root reference whose entries are
references to objects that are ready to have their finalize code invoked. An
freachable reference keeps the object alive.
! The nondeterministic release of memory and non-memory resources is
another significant difference between .NET Framework and C++
destructors.

Topic Objective
To illustrate how garbage
collection, including
finalization, works.
Lead-in
This animation illustrates the
.NET Framework common
language runtime garbage
collection process, including
finalization.

To launch the animation,
click the button in the lower
left corner of the slide. To
play the animation, click the
play button in the lower left
corner of the screen.
Note
20 Module 9: Memory and Resource Management


Finalization Guidelines
! Avoid Finalization and Destructors If Possible
# Performance costs
# Complexity
# Delay of memory resource release
! If You Require Finalization, Finalize Code Should:
# Avoid calling other objects
# Avoid making assumptions about thread ID
! Classes with Finalization Should:
# Avoid making references to other objects

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This topic provides guidelines for handling finalization. To avoid problems that
result from the .NET Frameworks nondeterministic ordering of calls to finalize
code, you may need to use explicit resource management in addition to careful
design.
Avoid Finalization if Possible
You should only implement finalization, or implement a destructor in C#, on
classes that require finalization. If your class has only managed references and
does not have to manage non-memory resources, you should not implement
finalize code. Finalization adds overhead and complexity, and delays the
reclamation of an objects memory resources.
Implementing Finalization
If you must implement finalization, you should obey the following guidelines:
! Avoid calling other objects in finalization code.
In your finalization code, free any external resources that your object is
holding on to. However, you should avoid calling other objects, for
example, contained objects, because their finalize code may have already
been called. The .NET Framework common language runtime does not
specify any order on its invocation of the finalize code of freachable objects.
Therefore, if an object of type Foo refers to an object of type Bar, you
cannot know whether the finalize code of Foo will be called before or after
the finalize code of Bar. This nondeterminism may cause problems if the
finalize code of Foo calls a method in Bar that requires a resource that is
released by Bar in its finalize code.
! Avoid assumptions about thread ID.
As previously noted, finalization code should not make any assumptions
about the thread ID.
Topic Objective
To alert students to issues
that are associated with
finalization.
Lead-in
This topic provides
guidelines for handling
finalization.
Module 9: Memory and Resource Management 21


! Ensure that the Finalize code of your base class is called.
This call is performed automatically by the C# destructor syntax.
! Avoid references to other objects.
A class that requires finalization should avoid references to other objects
because a finalizable object will prolong its own lifetime and the lifetime of
objects that it references. If possible, you should factor such classes into the
following two classes:
One class that contains the resource that requires management and has
the finalize code but has no other object references.
A second class that holds the references to other objects but has no
finalize code.

22 Module 9: Memory and Resource Management


Controlling Garbage Collection
! To Force Garbage Collection
! To Suspend Calling Thread Until Threads Queue of Finalizers
Is Empty
! To Allow a Finalized Resurrected Object to Have Its Finalizer
Called Again
! To Request the System Not to Call the Finalizer Method
void System.GC.Collect();
void System.GC.Collect();
void System.GC.WaitForPendingFinalizers();
void System.GC.WaitForPendingFinalizers();
void System.GC.ReRegisterForFinalize(object obj);
void System.GC.ReRegisterForFinalize(object obj);
void System.GC.SuppressFinalize(object obj);
void System.GC.SuppressFinalize(object obj);

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The System.GC class of the .NET Framework contains methods that can be
used to control garbage collection. The Collect method with no arguments
forces the collection of all generations, as in the following code:
void System.GC.Collect();

For more information about generations, see Generations in this module.
Finalizers are run on a separate thread of execution. When the
WaitForPendingFinalizers method is called, the current thread is suspended
until the queue of finalizers that are waiting to run is empty. Because the
running of finalizers may trigger another garbage collection, which may, in
turn, re-queue new finalizers, there is no guarantee that the call to
WaitForPendingFinalizers will terminate.
void System.GC.WaitForPendingFinalizers();

After the garbage collection process calls an objects finalize code, garbage
collection assumes that there is no need to call it again. However, if an object is
resurrected, the ReRegisterForFinalize method may be called to force garbage
collection to call the objects finalize code again the next time the object is
destroyed. Note that if ReRegisterForFinalize is called multiple times, the
objects finalize code will also be called multiple times.
void System.GC.ReRegisterForFinalize(object obj);

If an object that has finalize code no longer requires finalization to manage its
resources, the object may call the SuppressFinalize method to improve
performance. For example, an object that supports explicit resource
management should call the SuppressFinalize method when it releases its
resources, as in the following code:
void System.GC.SuppressFinalize(object obj);

Topic Objective
To introduce techniques to
control garbage collection.
Lead-in
The System.GC class of the
.NET Framework contains
methods that can be used to
control the garbage
collection process.
Module 9: Memory and Resource Management 23


Demonstration: Finalization

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This demonstration shows how garbage collection handles finalization and
resurrection.
// This method demonstrates how the GC works.
private static void Introduction() {
Display(0,
"\n\nDemo start: Introduction to Garbage Collection.", +1);

// Create a new DerivedObj in the managed heap
// Note: Both BaseObj and DerivedObj constructors are called
DerivedObj obj = new DerivedObj("Introduction");

obj = null; // We no longer need this object

// The object is unreachable so forcing a GC causes it to be
// finalized.
Collect();

// Wait for the GC's Finalize thread to finish
// executing all queued Finalize code.
WaitForFinalizers();

// NOTE: The GC calls the most-derived (farthest away from
// the Object base class) destructor.
// A C# destructor automatically
// calls its base class destructor.

(Code continued on the following page.)
Topic Objective
To demonstrate how
garbage collection handles
finalization and resurrection.
Lead-in
This demonstration shows
how garbage collection
handles finalization and
resurrection.
For Your Information
Use the debugger to step
through the code while you
point out features and ask
students what they think will
happen next. In this section
run the Introduction and
ResurrectionDemo
methods.
24 Module 9: Memory and Resource Management


// This is the same test as above with one slight variation
obj = new DerivedObj("Introduction");
// obj = null; // Variation: this line is commented out
Collect();
WaitForFinalizers();
// If compiler optimization was turned on:
// Notice that we get identical results as above:
// the destructors Finalize code
// runs because the just in time compilers optimizer
// knows that obj is not referenced later in this function

// Now we explicitly release the object and the destructors
// Finalize code is run.
// If compiler optimization was turned off
// we now see the finalization called otherwise nothing.
obj = null;
Collect();
WaitForFinalizers();
Display(-1,
"Demo stop: Introduction to Garbage Collection.", 0);
}


// Resurrection

// This reference is accessed in the ResurrectObj.Finalize
// code and is used to create a strong reference to an
// object (resurrecting it).
static public ResurrectObj ResObjHolder; // Defaults to null

// This method demonstrates how the GC supports resurrection.
// NOTE: Resurrection is discouraged.
private static void ResurrectionDemo() {
Display(0, "\n\nDemo start: Object Resurrection.", +1);

// Create a ResurrectObj
ResurrectObj obj = new ResurrectObj("Resurrection");

// Destroy all strong references to the new ResurrectionObj
obj = null;

// Force the GC to determine that the object is unreachable.
Collect();
WaitForFinalizers();
// You should see the Finalize code called.

// However, the ResurrectObj's Finalize code
// resurrects the object keeping it alive.
// It does this by placing a
// reference to the dying-object in Application.ResObjHolder

// You can see that ResurrectObj still exists because
// the following line doesn't raise an exception.
ResObjHolder.Display("Still alive after Finalize called");

(Code continued on the following page.)
Module 9: Memory and Resource Management 25


// Prevent the ResurrectObj object from resurrecting
// itself again,
ResObjHolder.SetResurrection(false);

// Now, let's destroy this last reference to the
// ResurrectObj
ResObjHolder = null;

// Force the GC to determine that the object is unreachable.
Collect();
WaitForFinalizers();
// You should see the Finalize code called.
Display(-1, "Demo stop: Object Resurrection.", 0);
}

26 Module 9: Memory and Resource Management


" "" " Explicit Resource Management
! The IDisposable Interface and the Dispose Method
! Temporary Resource Usage Design Pattern

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Typically, your classes should provide an explicit and an implicit way to free
resources. A class provides explicit control by having a method that a client of
the object will call when the client has finished using the object.
In this section, you will learn how to perform explicit resource management by
using the IDisposable interface and Dispose method, and how to allocate
resources for temporary use.
Topic Objective
To provide an overview of
the section topics.
Lead-in
Your classes should provide
an explicit and an implicit
way to free resources.
Module 9: Memory and Resource Management 27


The IDisposable Interface and the Dispose Method
! Inherit from the IDisposable Interface
! Implement the Dispose Method
! Follow the .NET Framework SDKs Design Pattern
class ResourceWrapper : IDisposable
{
// see code example for details
}
class ResourceWrapper : IDisposable
{
// see code example for details
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Classes that contain non-memory resources should provide explicit resource
management by inheriting from the IDisposable interface and implementing its
Dispose method. A types Dispose method should release all of the resources
that it owns. It should also release all resources owned by its base types by
calling its parent types Dispose method. The parent types Dispose method
should release all resources that it owns and in turn call its parent types
Dispose method, propagating this pattern through the hierarchy of base types.
To ensure that resources are always cleaned up appropriately, a Dispose method
should be safely callable multiple times and should never throw an exception.
A Dispose method should call the GC.SuppressFinalize method for the object
it is disposing. If the object is currently on the finalization queue,
GC.SuppressFinalize prevents its Finalize code from being called. Remember
that executing Finalize code is costly to performance. If your Dispose method
has already done the work to clean up the object, then it is not necessary for the
garbage collector to call the objects Finalize code.
The following code example from the .NET Framework Software Development
Kit (SDK) illustrates one possible design pattern for implementing a Dispose
method for classes that encapsulate unmanaged resources. You may find this
pattern convenient to use because it is implemented throughout the .NET
Framework. However, this is not the only possible implementation of a Dispose
method. The base class implements a public Dispose method that can be called
by users of the class. The Dispose method in turn calls the appropriate virtual
Dispose method, depending upon the identity of the caller. The appropriate
cleanup code for the object is executed in the virtual Dispose method. The base
class provides a destructor as a safeguard in the event that Dispose is not called,
as in the following example of the ResourceWrapper design pattern:
Topic Objective
To explain how to use the
IDisposable interface and
the Dispose method when
working with explicit
resource management.
Lead-in
Classes that contain non-
memory resources should
provide explicit resource
management by inheriting
from the IDisposable
interface and implementing
its Dispose method.
For Your Information
The IDisposable interface
and Dispose method are
important topics that the
students will need to know
to do the lab. The
ResourceWrapper design
pattern should be discussed
in detail.
28 Module 9: Memory and Resource Management


// Design pattern for a base class
public class BaseResource: IDisposable
{
// Pointer to an external resource.
private IntPtr handle;
// Pointer to a managed object resource this class uses.
private Component Components;
// To track whether Dispose has been called.
private bool disposed = false;

// Constructor for the BaseResource Object.
public BaseResource()
{
handle = // Insert code here to allocate on the
// unmanaged side.
Components = new Component();
}

// Implement IDisposable.
public void Dispose()
{
Dispose(true);
// Take yourself off of the Finalization queue.
GC.SuppressFinalize(this);
}

// The virtual Dispose(bool) method is called by:
// IDisposable.Dispose() with disposing = true
// and by Finalize/C# Destructor with disposing = false
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if(!this.disposed)
{
// If IDisposable.Dispose was called,
// dispose all managed resources.
if(disposing)
{
// Free other state (managed objects)
Components.Dispose();
}
// If Finalize or IDisposable.Dispose,
// free your own state (unmanaged objects)
this.disposed = true;
Release(handle);
handle = IntPtr.Zero;
// Set any large fields to null
}
}

// Use C# destructor syntax for finalization code.
// This destructor will run only if the Dispose method
// does not get called.
// It gives your base class the opportunity to finalize.

(Code continued on the following page.)
Module 9: Memory and Resource Management 29


~BaseResource()
{
Dispose(false);
}

// Allow your Dispose method to be called multiple times,
// but throw an exception if a method requires resources
// that were released because an object has been disposed.

public void DoSomething()
{
// This method requires resources that may have been
// disposed,
// check to see if it has been disposed.
if(this.disposed)
{
throw new ObjectDisposedException("BaseResource");
}
}
}




// Design pattern for a derived class
public class MyResourceWrapper: BaseResource
{
private bool disposed = false;

public MyResourceWrapper()
{
// Constructor for this object.
}

protected override void Dispose(bool disposing)
{
if(!this.disposed)
{
if(disposing)
{
// Release any managed resources here.
}
// Release any unmanaged resources here.
this.disposed = true;
// Call Dispose on your base class.
base.Dispose(disposing);
}
}
}

// This derived class does not have Finalize code
// or a Dispose method because it inherits them from
// the base class.

30 Module 9: Memory and Resource Management


The following summarizes the way this design pattern works. Whenever an
objects Finalize code or Dispose method is called it is always the objects base
class implementation of these methods that is invoked. In the base class both
the Finalize code and Dispose method call the virtual Dispose method, and
this causes the code in the objects actual class to be invoked. The virtual
Dispose method cleans up its class specific resources. The bool flag indicates
whether the call originated from Finalize or Dispose code. If finalization is
occurring the virtual Dispose method should not call methods in other objects
as these other objects may have already been finalized. The virtual Dispose
method then calls its parents class virtual Dispose method allowing the parent
class method to release its class specific resources. This calling of the parent
virtual Dispose methods continues until the base class virtual Dispose
method is called. This design pattern allows each class in the objects class
hierarchy to release its class specific resources.
Guidelines for Working with Explicit Resource
Management
The preceding example of the ResourceWrapper design pattern highlights
some important guidelines that you should follow when working with explicit
resource management.
! Suppress finalization of your instance after Dispose has been called:
void System.GC.SuppressFinalize(Object obj);

! Propagate the Dispose method through containment hierarchies. Dispose
should dispose of all resources that are held by an object and any object that
is contained by that object.
For example, a TextReader object holds on to a Stream object and an
Encoding object, but the clients of the TextReader object are unaware
of the Stream object and the Encoding object. You can assume that
both the Stream and the Encoding objects have acquired external
resources. When a client calls Dispose on the TextReader object, the
TextReader object should in turn call Dispose on the Stream and the
Encoding objects, thus causing them to release their external resources.
! Do not assume that the Dispose method will be called. As a precaution, you
should also release resources in the destructor.
In finalization code, or code that is called by finalization, for example,
freeState, do not propagate the Dispose or Finalize method calls
through the containment hierarchy of the object because these contained
objects may have been finalized already. For the same reason, you
should generally avoid making calls to other objects from within code
that may execute during finalization.
! Throw an ObjectDisposedException when the methods of your class that
depend upon resources that have been disposed are called.
! Consider not having your object be fully usable after calling Dispose. It is
often difficult to recreate an object that has already been disposed.
! Allow your Dispose method to be called more than once without throwing
an exception. It is a no-op after the first call.

For Your Information
Students will need to
understand the concept of
containment hierarchies and
how this relates to the
Dispose method to do the
lab. Illustrate this important
concept with an example or
two.
For Your Information
Students will need to be
able to determine which
methods require resources
that may have been
disposed to be able to do
the lab. Illustrate this
important concept with an
example or two.
Module 9: Memory and Resource Management 31


Using a Domain-Specific Name
Occasionally, a domain-specific name is more appropriate than the Dispose
method. For example, a file encapsulation may want to use the name Close. In
this case, you should implement Dispose and have the Close method call it, as
shown in the following design pattern:

You can replace Close with a method name appropriate to your domain.

// Do not make this method virtual.
// A derived class should not be allowed
// to override this method.
public void Close()
{
// Call the Dispose method with no parameters.
Dispose();
}

For Your Information
Students will need to
implement a domain-specific
name in the lab.
Note
32 Module 9: Memory and Resource Management


Demonstration: The IDisposable Interface

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This demonstration shows how to perform explicit resource management by
using the IDisposable interface.

The DisposeObj class in the demonstration does not contain any
managed resources that have Dispose methods. Therefore, it can use an
implementation of the Dispose method that is simpler than the more general
design pattern.

// This method demonstrates how to implement a type that
// allows its users to explicitly dispose/close the object.
// For many objects this paradigm is strongly encouraged.
private static void DisposeDemo() {
Display(0,
"\n\nDemo start: Disposing an object versus Finalize.", +1);
DisposeObj obj = new DisposeObj("Explicitly disposed");
// Explicitly cleanup this object, Finalize should run
obj.Dispose();
obj = null;
Collect();
// Finalize should NOT run (it was suppressed)
WaitForFinalizers();

obj = new DisposeObj("Implicitly disposed");
obj = null;
Collect();
// No explicit cleanup, Finalize SHOULD run
WaitForFinalizers();
Display(-1,
"Demo stop: Disposing an object versus Finalize.", 0);
}

Topic Objective
To demonstrate how to
perform explicit resource
management by using the
IDisposable interface.
Lead-in
This demonstration shows
how to perform explicit
resource management by
using the IDisposable
interface.
For Your Information
Use the debugger to step
through the code while you
point out features and ask
students what they think will
happen next. In this section
run the DisposeDemo
method.
Note
Module 9: Memory and Resource Management 33


Temporary Resource Usage Design Pattern
! Temporary Resource Use
# Allocate a resource, use it, and dispose of it
! Try and Finally
! Using Statement
void DoSomething() {
Resource r = new Resource(...);
try { r.Foo(); }
finally {
if (r != null) ((IDisposable)r).Dispose();
}
}
void DoSomething() {
Resource r = new Resource(...);
try { r.Foo(); }
finally {
if (r != null) ((IDisposable)r).Dispose();
}
}
using (Resource r1 = new Resource()) {
r1.Foo();
}
using (Resource r1 = new Resource()) {
r1.Foo();
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In a temporary resource use scenario, you allocate, use, and dispose of a
resource in a short period of time. The best way to ensure that the resource is
disposed of, regardless of whether an exception is thrown, is to use a try and
finally block.
In the following example, a method named DoSomething needs to temporarily
use an object of class Resource where Resource implements the IDisposable
interface according to the guidelines that were specified in the preceding topic.
void DoSomething() {
Resource r = new Resource(...); // acquire resource
try {
r.Foo(); // use resource
}
finally { // release resource
if (r != null) ((IDisposable)r).Dispose();
}
}

Topic Objective
To explain how to allocate,
use, and dispose of a
resource in a short period of
time.
Lead-in
In a temporary resource use
scenario, you allocate, use,
and dispose of a resource in
a short period of time.
34 Module 9: Memory and Resource Management


Handling Nested Try and Finally Blocks
The syntax in the preceding example becomes even more awkward when more
than one resource is used, and the try and finally blocks require nesting, as in
the following example:
Resource r1 = new Resource();
try {
Resource r2 = new Resource();
try {
r1.Foo();
r2.Foo();
}
finally {
r2.Dispose();
}
}
finally {
r1.Dispose();
}

C# provides the following using statement to simplify the syntax:
using-statement:
using ( resource-acquisition ) embedded-statement
resource-acquisition:
local-variable-declaration
expression
The using statement requires that the type of the resource acquisition be a type
that implements System.IDisposable. Local variables that are declared in a
resource acquisition are read-only and must include an initializer. In the
following example, Resource is a reference type that implements IDisposable:
using (Resource r1 = new Resource()) {
r1.Foo();
}

This statement is semantically equivalent to the following statement:
Resource r1 = new Resource();
try {
r1.Foo();
}
finally {
if (r1 != null) ((IDisposable)r1).Dispose();
}

Module 9: Memory and Resource Management 35


The following example:
using (Resource r1 = new Resource(),
Resource r2 = new Resource()) {
r1.Foo();
r2.Foo();
}

is semantically equivalent to:
using (Resource r1 = new Resource())
using (Resource r2 = new Resource()) {
r1.Foo();
r2.Foo();
}

By expansion, the preceding example is semantically equivalent to:
Resource r1 = new Resource();
try {
Resource r2 = new Resource();
try {
r1.Foo();
r2.Foo();
}
finally {
if (r2 != null) ((IDisposable)r2).Dispose();
}
}
finally {
if (r1 != null) ((IDisposable)r1).Dispose();
}

36 Module 9: Memory and Resource Management


" "" " Optimizing Garbage Collection
! Weak References
! Generations
! Additional Performance Features

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The .NET Framework common language runtime garbage collection has several
advanced features that you can use to improve the performance of your
software.
In addition to optimizing garbage collection programmatically, you can use the
Performance Monitor tool (Perfmon.exe) to tune your application and build in
multiprocessor support to enhance performance.
Topic Objective
To provide an overview of
the section topics.
Lead-in
In the .NET Framework,
garbage collection has
several advanced features
that you can use to improve
the performance of your
software.
Module 9: Memory and Resource Management 37


Weak References
! A Weak Reference Allows an Object to Be Collected If
Memory Is Low
Object obj = new Object(); // create strong reference
WeakReference wr = new WeakReference(obj);
obj = null; // remove strong reference
// ...
obj = (Object) wr.Target;
if (obj != null) {//garbage collection hasnt occurred
// ...
}
else {// object was collected, reference is null
//...
}
Object obj = new Object(); // create strong reference
WeakReference wr = new WeakReference(obj);
obj = null; // remove strong reference
// ...
obj = (Object) wr.Target;
if (obj != null) {//garbage collection hasnt occurred
// ...
}
else {// object was collected, reference is null
//...
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
A weak reference to an object allows garbage collection to collect that object if
memory in the managed heap is low. Weak references are useful in an
application that has large amounts of easily reconstructed data. If the weak
references object has not had its memory resources released by garbage
collection, then the application can avoid the cost of reconstructing the data.
Strong References vs. Weak References
In the common language runtime, the garbage collection process reclaims
inaccessible or unreachable memory that is allocated to an object. An object
becomes unreachable if all references to it become invalid. For example, if an
objects references are set to a null reference, it is unreachable.
A directly or indirectly referenced object is reachable, and garbage collection is
not permitted to reclaim it. A reference to a reachable object is called a strong
reference.
A weak reference also references a reachable object, or target. You acquire a
strong reference to the target by assigning the value of the target property to a
variable. However, if there are no strong references to the target, the target
becomes eligible for garbage collection, even though it still has a weak
reference.
Topic Objective
To introduce the use of
weak references to
conserve memory
resources.
Lead-in
A weak reference allows
garbage collection to collect
objects if memory in the
managed heap is low.
38 Module 9: Memory and Resource Management


Retrieving the Target
There may be a delay between the time when an object becomes eligible for
garbage collection and the time when it is collected. If you attempt to retrieve
the target after it has been collected, you will only retrieve a null reference. If
the target has not yet been collected, you will retrieve a valid reference.
The following example shows how to use weak references to improve
performance:
Object obj = new Object(); // create strong reference
WeakReference wr = new WeakReference(obj);
obj = null; // remove strong reference
//...

obj = (Object) wr.Target;
if (obj != null) {
// garbage collection has not occurred,
// obj valid reference
// ...
}
else
{
// object was collected, reference is null
//...
}

A WeakReference object can specify whether the reference to its target is
maintained after finalization. In this way, it can specify whether the weak
reference should track the targets resurrection.
The following table defines two types of weak references.
Type of weak reference Definition

Short weak reference Does not track resurrection
Long weak reference Tracks resurrection

Module 9: Memory and Resource Management 39


Demonstration: Weak References

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This demonstration shows how garbage collection handles weak references.

Using the debugger to single-step through the WeakRefDemo code
may prevent the garbage collection process from collecting an object that is
only referenced by a weak reference. Instead of using the debugger to single-
step through the code you should set breakpoints at the beginning and end of
the WeakRefDemo method to observe that objects with only weak references
to them are collected.

Topic Objective
To demonstrate how
garbage collection handles
weak references.
Lead-in
This demonstration shows
how garbage collection
handles weak references.
For Your Information
In this section run the
WeakRefDemo method.
Read and follow the
procedure in the Note and
avoid using the debugger to
single-step through the
WeakRefDemo code.
Important
40 Module 9: Memory and Resource Management


// This method demonstrates how weak references (WR) work. A
// WR allows the GC to collect objects if the managed heap is
// low on memory.
// WRs are useful to apps that have large amounts of easily-
// reconstructed data that they want to keep around to improve
// performance. But, if the system is low on memory,
// the objects can be destroyed and replaced when
// the app knows that it needs it again.
private static void WeakRefDemo(Boolean trackResurrection) {
Display(0, String.Format(
"\n\nDemo start: WeakReferences that {0}track !
resurrections.",
trackResurrection ? "" : "do not "), +1);

// Create an object
BaseObj obj = new BaseObj("WeakRef");

// Create a WeakReference object that refers to the new
// object
WeakReference wr =
new WeakReference(obj, trackResurrection);

// The object is still reachable, so it is not finalized.
Collect();
// The Finalize code should NOT execute
WaitForFinalizers();
obj.Display("Still exists");

// Let's remove the strong reference to the object
obj = null; // Destroy strong reference to this object

// The following line creates a strong reference to the
// object
obj = (BaseObj) wr.Target;
Display("Strong reference to object obtained: " +
(obj != null));

// Destroy strong reference to this object again.
obj = null;

// The GC considers the object to be unreachable and
// collects it.
Collect();
WaitForFinalizers(); // Finalize should run.

// This object resurrects itself when its Finalize code is
// called.
// If wr is NOT tracking resurrection, wr thinks the object
// is dead
// If wr is tracking resurrection, wr thinks the object is
// still alive

(Code continued on the following page.)
Module 9: Memory and Resource Management 41


// NOTE: If the object referred to by wr doesn't have a
// Finalize code,
// then wr would think that the object is dead regardless of
// whether wr is tracking resurrection or not.

// The following line attempts to create a strong reference
// to the object.
obj = (BaseObj) wr.Target;
Display("Strong reference to object obtained: " +
(obj != null));

if (obj != null) {
// The strong reference was obtained so this wr must be
// tracking resurrection. At this point we have a strong
// reference to an object that has been finalized but its
// memory has not yet been reclaimed by the collector.
obj.Display("See, I'm still alive");

// Destroy the strong reference to the object
obj = null;

// Collect reclaims the object's memory since this
// object has no Finalize code registered for it
// anymore.
Collect();
WaitForFinalizers(); // We should see nothing here

obj = (BaseObj) wr.Target; // This now returns null
Display("Strong reference to object obtained: " +
(obj != null));
}
// Cleanup everything about this demo so there is no effect
// on the next demo
// Destroy strong reference (if it exists)
obj = null;
wr = null; // Destroy the WeakReference object (optional)
Collect();
WaitForFinalizers();

// NOTE: You are discouraged from using the
// WeakReference.IsAlive property
// because the object may be killed immediately after
// IsAlive returns making the return value incorrect.
// If the Target property returns a non-null value,
// then the object is alive and will stay alive
// since you have a reference to it. If Target returns null,
// then the object is dead.
Display(-1, String.Format(
"Demo stop: WeakReferences that {0}track resurrections.",
trackResurrection ? "" : "do not "), 0);
}

42 Module 9: Memory and Resource Management


Generations
! To Force Garbage Collection of Generation 0 Through a
Specified Generation:
! To Determine the Generation of an Object:
! To Return the Maximum Number of Generations That
the System Currently Supports:
void System.GC.Collect(int Generation);
void System.GC.Collect(int Generation);
Int32 System.GC.GetGeneration(Object obj);
Int32 System.GC.GetGeneration(Object obj);
Int32 System.GC.MaxGeneration;
Int32 System.GC.MaxGeneration;

*****************************ILLEGAL FOR NON-TRAINER USE******************************
To improve the performance of garbage collection, the .NET Framework uses a
system based on object generations, which works as follows:
1. When a managed object is created by using the new operator and is added to
the managed heap, it becomes part of generation 0.
2. After garbage collection is invoked, any generation 0 objects that remain in
the managed heap are promoted to become members of generation 1.
3. Any generation 1 objects that remain in the managed heap are promoted to
become members of generation 2.
Because generation 2 is the highest generation that is currently supported,
generation 2 objects that remain in the managed heap remain in
generation 2.

When garbage collection is invoked to free heap space, its performance is
improved because it only compacts the section of the managed heap that
contains generation 0 objects.
Typically, the newer an object is, the shorter its lifetime will be. Therefore,
sufficient space is usually freed when generation 0 is compacted. If sufficient
space cannot be obtained when generation 0 is compacted, garbage collection
will compact the older generations.
Topic Objective
To explain how the .NET
Framework common
language runtime uses a
system of generations to
make garbage collection
more efficient.
Lead-in
To improve the performance
of garbage collection, the
.NET Framework uses a
system based on object
generations.
Module 9: Memory and Resource Management 43


To force the collection of generations from 0 through a specified generation,
you can call the .NET Framework System.GC.Collect method, as follows:
void System.GC.Collect(int Generation);

The System.GC.GetGeneration method returns the current generation of an
object as follows:
Int32 System.GC.GetGeneration(Object obj);

The System.GC.MaxGeneration property returns the maximum number of
generations that the system currently supports. It is written as follows:
Int32 System.GC.MaxGeneration;

44 Module 9: Memory and Resource Management


Demonstration: Generations

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This demonstration shows how garbage collection handles generations.
// This method demonstrates how objects are promoted between
// generations.
// Applications could take advantage of this info to improve
// performance but most applications will ignore this info.
private static void GenerationDemo() {
Display(0, "\n\nDemo start: Understanding Generations.",
+1);

// Let's see how many generations the managed heap supports
// (we know it's 2)
Display("Maximum GC generations: " + GC.MaxGeneration);

// Create a new BaseObj in the heap
GenObj obj = new GenObj("Generation");

// Since this object is newly created, it should be in
// generation 0
obj.DisplayGeneration(); // Displays 0

// Performing a GC promotes the object's generation
Collect();
obj.DisplayGeneration(); // Displays 1

Collect();
obj.DisplayGeneration(); // Displays 2

Collect();
obj.DisplayGeneration(); // Displays 2 (max generation)

(Code continued on the following page.)
Topic Objective
To demonstrate how
garbage collection handles
generations.
Lead-in
This demonstration shows
how garbage collection
handles generations.
For Your Information
Use the debugger to step
through the code while you
point out features and ask
students what they think will
happen next. In this section
run the GenerationDemo
method.
Module 9: Memory and Resource Management 45


// Destroy the strong reference to this object
obj = null;

Collect(0); // Collect objects in generation 0
WaitForFinalizers(); // We should see nothing

// Collect objects in generations 0 and 1
Collect(1);
WaitForFinalizers(); // We should see nothing

Collect(2); // Same as Collect()
// Now, we should see the Finalize code run
WaitForFinalizers();

Display(-1, "Demo stop: Understanding Generations.", 0);
}

46 Module 9: Memory and Resource Management


Additional Performance Features
! Performance Monitoring
! Large Object Heap
! Multiprocessor Support
! Unsafe Code

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In addition to programmatically manipulating garbage collection through weak
references and generations, you can use other features and techniques to
improve application performance.
Performance Monitoring
You can obtain real-time information about the memory activity of the .NET
Framework common language runtime by using performance counters.
You can view these counters by using the Performance Monitor tool
(Perfmon.exe). To execute this program, click Start, click Run, and in the text
box, type perfmon.exe.
You can also execute the program by clicking Control Panel, double-clicking
Administrative Tools, and double-clicking Performance.
To view .NET Framework common language runtime memory statistics in the
Performance window:
1. Click the System Monitor icon under the Console Root folder.
2. Click the + button in the right pane to add a counter to the System Monitor.
3. Specify the computer that you wish to monitor.
4. In the Performance object field, select .NET CLR Memory.
5. Select the desired counters and application instance.
You can obtain descriptions of the counters by clicking the Explain button.

In the future, you will also be able to read these counters by using the APIs that
will be provided by the .NET Framework class library.
Topic Objective
To introduce additional
resources for improved
application performance.
Lead-in
In addition to
programmatically
manipulating garbage
collection through weak
references and generations,
you can use other features
and techniques to improve
application performance.
Module 9: Memory and Resource Management 47


Large Object Heap
Objects that are larger than 20,000 bytes are allocated from a separate special
heap. Garbage collection handles large objects in the same way as it handles
other managed heap objects, except it does not compact the separate special
heap to avoid the additional performance cost of shifting large blocks of
memory.
Multiprocessor Support
The .NET Framework common language runtime provides two forms of
garbage collection: a server version (MSCorSvr.dll) and a workstation version
(MSCorWks.dll). The server version is multithreaded and highly scalable, while
the workstation version is single-threaded and concurrent. Both versions feature
low fragmentation, low overhead, and efficient use of cache space.
The server version of the common language runtime contains features that
improve the efficiency of resource collection. With synchronization-free
allocations and scalable collections, a multiprocessor system receives a
managed heap that is split into as many sections as there are CPUs. Each CPU
has its own thread, which enables the runtime to perform garbage collection on
different heaps simultaneously. If you have a garbage collection bottleneck, you
can improve performance by scaling your application to a multiprocessor
system.
Using Unmanaged Code to Control Garbage Collection
When you need a higher degree of control or that extra margin of performance,
C# provides the ability to write code that can deal directly with pointer types,
and fix objects to temporarily prevent garbage collection from moving them.
You must clearly mark such code in C# with the unsafe modifier. This ensures
that other developers cannot possibly use unsafe features accidentally, and that
the compiler and the common language runtime work together to ensure that
unmanaged code cannot masquerade as safe code.
You should note that unmanaged code is not verifiable by the .NET Framework
common language runtime and should only be executed when trusted. It does,
however, offer developers and users a way to optimize performance.
Further discussion of unmanaged code and memory management is beyond the
scope of this course.
48 Module 9: Memory and Resource Management


Lab 9: Memory and Resource Management

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives
After completing this lab, you will be able to:
! Create an application, with classes, that explicitly manages resources by
implementing a design pattern based on the IDisposable interface.
! Create an application that implicitly manages resources by using
destructors.

Lab Setup
Starter and solution files are associated with this lab. The starter files are in the
folder <install folder>\Labs\Lab09\Starter. The solution files for this lab are in
the folder <install folder>\Labs\Lab09\Solution.
Scenario
In this lab, you are provided with a Microsoft Visual Studio .NET console
application as a starting point. The application is named Memory and Resource
Management. It allows a user to enter text data. When the user has finished
entering data, the application outputs the number of completed sentences and
the character count, and then the text. The text is formatted so that each
sentence is displayed on a separate line without an ending period.
The application is implemented by using a SentenceFormatter object that
buffers the input text until a completed sentence is detected or until its Close
method is called.
Topic Objective
To introduce the lab.
Lead-in
In this lab, you will learn
how to use classes to create
an application that explicitly
manages resources by
implementing a design
pattern that is based on the
IDisposable interface. You
will also learn how to create
an application that implicitly
manages resources by
using destructors.
Module 9: Memory and Resource Management 49


When a completed sentence is detected or its Close method is called, the
SentenceFormatter object sends its buffered text to a SimpleTextBuffer
object. The SimpleTextBuffer object buffers this text until it is closed or
finalized. At that time, the SimpleTextBuffer object outputs its buffered text to
the console.
In this lab, the Memory and Resource Management application is used to
illustrate some key points about .NET Framework resource management. To
provide explicit and implicit resource management, you will modify this
program to follow the design pattern that is described in Module 9.
Estimated time to complete this lab: 60 minutes
50 Module 9: Memory and Resource Management


Exercise 1
Programming for Explicit Resource Management
In this exercise, you will modify the Memory and Resource Management
application to incorporate explicit resource management.
! Examine the application
1. In Visual Studio .NET, open the Memory and Resource Management
project, which is located in <install folder>\Labs\Lab09\Starter\
Memory and Resource Management.
2. Open the Memory and Resource Management.cs file and examine the code.
3. Build and run the Memory and Resource Management application.
The following text should appear in the console output:
Enter text, when finished enter an empty line

4. Enter the following text, which is followed by an empty line, as prompted:
hello world.are you out there?

Text similar to the following should appear in the console output:
SimpleTextBuffer:

hello world
are you out there?

Completed Sentences 1, Output Characters 29

hit enter to exit program


Module 9: Memory and Resource Management 51


! Add explicit resource management

For detailed explanations about and code examples for adding explicit
resource management, see The IDisposable Interface and the Dispose method
in this module.

1. Modify the SentenceFormatter and SimpleTextBuffer classes to inherit
from IDisposable.
2. For each of these classes, add a private member of type bool named
disposed to indicate whether the objects non-memory resources have been
released. Initialize the value of disposed to false.
3. For each of these classes, add a protected virtual void Dispose method that
takes a single bool parameter named disposing. If the objects disposed flag
indicates that the object has not already been disposed:
a. If the parameter disposing indicates that the object is not in finalization
mode, then propagate the Dispose/Close method call through any
containment hierarchies.

Propagating the Dispose/Close method call through a containment
hierarchy is discussed in the topic The IDisposable Interface and the
Dispose Method in Module 9.


The SentenceFormatter should send its text buffer to its contained
SimpleTextBuffer object named aSimpleTextBuffer before it calls that
SimpleTextBuffer objects Close method.

b. Free the objects internal state and set disposed to indicate that the
object has been disposed.
Multiple calls to protected virtual void Dispose should not throw an
exception.
4. For both classes, add a public void Dispose method that takes no
arguments. This method calls the protected virtual void Dispose method
created in step 3 with true as its argument followed by a call to the GC
classs method to suppress finalization.
5. For both classes, modify the Close method to call the public void Dispose
method.
6. For the SentenceFormatter and SimpleTextBuffer methods, which require
resources that are freed by their classes protected virtual void Dispose
method, add an initial check that throws an ObjectDisposedException if
those resources have been released.

Tip
Tip
Note
52 Module 9: Memory and Resource Management



When the SentenceFormatters protected virtual void Dispose method
is called, it frees its sentenceBuffer and aSimpleTextBuffer resources. The
SentenceFormatters SentenceCount property does not use either of these
resources and therefore does not need to check if these resources have been
released. The ProcessInput method uses both these resources and therefore
must check whether these resources have been released.


Explicit resource management will be investigated further in the next
exercise.


Tip
Note
Module 9: Memory and Resource Management 53


Exercise 2
Programming for Implicit Resource Management
In this exercise, you will modify the Memory and Resource Management
application to incorporate implicit resource management.
! Add implicit resource management
1. Add destructors to the SentenceFormatter and SimpleTextBuffer classes.
These destructors should free only their objects internal state by calling the
protected virtual void Dispose method with false. Add code to the
destructors to output to the console the name of the class and the fact that
the destructor was executed.

Even though you have inserted code to do implicit resource
management, the program is still explicitly managing its resources through
the following method call in the Main method:
aSentenceFormatter.Close();


2. Build and run the Memory and Resource Management application.
As in the preceding exercise, the following text should appear in the console
output:
Enter text, when finished enter an empty line

3. Enter the following text, and then enter an empty line, as prompted:
hello world.are you out there?

Text similar to the following should appear in the console output:
SimpleTextBuffer:

hello world
are you out there?

Completed Sentences 1, Output Characters 29

hit enter to exit program



With explicit resource management, the output is identical to the output
in Exercise 1. All the characters you entered are output and counted.

Note
Note
54 Module 9: Memory and Resource Management


! Test implicit resource management performance
1. In the Main method, add comments to the beginning of the following line of
code so that the Close method will not be invoked:
aSentenceFormatter.Close();

2. In the preceding step there is a statement after the code that outputs the
sentence and character count to the console. After this Console.WriteLine
statement, add code to assign the SimpleTextBuffer and
SentenceFormatter objects to null, invoke garbage collection on all
generations, and wait for pending finalizers.
3. Build and run the Memory and Resource Management application.
As in the preceding exercise, the following text should appear in the console
output:
Enter text, when finished enter an empty line

4. Enter the following text, followed by an empty line, as prompted:
hello world.are you out there?

Text similar to the following should appear in the console output:
Completed Sentences 1, Output Characters 11

SentenceFormatter destructor called
SimpleTextBuffer:

hello world
SimpleTextBuffer destructor called

hit enter to exit program



Observe the difference in the number of output characters when using
explicit and implicit resource management. Explicit resource management
outputs all the characters you entered because it propagates the Close/Dispose
method call down the containment hierarchy, thereby flushing all of the buffers.
Implicit resource management cannot safely do this propagation because of the
nondeterministic order of destructor calls. Therefore, not all of the characters
you entered are output.

Note
Module 9: Memory and Resource Management 55


Review
! Memory Management Basics
! Non-Memory Resource Management
! Implicit Resource Management
! Explicit Resource Management
! Optimizing Garbage Collection

*****************************ILLEGAL FOR NON-TRAINER USE******************************
1. Describe two common problems with manual memory management that the
.NET Frameworks automatic memory management addresses.
Failure to release memory (Memory leaks)
Accessing invalid memory (dangling pointer references)


2. Describe the .NET Framework mechanism that is used to provide implicit
resource management.
During the finalization phase of garbage collection, garbage collection
calls Finalize code (C# destructor), which allows an object to properly
clean up its resources before its memory resources are freed.


3. State why explicit resource management is desirable. Describe the interface
that a class should inherit from and the method(s) that a class should
implement to provide explicit memory management.
Implicit resource management is non-deterministic, and therefore an
application cannot know exactly when a resource will be freed. Explicit
resource management allows a client to invoke a method to
deterministically free the resource. Therefore, classes should inherit
from IDisposable and implement its Dispose method.
Also garbage collections non-deterministic ordering of calls to finalize
code makes it dangerous for classes to refer to other objects during
finalization. Therefore, explicit resource management may be the only
safe way for a class to refer to other objects when it releases resources,
as, for example, when it flushes buffers.


Topic Objective
To reinforce module
objectives by reviewing
key points.
Lead-in
The review questions cover
some of the key concepts
taught in the module.
56 Module 9: Memory and Resource Management


4. State the purpose of weak references.
Weak references are useful in applications that have large amounts of
easily reconstructed data that should be maintained to improve
performance. A weak reference allows garbage collection to collect
these objects if memory in the managed heap is low.


5. Explain how and why generations are used by garbage collection.
When garbage collection is invoked to free heap space, its performance
is improved because it only compacts the section of the managed heap
that contains generation 0 objects. Typically, the newer an object is, the
shorter its lifetime will be. Therefore, sufficient space is usually freed
when generation 0 is compacted. If sufficient space cannot be obtained
when generation 0 is compacted, garbage collection will compact the
older generations.




THIS PAGE INTENTIONALLY LEFT BLANK









Contents
Overview 1
Streams 2
Readers and Writers 5
Basic File I/O 8
Lab 10: Files 21
Review 26

Module 10: Data
Streams and Files


Information in this document, including URL and other Internet Web site references, is subject to
change without notice. Unless otherwise noted, the example companies, organizations, products,
domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious,
and no association with any real company, organization, product, domain name, e-mail address,
logo, person, place or event is intended or should be inferred. Complying with all applicable
copyright laws is the responsibility of the user. Without limiting the rights under copyright, no
part of this document may be reproduced, stored in or introduced into a retrieval system, or
transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or
otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual
property rights covering subject matter in this document. Except as expressly provided in any
written license agreement from Microsoft, the furnishing of this document does not give you any
license to these patents, trademarks, copyrights, or other intellectual property.

2001-2002 Microsoft Corporation. All rights reserved.

Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint,
Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and
Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A.
and/or other countries.

The names of actual companies and products mentioned herein may be the trademarks of their
respective owners.



Module 10: Data Streams and Files iii


Instructor Notes
After completing this module, students will be able to:
! Use Stream objects to read and write bytes to backing stores, such as
strings and files.
! Use BinaryReader and BinaryWriter objects to read and write primitive
types as binary values.
! Use StreamReader and StreamWriter objects to read and write characters
to a stream.
! Use StringReader and StringWriter objects to read and write characters to
strings.
! Use Directory and DirectoryInfo objects to create, move, and enumerate
through directories and subdirectories.
! Use FileSystemWatcher objects to monitor and react to changes in the file
system.
! Explain the key features of the Microsoft .NET Framework isolated storage
mechanism.

Materials and Preparation
This section provides the materials and preparation tasks that you need to teach
this module.
Required Materials
To teach this module, you need the Microsoft PowerPoint file 2349B_10.ppt.
Preparation Tasks
To prepare for this module, you should:
! Read all of the materials for this module.
! Complete the lab.

Presentation:
45 Minutes

Lab:
45 Minutes
iv Module 10: Data Streams and Files


Module Strategy
Use the following strategy to present this module:
! Streams
Briefly review fundamental stream operations and introduce the stream
classes that are provided by System.IO. Point out that this module discusses
synchronous operations only; asynchronous operations are beyond the scope
of this course.
Tell students that the NetworkStream class is covered in more detail in
Module 11, Internet Access, in Course 2349B, Programming with the
Microsoft .NET Framework (Microsoft Visual C#

.NET).
! Readers and Writers
Cover the commonly used reader and writer classes that are used to input
and output to streams and strings that use types other than bytes.
! Basic File I/O
Discuss in more detail the stream classes that are provided by System.IO
for manipulating files and directories.
Discuss the security issues that are associated with writing code that will be
downloaded over the Internet.

Module 10: Data Streams and Files 1


Overview
! Streams
! Readers and Writers
! Basic File I/O

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The System.IO namespace contains types that allow synchronous and
asynchronous reading from and writing to data streams and files. This module
discusses synchronous operations only, because asynchronous operations are
beyond the scope of this course.
After completing this module, you will be able to:
! Use Stream objects to read and write bytes to backing stores, such as
strings and files.
! Use BinaryReader and BinaryWriter objects to read and write primitive
types as binary values.
! Use StreamReader and StreamWriter objects to read and write characters
to a stream.
! Use StringReader and StringWriter objects to read and write characters to
strings.
! Use Directory and DirectoryInfo objects to create, move, and enumerate
through directories and subdirectories.
! Use FileSystemWatcher objects to monitor and react to changes in the file
system.
! Explain the key features of the Microsoft .NET Framework isolated
storage mechanism.

Topic Objective
To provide an overview of
the module topics and
objectives.
Lead-in
In this module, you will learn
about how to use types that
allow reading from and
writing to data streams and
files.
For Your Information
When you talk about a
particular class, you may
want to display the class
information for System.IO
from the .NET Framework
Reference section in the
.NET Framework SDK.
2 Module 10: Data Streams and Files


Streams
! A Way to Read and Write Bytes from and to a Backing Store
" Stream classes inherit from System.IO.Stream
! Fundamental Stream Operations: Read, Write, and Seek
" CanRead, CanWrite, and CanSeek properties
! Some Streams Support Buffering for Performance
" Flush method outputs and clears internal buffers
! Close Method Frees Resources
" Close method performs an implicit Flush for buffered streams
! Stream Classes Provided by the .NET Framework
" NetworkStream, BufferedStream, MemoryStream, FileStream,
CryptoStream
! Null Stream Instance Has No Backing Store

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Streams provide a way to read and write bytes from and to a backing store. A
backing store is a storage medium, such as a disk or memory.
All classes that represent streams inherit from the Stream class. The Stream
class and its subclasses provide a generic view of data sources and repositories,
and isolate the programmer from the specific details of the operating system
and underlying devices.
Fundamental Stream Operations
Streams allow you to perform three fundamental operations:
1. You can read from streams.
Reading is the transfer of data from a stream into a data structure, such as an
array of bytes.
2. You can write to streams.
Writing is the transfer of data from a data structure into a stream.
3. Streams can support seeking.
Seeking is the querying and modifying of the current position within a
stream. Seek capability depends on the kind of backing store that a stream
has. For example, network streams have no unified concept of a current
position and therefore typically do not support seeking.

Depending on the underlying data source or repository, streams may support
only some of these capabilities. An application can query a stream for its
capabilities by using the CanRead, CanWrite, and CanSeek properties.
The Read and Write methods read and write byte data. For streams that
support seeking, the Seek and SetLength methods and the Position and Length
properties can be used to query and modify the current position and length of a
stream.
Topic Objective
To introduce the functions of
the Stream class and its
subclasses.
Lead-in
Streams provide a way to
read and write bytes from
and to a backing store. A
backing store is a storage
medium, such as a disk or
memory.
Module 10: Data Streams and Files 3


Support for Buffering
Some stream implementations perform local buffering of the underlying data to
improve performance. For such streams, you can use the Flush method to clear
internal buffers and ensure that all data has been written to the underlying data
source or repository.
Calling the Close method on a stream flushes any buffered data, essentially
calling the Flush method for you. The Close method also releases operating
system resources, such as file handles, network connections, or memory that is
used for any internal buffering.
Stream Classes Provided by the .NET Framework
The .NET Framework contains several stream classes that derive from the
System.IO.Stream class. The System.Net.Sockets namespace contains the
NetworkStream class. NetworkStream provides the underlying stream of data
for network access and will be discussed in more detail in Module 11, Internet
Access, in Course 2349B, Programming with the Microsoft .NET Framework
(Microsoft Visual C#

.NET).
The System.IO namespace contains the BufferedStream, MemoryStream,
and FileStream classes, which are derived from the System.IO.Stream class.
BufferedStream Class
The BufferedStream class is used to buffer reads and writes to another stream.
A buffer is a block of bytes in memory that is used to cache data, thereby
reducing the number of calls to the operating system. Buffers thus can be used
to improve read and write performance. Another class cannot inherit from the
BufferedStream class.
MemoryStream Class
The MemoryStream class provides a way to create streams that have memory
as a backing store, instead of a disk or a network connection. The
MemoryStream class creates a stream out of an array of bytes.
FileStream Class
The FileStream class is used for reading from and writing to files. By default,
the FileStream class opens files synchronously, but it provides a constructor to
open files asynchronously.
4 Module 10: Data Streams and Files


CryptoStream Class
The CryptoStream class defines a stream that links data streams to
cryptographic transformations. The common language runtime uses a stream-
oriented design for cryptography. The core of this design is CryptoStream.
Any cryptographic objects that implement CryptoStream can be chained
together with any objects that implement Stream, so the streamed output from
one object can be fed into the input of another object. The intermediate result
(the output from the first object) does not need to be stored separately. For
further details about the CryptoStream class see the .NET Framework SDK.
Null Stream Instance
There are times when an application needs a stream that simply discards its
output and returns no input. You can obtain such a stream that has no backing
store and that will not consume any operating resources from the Stream
classs public static field named Null.
For example, you may code an application to always write its output to the
FileStream that is specified by the user. When the user does not want an output
file, the application directs its output to the Null stream. When the Write
methods of Stream are invoked on this Null stream, the call simply returns, and
no data is written. When the Read methods are invoked, the Null stream returns
zero without reading data.
Module 10: Data Streams and Files 5


Readers and Writers
! Classes That Are Derived from System.IO.Stream Take Byte Input
and Output
! Readers and Writers Take Other Types of Input and Output and
Read and Write Them to Streams or Strings
! BinaryReader and BinaryWriter Read and Write Primitive Types to
a Stream
! TextReader and TextWriter Are Abstract Classes That Implement
Read Character and Write Character Methods
! TextReader and TextWriter Derived Classes Include:
" StreamReader and StreamWriter, which read and write to a stream
" StringReader and StringWriter, which read and write to a string
and StringBuilder respectively

*****************************ILLEGAL FOR NON-TRAINER USE******************************
As discussed in Streams in this module, the Stream class is designed for byte
input and output. You can use the reader and writer classes to input and output
to streams and strings that use other types.
The following table describes some commonly used reader and writer classes.
Class Description

BinaryReader and BinaryWriter These classes read and write primitive types as
binary values in a specific encoding to and from
a stream.
TextReader and TextWriter The implementations of these classes are
designed for character input and output.
StreamReader and StreamWriter These classes are derived from the TextReader
and TextWriter classes, and read and write their
characters to a stream.
StringReader and StringWriter Theses classes also derive from the TextReader
and TextWriter classes, but read their
characters from a string and write their
characters to a StringBuilder class.

A reader or writer is attached to a stream so that the desired types can be read or
written easily.
Topic Objective
To show how reader and
writer classes are used to
input and output to streams
and strings.
Lead-in
As previously mentioned,
the Stream class is
designed for byte input and
output. You can use the
reader and writer classes to
input and output to streams
and strings using other
types.
6 Module 10: Data Streams and Files


The following example shows how to write data of type Integer to and read
from a new, empty file stream that is named Test.data. After creating the data
file in the current directory, the BinaryWriter class is used to write the integers
0 through 10 to Test.data. Then the BinaryReader class reads the file and
displays the files content to the console.
using System;
using System.IO;

class MyStream {
private const string FILE_NAME = "Test.data";
public static void Main(String[] args) {
// Create the new, empty data file.
if (File.Exists(FILE_NAME)) {
Console.WriteLine("{0} already exists!", FILE_NAME);
return;
}
FileStream fs = new FileStream(FILE_NAME,
FileMode.CreateNew);
// Create the writer for data.
BinaryWriter w = new BinaryWriter(fs);
// Write data to Test.data.
for (int i = 0; i < 11; i++) {
w.Write( (int) i);
}
w.Close();
fs.Close();
// Create the reader for data.
fs = new FileStream(FILE_NAME, FileMode.Open,
FileAccess.Read);
BinaryReader r = new BinaryReader(fs);
// Read data from Test.data.
for (int i = 0; i < 11; i++) {
Console.WriteLine(r.ReadInt32());
w.Close();
}
}
}

Module 10: Data Streams and Files 7


In the following example, the code defines a string and converts it to an array of
characters, which can then be read as desired by using the appropriate
StringReader.Read method:
using System;
using System.IO;

public class CharsFromStr
{
public static void Main(String[] args) {
// Create a string to read characters from.
String str = "Some number of characters";
// Size the array to hold all the characters of the
// string, so that they are all accessible.
char[] b = new char[24];
// Create a StringReader and attach it to the string.
StringReader sr = new StringReader(str);
// Read 13 characters from the array that holds
// the string, starting from the first array member.
sr.Read(b, 0, 13);
// Display the output.
Console.WriteLine(b);
// Close the StringReader.
sr.Close();
}
}

The preceding example produces the following output:
Some number o

System.Text.Encoding
Internally, the common language runtime represents all characters as Unicode.
However, Unicode can be inefficient when transferring characters over a
network or when persisting in a file. To improve efficiency, the .NET
Framework class library provides several types that are derived from the
System.Text.Encoding abstract base class. These classes know how to encode
and decode Unicode characters to ASCII, UTF-7, UTF-8, Unicode, and other
arbitrary code pages. When you construct a BinaryReader, BinaryWriter,
StreamReader, or StreamWriter, you can choose any of these encodings. The
default encoding is UTF-8.
8 Module 10: Data Streams and Files


# ## # Basic File I/O
! FileStream Class
! File and FileInfo Class
! Reading Text Example
! Writing Text Example
! Directory and DirectoryInfo Class
! FileSystemWatcher
! Isolated Storage

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The .NET Frameworks System.IO namespace provides a number of useful
classes for manipulating files and directories.

Default security policy for the Internet and intranets does not allow
access to files. Therefore, do not use the regular, nonisolated storage IO classes
if you are writing code that will be downloaded over the Internet. Use Isolated
Storage instead.


When a file or network stream is opened, a security check is
performed only when the stream is constructed. Therefore, be careful when
handing off these streams to less trusted code or application domains.

Topic Objective
To introduce the classes of
the System.IO namespace,
which are discussed in this
section.
Lead-in
The .NET Frameworks
System.IO namespace
provides a number of useful
classes for manipulating
files and directories.
Important
Caution
Module 10: Data Streams and Files 9


FileStream Class
! The FileStream Class Is Used for Reading from and Writing to Files
! FileStream Constructor Parameter Classes
" FileMode Open, Append, Create
" FileAccess Read, ReadWrite, Write
" FileShare None, Read, ReadWrite, Write
! Random Access to Files by Using the Seek Method
" Specified by byte offset
" Offset is relative to seek reference point: Begin, Current, End
FileStream f = new FileStream(name, FileMode.Open,
FileAccess.Read, FileShare.Read);
FileStream f = new FileStream(name, FileMode.Open,
FileAccess.Read, FileShare.Read);

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The FileStream class is used for reading from and writing to files. The
FileMode, FileAccess, and FileShare types are used as parameters in some
FileStream constructors.
FileMode Parameter
FileMode parameters control whether a file is overwritten, created, or opened,
or any combination of those operations. The following table describes constants
that are used with the FileMode parameter class.
Constant Description

Open This constant is used to open an existing file.
Append This constant is used to append to a file.
Create This constant is used to create a file if it does not exist.

FileAccess Enumeration
The FileAccess enumeration defines constants for read, write, or read/write
access to a file. This enumeration has a FlagsAttribute that allows a bitwise
combination of its member values. A FileAccess parameter is specified in many
of the constructors for File, FileInfo, and FileStream, and in other class
constructors where it is important to control the kind of access that users have
to a file.
Topic Objective
To define the FileStream
class and the types that are
used as parameters in some
FileStream constructors.
Lead-in
The FileStream class is
used for reading from and
writing to files. The
FileMode, FileAccess, and
FileShare types are used as
parameters in some
FileStream constructors.
10 Module 10: Data Streams and Files


FileShare Enumeration
The FileShare enumeration contains constants for controlling the kind of
access that other FileStreams can have to the same file. This enumeration has a
FlagsAttribute that allows a bitwise combination of its member values.
The FileShare enumeration is typically used to define whether two processes
can simultaneously read from the same file. For example, if a file is opened and
FileShare.Read is specified, other users can open the file for reading but not
for writing. FileShare.Write specifies that other users can simultaneously write
to the same file. FileShare.None declines sharing of the file.
In the following example, a FileStream constructor opens an existing file for
read access and allows other users to read the file simultaneously:
FileStream f = new FileStream(name, FileMode.Open,
FileAccess.Read, FileShare.Read);

Using the Seek Method for Random Access to Files
FileStream objects support random access to files by using the Seek method.
The Seek method allows the read/write position within the file stream to be
moved to any position within the file. The read/write position can be moved by
using byte offset reference point parameters.
The byte offset is relative to the seek reference point, as represented by the
three properties of the SeekOrigin class, which are described in the following
table.
Property Name Description

Begin The seek reference position of the beginning of a stream.
Current The seek reference position of the current position within a stream.
End The seek reference position of the end of a stream.

Module 10: Data Streams and Files 11


File and FileInfo Class
! File Is a Utility Class with Static Methods Used to:
" Create, copy, delete, move, and open files
! FileInfo Is a Utility Class with Instance Methods Used to:
" Create, copy, delete, move, and open files
" Can eliminate some security checks when reusing
an object.
! Example:
" Assign to aStream a newly created file named foo.txt in
the current directory
FileStream aStream = File.Create("foo.txt");
FileStream aStream = File.Create("foo.txt");

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The File and FileInfo classes are utility classes with methods that are primarily
used for the creation, copying, deletion, moving, and opening of files.
All methods of the File class are static and can therefore be called without
having an instance of a file. The FileInfo class contains all instance methods.
The static methods of the File class perform security checks on all methods. If
you are going to reuse an object several times, consider using the corresponding
instance method of FileInfo instead, because the security check will not always
be necessary.
For example, to create a file named Foo.txt and return a FileStream object, use
the following code:
FileStream aStream = File.Create("Foo.txt");

To create a file named Foo.txt and return a StreamWriter object, use the
following code:
StreamWriter sw = File.CreateText("Foo.txt");

To open a file named Foo.txt and return a StreamReader object, use the
following code:
StreamReader sr = File.OpenText("Foo.txt");

Topic Objective
To introduce the File and
FileInfo classes and
demonstrate how they are
used to create a new object.
Lead-in
The File and FileInfo
classes are utility classes
with methods that are
primarily used for the
creation, copying, deletion,
moving, and opening of
files.
12 Module 10: Data Streams and Files


Reading Text Example
! Read Text from a File and Output It to the Console
//...
StreamReader sr = File.OpenText(FILE_NAME);
String input;
while ((input=sr.ReadLine())!=null) {
Console.WriteLine(input);
}
Console.WriteLine (
"The end of the stream has been reached.");
sr.Close();
//...
//...
StreamReader sr = File.OpenText(FILE_NAME);
String input;
while ((input=sr.ReadLine())!=null) {
Console.WriteLine(input);
}
Console.WriteLine (
"The end of the stream has been reached.");
sr.Close();
//...

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In the following example of reading text, you read an entire file and are notified
when the end of the file is detected.
using System;
using System.IO;
public class TextFromFile {
private const string FILE_NAME = "MyFile.txt";
public static void Main(String[] args) {
if (!File.Exists(FILE_NAME)) {
Console.WriteLine("{0} does not exist!", FILE_NAME);
return;
}
StreamReader sr = File.OpenText(FILE_NAME);
String input;
while ((input=sr.ReadLine())!=null) {
Console.WriteLine(input);
}
Console.WriteLine (
"The end of the stream has been reached.");
sr.Close();
}
}

This code creates a StreamReader object that points to a file named MyFile.txt
through a call to File.OpenText. StreamReader.ReadLine returns each line as
a string. When there are no more characters to read, a message is displayed to
that effect, and the stream is closed.
Topic Objective
To provide an example of
reading.
Lead-in
In the following example,
you read an entire file and
are notified when the end of
the file is detected.
Module 10: Data Streams and Files 13


Writing Text Example
! Create a File
! Write a String, an Integer, and a Floating Point Number
! Close the File
//...
StreamWriter sw = File.CreateText("MyFile.txt");
sw.WriteLine ("This is my file");
sw.WriteLine (
"I can write ints {0} or floats {1}", 1, 4.2);
sw.Close();
//...
//...
StreamWriter sw = File.CreateText("MyFile.txt");
sw.WriteLine ("This is my file");
sw.WriteLine (
"I can write ints {0} or floats {1}", 1, 4.2);
sw.Close();
//...

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The following example creates a new text file that is named MyFile.txt, writes a
string, integer, and floating-point number to it, and finally closes the file.
using System;
using System.IO;

public class TextToFile {
private const string FILE_NAME = "MyFile.txt";
public static void Main(String[] args) {
if (File.Exists(FILE_NAME)) {
Console.WriteLine("{0} already exists!", FILE_NAME);
return;
}
StreamWriter sw = File.CreateText(FILE_NAME);
sw.WriteLine ("This is my file.");
sw.WriteLine (
"I can write ints {0} or floats {1}, and so on.",
1, 4.2);
sw.Close();
}
}

Topic Objective
To provide an example of
writing text.
Lead-in
This example creates a new
text file that is named
MyFile.txt, writes a string,
integer, and floating point
number to it, and finally
closes the file.
14 Module 10: Data Streams and Files


Directory and DirectoryInfo Class
! Directory Has Static Methods Used to:
" Create, move, and enumerate through directories
and subdirectories
! DirectoryInfo Has Instance Methods Used to:
" Create, move, and enumerate through directories
and subdirectories
" Can eliminate some security checks when reusing an object
! Example:
" Enumerating through the current directory
! Use Path Class Objects to Process Directory Strings
DirectoryInfo dir = new DirectoryInfo(".");
foreach (FileInfo f in dir.GetFiles("*.cs")) {
String name = f.FullName; }
DirectoryInfo dir = new DirectoryInfo(".");
foreach (FileInfo f in dir.GetFiles("*.cs")) {
String name = f.FullName; }

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The Directory and DirectoryInfo classes expose routines for creating, moving,
and enumerating through directories and subdirectories. All methods of the
Directory class are static and can therefore be called without having an
instance of a directory. The DirectoryInfo class contains all instance methods.
The static methods of the Directory class do a security check on all methods. If
you are going to reuse an object several times, consider using the corresponding
instance method of DirectoryInfo instead, because the security check will then
not always be necessary.
The following example shows how to use the DirectoryInfo class to create a
listing of a directory:
using System;
using System.IO;

class DirectoryLister
{
public static void Main(String[] args)
{
DirectoryInfo dir = new DirectoryInfo(".");
foreach (FileInfo f in dir.GetFiles("*.cs"))
{
String name = f.FullName;
long size = f.Length;
DateTime creationTime = f.CreationTime;
Console.WriteLine("{0,-12:N0} {1,-20:g} {2}", size,
creationTime, name);
}
}
}

Topic Objective
To explain how the
Directory and
DirectoryInfo classes are
used to create directory
listings.
Lead-in
The Directory and
DirectoryInfo classes
expose routines for creating,
moving, and enumerating
through directories and
subdirectories.
Module 10: Data Streams and Files 15


In the preceding example, the DirectoryInfo object is the current directory,
denoted by ("."). The code lists the names of all of the files in the current
directory that have a .cs extension, together with their file size and creation
time.
Assuming that there are .cs files in the \Bin subdirectory of drive C, the output
of this code appears as follows:
953 7/20/2000 10:42 AM C:\Bin\paramatt.cs
664 7/27/2000 3:11 PM C:\Bin\tst.cs
403 8/8/2000 10:25 AM C:\Bin\dirlist.cs

If you want a list of files in another directory, such as C:\, remember to use the
backslash (\) escape character, as in the following example:
"C:\\"

Or, use an @-quoted string literal in C#, as in the following example:
@"C:\"

Paths
To processes directory strings in a cross-platform manner, use the Path class.
The members of the Path class enable you to quickly and easily perform
common operations, such as determining whether a file extension is part of a
path, and combining two strings into one path name.
16 Module 10: Data Streams and Files


FileSystemWatcher
! FileSystemWatcher Is Used to Monitor a File System
! Creating a FileSystemWatcher Object
! Configure
! Begin Watching
! Catch Events
FileSystemWatcher watcher = new FileSystemWatcher();
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = args[0];
watcher.Filter = "*.txt";
watcher.NotifyFilter = NotifyFilters.FileName;
watcher.Renamed += new
RenamedEventHandler(OnRenamed);
watcher.Path = args[0];
watcher.Filter = "*.txt";
watcher.NotifyFilter = NotifyFilters.FileName;
watcher.Renamed += new
RenamedEventHandler(OnRenamed);
watcher.EnableRaisingEvents = true;
watcher.EnableRaisingEvents = true;
public static void OnRenamed(object s, RenamedEventArgs e) {
Console.WriteLine("File: {0} renamed to {1}",
e.OldFullPath, e.FullPath); }
public static void OnRenamed(object s, RenamedEventArgs e) {
Console.WriteLine("File: {0} renamed to {1}",
e.OldFullPath, e.FullPath); }

*****************************ILLEGAL FOR NON-TRAINER USE******************************
You use the FileSystemWatcher component to monitor a file system and react
when changes to it occur. By using the FileSystemWatcher component, you
can quickly and easily launch business processes when specified files or
directories are created, modified, or deleted.
For example, if a group of users is collaborating on a document that is stored in
a shared directory on a server, you can use the FileSystemWatcher component
to easily program your application to watch for changes to the shared directory.
When a change is detected, the component can run processing that notifies each
user through e-mail.
You can configure the component to watch an entire directory and its contents
or a specific file or set of files within a specific directory. To watch for changes
in all files, set the Filter property to an empty string (""). To watch a specific
file, set the Filter property to the file name. For example, to watch for changes
in the file MyDoc.txt, set the Filter property to "MyDoc.txt". You can also
watch for changes in a certain type of file. For example, to watch for changes in
text files, set the Filter property to "*.txt".

Hidden files are not ignored.

There are several types of changes you can watch for in a directory or file. For
example, you can watch for changes in Attributes, the LastWrite date and
time, or the Size of files or directories. This is done by setting the
FileSystemWatcher.NotifyFilter property to one of the NotifyFilters values.
For more information on the type of changes you can watch, see NotifyFilters
in the .NET Framework Software Development Kit (SDK).
You can watch for renaming, deletion, or creation of files or directories. For
example, to watch for renaming of text files, set the Filter property to "*.txt"
and call one of the WaitForChanged methods with the WatcherChangeTypes
value Renamed provided.
Topic Objective
To explain how the
FileSystemWatcher
component can be used to
monitor and react to
changes in a file system.
Lead-in
You use the
FileSystemWatcher
component to monitor a file
system and react when
changes to it occur.
Note
Module 10: Data Streams and Files 17


Creating a FileSystemWatcher Component
The following example creates a FileSystemWatcher component to watch the
directory that is specified at run time. The component is set to watch for
changes in LastWrite and LastAccess times, and the creation, deletion, or
renaming of text files in the directory. If a file is changed, created, or deleted,
the path to the file prints to the console. When a file is renamed, the old and
new paths print to the console.
using System;
using System.IO;

public class Watcher
{

public static void Main(string[] args)
{
// If a directory is not specified, exit program.
if(args.Length != 1)
{
// Display the proper way to call the program.
Console.WriteLine(
"Usage: Watcher.exe (directory)");
return;
}

// Create a new FileSystemWatcher
// and set its properties.
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = args[0];
/* Watch for changes in LastAccess and LastWrite
times, and the renaming of files or directories */
watcher.NotifyFilter =
NotifyFilters.LastAccess |
NotifyFilters.LastWrite |
NotifyFilters.FileName |
NotifyFilters.DirectoryName;
// Only watch text files.
watcher.Filter = "*.txt";

// Add event handlers.

// The Changed event occurs when changes are made to
// the size, system attributes, last write time, last
// access time, or security permissions in a file or
// directory in the specified Path of a
// FileSystemWatcher.
watcher.Changed += new
FileSystemEventHandler(OnChanged);

(Code continued on the following page.)
18 Module 10: Data Streams and Files


// The Created event occurs when a file or directory
// in the specified Path of a FileSystemWatcher is
// created.
watcher.Created += new
FileSystemEventHandler(OnChanged);

// The Deleted event occurs when a file or directory
// in the specified Path of a FileSystemWatcher is
// deleted.
watcher.Deleted += new
FileSystemEventHandler(OnChanged);

// The Deleted event occurs when a file or directory
// in the specified Path of a FileSystemWatcher is
// deleted.

watcher.Renamed += new
RenamedEventHandler(OnRenamed);

// Begin watching.
watcher.EnableRaisingEvents = true;

// Wait for the user to quit the program.
Console.WriteLine("Press \'q\' to quit the sample.");
while(Console.Read()!='q');
}
// Define the event handlers.
public static void OnChanged(
object source, FileSystemEventArgs e)
{
// Specify what is done when a file is changed,
// created, or deleted.
Console.WriteLine("File: " + e.FullPath + " " +
e.ChangeType);
}

public static void OnRenamed(
object source, RenamedEventArgs e)
{
// Specify what is done when a file is renamed.
Console.WriteLine("File: {0} renamed to {1}",
e.OldFullPath, e.FullPath);
}
}

Module 10: Data Streams and Files 19


Isolated Storage
! Isolated Storage Provides Standardized Ways of
Associating Applications with Saved Data
! Semi-Trusted Web Applications Require:
" Isolation of their data from other applications' data
" Safe access to a computers file system
! System.IO.IsolatedStorage Namespace Contains:
public sealed class IsolatedStorageFile : IsolatedStorage, IDisposable
public sealed class IsolatedStorageFile : IsolatedStorage, IDisposable
public class IsolatedStorageFileStream : FileStream
public class IsolatedStorageFileStream : FileStream

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Basic file I/O functionality, found in the System.IO root, provides the ability to
access, store, and manipulate data that is stored in hierarchical file systems
whose files are referenced by using unique paths. For some applications, such
as downloaded Web applications and code that may come from un-trusted
sources, the basic file system does not provide the necessary isolation and
safety. Isolated storage is a data storage mechanism that provides isolation and
safety by defining standardized ways of associating code with saved data.
Isolation
When an application stores data in a file, the file name and storage location
must be carefully chosen to minimize the possibility that the storage location
will be known to another application and, therefore, vulnerable to corruption.
Isolated storage provides the means to manage downloaded Web application
files to minimize storage conflicts.
Security Risks of Semi-Trusted Code
It is important to restrict semi-trusted codes access from a computer's file
system. Allowing code that has been downloaded and run from the Internet to
have access to I/O functions leaves a system vulnerable to viruses and
unintentional damage.
The security risks associated with file access are sometimes addressed by using
access control lists (ACLs), which restrict the access that users have to files.
However, this approach is often not feasible with Web applications because it
requires administrators to configure ACLs on all of the systems on which the
application will run.
Topic Objective
To introduce isolated
storage and its potential
uses.
Lead-in
For some applications, such
as downloaded Web
applications and code that
may come from untrusted
sources, the basic file
system does not provide the
necessary isolation and
safety.
20 Module 10: Data Streams and Files


Safety Through Isolated Storage
Administrators can use tools that are designed to manipulate isolated storage to
configure file storage space, set security policies, and delete unused data. With
isolated storage, code no longer needs to invent unique paths to specify safe
locations in the file system, while data is protected from unauthorized access.
There is no need for hard coding of information that indicates where an
applications storage area is located. With isolated storage, partially trusted
applications can store data in a manner that is controlled by the computers
security policy. Security policies rarely grant permission to access the file
system by using standard I/O mechanisms. However, by default, code that runs
from a local computer, a local network, or the Internet is granted the right to use
isolated storage. Web applications can also use isolated storage with roaming
user profiles, thereby allowing a users isolated stores to roam with their profile.
The namespace System.IO.IsolatedStorage contains the IsolatedStorageFile
and IsolatedStorageFileStream classes, which applications can use to access
the files and directory in their isolated storage area.
Further discussion of isolated storage is beyond the scope of this course.
Module 10: Data Streams and Files 21


Lab 10: Files

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives
After completing this lab, you will be able to:
! Create an application that reads and writes characters to and from files.
! Create an application that can use StringReader and StreamReader
objects to read character data from files or strings.

Lab Setup
Starter and solution files are associated with this lab. The starter files are in the
folder <install folder>\Labs\Lab10\Starter, and the solution files are in the
folder <install folder>\Labs\Lab10\Solution.
Scenario
In this lab, you are provided with a Microsoft Visual Studio .NET console
application as a starting point. The application, named Files, opens one or more
files, which are specified on the command line, and counts each files bytes,
characters, words, and lines. The results from each file and the total of all files
are displayed on the console.
The application supports an f switch to allow the output display to be
redirected to a file and a t switch to run a special test mode of operation where
input is obtained from a coded-in test string, instead of from user-specified
files. The application is based on a slightly modified version of the .NET
Framework SDK sample, Word Count.
Estimated time to complete this lab: 45 minutes
Topic Objective
To introduce the lab.
Lead-in
In this lab, you will create an
application that reads and
writes characters to and
from files, and create an
application that can use
StringReader and
StreamReader objects to
read character data from
either files or strings.
22 Module 10: Data Streams and Files


Exercise 1
Reading and Writing Files and Strings
In this exercise, you will modify the Files application to output to a specified
file.
! Examine the application
1. In Visual Studio .NET, open the Files project, which is located in
<install folder>\Labs\Lab10\Starter\Files.
2. Open the WordCount.cs file and examine the code. Pay attention to those
methods that you will be modifying: The Main method of the Application
class and the CountStats method of the WordCounter class.
3. Build the WordCount application.
4. Set the applications command line arguments by performing the following
steps:
a. If the Solution Explorer pane in the Visual Studio .NET window is not
visible, on the View menu, click Solution Explorer.
b. In the Solution Explorer pane, right-click Files to display the context-
sensitive menu, and then click Properties.
c. In the Files Property Pages dialog box, click the Configuration
Properties folder, and then click Debugging.
d. In the right pane under Start Options, set the command line arguments,
as follows:
-a -o -t -foutput.txt test.txt

5. View the WordCount.cs file, and locate the last line in Applications Main
method, which is:
return 0;

6. Right-click this line, and select Run To Cursor. You should see the
following output:
Replace this Console.WriteLine in Application's Main!
method with code as per the lab
Lines Words Chars Bytes Pathname
Replace this Console.WriteLine in WordCounter's!
CountStats method
0 0 0 0 Test String
----- ----- ----- ----- ---------------------
0 0 0 0 Total in all files
Word usage sorted alphabetically (0 unique words)
Word usage sorted by occurrence (0 unique words)

7. Stop debugging, and in the Main method code of the Application class,
locate the following line of code:
if (ap.OutputFile != null) {

If ap.OutputFile does not contain null, then it contains the name of the
output file that is specified in the f command line switch.
Module 10: Data Streams and Files 23


8. Replace the following lines Console.WriteLine call with code that:
a. Assigns fsOut to a FileStream object that creates a file with the
specified name with Write access and no file sharing.
b. Assigns sw to a StreamWriter object that is bound to the FileStream
object created in step a.
c. Redirects the consoles output to the file by associating the
StreamWriter object with the console by using the following
command:
Console.SetOut(sw);

9. Rebuild and run the application, and examine the output file that is specified
in the command line switch options.
The file should be located in the bin\Debug subdirectory. It should contain
the following text:
Lines Words Chars Bytes Pathname
Replace this Console.WriteLine in WordCounter's!
CountStats method
0 0 0 0 Test String
----- ----- ----- ----- ---------------------
0 0 0 0 Total in all files
Word usage sorted alphabetically (0 unique words)
Word usage sorted by occurrence (0 unique words)


! Add the test mode and normal mode CountStats processing
1. Locate the following lines in the CountStats method code of the
WordCounter class:
Boolean Ok = true;
numLines = numWords = numChars = numBytes = 0;
try {

If the t option has been set, the CountStats caller will have set the
pathname parameter to the empty string, which is called String.Empty.
2. Replace the following lines Console.WriteLine call with code that:
a. Declares a variable of type TextReader and names it tr.
You use the type TextReader because it is the common base type for
both StringReader and StreamReader. Polymorphism will allow code
that uses a TextReader object to be provided with either a
StringReader or StreamReader object.
b. If the pathname is empty:
i. Create a StringReader that is named sr, which is bound to a
member of WordCounter that is named testString.
ii. Assign the number of bytes in this string to numBytes.
iii. Assign sr to tr.
This assignment does an implicit cast of a StringReader to a
TextReader.
24 Module 10: Data Streams and Files


c. If the pathname is not empty:
i. Create a FileStream that is named fsIn by opening the file that is
specified in the parameter pathname with read access and shared read
access.
ii. Assign the number of bytes in fsIn to numBytes.
iii. Create a StreamReader that is named sr that is bound to this file.
iv. Assign sr to tr.
This assignment does an implicit cast of a StreamReader to a
TextReader
d. In this module, uncomment out the for loop that follows the comment:
// Process every line in the file

e. Following the for loop, add code to close the TextReader object.
3. Rebuild and run the application, and examine the output file in the
bin\Debug subdirectory. It should contain the following text:
Lines Words Chars Bytes Pathname
2 3 16 17 Test String
----- ----- ----- ----- ---------------------
2 3 16 17 Total in all files
Word usage sorted alphabetically (2 unique words)
2: "hello"
1: "world"
Word usage sorted by occurrence (2 unique words)
1: world
2: hello

4. Change the command line options that Visual Studio .NET will use to run
the application to remove the test switch. It should be set to:
-a -o -foutput.txt test.txt

Module 10: Data Streams and Files 25


5. Run the application, and examine the output file in the bin\Debug
subdirectory. It should contain the following text:
Lines Words Chars Bytes Pathname
5 16 65 73 ...\test.txt
----- ----- ----- ----- ---------------------
5 16 65 73 Total in all files
Word usage sorted alphabetically (14 unique words)
1: "aid"
1: "all"
1: "come"
1: "country"
1: "for"
1: "good"
1: "is"
1: "men"
1: "now"
1: "of"
2: "the"
1: "their"
1: "time"
2: "to"
Word usage sorted by occurrence (14 unique words)
1: aid
1: all
1: come
1: country
1: for
1: good
1: is
1: men
1: now
1: of
1: their
1: time
2: the
2: to

6. Examine the file Test.txt in the bin\Debug subdirectory, and verify that the
output is what you expected.


26 Module 10: Data Streams and Files


Review
! Streams
! Readers and Writers
! Basic File I/O

*****************************ILLEGAL FOR NON-TRAINER USE******************************
1. Name at least three types of .NET Framework streams and how they differ.
FileStream does reads and writes to a file.
MemoryStream does reads and writes to memory.
BufferedStream is used to buffer reads and writes to another stream.
NetworkStream provides the underlying stream of data for network
access.


2. Name the three basic stream operations.
Read, Write, and Seek.


3. Name the classes that are used to read and write primitive types as binary
values.
BinaryReader and BinaryWriter.


4. Name the method used to provide random access to files.
Seek.


Topic Objective
To reinforce module
objectives by reviewing
key points.
Lead-in
The review questions cover
some of the key concepts
taught in the module.
Module 10: Data Streams and Files 27


5. Name the class that you would use to monitor changes to a file system.
FileSystemWatcher.


6. Name the two important features that the .NET Frameworks isolated
storage provides for an application.
Isolation and Safety.






THIS PAGE INTENTIONALLY LEFT BLANK








Contents
Overview 1
Internet Application Scenarios 2
The WebRequest and WebResponse Model 3
Application Protocols 16
Handling Errors 25
Security 28
Best Practices 35
Lab 11: Creating a DateTime Client/Server
Application 36
Review 41
Course Evaluation 43

Module 11:
Internet Access



Information in this document, including URL and other Internet Web site references, is subject to
change without notice. Unless otherwise noted, the example companies, organizations, products,
domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious,
and no association with any real company, organization, product, domain name, e-mail address,
logo, person, place or event is intended or should be inferred. Complying with all applicable
copyright laws is the responsibility of the user. Without limiting the rights under copyright, no
part of this document may be reproduced, stored in or introduced into a retrieval system, or
transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or
otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual
property rights covering subject matter in this document. Except as expressly provided in any
written license agreement from Microsoft, the furnishing of this document does not give you any
license to these patents, trademarks, copyrights, or other intellectual property.

2001-2002 Microsoft Corporation. All rights reserved.

Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint,
Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and
Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A.
and/or other countries.

The names of actual companies and products mentioned herein may be the trademarks of their
respective owners.



Module 11: Internet Access iii


Instructor Notes
After completing this module, students will be able to:
! Use the basic request/response model to send and receive data over the
Internet.
! Use the System.Net classes to communicate with other applications by
using the HTTP, Transmission Control Protocol (TCP), User Datagram
Protocol (UDP), and Socket Internet protocols.

Materials and Preparation
This section provides the materials and preparation tasks that you need to teach
this module.
Required Materials
To teach this module, you need the Microsoft PowerPoint file 2349B_11.ppt.
Preparation Tasks
To prepare for this module, you should:
! Read all of the materials for this module.
! Complete the lab.

Presentation:
60 Minutes

Lab:
45 Minutes
iv Module 11: Internet Access


Module Strategy
Use the following strategy to present this module:
! Internet Application Scenarios
Briefly introduce examples of Internet applications that use the System.Net
classes, including server-side ASP.NET applications, peer-to-peer Microsoft
Windows Forms applications that act as servers and clients to send and
receive data, and client applications that periodically access the network for
updates.
! The WebRequest and WebResponse Model
Introduce the WebRequest and WebResponse model. Explain how the
Microsoft .NET Framework uses the Uniform Resource Identifier (URI) to
identify the desired communication protocol and Internet resource. Discuss
network streams as the means of obtaining and receiving Web data.
Explain how to use the WebRequest class to request data from a server,
invoke the request for the Internet resource, and send data through a
network stream. Discuss how the WebResponse.GetResponseStream
method serves as the means of obtaining a stream that contains response
data from a network resource.
! Application Protocols
Discuss the HTTP, TCP, and UDP protocol support that is provided in the
.NET Framework, as well as information about using the Windows Sockets
interface to implement custom protocols.
! Handling Errors
Discuss how the WebRequest and WebResponse classes can throw system
exceptions, such as InvalidArgumentException, and Web-specific
exceptions, which are instances of WebException and thrown by the
GetResponse method.
! Security
Explain how an application can provide security for sending and receiving
data over the Internet by using a Web proxy, Secure Sockets Layer (SSL)
encryption, Internet authentication, and the NET Framework code access
permissions.
! Best Practices
Briefly outline the list of recommendations that will help students use the
classes that are contained in System.Net more effectively.

Module 11: Internet Access 1


Overview
! Internet Application Scenarios
! The WebRequest and WebResponse Model
! Application Protocols
! Handling Errors
! Security
! Best Practices

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The Microsoft .NET Framework System.Net and System.Net.Sockets
namespaces provide a layered, extensible, and managed implementation of
Internet protocols that applications can use to send or receive data over the
Internet. The System.Net classes provide functionality that is similar to the
Microsoft WinInet API. These classes provide varying levels of detail, from a
generic request/response model to control over application protocols and
sockets. In particular, the System.Net classes are designed for writing
scaleable, high-performance applications.
An application can use the System.Net classes to communicate with any other
application that supports the basic Internet protocols. However, that other
application need not be a .NET application. The .NET Framework provides
alternative mechanisms for inter-application communication.
For example, .NET Framework remoting implements a generic mechanism for
.NET Framework objects to interact with one another across application
domains. In addition, the System.Web.Services namespace contains classes for
applications to build and use XML Web services that are based on the standard
Simple Object Access Protocol (SOAP).
For more information about the remoting and XML Web services approaches to
inter-application communication, see Module 12, Serialization, in Course
2349B, Programming with the Microsoft .NET Framework (Microsoft
Visual C#

.NET).
After completing this module, you will be able to:
! Use the basic request/response model to send and receive data over the
Internet.
! Use the System.Net classes to communicate with other applications by
using the Hypertext Transfer Protocol (HTTP), Transmission Control
Protocol (TCP), User Datagram Protocol (UDP), and Socket Internet
protocols.

Topic Objective
To provide an overview of
the module topics and
objectives.
Lead-in
In this module, you will learn
about the basic
request/response model that
is used to send and receive
data over the Internet, the
System.Net classes that
are used to communicate
with other applications, and
various techniques to
enhance application security
and performance.
2 Module 11: Internet Access


Internet Application Scenarios
! Server-Side ASP.NET Applications
" Obtain data from back-end sources for a browser
request
! Peer-to-Peer Applications
" Send and receive data by acting as servers and clients
! Client Applications That Periodically Access the
Network
" A robust implementation of HTTP 1.1, including:
Pipelining, chunking, authentication, pre-authentication,
encryption, proxy support, server certificate validation,
and connection management

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Several types of applications use the System.Net classes to send or receive data
over the Internet. The following three Internet application scenarios are
examples of Internet applications that use the System.Net classes:
! Server-side ASP.NET applications that request data from server resources in
response to a browser request.
The System.Net classes are designed for writing scalable, high-performance
ASP.NET middle-tier applications. The server-side ASP.NET scenario
requires a robust middle-tier networking stack that can tolerate a high load.
The System.Net classes specifically fulfill this important customer
requirement. Such features as connection management, pipelining,
Keep-alive, and asynchronous operations ensure strong support for the
middle tier.
In addition, because the System.Net classes are part of an overall
framework, integration with ASP.NET features, such as impersonation and
caching, is seamless.
! Peer-to-peer Windows Forms applications that act as servers and clients to
send and receive data.
! Client applications that periodically access the network for updates.
The System.Net classes expose a robust implementation of the HTTP
protocol. Because a large share of Internet traffic travels over the HTTP
protocol, the protocols importance as an application protocol is significant.
The System.Net classes support most of the HTTP 1.1 protocol features.
The advanced features of HTTP 1.1 include pipelining, chunking,
authentication, pre-authentication, encryption, proxy support, server
certificate validation, connection management, and HTTP extensions.

Topic Objective
To introduce Internet
application scenarios that
use the System.Net
classes.
Lead-in
Several types of
applications use the
System.Net classes to send
or receive data over the
Internet.
Module 11: Internet Access 3


# ## # The WebRequest and WebResponse Model
! Uniform Resource Identifier
! NetworkStream Class
! Creating a WebRequest
! Invoking a WebRequest
! Sending Data
! Receiving Data
! Using the WebRequest and WebResponse Model

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Internet applications can be classed broadly into two types: client applications
that request information, and server applications that respond to information
requests from clients. The classic Internet client/server application is the World
Wide Web, where people use browsers to access documents and other data that
is stored on Web servers worldwide.
Applications are not limited to playing either the client or server role; the
familiar middle-tier application server responds to requests from clients by
requesting data from another server. In this case, it is acting as both a server and
a client.
The client application makes a request by identifying the desired Internet
resource and the communication protocol that will be used to exchange the
request and response. If necessary, the client application also specifies any
additional data that is required to complete the request, such as proxy location
or authentication information. Authentication information includes such
information as user name and password. When the request is formed, it can be
sent to the server.
After the server has received the request and processed the response, the
response is returned to the client application. The response includes information
that supplements the contents of the response, such as the type of content,
which may include raw text or XML data.
Topic Objective
To introduce the topics in
the section.
Lead-in
Internet applications can be
classed broadly into two
types: client applications
that request information,
and server applications that
respond to information
requests from clients.
4 Module 11: Internet Access


The .NET Framework provides classes that can be used to implement a
request/response model to access Internet resources. The two principal classes
are the WebRequest class, which contains a request for the resource; and the
WebResponse class, which provides a container for the incoming response. In
addition, the Uri class is used to contain a Uniform Resource Identifier (URI),
which identifies the Internet resource that you are seeking. The
NetworkStream class is used to write and read the data.

A Uniform Resource Identifier URI is a compact representation of a
resource that is available to your application through the Internet. You may be
more familiar with the term, URL, which stands for Uniform Resource Locator.
URLs form a subset of the more general URI naming scheme. A URL identifies
an Internet resource that has a Web page address.

For applications that need to make simple requests for Internet resources, the
WebClient class provides common methods for uploading data to or
downloading data from an Internet server. WebClient relies on the
WebRequest class to provide access to Internet resources; therefore, the
WebClient class can use any registered pluggable protocol.
Note
Module 11: Internet Access 5


Uniform Resource Identifier
! URI Contains:
" Scheme identifier specifies protocol to be used
" Server identifier specifies DNS name or TCP address
" Path identifier specifies location on the server
" Optional query string provides additional request information
! Example: http://www.contoso.com/whatsnew.aspx?date=today
" Scheme identifier http
" Server identifier www.contoso.com
" Path identifier /whatsnew.aspx
" Query String ?date=today

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The .NET Framework uses the URI to identify the desired communication
protocol and Internet resource.
The URI consists of at least three, and possibly four, parts:
! The scheme identifier, which identifies the communications protocol that is
used by the request and response
! The server identifier, which consists of a Domain Name System (DNS) host
name or TCP address that uniquely identifies the server on the Internet
! The path identifier, which locates the requested information on the server
! An optional query string, which passes information from the client to the
server

For example, the URI http://www.contoso.com/whatsnew.aspx?date=today
consists of the scheme identifier http, the server identifier www.contoso.com,
the path identifier /whatsnew.aspx, and the query string ?date=today.
Topic Objective
To introduce the four parts
of the URI.
Lead-in
The .NET Framework uses
the URI to identify the
desired communication
protocol and Internet
resource.
6 Module 11: Internet Access


NetworkStream Class
! A NetworkStream Object Provides:
" A Way to Send and Receive All Types of Web Data
" Methods That Are Compatible with Other .NET Streams
" Processing of Data As It Arrives
! System.Text.Encoding Characters from and to Bytes
! Sequential Blocks Use StreamReader and StreamWriter
// reading ASCII stream to string
Byte[] read = new Byte[32];
int bytes = anASCIIStream1.Read(read, 0, read.Length);
string stringData = Encoding.ASCII.GetString(read);
// writing string to ASCII stream
Byte[] asciiBytes = Encoding.ASCII.GetBytes(stringData);
anASCIIStream2.Write(asciiBytes, 0, asciiBytes.Length);
// reading ASCII stream to string
Byte[] read = new Byte[32];
int bytes = anASCIIStream1.Read(read, 0, read.Length);
string stringData = Encoding.ASCII.GetString(read);
// writing string to ASCII stream
Byte[] asciiBytes = Encoding.ASCII.GetBytes(stringData);
anASCIIStream2.Write(asciiBytes, 0, asciiBytes.Length);

*****************************ILLEGAL FOR NON-TRAINER USE******************************
When resources on the Internet are obtained by using the System.Net classes, a
Stream object represents the data that is being sent and received.
Streams provide:
! A common way to send and receive Web data.
Whether the actual contents of the file are HTML, XML, or another format,
an application uses Stream.Write and Stream.Read to send and receive
byte data.
! Compatibility with streams across the .NET Framework.
Streams are used throughout the .NET Framework, which provides a rich
infrastructure for handling them. For example, by changing only the few
lines of code that initialize the stream, you can modify an application that
reads XML data from a file stream to read data from a network stream
instead.
The major differences between the NetworkStream class and other streams
are that the NetworkStream class is not seekable, the CanSeek property
always returns false, and the Seek and Position methods throw a
NotSupportedException.
! Processing of data as it arrives.
Streams provide access to data as it arrives from the Internet, rather than
forcing your application to wait for an entire data set to be downloaded.

Topic Objective
To explain the function of
Network streams in the
.NET Framework.
Lead-in
When resources on the
Internet are obtained by
using the System.Net
classes, the data that is
being sent and received is
represented through a
Stream object.
Module 11: Internet Access 7


Conversion Between Characters and Bytes
The System.Text namespace contains classes for converting blocks of
characters to and from blocks of bytes. In particular, the Encoding class has
methods to convert arrays and strings of Unicode characters to and from arrays
of bytes, as in the following example:
// variable named anASCIIStream1 of type Stream
// has been previously assigned to a Stream
// containing bytes representing 7 bit ASCII character
//
// reading ASCII stream and converting to string
Byte[] read = new Byte[32];
int bytes = anASCIIStream1.Read(read, 0, read.Length);
string stringData = Encoding.ASCII.GetString(read);
// ...
// variable named anASCIIStream2 of type Stream
// has been previously assigned to a writable Stream
//
// converting string and writing 7 bit ASCII characters
Byte[] asciiBytes =
Encoding.ASCII.GetBytes(stringData);
anASCIIStream2.Write(asciiBytes, 0, asciiBytes.Length);

Converting Data from Sequential Blocks
When the data that must be converted is only available in sequential blocks,
such as data that is read from a long stream, an application may choose to use a
decoder or an encoder to perform the conversion. However, you can use the
StreamReader and StreamWriter classes to facilitate decoding and encoding
characters, as in the following example:
// variable anASCIIStream has been previously assigned
// to a Stream of ASCII bytes
StreamReader sr = new StreamReader(
anASCIIStream,Encoding.ASCII);

int length = 1024;
char[] Buffer = new char[1024];
int bytesread = 0;

//Read from the stream and write data to console
bytesread = sr.Read( Buffer, 0, length);
while( bytesread > 0 ) {
Console.Write( Buffer,0, bytesread);
bytesread = sr.Read( Buffer, 0, length);
}

//Close the stream when finished
sr.Close();

8 Module 11: Internet Access


Creating a WebRequest
! The WebRequest Encapsulates Details of Request
" Created by calling WebRequest.Create method
" Set any property values that are required
" Cast to access protocol-specific features
WebRequest req =
WebRequest.Create("http://www.contoso.com/");
WebRequest req =
WebRequest.Create("http://www.contoso.com/");
HttpWebRequest httpReq = (HttpWebRequest)
WebRequest.Create("http://www.contoso.com/");
// Turn off connection keep-alives.
httpReq.KeepAlive = false;
HttpWebRequest httpReq = (HttpWebRequest)
WebRequest.Create("http://www.contoso.com/");
// Turn off connection keep-alives.
httpReq.KeepAlive = false;
req.Credentials = new
NetworkCredential("username","password");
req.Credentials = new
NetworkCredential("username","password");

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Client applications request data from servers by using the WebRequest class
and its descendents. The WebRequest class encapsulates the details of the
process of connecting to the server, sending the request, and receiving the
response.
Calling WebRequest.Create
Applications create WebRequest instances through the static
WebRequest.Create method. WebRequest.Create is a static method that
creates a descendent WebRequest instance that is based on the URI scheme
that is passed.
For example, the following code creates an HTTP request to
www.contoso.com:
WebRequest req =
WebRequest.Create("http://www.contoso.com/");

Setting Required Property Values
Clients set required property values in the WebRequest instance. For example,
to support authentication, you can set the Credentials property to an instance of
the NetworkCredential class, as shown in the following code:
req.Credentials = new
NetworkCredential("username","password");

Topic Objective
To describe how client
applications use the
WebRequest.
Lead-in
Client applications request
data from servers by using
the WebRequest class and
its descendents.
Module 11: Internet Access 9


Casting WebRequest Objects to HttpWebRequest
To handle HTTP protocol requests to the Internet, the .NET Framework
provides an HttpWebRequest class that is derived from the WebRequest
class. In most cases, the WebRequest class provides all of the properties that
you need to make a request. However, if you need to access the HTTP
protocol-specific properties of the request, you can typecast WebRequest
objects that are created by the WebRequest.Create to HttpWebRequest, as in
the following example:
HttpWebRequest httpReq = (HttpWebRequest)
WebRequest.Create("http://www.contoso.com/");

// Turn off connection keep-alives.
httpReq.KeepAlive = false;

Supporting Additional Protocols
The .NET Framework provides protocol-specific WebRequest and
WebResponse descendants for URIs that begin with http:, https:, and file:.
The programmable pluggable protocols of System.Net allow applications to
provide access through other protocols. To support additional protocols, you
should implement protocol-specific descendants of WebRequest and
WebResponse and register the descendants constructor with the
WebRequest.RegisterPrefix method.
For more information about supporting additional protocols, see the .NET
Framework Software Development Kit (SDK) documentation.
10 Module 11: Internet Access


Invoking a WebRequest
! Request Is Made by Calling the GetResponse Method
" Cast to access HTTP-specific features
WebResponse resp = req.GetResponse();
WebResponse resp = req.GetResponse();
HttpWebResponse httpResp =
(HttpWebResponse)httpReq.GetResponse();
//Get the HTTP content length returned by the server.
String contentLength =
httpResp.ContentLength.ToString();
HttpWebResponse httpResp =
(HttpWebResponse)httpReq.GetResponse();
//Get the HTTP content length returned by the server.
String contentLength =
httpResp.ContentLength.ToString();

*****************************ILLEGAL FOR NON-TRAINER USE******************************
After creating the WebRequest, you invoke the request for the Internet
resource by calling the GetResponse method on the WebRequest instance.
The GetResponse method is responsible for:
! Constructing the protocol-specific request from the properties of the
WebRequest instance.
! Making the TCP or UDP socket connection to the server.
! Sending the request.

The GetResponse method returns an instance that is derived from
WebResponse that matches the instance that is derived from WebRequest, as
in the following example:
WebResponse resp = req.GetResponse();

The WebResponse class is also an abstract class that defines properties and
methods that are available to all applications that use pluggable protocols.
WebResponse descendents are responsible for implementing these properties
and methods for the underlying protocol.
For example, the HttpWebResponse class implements the WebResponse class
for the HTTP protocol. If you need to access the HTTP protocol-specific
properties of the response, you can typecast WebResponse objects to
HttpWebResponse, as in the following example:
HttpWebResponse httpResp =
(HttpWebResponse)httpReq.GetResponse();

//Get the HTTP content length returned by the server.
String contentLength = httpResp.ContentLength.ToString();

Topic Objective
To explain how to invoke the
request for an Internet
resource by calling the
GetResponse method on
the WebRequest instance.
Lead-in
After creating the
WebRequest, you invoke
the request for the Internet
resource by calling the
GetResponse method on
the WebRequest instance.
Module 11: Internet Access 11


Sending Data
! For Requests That Send Data to the Server
// ...
try
{
byte[] sendData =
Encoding.ASCII.GetBytes("some data");
int sendLength = sendData.Length;
HttpWebRequest httpReq =
(HttpWebRequest) WebRequest.Create(
"http://www.contoso.com/");
httpReq.Method = "POST";
httpReq.ContentLength = sendLength;
Stream sendStream = httpReq.GetRequestStream();
sendStream.Write(sendData,0,sendLength);
sendStream.Close();
}
catch(Exception e) {//...} //...
// ...
try
{
byte[] sendData =
Encoding.ASCII.GetBytes("some data");
int sendLength = sendData.Length;
HttpWebRequest httpReq =
(HttpWebRequest) WebRequest.Create(
"http://www.contoso.com/");
httpReq.Method = "POST";
httpReq.ContentLength = sendLength;
Stream sendStream = httpReq.GetRequestStream();
sendStream.Write(sendData,0,sendLength);
sendStream.Close();
}
catch(Exception e) {//...} //...

*****************************ILLEGAL FOR NON-TRAINER USE******************************
For requests that send data to the server, such as HTTP-POST or FTP-PUT
requests, the data is sent through a network stream that is provided by the
WebRequest.GetRequestStream method. You use the resulting Stream
object to write the data. When you have finished uploading, you must close the
request stream with the Stream.Close method.
The following example shows how to create a request that uses HTTP-POST to
send data to the server:
// ...
try
{
byte[] sendData = Encoding.ASCII.GetBytes("some data");
int sendLength = sendData.Length;
HttpWebRequest httpReq =
(HttpWebRequest) WebRequest.Create(
"http://www.contoso.com/");
httpReq.Method = "POST";
httpReq.ContentLength = sendLength;
Stream sendStream = httpReq.GetRequestStream();
sendStream.Write(sendData,0,sendLength);
sendStream.Close();
}
catch(Exception e)
{//...
}
//...

After closing the stream, you can call GetResponse to ensure that the server
received the data correctly.
Topic Objective
To describe how requests
that send data to the server
are created.
Lead-in
For requests that send data
to the server, such as
HTTP-POST or FTP-PUT
requests, the data is sent
through a network stream
that is provided by the
WebRequest.GetRequest
Stream method.
12 Module 11: Internet Access


Receiving Data
! Reading Response Data
// Get the response stream.
Stream respstrm = resp.GetResponseStream();
// Create a buffer to hold the response data.
int BufferSize = 512;
Byte[] Buffer = new Byte[BufferSize];
// Read the stream to access the data.
int bytesRead = respstrm.Read(Buffer, 0, BufferSize);
while (bytesRead > 0) {
Console.Write(
Encoding.ASCII.GetString(Buffer, 0, bytesRead));
bytesRead = respstrm.Read(Buffer, 0, BufferSize);
}
respstrm.Close();
// Get the response stream.
Stream respstrm = resp.GetResponseStream();
// Create a buffer to hold the response data.
int BufferSize = 512;
Byte[] Buffer = new Byte[BufferSize];
// Read the stream to access the data.
int bytesRead = respstrm.Read(Buffer, 0, BufferSize);
while (bytesRead > 0) {
Console.Write(
Encoding.ASCII.GetString(Buffer, 0, bytesRead));
bytesRead = respstrm.Read(Buffer, 0, BufferSize);
}
respstrm.Close();

*****************************ILLEGAL FOR NON-TRAINER USE******************************
To obtain the stream that contains response data from the network resource, use
the GetResponseStream method of the WebResponse instance, as in the
following example:
// Get the response stream.
Stream respstrm = resp.GetResponseStream();
// Create a buffer to hold the response data.
int BufferSize = 512;
Byte[] Buffer = new Byte[BufferSize];
// Read the stream to access the data.
int bytesRead = respstrm.Read(Buffer, 0, BufferSize);
while (bytesRead > 0) {
Console.Write(
Encoding.ASCII.GetString(Buffer, 0, bytesRead));
bytesRead = respstrm.Read(Buffer, 0, BufferSize);
}
respstrm.Close();

If your application requires only the header information that is returned in the
WebResponse and ignores any returned data, then you do not need to get the
response stream.
Closing Responses
After reading the data from the response, you must close any opened stream by
using the Stream.Close method or close the response by using the
WebResponse.Close method, as in the following example:
resp.Close();

Topic Objective
To explain how to obtain the
stream that contains
response data from the
network.
Lead-in
To obtain the stream that
contains response data from
the network resource, use
the GetResponseStream
method of the
WebResponse instance.
Module 11: Internet Access 13


You do not have to call the Close method on both the response stream and the
WebResponse instance, but it is recommended. WebResponse.Close calls
Stream.Close when it closes the response. If you do not close each response,
your application will run out of connections to the server and be unable to
process additional requests.
Considerations for Working with NetworkStreams
When using streams from network resources, remember the following:
! Because the NetworkStream class cannot change position in the stream, the
CanSeek property always returns false. The Seek and Position methods
throw a NotSupportedException.
! When you use WebRequest and WebResponse, stream instances that are
created by calling GetResponseStream are read-only, and stream instances
that are created by calling GetRequestStream are write-only.
! The call to GetResponse may block if network resources are not available.
You should consider using an asynchronous request with the
BeginGetResponse and EndGetResponse methods.
! The call to GetRequestStream may block while the connection to the
server is created.
You should consider using an asynchronous request for the stream with the
BeginGetRequestStream and EndGetRequestStream methods.
Asynchronous operations are beyond the scope of this course.
! You can use the StreamReader class to make the task of encoding easier.
The following code example uses a StreamReader to read an ASCII-
encoded stream from a WebResponse instance. The creation of the request
is not shown.
// Create a response object.
WebResponse response = request.GetResponse();
// Get a readable stream from the server.
StreamReader sr =
new StreamReader(
response.GetResponseStream(), Encoding.ASCII);
// Use the stream. Remember when you are through
// with the stream to close it
//...
sr.Close();


14 Module 11: Internet Access


Using the WebRequest and WebResponse Model
//...
WebRequest wReq = WebRequest.Create
("http://localhost/postinfo.html");
WebResponse wResp = wReq.GetResponse();
// Get a readable stream from the server
StreamReader sr = new StreamReader(
wResp.GetResponseStream(),Encoding.ASCII);
int length = 1024;
char[] Buffer = new char[1024];
int bytesread = 0;
//Read from the stream and write data to console
bytesread = sr.Read( Buffer, 0, length);
while( bytesread > 0 ) {
Console.Write( Buffer,0, bytesread);
bytesread = sr.Read( Buffer, 0, length);
}
//Close the stream when finished
sr.Close();
//...
//...
WebRequest wReq = WebRequest.Create
("http://localhost/postinfo.html");
WebResponse wResp = wReq.GetResponse();
// Get a readable stream from the server
StreamReader sr = new StreamReader(
wResp.GetResponseStream(),Encoding.ASCII);
int length = 1024;
char[] Buffer = new char[1024];
int bytesread = 0;
//Read from the stream and write data to console
bytesread = sr.Read( Buffer, 0, length);
while( bytesread > 0 ) {
Console.Write( Buffer,0, bytesread);
bytesread = sr.Read( Buffer, 0, length);
}
//Close the stream when finished
sr.Close();
//...

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The following code example fully demonstrates the use of the WebRequest
and WebResponse model as discussed in this section:
using System;
using System.Net;
using System.IO;
using System.Text;

class App
{
public static void Main(string[] args)
{
try
{
WebRequest wReq = WebRequest.Create
("http://localhost/postinfo.html");
WebResponse wResp = wReq.GetResponse();

// Get a readable stream from the server
StreamReader sr = new StreamReader(
wResp.GetResponseStream(),Encoding.ASCII);

int length = 1024;
char[] Buffer = new char[1024];
int bytesread = 0;

(Code continued on the following page.)
Topic Objective
To provide an example of
the WebRequest and
WebResponse model.
Lead-in
You will now see an
example of the use of the
WebRequest and
WebResponse model as
discussed in this section.
Module 11: Internet Access 15


//Read from the stream and write data to console
bytesread = sr.Read( Buffer, 0, length);
while( bytesread > 0 )
{
Console.Write( Buffer,0, bytesread);
bytesread = sr.Read( Buffer, 0, length);
}

//Close the stream when finished
sr.Close();
}

catch(Exception e)
{
Console.WriteLine(
"\r\nThe request URI could not be found or was!
malformed:\n {0}",
e.ToString());
}
}
}

16 Module 11: Internet Access


# ## # Application Protocols
! HTTP
! Internet Domain Name System
! TCP and UDP
! Sockets

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The .NET Framework supports application protocols that are currently in
common use on the Internet.
This section provides information about using the HTTP, TCP, and UDP
protocol support that is provided in the .NET Framework, in addition to
information about using the Windows Sockets interface to implement custom
protocols.
Topic Objective
To introduce the topics in
the section.
Lead-in
The .NET Framework
supports application
protocols that are currently
in common use on the
Internet.
Module 11: Internet Access 17


HTTP
! Classes That Provide HTTP and HTTPS Protocols
" HttpWebRequest and HttpWebResponse
! Support Most HTTP 1.1 features
! HTTP Redirects Automatically if AllowAutoRedirect
Property Is true (the Default)
! Use ServicePoint, ServicePointManager, and
ConnectionGroupName Classes to Manage Connections

*****************************ILLEGAL FOR NON-TRAINER USE******************************
With the HttpWebRequest and HttpWebResponse classes, the .NET
Framework provides comprehensive support for the HTTP protocol, which
makes up the majority of all Internet traffic.
Classes for the HTTP and HTTPS Protocols
The HttpWebRequest and HttpWebResponse classes are derived from the
WebRequest and WebResponse classes, as was described in Creating a
WebRequest in this module. HttpWebRequest and HttpWebResponse are
returned by default whenever the static method WebRequest.Create
encounters a URI that begins with http or https.
In most cases, the WebRequest and WebResponse classes provide all that is
necessary to make the request, but if you need access to the HTTP-specific
features that are exposed as properties, you can typecast these classes to
HttpWebRequest or HttpWebResponse, as demonstrated in Creating a
WebRequest in this module.

Do not use the HttpWebRequest constructor. Use the
WebRequest.Create method to initialize new HttpWebRequest instances. If
the scheme for the URI is http:// or https://, Create returns an
HttpWebRequest instance.

Support for HTTP 1.1 Features
The HttpWebRequest and HttpWebResponse classes encapsulate a standard
HTTP request/response transaction and provide access to common HTTP
headers. These classes also support most HTTP 1.1 features, including
pipelining, chunking, authentication, pre-authentication, encryption, proxy
support, server certificate validation, and connection management. Custom
headers and headers that are not provided through properties can be stored in
and accessed through the Headers property.
Topic Objective
To describe the support that
the .NET Framework
provides for the HTTP
protocol.
Lead-in
With the HttpWebRequest
and HttpWebResponse
classes, the .NET
Framework provides
comprehensive support for
the HTTP protocol, which
makes up the majority of all
Internet traffic.
Note
18 Module 11: Internet Access


Support for HTTP Redirects
You can make your application follow HTTP redirects automatically by setting
the AllowAutoRedirect property to true, which is the default value. The
application redirects requests, and the ResponseURI property of
HttpWebResponse contains the actual Internet resource that responds to the
request. If you set AllowAutoRedirect to false, your application must be able
to handle redirects as HTTP protocol errors.
Applications receive HTTP protocol errors by catching a WebException with
the value of the Status property set to WebExceptionStatus.ProtocolError.
The Response property contains the WebResponse that is sent by the server
and indicates the actual HTTP error that is encountered.
Managing Internet Connections
Applications that use HTTP to connect to data resources can use the .NET
Framework ServicePoint and ServicePointManager classes to manage the
number of connections to the Internet and to optimize scale and performance.
The number of connections between a client and server can have a dramatic
effect on application throughput.
You can use ConnectionGroupName to form connection grouping that
associates specific requests within a single application to a defined connection
pool. You may have to use this technique with a middle-tier application that
connects to a back-end server on behalf of a user and uses an authentication
protocol that supports delegation, such as Kerberos, or by a middle-tier
application that supplies its own credentials.
For more information about classes that are used to manage connections and
their methods, see the .NET Framework SDK documentation.
Module 11: Internet Access 19


Internet Domain Name System
! Dns Class Retrieves Data About a Host from DNS
" GetHostByName query for www.contoso.com
" Resolve query for www.contoso.com
IPHostEntry hostInfo =
Dns.GetHostByName("www.contoso.com");
IPHostEntry hostInfo =
Dns.GetHostByName("www.contoso.com");
IPHostEntry hostInfo =
Dns.Resolve("www.contoso.com");
IPHostEntry hostInfo =
Dns.Resolve("www.contoso.com");

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The Dns class is a static class that retrieves information about a specific host
from the Internet Domain Name System (DNS).
The host information from a GetHostByName query is returned in an instance
of the IPHostEntry class. If the specified host has more than one entry in the
DNS database, IPHostEntry contains multiple Internet Protocol (IP) addresses
and aliases.
The following example queries the DNS database for information about a host
that is called www.contoso.com:
IPHostEntry hostInfo = Dns.GetHostByName("www.contoso.com");

The Resolve method queries a DNS server for the IP address that is associated
with a host name, such as www.contoso.com, or for an IP address in
dotted-quad notation, such as 192.168.1.2.
When the host name is a DNS-style host name that is associated with multiple
IP addresses, only the first IP address that resolves to that host name is returned,
as in the following example:
IPHostEntry hostInfo = Dns.Resolve("www.contoso.com");

Topic Objective
To explain how to use the
Dns class to retrieve
information about a host.
Lead-in
The Dns class is a static
class that retrieves
information about a specific
host from the Internet
Domain Name System.
20 Module 11: Internet Access


TCP and UDP
! TCP Client Connecting to a Server/Listener
! TCP Server/Listener Monitors Port for Clients
TcpClient tcpc = new TcpClient(serverURI, 14);
Stream s = tcpc.GetStream();
Byte[] read = new Byte[32];
int bytes = s.Read(read, 0, read.Length);
String strInData = Encoding.ASCII.GetString(read);
TcpClient tcpc = new TcpClient(serverURI, 14);
Stream s = tcpc.GetStream();
Byte[] read = new Byte[32];
int bytes = s.Read(read, 0, read.Length);
String strInData = Encoding.ASCII.GetString(read);
TcpListener tcpl = new TcpListener(14);
tcpl.Start();
while (!done) {
// Accept will block until someone connects
Socket s = tcpl.AcceptSocket();
//Code to handle request goes here
}
tcpl.Stop();
TcpListener tcpl = new TcpListener(14);
tcpl.Start();
while (!done) {
// Accept will block until someone connects
Socket s = tcpl.AcceptSocket();
//Code to handle request goes here
}
tcpl.Stop();

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Applications can use TCP and UDP services with the TcpClient, TcpListener,
and UdpClient classes. These classes, which are built on top of the Socket
class, represent the data that is sent and received from the network as streams.
These classes also take care of the details of creating a connection.
Using the TcpClient Class
You use the TcpClient class to request data from an Internet resource that uses
TCP. The methods and properties of TcpClient abstract the details for creating
a Socket instance that requests and receives data through TCP. The connection
to the Internet is represented as a stream; therefore data can be read and written
in a standard manner.
The following code demonstrates how to set up a TcpClient object to connect
to a server on TCP port 14:
TcpClient tcpc = new TcpClient(serverURI, 14);
Stream s = tcpc.GetStream();
Byte[] read = new Byte[32];
int bytes = s.Read(read, 0, read.Length);
String strInData = Encoding.ASCII.GetString(read);

Topic Objective
To explain how to use the
TcpClient and TcpListener
classes to request data from
an Internet resource and to
monitor TCP ports.
Lead-in
Applications can use TCP
and UDP services with the
TcpClient, TcpListener,
and UdpClient classes.
Module 11: Internet Access 21


Using the TcpListener Class
You use a TcpListener object to monitor a TCP port for incoming requests and
then create a Socket instance that manages the connection to the client. The
Start method enables listening, and the Stop method disables listening on the
port. The AcceptSocket method accepts incoming connection requests and
creates the socket that will handle the request.
The following code demonstrates how to set up a TcpListener object to
monitor TCP port 14:
TcpListener tcpl = new TcpListener(14); // listen on port 14
tcpl.Start();
while (!done) {
// Accept will block until someone connects
Socket s = tcpl.AcceptSocket();
//Code to handle request goes here
}
tcpl.Stop();

22 Module 11: Internet Access


Sockets
! System.Net Classes Are Built on System.Net.Sockets
! System.Net.Sockets Are Based on the WinSock32 API
! See Example in Student Notes

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The System.Net.Sockets namespace contains an implementation of the
Windows Sockets interface. All other network access classes in the
System.Net namespace are built on top of this implementation of sockets.
The .NET Frameworks Socket class is a managed code version of the socket
services that are provided by the WinSock32 API. If you are familiar with the
Winsock API, you should be comfortable using the Socket class to develop
applications. In most cases, the Socket class methods simply marshal data into
their native Microsoft Win32 counterparts and handle necessary security
checks.
Topic Objective
To describe the Socket
class and provide an
example of how the Socket
class can be used to send
data to an HTTP server and
to receive the response.
Lead-in
The System.Net.Sockets
namespace contains an
implementation of the
Windows Sockets
interface.
Module 11: Internet Access 23


The following example shows how the Socket class can be used to send data to
an HTTP server and receive the response:
using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;

class App
{
public static void Main(string[] args)
{
DoSocketGet("localhost");
}
public static string DoSocketGet(string server)
{
//Set up variables and String to write to the server
Encoding ASCII = Encoding.ASCII;
string Get = "GET / HTTP/1.1\r\nHost: " + server +
"\r\nConnection: Close\r\n\r\n";
Byte[] ByteGet = ASCII.GetBytes(Get);
Byte[] RecvBytes = new Byte[256];
String strRetPage = "";

// IPHostEntry and IPEndPoint
// represent the endpoint that
// will receive the request
IPHostEntry hostEntry = Dns.Resolve(server);
IPEndPoint EPhost = new
IPEndPoint(hostEntry.AddressList[0], 80);

//Create the Socket for sending data over TCP
Socket mySocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp );
// Connect to host using IPEndPoint
try
{
mySocket.Connect(EPhost);
// Sent the GET text to the host
mySocket.Send(ByteGet, ByteGet.Length, 0);

// Receive the page, loop until all received
Int32 bytes = mySocket.Receive(RecvBytes,
RecvBytes.Length, 0);
strRetPage = "Default HTML page on " + server +
":\r\n";
strRetPage = strRetPage + ASCII.GetString(RecvBytes,
0, bytes);

(Code continued on the following page.)
24 Module 11: Internet Access


while (bytes > 0)
{
bytes = mySocket.Receive(RecvBytes,
RecvBytes.Length, 0);
strRetPage = strRetPage +
ASCII.GetString(RecvBytes, 0,
bytes);
}
}
catch (Exception e)
{
Console.WriteLine("Exception {0}",e.ToString());
}
return strRetPage;
}
}

Module 11: Internet Access 25


Handling Errors
! A WebException Can Be Thrown by GetResponse
" Has Status property, value from WebExceptionStatus
" If WebExceptionStatus.ProtocolError, then WebResponse
contains protocol error information
try
{ // ... Create a request, get response and process stream. }
catch (WebException webExcp)
{
Console.WriteLine("A WebException has been caught");
Console.WriteLine(webExcp.ToString());
WebExceptionStatus status = webExcp.Status;
if (status == WebExceptionStatus.ProtocolError) {
Console.Write(
"The server returned protocol error ");
Console.WriteLine(webExcp.Response.ToString());
}
}
catch (Exception e)
{// Code to catch other exceptions goes here.}
try
{ // ... Create a request, get response and process stream. }
catch (WebException webExcp)
{
Console.WriteLine("A WebException has been caught");
Console.WriteLine(webExcp.ToString());
WebExceptionStatus status = webExcp.Status;
if (status == WebExceptionStatus.ProtocolError) {
Console.Write(
"The server returned protocol error ");
Console.WriteLine(webExcp.Response.ToString());
}
}
catch (Exception e)
{// Code to catch other exceptions goes here.}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The WebRequest and WebResponse classes throw system exceptions, such as
InvalidArgumentException, and Web-specific exceptions, which are instances
of WebException and are thrown by the GetResponse method.
Each WebException includes a Status property that contains a value from the
WebExceptionStatus class. You can examine the Status property to determine
the particular error that has occurred and take the proper steps to resolve the
error.
The following table describes some of the possible values for the Status
property. For a complete list of values, see the .NET Framework SDK
documentation.
Value Description

ConnectFailure The remote service could not be contacted at the
transport level.
ConnectionClosed The connection was closed prematurely.
KeepAliveFailure The server closed a connection made with the Keep-alive
header set.
NameResolutionFailure The name service could not resolve the host name.
ProtocolError The response received from the server was complete but
indicated an error at the protocol level.
ReceiveFailure A complete response was not received from the remote
server.
RequestCanceled The request was canceled.
SecureChannelFailure An error occurred in a secure channel link.
SendFailure A complete request could not be sent to the remote
server.
Topic Objective
To explain how the Status
property is used to
determine if an error has
occurred.
Lead-in
The WebRequest and
WebResponse classes
throw system exceptions
and Web-specific
exceptions, which are
instances of WebException
and thrown by the
GetResponse method.
26 Module 11: Internet Access


(continued)
Value Description

ServerProtocolViolation The server response was not a valid HTTP response.
Success No error was encountered.
Timeout No response was received within the time-out set for the
request.
TrustFailure A server certificate could not be validated.

When the Status property is WebExceptionStatus.ProtocolError, a
WebResponse that contains the response from the server is available. You can
examine this response to determine the actual source of the protocol error.
The following example shows how to catch a WebException; the invalid URL
argument in the WebRequest.Create call will throw an exception:
using System;
using System.Net;
using System.IO;
using System.Text;

class App
{
public static void Main(string[] args)
{
try
{
// Create a request instance
// Note invalid URL will throw exception
WebRequest myRequest =
WebRequest.Create("http://localhost_bad_URL");

// Get the response.
WebResponse myResponse = myRequest.GetResponse();

//Get a readable stream from the server.
StreamReader sr = new StreamReader(
myResponse.GetResponseStream(), Encoding.ASCII);

int length = 1024;
char [] Buffer = new char[1024];
int bytesread = 0;
//Read from the stream and write data to Console.
bytesread = sr.Read( Buffer, 0, length);
while( bytesread > 0 )
{
Console.Write( Buffer, 0, bytesread);
bytesread = sr.Read( Buffer, 0, length);
}
sr.Close();
myResponse.Close();
}
(Code continued on the following page.)
Module 11: Internet Access 27


catch (WebException webExcp)
{
Console.WriteLine("A WebException has been caught");
// Write out the WebException message.
Console.WriteLine(webExcp.ToString());
// Get the WebException status code.
WebExceptionStatus status = webExcp.Status;
// If status is WebExceptionStatus.ProtocolError,
// there has been a protocol error and a
// WebResponse should exist.
// Display the protocol error.
if (status == WebExceptionStatus.ProtocolError)
{
Console.Write(
"The server returned protocol error ");
Console.WriteLine(webExcp.Response.ToString());
}
}
catch (Exception e)
{
// Code to catch other exceptions goes here.
}

}
}

Applications that use the Socket class throw instances of SocketException
when errors occur on the Windows socket. The TcpClient, TcpListener, and
UdpClient classes are built on top of the Socket class and also throw instances
of SocketException.
When a SocketException is thrown, the Socket class sets the ErrorCode
property to the last operating system socket error that occurred.
28 Module 11: Internet Access


# ## # Security
! Web Proxy
! Secure Sockets Layer
! Internet Authentication
! Permissions

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Your application can provide security for sending and receiving data over the
Internet by using a Web proxy, Secure Sockets Layer (SSL) encryption,
Internet authentication, and the NET Frameworks code access permissions.
Topic Objective
To introduce the topics in
the section.
Lead-in
Your application can provide
security for sending and
receiving data over the
Internet by using a Web
proxy, SSL encryption,
Internet authentication, and
the NET Frameworks code
access permissions.
Module 11: Internet Access 29


Web Proxy
! Global Proxy for All Web Requests
" Proxy named webproxy using port 80
! Overriding the Global Proxy Setting
" Request uses proxy named alternateproxy and port 80
WebProxy proxyObject = new WebProxy(
"http://webproxy:80/");
GlobalProxySelection.Select = proxyObject;
WebProxy proxyObject = new WebProxy(
"http://webproxy:80/");
GlobalProxySelection.Select = proxyObject;
WebRequest req = WebRequest.Create(
"http://www.contoso.com/");
req.Proxy = new WebProxy(
"http://alternateproxy:80/");
WebRequest req = WebRequest.Create(
"http://www.contoso.com/");
req.Proxy = new WebProxy(
"http://alternateproxy:80/");

*****************************ILLEGAL FOR NON-TRAINER USE******************************
If your Web site uses a proxy to provide access to the Internet, you must
configure a proxy instance to enable your application to communicate with the
Web proxy.
Creating a Global Proxy Instance
The following example shows how to create a global proxy instance that
enables any WebRequest to use a proxy to communicate with the Internet. The
example assumes that the proxy server is named webproxy and that it
communicates on port 80, the standard HTTP port.
WebProxy proxyObject = new WebProxy("http://webproxy:80/");
GlobalProxySelection.Select = proxyObject;

Overriding the Global Proxy Selection
You can override the global proxy selection by assigning an instance that
implements the IWebProxy interface to the Proxy property of the
WebRequest. The following code sends a WebRequest to
http://www.contoso.com. The WebRequest overrides the global proxy
selection with a proxy server that is named alternateproxy on port 80.
WebRequest req = WebRequest.Create("http://www.contoso.com/");
req.Proxy = new WebProxy("http://alternateproxy:80/");

Topic Objective
To show how to use a Web
proxy to communicate with
the Internet.
Lead-in
If your Web site uses a
proxy to provide access to
the Internet, you must
configure a proxy instance
to enable your application to
communicate with the Web
proxy.
30 Module 11: Internet Access


Secure Sockets Layer
! SSL Is Used Automatically If the URI Begins with https
String MyURI = "https://www.contoso.com/";
WebRequest wReq = WebRequest.Create(MyURI);
String MyURI = "https://www.contoso.com/";
WebRequest wReq = WebRequest.Create(MyURI);

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The WebRequest and WebResponse classes use SSL automatically. The
WebRequest object decides to use SSL on the basis of the URI that it is
assigned. If the URI begins with https:, SSL is used. If the URI begins with
http:, SSL is not used.
The following example illustrates the use of SSL:
String MyURI = "https://www.contoso.com/";
WebRequest wReq = WebRequest.Create(MyURI);

Topic Objective
To explain how SSL is used
for secure network
communication.
Lead-in
The WebRequest and
WebResponse classes use
SSL automatically.
Module 11: Internet Access 31


Internet Authentication
! .NET Supports Various Kinds of Authentication
" Basic, digest, negotiate, NTLM, and Kerberos
authentication
" Users can also create their own authentication
! Credentials Stored in Classes
" NetworkCredential for a single Internet resource
" CredentialCache for multiple Internet resources
! Authentication Managed by the AuthenticationManager
! Some Schemes Allow Pre-Authentication to Save Time

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The System.Net classes support a variety of client authentication mechanisms,
including the standard Internet authentication methods: basic, digest, negotiate,
NTLM, and Kerberos authentication, and custom methods that you can create.
Classes and Interfaces Used for Authentication
Authentication credentials are stored in the NetworkCredential and
CredentialCache classes, which implement the ICredentialLookup interface.
When one of these classes is queried for credentials, it returns an instance of the
NetworkCredential class.
The AuthenticationManager class manages the authentication process, while
an authentication module class that implements the IAuthenticationModule
interface performs the actual authentication process. You must register a custom
authentication module with the AuthenticationManager before it can be used.
Modules for the basic, digest, negotiate, NTLM, and Kerberos authentication
methods are registered by default.
The NetworkCredential Class
The NetworkCredential class stores a set of credentials, which is associated
with a single Internet resource and that is identified by a URI, and returns them
in response to any call to the GetCredential method. The NetworkCredential
class is typically used by applications that access a limited number of Internet
resources or by applications that use the same set of credentials in all cases.
The CredentialCache Class
The CredentialCache class stores a collection of credentials for various
Internet resources. When the GetCredential method is called,
CredentialCache returns the proper set of credentials, as determined by the
URI of the Internet resource and the requested authentication scheme. Because
the CredentialCache class stores all of the credentials and provides them as
requested, applications that use a variety of Internet resources with different
authentication schemes benefit from using the CredentialCache class.
Topic Objective
To introduce the client
authentication mechanisms
that are supported by the
System.Net classes.
Lead-in
The System.Net classes
support a variety of client
authentication mechanisms,
including the standard
Internet authentication
methods: basic, digest,
negotiate, NTLM, and
Kerberos authentication,
and custom methods that
you can create.
32 Module 11: Internet Access


The Authentication Process
When an Internet resource requests authentication, the
WebRequest.GetResponse method sends the WebRequest and the request for
credentials to the AuthenticationManager. The request is then authenticated
according to the following procedure:
1. The AuthenticationManager calls the Authenticate method on each of the
registered authentication modules in the order that they were registered.
2. The AuthenticationManager uses the first module that does not return null
to carry out the authentication process.
The details of the process vary depending on the type of authentication
module involved.
3. When the authentication process is complete, the authentication module
returns an Authorization instance to the WebRequest that contains the
information that is needed to access the Internet resource.

Some authentication schemes can authenticate a user without first making a
request for a resource. An application can save time by pre-authenticating the
user with the resource, thus eliminating at least one roundtrip to the server.
Alternatively, the application can perform authentication during program
startup to be more responsive to the user later. Authentication schemes that can
use pre-authentication set the CanPreAuthenticate property to true.
Basic and Digest Authentication
The System.Net implementation of basic and digest authentication complies
with RFC2617, HTTP Authentication: Basic and Digest Authentication,
which is available on the World Wide Web Consortium (W3C) Web site at
http://www.w3c.org.
To use basic and digest authentication, an application must provide a user name
and password in the Credentials property of the WebRequest object that it
uses to request data from the Internet, as shown in the following example:

String literals in an application are stored and transported as clear text.
Therefore, you should avoid putting sensitive information such as passwords in
string literals.

// variables named username and password
// of type string have been previously assigned
String MyURI = "http://www.contoso.com/";
WebRequest wReq = WebRequest.Create(MyURI);
wReq.Credentials = new NetworkCredential(
username, password);

Caution
Module 11: Internet Access 33


NTLM and Kerberos Authentication
Default NTLM authentication and Kerberos authentication use the Microsoft
Windows NT user credentials that are associated with the calling application
to attempt authentication with the server to pass the username, password, and
domain to the host, as in the following example:
// variables named username, password, and domain
// of type string have been previously assigned
String MyURI = "http://www.contoso.com/";
WebRequest wReq = WebRequest.Create(MyURI);
wReq.Credentials =
new NetworkCredential(username, password, domain);

Applications that need to connect to Internet services by using the credentials of
the application user can do so with the users default credentials, as in the
following example:
String MyURI = "http://www.contoso.com/";
WebRequest wReq = WebRequest.Create(MyURI);
wReq.Credentials = CredentialCache.DefaultCredentials;

The negotiate authentication module determines whether the remote server is
using NTLM or Kerberos authentication and sends the appropriate response.

NTLM authentication does not work through a proxy server.

Passport Authentication
Passport authentication is a centralized authentication service provided by
Microsoft that offers a single logon and core profile services for member sites.
This benefits the user because it is no longer necessary to log on to access new
protected resources or sites. If you want your site to be compatible with
Passport authentication and authorization, this is the provider you should use.
For more information, see the Passport documentation located at
http://www.passport.com/business.
Note
34 Module 11: Internet Access


Permissions
! WebPermissions
" Controls an application's right to request data from a URI or to serve
a URI to the Internet
! SocketPermissions
" Controls an application's right to accept data on a local port or to
contact applications
! Choose Permission Class Based on Application Use
" WebRequest and its descendents use WebPermissions
" Socket-level access uses SocketPermissions
! Both Classes Support Two Kinds of Permissions
" Accept application can answer an incoming connection
" Connect application can initiate a connection

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The WebPermissions and SocketPermissions classes provide Internet security
for applications that use System.Net. The WebPermissions class controls an
applications right to request data from a URI or to serve a URI to the Internet.
The SocketPermissions class controls an applications right to accept data on a
local port or to contact applications through a transport protocol at another
address that is based on the host, port number, and transport protocol.
You should choose the permission class on the basis of your application type.
Applications that use WebRequest and its descendents should use the
WebPermissions class to manage permissions. Applications that use
socket-level access should use the SocketPermissions class to manage
permissions.
WebPermissions and SocketPermissions define two permissions: accept and
connect. Accept grants the application the right to answer an incoming
connection from another party. Connect grants the application the right to
initiate a connection to another party.
For WebPermissions, accept means that an application can export a particular
URI anywhere on the Internet. Connect means that an application can access
that URI, whether it is remote or local.
For SocketPermissions, accept means that an application can accept incoming
connections on a local transport address. Connect means that an application can
connect to a remote, or potentially local, transport address.
Topic Objective
To explain which permission
classes best suit which
application types.
Lead-in
The WebPermissions and
SocketPermissions
classes provide Internet
security for applications that
use System.Net.
Module 11: Internet Access 35


Best Practices
! When Possible, Use WebRequest and WebResponse,
Instead of Protocol-Specific Subclasses
! For Better Performance, Use Asynchronous Methods
! Tune Performance by Adjusting the Number
of Connections
" ConnectionLimit property in the ServicePoint instance
! When Possible, Use TcpClient or UdpClient, Instead of
Writing Directly to a Socket
! Use the CredentialCache Class If Credentials
Are Required

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The following recommendations will help you use the classes that are contained
in System.Net more effectively:
! Whenever possible, use WebRequest and WebResponse, instead of
typecasting to descendent classes.
Applications that use WebRequest and WebResponse can take advantage
of new Internet protocols without extensive code changes.
! When writing ASP.NET applications that run on a server that uses the
System.Net classes, it is often better, from a performance standpoint, to use
the asynchronous methods for GetResponse and GetResponseStream.
! Set the ConnectionLimit property in the ServicePoint instance for your
application.
The number of connections opened to an Internet resource can have a
significant effect on network performance and throughput. By default,
System.Net uses two connections per application for each host. Setting the
ConnectionLimit property in the ServicePoint instance for your
application can increase this number.
! When writing socket-level protocols, try to use the TcpClient or UdpClient
classes, instead of writing directly to a socket.
The TcpClient and UdpClient classes encapsulate the creation of TCP and
UDP sockets without requiring you to handle the details of the connection.
! When accessing sites that require credentials, use the CredentialCache
class to create a cache of credentials, rather than supplying them with every
request.
The CredentialCache class will search the cache to find the appropriate
credential to present with a request, thus relieving you of the responsibility
of creating and presenting credentials based on the URI.

Topic Objective
To introduce best practices
that will help students use
the System.Net classes
more effectively.
Lead-in
The following
recommendations will help
you use the classes that are
contained in System.Net
more effectively.
36 Module 11: Internet Access


Lab 11: Creating a DateTime Client/Server Application

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives
After completing this lab, you will be able to:
! Create a client application that uses the System.Net.Sockets.TcpClient
class to connect to and obtain date and time information from a server.
! Create a server application that uses the System.Net.Sockets.TcpListener
class to accept requests from and provide date and time information to
clients.

Lab Setup
Only solution files are associated with this lab. The solution files for this lab are
in the folder <install folder>\Labs\Lab11\Solution.
Scenario
In this lab, you will create two Microsoft Visual Studio .NET console
applications: DateTimeClient and DateTimeServer. The DateTimeClient
application will make a TCP connection to the DateTimeServer application and
obtain a stream that contains date and time information. The DateTimeClient
will read the stream and convert the streams ASCII data into a string that is
then output to the console.
Estimated time to complete this lab: 45 minutes
Topic Objective
To introduce the lab.
Lead-in
In this lab, you will create a
client application that uses
the System.Net.Sockets
.TcpClient class to connect
to and obtain date and time
information from a server.
You will also create a server
application that uses the
System.Net.Sockets
.TcpListener class to
accept requests from and
provide date and time
information to clients.
Module 11: Internet Access 37


Exercise 1
Creating the DateTime Server
In this exercise, you will create a server application that will provide date and
time information to clients through TCP.
! Create the server application
1. In Visual Studio .NET, create a new C# console application project named
DateTimeServer in <install folder>\Labs\Lab11.
2. Rename the starting C# source file Datetimeserver.cs.
3. Add the following using statements:
using System.Net;
using System.Net.Sockets;
using System.Text;

4. Rename the wizard generated Class1 to Server.
5. Modify the Main method in the following steps.
6. In the try section of a try/catch block:
a. Instantiate a TcpListener object to listen on port 14.
b. Start the TcpListener object.
c. Write out the following message to the console:
Waiting for clients to connect
Press Ctrl+c to Quit...

d. Enter an infinite loop that will:
i. Accept TCP client connections using the AcceptSocket method.
ii. Call a DateTime method to get the current date and time.
iii. Create a string that consists of the short version of the date, followed
by the long version of the time.
iv. Convert the string to an ASCII-encoded byte array.
v. Send this byte array to the TCP client.
vi. Close the socket.
vii. Write out to the console a message that contains the string that was
just sent.
7. In the catch section of the try/catch block:
Catch any exceptions of type SocketException and if the exceptions
ErrorCode property has a value of 10048, then write to the console a
message that states Connection to this port failed. There is another
server is listening on this port.
8. Build the server application.

38 Module 11: Internet Access


Exercise 2
Creating the DateTime Client
In this exercise, you will create a client application. The client application takes
as its runtime argument the name of the computer on which the server is
running. The client connects to the server to obtain date and time information
through TCP. The client then displays the date and time information on the
console.
! Create the client application
1. In Visual Studio .NET, create a new C# console application project named
DateTimeClient in <install folder>\Labs\Lab11.
2. Rename the starting C# source file Datetimeclient.cs.
3. Add the following using statements:
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;

4. Rename the wizard generated Class1 to Client.
5. Make the static Main method capable of handling runtime-supplied
arguments.
6. In the Main method, add code to:
a. Create a TcpClient object.
b. Create a byte array object of size 32 bytes.
c. Check the number of runtime arguments. If the number of arguments is
not one, print out an error message to the console, and exit. The message
should state:
Please specify a server name in the command line

d. In a try/catch block, verify that the named server computer exists by
calling the GetHostByName method of the Dns class. Catch any
SocketException exceptions and write out to a console window the
message Cannot find server: , followed by the name of the server and
the exceptions data. Then exit the application.
e. Connect to the named server using port 14.
f. Declare a variable of type Stream.
g. In a try/catch block, get the stream and assign it to the variable declared
in the preceding step. Catch any InvalidOperationException
exceptions and write out to a console window the message Cannot
connect to server , followed by the servers name, and then exit the
application.
h. Read all of the bytes in the stream into the byte array that you created in
step 6b, and store the number of bytes that are read in an integer.
i. Convert the ASCII-encoded byte array into a string.
j. Write out to a console window a message that states the number of bytes
received and the current date and time string retrieved from the server.
Module 11: Internet Access 39


k. Close the TcpClient object.
l. Write out to a console window the following message:
Press Enter to exit.
m. Do a read from the console to wait for the Enter key and exit the
application.
7. Build the client application.

! Test the client and server applications
1. Ensure that there are no server applications running on the computer using
port 14, for example, other instances of the server application.

You can display your computers current TCP/IP network connections
by running the program netstat in a command prompt window. The
following example shows that port 7 is in use by a process with PID 1504:
C:\>netstat -o -n -a

Active Connections

Proto Local Address Foreign Address State PID
TCP 0.0.0.0:7 0.0.0.0:0 LISTENING 1504


2. Run the server application in a Visual Studio .NET Command Prompt
window or in the Visual Studio .NET debugger.
3. Run the client application in another Visual Studio .NET Command Prompt
window or in another Visual Studio .NET debugger. Do not specify any
runtime arguments.
4. Note that the client application returns with the proper error message, as
follows:
Please specify a server name in the command line

5. Run the client application as directed in step 3 with a runtime argument that
contains a nonexistent server name. For example, assuming that there is no
computer named foo on the network, enter the following text in a
Visual Studio .NET Command Prompt window:
datetimeclient foo

Tip
40 Module 11: Internet Access


6. Note that the client application returns with the proper error message, for
example:
Cannot find server: foo


7. Run the client application as directed in step 3 with a runtime argument that
contains the name of the computer on which the Datetimeserver application
is running. If the server application is running on your current computer,
specify the local computer as follows:
datetimeclient localhost

Note that the client application outputs the number of bytes received and the
current date and time string retrieved from the server.

Module 11: Internet Access 41


Review
! Internet Application Scenarios
! The WebRequest and WebResponse Model
! Application Protocols
! Handling Errors
! Security
! Best Practices

*****************************ILLEGAL FOR NON-TRAINER USE******************************
1. Name the four parts of the following URI:
http://www.microsoft.com/default.htm?foo=bar
Scheme identifier http
Server identifier www.Microsoft.com
Path identifier /default.htm
Query String ?foo=bar


2. Write the line of code that creates a WebRequest to the URI in question 1.
WebRequest req = WebRequest.Create
("http://www.microsoft.com/default.htm?foo=bar");


3. Write the line of code that gets a WebResponse from the WebRequest in
question 2.
WebResponse resp = req.GetResponse();


4. Write the line of code that gets a Stream from the WebResponse in
question 3.
Stream respstrm = resp.GetResponseStream();


Topic Objective
To reinforce module
objectives by reviewing
key points.
Lead-in
The review questions cover
some of the key concepts
taught in the module.
42 Module 11: Internet Access


5. Name the type of Web-specific exceptions that are thrown by the
GetResponse method.
WebException


6. State how a WebRequest can be made to use the Secure Socket Layer
(SSL) protocol.
URI begins with https.


7. Name at least three authentication methods that are supported by the .NET
Framework.
Basic
Digest
Negotiate
NTLM
Kerberos




Module 11: Internet Access 43


Course Evaluation

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Your evaluation of this course will help Microsoft understand the quality of
your learning experience.
At a convenient time before the end of the course, please complete a course
evaluation, which is available at http://www.metricsthatmatter.com/survey/.
Microsoft will keep your evaluation strictly confidential and will use your
responses to improve your future learning experience.


Topic Objective
To direct students to a Web
site to complete a course
evaluation.
Lead-in
Between now and the end of
the course, you can go to
the Web site listed on this
page to complete a course
evaluation.



THIS PAGE INTENTIONALLY LEFT BLANK









Contents
Overview 1
Serialization Scenarios 2
Serialization Attributes 4
Object Graph 5
Serialization Process 7
Serialization Example 9
Deserialization Example 10
Custom Serialization 12
Custom Serialization Example 14
Security Issues 17
Lab 12: Serialization 18
Review 27

Module 12: Serialization


Information in this document, including URL and other Internet Web site references, is subject to
change without notice. Unless otherwise noted, the example companies, organizations, products,
domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious,
and no association with any real company, organization, product, domain name, e-mail address,
logo, person, place or event is intended or should be inferred. Complying with all applicable
copyright laws is the responsibility of the user. Without limiting the rights under copyright, no
part of this document may be reproduced, stored in or introduced into a retrieval system, or
transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or
otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual
property rights covering subject matter in this document. Except as expressly provided in any
written license agreement from Microsoft, the furnishing of this document does not give you any
license to these patents, trademarks, copyrights, or other intellectual property.

2001-2002 Microsoft Corporation. All rights reserved.

Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint,
Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and
Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A.
and/or other countries.

The names of actual companies and products mentioned herein may be the trademarks of their
respective owners.



Module 12: Serialization iii


Instructor Notes
After completing this module, students will be able to:
Write an application that serializes and deserializes an object graph by using
either a binary or Simple Object Access Protocol (SOAP) XML format.

Materials and Preparation
This section provides the materials and preparation tasks that you need to teach
this module.
Required Materials
To teach this module, you need the Microsoft PowerPoint file 2349B_12.ppt.
Preparation Tasks
To prepare for this module, you should:
! Read all of the materials for this module.
! Complete the lab.

Presentation:
30 Minutes

Lab:
45 Minutes
iv Module 12: Serialization


Module Strategy
Use the following strategy to present this module:
! Serialization Scenarios
Discuss briefly how serialization is used in scenarios such as persisting in-
memory objects to disk and in remoting. Mention the Microsoft .NET
Frameworks support for serialization and deserialization as an introduction
to the rest of the module.
! Serialization Attributes
Explain how to mark a class with serialization attributes in C# by using the
Serializable attribute. Also cover the NonSerialized attribute.
! Object Graph
Use the diagram on the Object Graph slide to discuss the object graph
concept and the algorithm that is used to serialize or deserialize an object
graph.
! Serialization Process
Introduce the classes that are used in the serialization process.
! Serialization Example
Discuss the code example on the Serialization Example slide in which
default serialization is performed on a graph of objects, whose root is an
ArrayList, and the serialized stream is written to a FileStream in binary
format.
! Deserialization Example
Use the preceding serialization example to show how to create a clone of the
graph by deserializing it.
! Custom Serialization
Discuss when to use custom serialization and the implementation details of
using the ISerializable interface to perform custom serialization and
deserialization.
! Custom Serialization Example
Show how to provide custom serialization for a class named
ISerializableExample.
! Security Issues
Because the serialization engine handles both the public and private state of
the objects that are passed to it, emphasize that streams with private data
should be treated carefully, and that some form of encryption should be used
for sensitive data, before that data is transmitted over the wire or persisted to
disk.

Module 12: Serialization 1


Overview
! Serialization Scenarios
! Serialization Attributes
! Object Graph
! Serialization Process
! Serialization Example
! Deserialization Example
! Custom Serialization
! Custom Serialization Example
! Security Issues

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Serialization is the process of converting a graph of objects into a linear
sequence of bytes. That sequence of bytes can be sent elsewhere, for example,
to a remote computer, and be deserialized, thereby making a clone in the remote
memory of the original graph of objects.
After completing this module, you will be able to:
Write an application that serializes and deserializes an object graph by using
either a binary or Simple Object Access Protocol (SOAP) XML format.

Topic Objective
To provide an overview of
the module topics and
objectives.
Lead-in
In this module, you will learn
about serialization and learn
how to write an application
that serializes and
deserializes an object graph
by using a binary or SOAP
XML format.
2 Module 12: Serialization


Serialization Scenarios
! Persistence
" Store and retrieve a graph of objects to and from a file
! Remoting
" Pass by value arguments that are transmitted between
processes

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Serialization is used in some very common scenarios, such as persisting a graph
of objects to disk or to objects in another process. The Microsoft .NET
Framework provides support for serialization and deserialization.
Persistence
Consider a simple single-user desktop application, such as a two-dimensional
drafting package that is built by using object-oriented techniques. In such an
application, a drawing is composed of different kinds of graphical objects of
various types. The application represents the drawing as a graph of in-memory
objects. One object represents the root of the entire picture. For example, a
simple round table could be represented by a graph that consists of a root object
that is an instance of a circle class. This instance of the circle class has four
children that are each instances of a line class.
To save the entire drawing to a disk file so the drawing can be restored after
rebooting the computer, you could force each class to implement a serialize and
corresponding deserialize method. However, this approach is a potentially
burdensome task for the application programmer.
Serialization in the .NET Framework
The .NET Framework common language runtime reduces the amount of work
that is involved in serialization. At run time, the common language runtime
maintains metadata that allows serialization code to discover the types and
values of all fields and properties that make up any object. Using the common
language runtime, an application requires only a few lines of code to serialize a
object, such as the drawing described in the preceding paragraphs, and write it
to a file, or to deserialize such a file into an in-memory graph of objects.
Topic Objective
To show how serialization is
used.
Lead-in
Serialization is used in some
very common scenarios,
such as persisting a graph
of objects to disk or to
objects in another process.
Module 12: Serialization 3


Remoting
In distributed computing, objects in one process may need to communicate with
objects in another process. In the .NET Framework, the term remoting is
typically used to refer to the process in which an object invokes a method in
another object that is not in the same application domain. If the remote method
takes as one of its arguments an object that lies at the root of a graph of objects,
and if all of the objects in the graph are marked as remote-by-value, you must
serialize a copy of the object graph and pass the graph to the remote object. The
remote object must then deserialize the argument into an in-memory graph of
objects.
For more information about remoting, see Module 13, Remoting and Web
Services, in Course 2349B, Programming with the Microsoft .NET Framework
(Microsoft Visual C# .NET).
4 Module 12: Serialization


Serialization Attributes
! To Mark a Class, Use Serializable Attribute
! To Skip Specified Members, Use NonSerialized Attribute
! To Provide Custom Serialization, Implement
ISerializable
[Serializable] public class MyClass {}
[Serializable] public class MyClass {}
[Serializable] public class MyClass {
[NonSerialized] int _cashSize;
//...
}
[Serializable] public class MyClass {
[NonSerialized] int _cashSize;
//...
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
If you are writing a class, you should be aware of serialization. The common
language runtimes serialization services are built with the assumption that a
type is not serializable unless the type is specifically marked as serializable. In
the simplest case, all you need to do is mark a class as serializable because the
runtime metadata understands everything about each objects layout in memory,
and its field and property definitions.
To mark a type as serializable in C#, you use the Serializable attribute, which
is a reserved custom attribute. All fields in classes with this attribute are
serialized, even private fields.
In the following example, MyClass is marked as serializable:
[Serializable] public class MyClass {}

For slightly more complex classes that have state that is invalid to serialize, the
runtime provides support for marking those fields and properties as transient.
For example, the following code uses the NonSerialized attribute to ensure that
the _cashSize member of MyClass is not serialized:
[Serializable] public class MyClass
{
[NonSerialized] int _cashSize;
//...
}

The small set of classes that need to participate in their own serialization and
deserialization can provide a custom implementation of the ISerializable
interface. For more information about custom serialization, see Custom
Serialization in this module.
Topic Objective
To explain how to mark a
class with serialization
attributes in C#.
Lead-in
If you are writing a class,
you should be aware of
serialization.
Module 12: Serialization 5


Object Graph
Dog
Cat Duck Mouse
Horse
Duck
3
3
7
7
4
4
2
2
9
9
1
1

*****************************ILLEGAL FOR NON-TRAINER USE******************************
An object graph is a set of objects with references to each other. Serialization
must provide a way to represent the links between the graphs objects in the
serialized stream that is created in the serialization process.
Understanding Object Graphs
Serialization of an object graph must provide a way to represent the links
between the graphs objects in the serialized stream that it creates. The value
that is held in the field of the in-memory object, which links to another object,
is essentially a 32-bit address. This address has meaning only in the owner
address space and may change during garbage collection. Therefore,
serialization must allocate a unique number to each object in the stream.
The illustration in the slide shows a graph of animal objects. Each object is
represented as a box with its identification number inside the box and its class
name to the right of the box.
You can represent the graph of objects that is shown in this illustration with a
serialized stream, as in the following example:
Dog, 3, ref4, ref7, ref1 || Cat, 4, ref9 || Duck, 7 || Mouse,
1, ref9, ref2 || Horse, 9, ref4 || Duck, 2

The order in which you stream out the objects does not matter, nor does it
matter what numbers you assign to the objects. What does matter is that no two
objects are assigned the same number. The object numbers are significant only
within a serialized stream. They are simply a way to represent the graph
topology and to allow you to reconstruct a copy of that graph, perhaps on
another computer.
Topic Objective
To define an object graph
and explain its function.
Lead-in
An object graph is a set of
objects that share a set of
references to each other.
6 Module 12: Serialization


Tracking Object References
An algorithm that visits objects one at a time clearly must keep track of which
objects it has already visited, for example, by using an internal list. Without due
care, the algorithm may incorrectly serialize or deserialize an object graph.
For example, in the object graph in the illustration, to avoid entering an infinite
loop, you must detect the cycle in the graph that occurs because of the mutual
references between Cat 4 and Horse 9. During serialization, you must note that
the Cat 4 that is linked to by Dog 3 is the same Cat 4 that is linked to by Horse
9 to ensure that deserialization will result in both Dog 3 and Horse 9 referring
to the same Cat 4 object and not to two different copies.
Module 12: Serialization 7


Serialization Process
! Classes Used by the Default Serialization Process
" ObjectIDGenerator generates IDs for objects
" ObjectManager tracks objects as they are being
deserialized
! Examples of Classes Used with Serialized Streams
" FileStream, MemoryStream, NetworkStream
! Formatter Class
" Writes or reads data in a specified format to the output
or input streams
" Runtime provides BinaryFormatter and SoapFormatter

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The process of serializing an object graph involves identifying the individual
objects in the graph and the relationships between them.
Classes Used by the Default Serialization Process
To create and track object ID numbers, serialization uses several classes, as
follows:
! ObjectIDGenerator
The ObjectIDGenerator class generates IDs for objects. It keeps track of
objects that have already been seen, so when you ask for the ID of an object,
the ObjectIDGenerator knows whether to return the existing ID, or to
generate, and remember, a new ID.
! ObjectManager
The ObjectManager class keeps track of objects as they are being
deserialized. During deserialization, queries are made to the
ObjectManager to determine whether a reference to an object that is in the
serialized stream refers to an object that has already been deserialized or to
an object that has not yet been deserialized. A reference to an object that has
already been deserialized is called a backward reference. A reference to an
object that has not yet been serialized is called a forward reference.

Topic Objective
To introduce the classes
that are used in the
serialization process.
Lead-in
The process of serializing
an object graph involves
identifying the individual
objects in the graph and the
relationships between them.
8 Module 12: Serialization


Both the ObjectIDGenerator and ObjectManager classes are pluggable, so
you can build your own alternatives.

During deserialization, fields are returned in the order in which they are
returned from reflection. Reflection does not guarantee that it will follow
metadata ordering.

You can serialize to many different kinds of streams, for example, to a
FileStream, a MemoryStream, or a NetStream. The serialized streams
format is determined by the formatter object that you instantiate.
The .NET Framework runtime provides the following formatter classes:
! BinaryFormatter
The BinaryFormatter class is used for a compact binary representation.
! SoapFormatter
The SoapFormatter class is used for an XML representation.

Because formatters are pluggable, you can also build your own formatters.
Note
Module 12: Serialization 9


Serialization Example
class SerializeExample{
public static void Main(String[] args) {
// create the object graph
ArrayList l = new ArrayList();
for (int x=0; x< 100; x++) {
l.Add (x);
}
// create the filestream
FileStream s = File.Create("foo.bin");
// create the BinaryFormatter
BinaryFormatter b = new BinaryFormatter();
// serialize the graph to the stream
b.Serialize(s, l);
s.Close();
}
}
class SerializeExample{
public static void Main(String[] args) {
// create the object graph
ArrayList l = new ArrayList();
for (int x=0; x< 100; x++) {
l.Add (x);
}
// create the filestream
FileStream s = File.Create("foo.bin");
// create the BinaryFormatter
BinaryFormatter b = new BinaryFormatter();
// serialize the graph to the stream
b.Serialize(s, l);
s.Close();
}
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The following code sample shows how to perform default serialization of a
graph of objects, whose root is an ArrayList. The serialized stream is written to
a FileStream in binary format.
using System;
using System.IO;
using System.Collections;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

class SerializeExample
{

public static void Main(String[] args)
{
// create the object graph
ArrayList l = new ArrayList();
for (int x=0; x< 100; x++)
{
l.Add (x);
}
// create the filestream
FileStream s = File.Create("foo.bin");
// create the BinaryFormatter
BinaryFormatter b = new BinaryFormatter();
// serialize the graph to the stream
b.Serialize(s, l);
s.Close();
}
}

Topic Objective
To provide an example of
serialization of an object
graph.
Lead-in
This code sample shows
how to perform default
serialization of a graph of
objects, whose root is an
ArrayList.
10 Module 12: Serialization


Deserialization Example
class DeSerialize
{
public static void Main(String[] args)
{
// open the filestream
FileStream s = File.OpenRead("foo.bin");
// create the formatter
BinaryFormatter b = new BinaryFormatter();
// deserialize
ArrayList p = (ArrayList) b.Deserialize(s);
s.Close();
// print out the new object graph
// see module text for PrintValues code
PrintValues(p);
}
class DeSerialize
{
public static void Main(String[] args)
{
// open the filestream
FileStream s = File.OpenRead("foo.bin");
// create the formatter
BinaryFormatter b = new BinaryFormatter();
// deserialize
ArrayList p = (ArrayList) b.Deserialize(s);
s.Close();
// print out the new object graph
// see module text for PrintValues code
PrintValues(p);
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The preceding Serialization Example shows how to perform default
serialization of a graph of objects, whose root is an ArrayList, with a serialized
stream that is written to a FileStream in binary format.
The following code sample shows how to create a clone of the graph by
deserializing it. The root of the clone graph is called p.
using System;
using System.IO;
using System.Collections;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

class DeSerialize
{

public static void Main(String[] args)
{
// open the filestream
FileStream s = File.OpenRead("foo.bin");

// create the formatter
BinaryFormatter b = new BinaryFormatter();
// deserialize
ArrayList p = (ArrayList) b.Deserialize(s);
s.Close();
// print out the new object graph
PrintValues(p);
}

(Code continued the following page.)
Topic Objective
To show how to create a
clone of the graph by
deserializing it.
Lead-in
The preceding Serialization
Example shows how to
perform default serialization
of a graph of objects, whose
root is an ArrayList, with a
serialized stream that is
written to a FileStream in
binary format.
Module 12: Serialization 11


public static void PrintValues( IEnumerable myList )
{
System.Collections.IEnumerator myEnumerator =
myList.GetEnumerator();
while ( myEnumerator.MoveNext() )
Console.WriteLine( "{0}", myEnumerator.Current );
}

}

12 Module 12: Serialization


Custom Serialization
! Customize Serialization by Implementing ISerializable:
" When some of the data is not valid after deserialization
" When some of the data must be obtained by calculation
! ISerializable Requires:
" GetObjectData method, called during serialization,
which returns a PropertyBag of type SerializationInfo
" PropertyBag, which contains the type of the object
being serialized and the name/object pairs for the values
being serialized
" A constructor, called during deserialization, which uses
SerializationInfo to reconstitute the state of the object

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This module has so far discussed the default serialization process. However,
you may also want to customize the way that data from a given object is
serialized.
Custom serialization can be useful when some of the data that is associated with
an object is no longer valid after deserialization. You may want to use custom
serialization when working with pointers or hashcodes, or when you want to
create data through calculations or other means that allow you to reconstruct the
full state of the object during deserialization.
To perform custom serialization, you should implement the ISerializable
interface on the given object.
Implementation Details Required by ISerializable
To implement the ISerializable interface, you implement the GetObjectData
method on your object and add a constructor that takes a SerializationInfo and
a StreamingContext, as shown in Custom Serialization Example in this
module.
When GetObjectData is called during serialization, you are responsible for
populating a SerializationInfo object. A SerializationInfo object is a
PropertyBag that contains the type of the object that is being serialized and the
name/object pairs for the values that are being serialized.
The Formatter emits the data out onto the wire in the method required by its
particular format. You are free to serialize as few or as many fields as you want,
but the data that is transmitted must be sufficient to reconstitute the entire state
of the object. If the base object of the current class implements ISerializable, it
is usually correct to call the base objects ISerializable.GetObjectData and
add any additional fields that are required for serializing the derived class to the
returned SerializationInfo.
Topic Objective
To explain what is required
to implement ISerializable
for performing custom
serialization.
Lead-in
This module has so far
discussed the default
serialization process.
However, you may also
want to customize the way
data from a given object is
serialized.
Module 12: Serialization 13


Deserialization
Deserialization occurs during the call to the classs constructor. If you need to
create custom deserialization of an object, you use the objects
SerializationInfo, which has been populated with the type of the object and the
name/object pairs that were transmitted over the stream. You are responsible for
completely reconstituting the state of the object from this information. If the
base class also implements ISerializable, you are responsible for calling the
base classs constructor. The serialization infrastructure will delay the call on
this constructor until the entire SerializationInfo has been completed.
If, for example, the SerializationInfo that is transmitted references objects A,
B, and C, the SerializationInfo that is passed to the constructor will have been
populated with references to objects A, B, and C. However, there is no
guarantee that any of the objects that are referenced by A, B, or C has been
completed.
Because there is no guarantee that any of the objects that are referenced by A,
B, or C have been completed, you cannot safely call any code on A, B, or C that
may require the objects to which they refer. For some objects, this code may
include code as simple as GetHashCode.
If your code requires you to perform any execution that is based on the value of
data that is contained in the objects that are referenced, it is usually best to
cache the SerializationInfo and then implement IDeserializationCallback.
14 Module 12: Serialization


Custom Serialization Example
[Serializable] public class ExampleFoo : ISerializable
{
public int i, j, k;
public ExampleFoo() {}
internal ExampleFoo(SerializationInfo si,
StreamingContext context) {
//Restore our scalar values.
i = si.GetInt32("i");
j = si.GetInt32("j");
k = si.GetInt32("k");
}
public void GetObjectData(SerializationInfo si,
StreamingContext context) {
//SerializationInfo - essentially a property bag
//Add our three scalar values;
si.AddValue("i", i);
si.AddValue("j", j);
si.AddValue("k", k);
Type t = this.GetType();
si.AddValue("TypeObj", t);
}
}
[Serializable] public class ExampleFoo : ISerializable
{
public int i, j, k;
public ExampleFoo() {}
internal ExampleFoo(SerializationInfo si,
StreamingContext context) {
//Restore our scalar values.
i = si.GetInt32("i");
j = si.GetInt32("j");
k = si.GetInt32("k");
}
public void GetObjectData(SerializationInfo si,
StreamingContext context) {
//SerializationInfo - essentially a property bag
//Add our three scalar values;
si.AddValue("i", i);
si.AddValue("j", j);
si.AddValue("k", k);
Type t = this.GetType();
si.AddValue("TypeObj", t);
}
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The following example shows how to provide custom serialization for a class:
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Soap;

public class Sample
{
public static void Main()
{
ToFile();
FromFile();
}

(Code continued the following page.)
Topic Objective
To provide an example of
custom serialization.
Lead-in
This example shows how to
provide custom serialization
for a class named
ISerializableExample.
Module 12: Serialization 15


public static void ToFile()
{
Console.WriteLine("ToFile");
ExampleFoo fooOut = new ExampleFoo();
fooOut.i = 1;
fooOut.j = 20;
fooOut.k = 50;

Console.WriteLine("i: {0}", fooOut.i);
Console.WriteLine("j: {0}", fooOut.j);
Console.WriteLine("k: {0}", fooOut.k);

IFormatter objFormatterToStream = new SoapFormatter();
Stream toStream = new FileStream("myFoo.xml",
FileMode.Create, FileAccess.Write, FileShare.None);
objFormatterToStream.Serialize(toStream, fooOut);
toStream.Close();
}

public static void FromFile()
{
Console.WriteLine("FromFile");
//Then you can read it back in with code like this:
IFormatter objFormatterFromStream = new SoapFormatter();
Stream fromStream = new FileStream("myFoo.xml",
FileMode.Open, FileAccess.Read, FileShare.Read);
ExampleFoo fooIn = (ExampleFoo)
objFormatterFromStream.Deserialize(fromStream);
fromStream.Close();

Console.WriteLine("i: {0}", fooIn.i);
Console.WriteLine("j: {0}", fooIn.j);
Console.WriteLine("k: {0}", fooIn.k);
}
}


[Serializable]
public class ExampleFoo : ISerializable
{
public int i, j, k;

public ExampleFoo()
{
}

internal ExampleFoo(SerializationInfo si,
StreamingContext context)
{
//Restore our scalar values.
i = si.GetInt32("i");
j = si.GetInt32("j");
k = si.GetInt32("k");
}

(Code continued on the following page.)
16 Module 12: Serialization


public void GetObjectData(SerializationInfo si,
StreamingContext context)
{
//SerializationInfo is essentially a property bag.

//Add our three scalar values;
si.AddValue("i", i);
si.AddValue("j", j);
si.AddValue("k", k);

Type t = this.GetType();
si.AddValue("TypeObj", t);
}
}

Outputs:
ToFile
i: 1
j: 20
k: 50
FromFile
i: 1
j: 20
k: 50

Module 12: Serialization 17


Security Issues
! Serialization Handles an Objects Private Data
" If private data is sensitive, consider encrypting the
stream before transmitting or saving to a disk
" System.Security.Cryptography namespace contains
classes to perform cryptography
The CryptoStream class can be used to encrypt
streams of serialized data

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The serialization engine handles both the public and private state of the objects
that are passed to it. When serializing an object to a stream, you must remember
that the stream now contains the public and private data of the object.
If the private data is sensitive, you should treat the stream with particular care.
For example, the stream should not be transmitted over the wire or persisted to
disk without some form of encryption.
Topic Objective
To alert students to the
need for security when
serializing objects.
Lead-in
The serialization engine
handles both the public and
private state of the objects
that are passed to it.
18 Module 12: Serialization


Lab 12: Serialization

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives
After completing this lab, you will be able to:
Create an application that uses serialization as it is implemented by the
.NET Framework, to persist an object graph to and from a disk file in both
binary and SOAP XML format.

Lab Setup
Starter and solution files are associated with this lab. The starter files are in the
folder <install folder>\Labs\Lab12\Starter, and the solution files are in the
folder <install folder>\Labs\Lab12\Solution.
Scenario
In this lab, you will create a Microsoft Visual Studio .NET console application
that uses the common language runtimes ability to serialize an object graph in
memory to disk. You will create binary and SOAP formatter implementations
of the application.
In the first exercise, you will create a singly-linked linked list, which you will
fill with values and serialize to a file on disk. You will then deserialize the list
from the file on disk, thus restoring the list to an object graph in memory.
During deserialization, the elements within the list are swapped multiple times.
In the second exercise, you will modify the application to demonstrate the
ability of the .NET Frameworks serialization mechanism to handle object
graphs that contain multiple references to the same object and that contain
objects that have mutual references, which can create cycles in the graph.
Estimated time to complete this lab: 45 minutes
Topic Objective
To introduce the lab.
Lead-in
In this lab, you will write a
client/server application that
uses serialization.
Module 12: Serialization 19


Exercise 1
Creating the Basic Serialization Application
In this exercise, you will modify the Serialization application to provide
methods to serialize and deserialize a linked list.
! To create the basic Serialization application in binary format
1. In the <install folder>\Labs\Lab12\Starter directory, open the Serialization
project in Visual Studio .NET and examine the Serialize.cs and
LinkedList.cs source files.
2. In Serialize.cs, locate the SaveListToDisk method, and add code to:
a. Create a Stream object that is initialized to a file named Linkedlist.bin
by using the static File.Create method.
b. Create a new BinaryFormatter object.
c. Invoke the method of the BinaryFormatter that serializes the
LinkedList parameter to the stream.
d. Close the file.
3. In Serialize.cs, locate the LoadListFromDisk method and add code to:
a. Create a Stream object that is initialized to a file named Linkedlist.bin
by using the static File.OpenRead method.
b. Create a new BinaryFormatter object.
c. Invoke the method of the BinaryFormatter that deserializes the stream
into a LinkedList named list2.
d. Close the file.
e. Output the contents of list2 to console by calling the LinkedList method
Draw().
f. Return list2.
4. Build the Serialization application.
20 Module 12: Serialization


5. Step through the application in the Visual Studio .NET debugger, and note
that due to the random swapping your output may vary slightly from the
following:
Entering Scope 1
Creating and filling List ..
List: 1 2 3 4 5 6 7 8 9
Serializing LinkedList to file ..
Leaving Scope 1

Entering Scope 2
Deserializing LinkedList from binary file ..
List: 1 2 3 4 5 6 7 8 9
Swapping Entries
Swapping 1 and 2
Swapping 3 and 4
Swapping 5 and 6
Swapping 7 and 8
List: 2 1 4 3 6 5 8 7 9
Serializing LinkedList to file ..
Leaving Scope 2

Entering Scope 3
Deserializing LinkedList from binary file ..
List: 2 1 4 3 6 5 8 7 9
Swapping Random Entries
Swapping 2 and 8
Swapping 4 and 3
Swapping 4 and 3
Swapping 6 and 5
Swapping 1 and 4
Swapping 1 and 8
Swapping 2 and 4
Swapping 3 and 5
Swapping 7 and 2
Swapping 5 and 4
Swapping 6 and 2
Swapping 9 and 5
Swapping 1 and 7
Swapping 7 and 8
Swapping 8 and 2
List: 2 1 7 4 3 8 9 6 5
Serializing LinkedList to file ..
Leaving Scope 3

Entering Scope 4
Deserializing LinkedList from binary file ..
List: 2 1 7 4 3 8 9 6 5
Removing Entries
Removing 1
Removing 2
Removing 3
List: 7 4 8 9 6 5
Serializing LinkedList to file ..
Leaving Scope 4

Module 12: Serialization 21



You can open a binary file using Visual Studio .NET by running
Microsoft Windows Explorer, Explorer.exe, and navigating to the binary
files icon. Then either right-click on the icon and choose Open With
Microsoft Visual Studio .NET, or drag and drop the icon into a running
Visual Studio .NET applications left-hand File View pane.

6. Using Visual Studio .NET, open and visually examine the contents of the
Linkedlist.bin file in the bin\Debug subdirectory, and note the serialized list
datas format, structure, and size.

! To create the basic Serialization application in SOAP format
1. Add code to the SaveListToDisk and LoadListFromDisk methods to use
the SoapFormatter to write to and read the LinkedList parameter to and
from a file named Linkedlist.soap.
2. Build and execute the application, and confirm that the console output
remains correct.
3. Using Visual Studio .NET, open and visually examine the contents of the
Linkedlist.soap file in the bin\Debug subdirectory, and note the serialized
list datas format and size. Compare this list datas format, structure, and
size to the Linkedlist.bin list datas format, structure, and size.

Note
22 Module 12: Serialization


Exercise 2
Handling Complex Object Graphs
In this exercise, you will modify the Serialization application to create and
manipulate an object graph that has multiple references to the same object and
has reference cycles.
! To create the complex Serialization application in SOAP format
1. In Serialize.cs, locate the Ser class and add a static method named
SaveArrayToDisk that returns a void and that takes as its single argument
an array of type Node. Within SaveArrayToDisk, add code to:
a. Iterate through the array of nodes, and, for each node, print out the
arrays index, the nodes value, and the value of the Node object in the
nodes NextNode field.
b. Write out a message to the console that states that the application is
serializing the array to a file.
c. Create a stream object that is initialized with the file named Array.soap
by using the static File.Create method.
d. Create a SoapFormatter object.
e. Serialize the node array to the file.
f. Close the file.
2. In the Ser class, add a static method named LoadArrayFromDisk that
takes no arguments and returns an array of type Node. Within
LoadArrayFromDisk, add code to:
a. Write out a message to the console that states that the application is
deserializing an array from the file.
b. Create a stream object that is initialized with the file named Array.soap
by using the static File.OpenRead method.
c. Create a SoapFormatter object.
d. Using the SoapFormatter object, deserialize the stream into an array of
type Node.
e. Close the file.
f. Iterate through the array of nodes, and, for each node, print out the
arrays index, the nodes value, and the value of the Node object in the
nodes NextNode field.
g. Return the array of type Node.
Module 12: Serialization 23


3. In the Ser class add a static public method named Scope5 that takes no
arguments and returns void. Within Scope5, add code to:
a. Create two objects of type Node named n0 and n1 that are initialized to
values 0 and 1 respectively.
b. Write out a message to the console that states that the application is
entering Scope5 and creating a graph cycle.
c. Assign n1 to the NextNode field of n0.
d. Assign n0 to the NextNode field of n1.
e. Create an array of type Node that is initialized to contain references to
the three objects: n0, n1, and n0. Note that index 0 and 2 refer to the
same object.
f. Call the SaveArrayToDisk method and pass in the array of type Node.
g. Write out a message to the console that states that the application is
leaving Scope5.
4. In the Ser class, add a static public method named Scope6 that takes no
arguments and returns void. Within Scope6, add code to:
a. Write out a message to the console that states that the application is
entering Scope6 and creating a graph cycle.
b. Assign to a variable named nodes of type array of Node the array that is
returned by the LoadArrayFromDisk method. The array references the
objects: n0, n1, and n0 that were created in step 1. Note that index 0 and
2 refer to the same object.
c. Write out a message to the console that states that the value of n0 is
changing to 42 and that this change should result in a change in the value
in both locations in the array.
d. Assign the integer 42 to the Value field of nodes[0].
e. Write out a message to the console that states that the arrays structure
should be preserved during serialization and deserialization.
f. Call SaveArrayToDisk to persist nodes to the disk.
g. Assign to a variable named nodes2 of type array of Node the array that
is returned by the LoadArrayFromDisk method.
h. Write out a message to the console that states that the value of n0 is
being incremented.
i. Increment by 1 the value of the first element of nodes2.
j. Call SaveArrayToDisk, and pass nodes2.
k. Write out a message to the console that states that the application is
leaving Scope6.
5. In the Main method of Ser, and after the call to the Scope4 method, add
calls to Scope5 and Scope6.
6. Build the Serialization application.
7. Step through the application in the Visual Studio .NET debugger, and note
console output similar to the following output:
24 Module 12: Serialization


Entering Scope 1
Creating and filling List ..
List: 1 2 3 4 5 6 7 8 9
Serializing LinkedList to file ..
Leaving Scope 1

Entering Scope 2
Deserializing LinkedList from soap file ..
Deserializing LinkedList from binary file ..
List: 1 2 3 4 5 6 7 8 9
Swapping Entries
Swapping 1 and 2
Swapping 3 and 4
Swapping 5 and 6
Swapping 7 and 8
List: 2 1 4 3 6 5 8 7 9
Serializing LinkedList to file ..
Leaving Scope 2

Entering Scope 3
Deserializing LinkedList from soap file ..
Deserializing LinkedList from binary file ..
List: 2 1 4 3 6 5 8 7 9
Swapping Random Entries
Swapping 8 and 7
Swapping 7 and 5
Swapping 6 and 5
Swapping 2 and 1
Swapping 1 and 8
Swapping 4 and 2
Swapping 2 and 3
Swapping 3 and 5
Swapping 9 and 4
Swapping 9 and 4
Swapping 6 and 8
Swapping 2 and 4
Swapping 2 and 1
Swapping 9 and 7
Swapping 1 and 5
List: 6 5 1 4 3 9 8 2 7
Serializing LinkedList to file ..
Leaving Scope 3

Entering Scope 4
Deserializing LinkedList from soap file ..
Deserializing LinkedList from binary file ..
List: 6 5 1 4 3 9 8 2 7
Removing Entries
Removing 1
Removing 2
Removing 3
List: 6 5 4 9 8 7
Serializing LinkedList to file ..
Leaving Scope 4

(Code continued on the following page.)
Module 12: Serialization 25


Entering Scope 5
Creating a circular reference:
n0's NextNode is n1 and n1's NextNode is n0
Also adding in a third node, n2, that is another reference
to n0
Node 0 Value: 0 Value of NextNode Ref: 1

Node 1 Value: 1 Value of NextNode Ref: 0

Node 2 Value: 0 Value of NextNode Ref: 1

Serializing Array to file ..
Leaving Scope 5

Entering Scope 6
Deserializing Array from file ..
Node 0 Value: 0 Value of NextNode Ref: 1

Node 1 Value: 1 Value of NextNode Ref: 0

Node 2 Value: 0 Value of NextNode Ref: 1

changing value of node n0 to 42

should change value in both locations in array

array's structure should be preserved during
serialization and deserialization


Node 0 Value: 42 Value of NextNode Ref: 1

Node 1 Value: 1 Value of NextNode Ref: 42

Node 2 Value: 42 Value of NextNode Ref: 1

Serializing Array to file ..
Deserializing Array from file ..
Node 0 Value: 42 Value of NextNode Ref: 1

Node 1 Value: 1 Value of NextNode Ref: 42

Node 2 Value: 42 Value of NextNode Ref: 1

incrementing value of node n0

Node 0 Value: 43 Value of NextNode Ref: 1

Node 1 Value: 1 Value of NextNode Ref: 43

Node 2 Value: 43 Value of NextNode Ref: 1

Serializing Array to file ..
Leaving Scope 6

26 Module 12: Serialization


8. Using Visual Studio .NET, open and visually examine the Array.soap file in
the bin\Debug subdirectory, and note the serialized array datas format,
structure, and size.

Module 12: Serialization 27


Review
! Serialization Scenarios
! Serialization Attributes
! Object Graph
! Serialization Process
! Serialization Example
! Deserialization Example
! Custom Serialization
! Custom Serialization Example
! Security Issues

*****************************ILLEGAL FOR NON-TRAINER USE******************************
1. Declare a serializable class named Foo with two integer fields F1 and F2 in
which F2 is transient and should not be serialized.
[Serializable] public class Foo {
int F1;
[NonSerialized] int F2;


2. Name and describe the two kinds of formatters that the .NET Framework
provides.
BinaryFormatter for a compact binary
SoapFormatter for XML representation


3. Describe what a class should do to provide custom serialization.
The class should inherit from the ISerializable interface, implement the
interfaces GetObjectData method, and provide a constructor that
takes SerializationInfo and StreamingContext parameters.


4. What kind of data that is not normally accessible by clients can be made
visible by serialization?
Private object state


Topic Objective
To reinforce module
objectives by reviewing
key points.
Lead-in
The review questions cover
some of the key concepts
taught in the module.



THIS PAGE INTENTIONALLY LEFT BLANK









Contents
Overview 1
Remoting 2
Remoting Configuration Files 19
Demonstration: Remoting 22
Lab 13.1: Building an Order-Processing
Application by Using Remoted Servers 28
XML Web Services 36
Lab 13.2: Using an XML Web Service 48
Review 54
Course Evaluation 56

Module 13: Remoting
and XML Web Services


Information in this document, including URL and other Internet Web site references, is subject to
change without notice. Unless otherwise noted, the example companies, organizations, products,
domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious,
and no association with any real company, organization, product, domain name, e-mail address,
logo, person, place or event is intended or should be inferred. Complying with all applicable
copyright laws is the responsibility of the user. Without limiting the rights under copyright, no
part of this document may be reproduced, stored in or introduced into a retrieval system, or
transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or
otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual
property rights covering subject matter in this document. Except as expressly provided in any
written license agreement from Microsoft, the furnishing of this document does not give you any
license to these patents, trademarks, copyrights, or other intellectual property.

2001-2002 Microsoft Corporation. All rights reserved.

Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint,
Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and
Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A.
and/or other countries.

The names of actual companies and products mentioned herein may be the trademarks of their
respective owners.


Module 13: Remoting and XML Web Services iii


Instructor Notes
After completing this module, students will be able to:
! Write and configure distributed applications that use .NET Remoting.
! Create an XML Web service by using Microsoft Visual Studio .NET and
ASP.NET.
! Consume an XML Web service by using the Web Services Description
Language tool (Wsdl.exe).

Materials and Preparation
This section provides the materials and preparation tasks that you need to teach
this module.
Required Materials
To teach this module, you need the Microsoft PowerPoint file 2349B_13.ppt.
Preparation Tasks
To prepare for this module, you should:
! Read all of the materials for this module.
! Practice the demonstrations.
! Complete the lab.

Presentation:
120 Minutes

Lab:
105 Minutes
iv Module 13: Remoting and XML Web Services


Demonstrations
This section provides demonstration procedures that will not fit in the margin
notes or are not appropriate for the student notes.
Remoting
In this demonstration, you will show students how a client application uses
.NET Remoting to make a method call on an object in a server application.
The code for this demonstration is contained in one project and is located in
<install folder>\Democode\Mod13\Demo13.1. In addition, the code for the
individual demonstration is provided in the student notes.
Using Visual Studio .NET to Create an XML Web Service
In this demonstration, you will show students how to use Visual Studio .NET to
create an XML Web service.
The code for this demonstration is contained in one project and is located in
<install folder>\Democode\Mod13\Demo13.2. In addition, the code for the
individual demonstration is provided in the student notes.
In both of the preceding demonstrations, use the debugger to step through the
code while you point out features.
Module 13: Remoting and XML Web Services v


Module Strategy
Use the following strategy to present this module:
! Remoting
Use the diagram on the Remoting Overview slide to introduce the services
that are provided by the Microsoft .NET Framework for use with remoting.
You will cover each of these services in more detail in the subsequent slides
in this section.
Explain how channels and formatters are used to transmit data. Discuss how
the .NET Framework supports server-side and client-side activation of
remote objects and describe the differences between server-side and client-
side activation.
Explain how to control the lifetime of client-activated remote objects by
using a leasing mechanism. Discuss how objects are marshaled in .NET
Remoting.
Explain how to register and activate a remote object from the server side
and the client side.
Conclude this section with a brief discussion of client compilation
techniques.
! Remote Configuration Files
Discuss the use of configuration files in remoting. Do not spend time on the
.NET Remoting configuration file format; instead refer students to the .NET
Framework Software Developers Guide (SDK) documentation.
Because the module is long, conclude this part of the lecture and instruct
students to do Lab 13.1.
! XML Web Services
Explain how to use Visual Studio .NET to implement an ASP.NET XML
Web service and how to access the XML Web service from a Web browser
and a client application. Use the Using Visual Studio .NET to Create an
XML Web Service demonstration to illustrate the concepts that are covered
in this section.
Introduce the XML Web service discovery process and the tools that are
available for discovery.

Module 13: Remoting and XML Web Services 1


Overview
! Remoting
! Remoting Configuration Files
! XML Web Services

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Microsoft .NET Remoting provides a framework that allows objects to interact
across remoting boundaries, for example, across application domains
(AppDomain).
XML Web services is a term for communication that uses industry-standard
HTTP and Simple Object Access Protocol (SOAP) protocols. You can
implement XML Web services by using remoting and by using ASP.NET.
After completing this module, you will be able to:
! Write and configure distributed applications that use .NET Remoting.
! Create an XML Web service by using Microsoft Visual Studio .NET and
ASP.NET.
! Consume an XML Web service by using the Web Services Description
Language tool (Wsdl.exe).

Topic Objective
To provide an overview of
the module topics and
objectives.
Lead-in
In this module, you will learn
about distributed
applications that use .NET
Remoting.
2 Module 13: Remoting and XML Web Services


" "" " Remoting
! Remoting Overview
! Channels and Formatters
! Activation and Proxies
! Lease-Based Lifetime
! Object Marshaling
! Server Side
! Client Side
! Client Compilation Techniques

*****************************ILLEGAL FOR NON-TRAINER USE******************************
.NET Remoting supports communication in the following scenarios:
! Between objects in different application domains
! In different processes
! On different computers

The common language runtime remoting infrastructure provides a rich set of
classes that enable you to ignore most of the complexities of deploying and
managing remote objects. Even with applications that run under different
runtime environments, the process of calling methods on remote objects is
almost identical to the process of calling local methods.
Topic Objective
To provide an overview of
the topics covered in this
section.
Lead-in
This section shows how
.NET Remoting supports
communication in various
scenarios.
Module 13: Remoting and XML Web Services 3


Remoting Overview
Client AppDomain
Client
Object
Channel
Formatter
Channel
Formatter
Server
Object
Remoting Boundary
Server AppDomain
Server
Proxy

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The .NET Framework provides several services that are used in remoting:
! Communication channels that are responsible for transporting messages to
and from remote applications
! Formatters that encode and decode messages before they are transported by
the channel
! Proxies that forward remote method calls to the proper object
! Remote object activation and lifetime support


Because the .NET context mechanism is beyond the scope of this course,
this module does not cover remoting calls between contexts.

Topic Objective
To provide an overview of
.NET Remoting.
Lead-in
The .NET Framework
provides several services
that are used in remoting.
Note
4 Module 13: Remoting and XML Web Services


Channels and Formatters
! Channels Transport Messages To and From Remote Objects
! Client Selects a Channel That Is Registered on the Server
# Before calling a remote object, the client registers the channel
Channels are registered on a per application domain basis
# One computer cannot have multiple channels listening to same port
! .NET Provides Implementation of HTTP and TCP Channels
# HTTP Channel default: SOAP protocol to transport XML messages
# TCP Channel default:TCP protocol to transport binary messages
Faster than HTTP SOAP Web Services but not as open
! Example: Programmatic Registration of TCP Channel on Port 8085
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
TcpChannel chan = new TcpChannel(8085);
ChannelServices.RegisterChannel(chan);
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
TcpChannel chan = new TcpChannel(8085);
ChannelServices.RegisterChannel(chan);

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Channels are used to transport messages to and from remote objects. When a
client calls a method on a remote object, the parameters, as well as other details
that are related to the call, are transported through the channel to the remote
object. Any results from the call are returned back to the client in the same way.
Formatters are used to encode and serialize data into messages before they are
transmitted over a channel.
Channel Selection
Because a client can use any of the channels that are registered on the server to
communicate with a remote object, you can select the channels that best suit
your needs. You can also customize any existing channel or build new ones that
use different communication protocols.
Channel selection is subject to the following rules:
! Channels must be registered before objects are registered. At least one
channel must be registered with the remoting infrastructure before a remote
object can be called.
! Channels are registered on a per application domain basis.
A single process may contain multiple application domains. When a process
dies, all channels that it registers are automatically destroyed.
! It is not valid to register a channel that listens on the same port on which
another channel is currently listening.
Though channels are registered on a per application domain basis, different
application domains on one computer cannot register the same channel that
listens on the same port.

Topic Objective
To explain how channels
and formatters are used to
transmit data.
Lead-in
Channels are used to
transport messages to and
from remote objects.
Formatters are used to
encode and serialize data
into messages before
transmission over a
channel.
Module 13: Remoting and XML Web Services 5


Clients can communicate with a remote object by using any registered channel.
The remoting framework ensures that the remote object is connected to the
proper channel when a client attempts to connect to it. The client is responsible
for specifying a channel before it attempts to communicate with a remote
object.
To specify a channel, you can use a .NET Remoting configuration file, or you
can call the RegisterChannel method on the ChannelServices class.
The .NET Framework provides support for HTTP, TCP, and SMTP channels.
Because .NET Remoting channels are pluggable, you can write and plug in
additional channels with unique transport and encoding requirements.
Formatters Used to Encode and Decode Messages
Each channel provides a default formatter. However you can specify the
formatter that you wish to use.

A Uniform Resource Identifier (URI) is a compact representation of a
resource that is available to your application through the Internet. You may be
more familiar with the term URL, which stands for Uniform Resource Locator.
URLs form a subset of the more general URI naming scheme. A URL identifies
an Internet resource that has a Web page address.

HTTP Channel
By default, the HTTP channel uses the SOAP protocol to transport messages to
and from remote objects. All messages are passed through the SOAP formatter,
where the message is changed into XML and serialized, and the required SOAP
headers are added to the stream.
Alternatively, you can specify the binary formatter, which results in a binary
data stream. In either case, the data stream is then transported to the target
Uniform Resource Identifier by using the HTTP protocol. You can create
industry-standard XML Web services by using the HTTP channel with the
default SOAP formatter.
TCP Channel
By default, the TCP channel uses a binary formatter to serialize all messages to
a binary stream and transports the stream to the target Uniform Resource
Identifier by using the TCP protocol.
Alternatively, you can specify the SOAP formatter, which results in an XML
data stream. You can obtain better performance by using the TCP channel with
the default binary formatter than you can by using XML Web services.
Note
6 Module 13: Remoting and XML Web Services


Code Example
The following code example shows how to programmatically register a TCP
Channel on port 8085 by using ChannelServices.RegisterChannel:
using System;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
//...
TcpChannel chan = new TcpChannel(8085);
ChannelServices.RegisterChannel(chan);
//...

Security Issues
If you have a choice between using the HttpChannel and the TcpChannel, it is
recommended that you use the HttpChannel and host your remote objects in
Internet Information Services (IIS), no matter what the user authentication and
authorization models are. IIS hosting provides support for wire-level protection
using Secure Sockets Layer (SSL) and authentication using Integrated
Windows Authentication (formerly NTLM authentication) or Kerberos. For
configuring SSL and authentication, see the IIS documentation.
The TcpChannel, as an implementation of the Transmission Control Protocol
(TCP), does not have default support for some of the robust authentication
standards that the HTTP standard does. Within a secured environment (one that
has wire-level protection such as IPSec), the high-speed TcpChannel can be
used, but it is not recommended over the Internet or a nonsecure intranet.
You can use cryptography to protect data from being viewed or modified and
thereby provide secure channels of communication over otherwise insecure
channels. The System.Security.Cryptography namespace contains a set of
classes that allow you to perform both symmetric and asymmetric
cryptography, create hashes, and provide random number generation.
Successful cryptography is the result of combining these tasks. See the .NET
SDK for documentation about the System.Security.Cryptography namespace.
Module 13: Remoting and XML Web Services 7


Activation and Proxies
! Before Using a Remote Object, the Client Must Activate It
# By calling new, Activator.CreateInstance, or Activator.GetObject
! Activation Returns Proxy Used by Client to Access Remote Object
# Proxy represents remote object in clients AppDomain
# Proxy forwards clients calls, and returns results and exceptions
! Server-Side Activation Automatic Instantiation by Server
# Single call object handles only one request (stateless)
# Singleton object services multiple clients and requests (stateful)
! Client-Side Activation Instantiation by Explicit Client Call
# State maintained between method calls for specific client instance

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The remoting framework supports server-side and client-side activation of
remote objects.
Server-side activation means that when a client attempts to access the object,
the object is instantiated on the server automatically. Client-side activation, on
the other hand, means that the object is instantiated in response to a deliberate
activation request from a client.
You can select the best activation model to control the instantiation and lifetime
of a remote object.
Methods of Remote Object Activation
Before a client can use a remote object, the remote object must be activated,
and the client must obtain a proxy to access the remote object.
You can activate a remote object by calling new or by calling the following
methods of the Activator class:
! Activator.CreateInstance
Used to create an object instance
! Activator.GetObject
Generally used to connect to an object that is already running at a specified
Uniform Resource Identifier

Topic Objective
To show how the remoting
framework supports server-
side and client-side
activation of remote objects
and to describe the
differences between server-
side and client-side
activation.
Lead-in
The remoting framework
supports server-side and
client-side activation of
remote objects.
8 Module 13: Remoting and XML Web Services


The Role of Proxies in Remote Object Interaction
When a client activates a remote object, the client obtains a proxy to the class
instance on the server. Any interaction with a remote object occurs by reference
through the proxy. The proxy object acts as a representative of the remote
object and ensures that all calls that are made on the proxy are forwarded to the
correct remote object instance. All methods that are called on the proxy are
automatically forwarded to the remote class, and any results are returned to the
client.
From the clients perspective, this process is identical to the process of making
a local call. Any exceptions that are thrown by the remote object are
automatically returned to the client. Because those exceptions are returned to
the client, the client can use try/catch blocks around sections of code to trap
and handle exceptions.
Server-Side Activation
Server-side activation supports single call and singleton modes of activation.
Single Call Objects
A single call object services only one request. Single call objects are useful in
scenarios in which:
! The overhead of creating an object is not significant.
! Objects are configured in a load-balanced fashion.
! State information is usually not needed between calls.

Because single call objects cannot hold state information between method calls,
they are sometimes referred to as stateless objects.
Singleton Objects
A singleton object services multiple clients and multiple requests. Therefore, a
singleton object can store state information between client invocations.
Singleton objects are useful when you want to share data explicitly between
clients and method invocations, and when the overhead of creating and
maintaining objects is substantial.
Because singleton objects can maintain their state over a prolonged period of
time, they are sometimes referred to as stateful objects.
Module 13: Remoting and XML Web Services 9


Client-Side Activation
Client-activated objects are activated on a request from the client. This method
of activating server objects is similar to the classic COM coclass activation. The
activation process is as follows:
1. When the client requests a server object, an activation request message is
sent to the remote application.
2. The server then creates an instance of the requested class and returns an
ObjRef object to the client application that invoked it.
3. A proxy is then created on the client side by using the ObjRef object.

A client-activated object can store state information between method calls for
its specific client. However, state information is not shared between multiple
client-activated objects. Each request for a remote object instance returns a
proxy to an independent instance of the server type.
A useful function of client-activated objects is that constructor arguments can
be passed by the local application to the constructor of the object in the remote
application.
10 Module 13: Remoting and XML Web Services


Lease-Based Lifetime
! A Leasing Mechanism Controls the Lifetime of a Client-
Activated Remote Object
! An Objects Lease Time Can Be Extended
! When an Objects Lease Time Reaches Zero
# The object is disconnected from remoting infrastructure
# The object may be garbage-collected
# A lease provides an alternative to reference counting

*****************************ILLEGAL FOR NON-TRAINER USE******************************
You can control the lifetime of client-activated remote objects by using a
leasing mechanism. When an object is first created, it is given a lease time.
When the lease time of the object reaches zero, the object is disconnected from
the remoting infrastructure. After the references to the object from within the
objects application domain have been freed, the object may be collected when
the next garbage collection occurs.
You can extend the lease on an object by using a number of mechanisms. For
more information about extending the lease on an object, see Lease-Based
Lifetime Concepts in the .NET Framework Software Development Kit (SDK)
documentation.
You can use leases to manage the lifetime of remote objects as an alternative to
reference counting, which tends to be complex and inefficient over unreliable
network connections.
A potential disadvantage of leasing is that the lifetime of a remote object may
be extended for longer than is required. However, the advantages of reducing
network traffic that is devoted to reference counting and the pinging of clients
outweigh the disadvantage of the extended lifetimes of remote objects.
Topic Objective
To explain how using a
leasing mechanism can
extend the lifetime of client-
activated remote objects.
Lead-in
You can control the lifetime
of client-activated remote
objects by using a leasing
mechanism.
Module 13: Remoting and XML Web Services 11


Object Marshaling
! Objects Instantiated Remotely Are Returned by Reference and
Accessed by the Client Through a Proxy
! Remote Call Parameters, Return Values, and Fields Can Be:
# Marshal-by-value objects A copy of the object is passed from one
AppDomain to another
- Value types and classes that are serializable
# Marshal-by-reference objects A reference to the object is passed
from one AppDomain to another
- Classes that derive from the System.MarshalByRefObject class
# Not-marshaled objects Objects suitable for local use only
- Any class that is not Marshal-By-Value or Marshal-By-Reference

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Because calls to a remote object can cause other objects to be passed across a
remoting boundary, you should understand how objects are marshaled in .NET
Remoting.
Inside the remoting boundary of the caller, for example, inside a single
application domain, objects are passed by reference, and primitive data types
are passed by value. Application domains are hard boundaries. Applications
running in different application domains share no information, no global
variables, and no static fields on classes. As mentioned in Remoting in this
module, .NET Remoting is used to communicate between objects in different
application domains.
All objects that are instantiated remotely are returned by reference. When a
client instantiates a remote object, it receives a proxy to the class instance on
the server. All methods that are called on the proxy are automatically forwarded
to the remote class, and any results are returned to the client.
The following examples show scenarios where objects are passed across
remoting boundaries because of calls to remote objects:
! Object parameters in a method call, such as myObj in:
public int myRemoteMethod (MyRemoteObject myObj)

! Object return values of method calls, such as MyRemoteObject instances
in:
public MyRemoteObject myRemoteMethod(String myString)

! Objects that result from property or field access of a remote object, such as
instances that are accessed in the myNestedObject field of myObj in:
myObj.myNestedObject


All objects in the .NET Framework fall into three general remoting categories:
marshal-by-value, marshal-by-reference, and not-marshaled.
Topic Objective
To explain how objects are
marshaled in .NET
Remoting.
Lead-in
Because calls to a remote
object can cause other
objects to be passed across
a remoting boundary, you
should understand how
objects are marshaled in
.NET Remoting.
12 Module 13: Remoting and XML Web Services


Marshal-By-Value Objects
Marshal-by-value objects include value types and classes that are serializable. A
copy of such marshal-by-value objects is passed from one application domain
to another.
You should use marshal-by-value objects when you need to move the complete
state of the object with the execution to the target application domain for
performance or processing purposes. In many scenarios, marshal-by-value
objects reduce boundary crossing, such as network, process, and application
domain roundtrips. Marshal-by-value objects have no distributed identity, and
no proxy is ever created to reference them, as shown in the following example:
[Serializable]
class Foo1A
{
//. . .
}

To participate in its own serialization when it is marshaled across application
domain boundaries, an object can expose the ISerializable interface, as in the
following example:
using System.Runtime.Serialization;
//...
[Serializable]
class Foo1B : ISerializable
{
//. . .
}

Marshal-By-Reference Objects
References to marshal-by-reference objects are made when the object reference
(ObjRef) is passed from one application to another. When the object reference
arrives in the remote application, it is converted to a proxy back to the original
object. The original object remains in the application domain in which it was
created. A marshal-by-reference objects class must derive from the
System.MarshalByRefObject class.
Use marshal-by-reference objects when an objects state should remain in the
application domain in which it was created and when only references to that
object should be marshaled at the time that the object is remoted. For example,
make a file object, whose internal representation contains a field that is an
operating system handle, application domain-bound. In this case, the operating
system handle would not be useful or appropriate in another application
domain, process, or computer.
All operations on a marshal-by-reference object are appropriately indirected so
that the common language runtime can intercept and forward them. This
indirection applies to fields, properties, and methods of marshal-by-reference
objects. For this reason, the performance overhead of marshal-by-reference
objects is greater than the performance overhead of marshal-by-value objects.
Module 13: Remoting and XML Web Services 13


The following example declares a class Foo2 that is marshal-by-reference:
class Foo2 : MarshalByRefObject
{
//. . .
}

Not-Marshaled Objects
Not-marshaled objects are the default for all objects that do not derive from
System.MarshalByRefObject and that do not have the [Serializable] custom
attribute. The use of not-marshaled objects is appropriate when an object should
not leave the application domain because the object was designed for local use
only, as in the following example:
class Foo3
{
//. . .
}

14 Module 13: Remoting and XML Web Services


Server Side
! Register the Channel
! Register Remote Objects by Using:
# The RegisterWellKnownServiceType call
# Or a configuration file
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(HelloServer),
"SayHello",
WellKnownObjectMode.SingleCall);
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(HelloServer),
"SayHello",
WellKnownObjectMode.SingleCall);
RemotingConfiguration.Configure("MyHello.exe.config");
RemotingConfiguration.Configure("MyHello.exe.config");

*****************************ILLEGAL FOR NON-TRAINER USE******************************
All remote objects must be registered with the remoting framework before
clients can access them. Object registration is usually performed by a hosting
application that starts up, registers one or more channels with
ChannelServices, registers one or more remote objects with
RemotingServices, and then waits until it is terminated.

The registered channels and objects are available only while the process
that registered them is alive. When the process terminates, all channels and
objects that are registered by this process are automatically removed from the
remoting services where they were registered.

The following information is required when you register a remote object with
the remoting framework:
! The type name of the remote object
! The object Uniform Resource Identifier that clients will use to locate the
object
! For server-side activation, the object mode that is required
The object mode can be single call or singleton.

Topic Objective
To explain how to register
and activate a remote object
from the server side.
Lead-in
Remote objects must be
registered with the remoting
framework before clients
can access them.
Note
Module 13: Remoting and XML Web Services 15


Methods of Remote Object Registration
You can register a remote object by calling
RemotingConfiguration.RegisterWellKnownServiceType, and passing the
information that is described in the preceding list as parameters, or by storing
that information in a configuration file and then calling
RemotingConfiguration.Configure and passing the name of the configuration
file as a parameter.
RegisterWellKnownServiceType and Configure perform the same function,
but Configure is more convenient to use because the contents of the
configuration file can be altered without recompiling the host application.
RegisterWellKnownServiceType
The following example code shows how to register the HelloServer class as a
SingleCall remote object by using the RegisterWellKnownServiceType
method.
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(HelloServer),
"SayHello",
WellKnownObjectMode.SingleCall);

In the preceding example, RemotingSamples.HelloServer is the name of the
class, and SayHello is the object Uniform Resource Identifier.
Configuration File
The following example code shows how to register the HelloServer class as a
SingleCall remote object by using the Configure method:
RemotingConfiguration.Configure("MyHello.exe.config");

In the preceding example, the configuration file, MyHello.exe.config stores the
same registration information that is stored in the parameters of the preceding
RegisterWellKnownServiceType method.
The format of the .NET Remoting configuration file is described in Remoting
Configuration Files in this module.
When the remote object in the preceding example is registered, the remoting
framework creates an object reference and then extracts the required metadata
about the object from the assembly. The objects required metadata, the
Uniform Resource Identifier, and the assembly name are then stored in the
object reference, which is filed in a remoting framework table that is used for
tracking registered remote objects.

The remote object itself is not instantiated by the registration process.
Instantiation occurs only when a client attempts to call a method on the object
or activates the object from the client side.

Note
16 Module 13: Remoting and XML Web Services


Client Side
! Register the Channel
! Activate Remote Object by Using:
# Activator.GetObject
# new and a configuration file
HelloServer obj = (HelloServer)Activator.GetObject(
typeof(RemotingSamples.HelloServer),
"tcp://localhost:8085/SayHello");
HelloServer obj = (HelloServer)Activator.GetObject(
typeof(RemotingSamples.HelloServer),
"tcp://localhost:8085/SayHello");
RemotingConfiguration.Configure("MyHello.exe.config");
HelloServer obj = new HelloServer();
RemotingConfiguration.Configure("MyHello.exe.config");
HelloServer obj = new HelloServer();
ChannelServices.RegisterChannel(new TcpChannel());
ChannelServices.RegisterChannel(new TcpChannel());

*****************************ILLEGAL FOR NON-TRAINER USE******************************
On the client side, a client registers the channel and activates the remote object.
Registering the Channel
A client must first register the channel by calling
ChannelServices.RegisterChannel. As previously mentioned in Server Side in
this module, the server must first have registered the selected channel.
The following code example shows how to register a TCP Channel:
ChannelServices.RegisterChannel(new TcpChannel());

You do not specify a port number when you register the client
channel.

Activating Remote Objects
After the client registers the channel, it can then activate the remote object by
using GetObject or the new operator. It is important to note that the object is
not instantiated when either of these calls is made. Actually, no network calls
are generated at all. The remoting framework obtains enough information from
the metadata to create the proxy without connecting to the remote object. A
network connection is only established when the client calls a method on the
proxy.
When the call arrives at the server, the remoting framework extracts the
Uniform Resource Identifier from the message, examines the remoting
framework tables to locate the reference for the object that matches the Uniform
Resource Identifier, and then instantiates the object if necessary, forwarding the
method call to the object. If the object is registered as SingleCall, it is
destroyed after the method call is completed. For each method that is called, a
new instance of the object is created.
Topic Objective
To explain how to register
and activate a remote object
from the client side.
Lead-in
On the client side, a client
registers the channel and
activates the remote object.
Important
Module 13: Remoting and XML Web Services 17


The only difference between GetObject and new is that GetObject allows you
to specify a Uniform Resource Identifier as a parameter, while new obtains the
Uniform Resource Identifier from the configuration file.
You can use CreateInstance or new for client-activated objects. Both
CreateInstance and new allow you to instantiate an object by using
constructors with parameters. The lifetime of client-activated objects is
controlled by the leasing service that is provided by the remoting framework.
Using Activator.GetObject
The following example code shows how to obtain a server-activated object by
using Activator.GetObject:
HelloServer obj = (HelloServer)Activator.GetObject(
typeof(RemotingSamples.HelloServer),
"tcp://localhost:8085/SayHello");

In the preceding example, "tcp://localhost:8085/SayHello" specifies
that a connection should be made to the remote object at the SayHello endpoint
by using TCP on port 8085.
Using the new Operator
To use the new operator, you must load a configuration file with the remote
objects information. For example, for a file called MyHello.exe.config, you
load the configuration file as follows:
RemotingConfiguration.Configure("MyHello.exe.config");

After the configuration file has been loaded, the client can activate the object as
follows:
HelloServer obj = new HelloServer();

18 Module 13: Remoting and XML Web Services


Client Compilation Techniques
! When the Client Is Compiled, the Compiler Needs Server
Class Data
! Class Information Can Be Provided by:
# A reference to the assembly where the class is stored
# Splitting the remote object into an implementation class
and an interface type
# Using Wsdl.exe to extract the required metadata directly
from the endpoint

*****************************ILLEGAL FOR NON-TRAINER USE******************************
When compiling the client code, the compiler requires type information about
the HelloServer class. This type information is discussed in the preceding
topic.
You can provide type information in one of the following ways:
! Provide a reference to the assembly in which the HelloServer class is
stored.
Providing a reference is useful when the client and server components are
developed at the same site.
! Split the remote object into an implementation class and an interface type,
and use the interface as a reference when compiling the client.
Splitting the remote object into an implementation class and an interface
type is useful when the client and server components are not developed at
the same site. The interface or interfaces can be compiled to a DLL and
shipped to the client sites when necessary. According to COM guidelines,
you should avoid changing the published interface.
! Use the Web Services Description Language tool (Wsdl.exe) to extract the
required metadata directly from the endpoint.
The Web Services Description Language tool lets you connect to the
endpoint that is provided, extract the metadata, and generate source code
that can then be used to compile the client. The Web Services Description
Language tool is useful when client and server components are developed at
different sites and when no interface classes are available.
To use the Web Services Description Language tool, point the tool at a
remote Uniform Resource Identifier and generate the required metadata.


The Web Service Utility extracts only metadata and does not generate the
source for the remote object.

Topic Objective
To explain how type
information is provided to
the compiler.
Lead-in
When compiling the client
code, the compiler requires
type information about the
HelloServer class.
Note
Module 13: Remoting and XML Web Services 19


Remoting Configuration Files
! Configure Method on RemotingConfiguration
! An Application Configuration File is an XML Document
# Configuration files are case-sensitive
# Can be specified on a machine or on an application level
Application level configuration takes priority over
machine level configuration
# For more information see Remoting Configuration File
Format in the .NET Framework SDK documentation
RemotingConfiguration.Configure(foo.exe.config);
RemotingConfiguration.Configure(foo.exe.config);

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Programmatic registration is a simple process, but it is not practical for use in
real-life situations where large numbers of remote objects must be managed on
a corporate network. Remoting configuration solves this problem by using a
configuration file, a simple mechanism, to register an object.
To configure an object with a configuration file, call the Configure method on
RemotingConfiguration, as shown in the following example:
using System;
using System.IO;
using System.Runtime.Remoting;

public class MyHost
{
public static void Main(String[] args)
{
if (args.Length == 0)
{
// Perform a default configuration, throw an exception
// or display usage information to the user
}
else
{
RemotingConfiguration.Configure (args[0]);
}
// The program should pause here till the
// registered objects are no longer required
}
}

Topic Objective
To explain how to configure
objects with configuration
files for use in remoting.
Lead-in
Programmatic registration is
a simple process, but it is
not practical for use in real-
life situations where large
numbers of remote objects
must be managed on a
corporate network.
20 Module 13: Remoting and XML Web Services


The Configure method loads the configuration file into memory, parses the
contents, and calls the relevant methods to register the channels and objects that
are described in the file.
You can also use configuration files to store such settings as binding policy and
security. The name of the configuration file includes the full module name and
the extension, with .config appended to that extension. For example, the
configuration file name for Foo.exe is Foo.exe.config.
Although .NET Remoting does not mandate how you name a configuration file,
you should use the naming convention that is described in the preceding
paragraph to ensure that specific security and binding policies are picked up
when an application is executed.
The Configure call on RemotingConfiguration only reads the relevant
sections in the configuration file that apply to remoting, while the rest of the
information is ignored.
Module 13: Remoting and XML Web Services 21


An application configuration file is an XML document that contains sections
for various feature areas. The general format is a follows:

Configuration files are case-sensitive.

<configuration>
<system.runtime.remoting>
<application>
<lifetime>
<!--Default lifetime information for all -->
<!--objects in the application. Individual -->
<!--service objects can customize these -->
</lifetime>
<service>
<!--Specifies one or more remote objects provided by -->
<!--this service. These can be client-activated -->
<!--as well as server-activated -->
</service>
<client>
<!--Specifies a remote object this client used -->
<!--One or more clients can be specified -->
</client>
<SOAPinterop>
<!--Specify type, assembly and XML information -->
<!--to use deserializing data from SOAP endpoints -->
</SOAPinterop>
<channels>
<!--List all channels the client or service require.-->
<!-Individual clients or services can customize the -->
<!-machine level settings -->
</channels>
</application>
<channels>
<!--Provide a list of the channel and sink providers.-->
<!--This data will normally be provided on a machine -->
<!--level in the machine configuration file -->
</channels>
<channelSinkProviders>
<clientProviders>
<!--one or more client providers -->
</clientProviders>
<serverProviders>
<!--one or more server providers -->
</serverProviders>
</channelSinkProviders>
</system.runtime.remoting>
</configuration>

All configuration information can be specified on a machine or on an
application level. Application level configuration takes priority over machine
level configuration.
For more information about the .NET Remoting configuration file format, see
Remoting Configuration File Format in the .NET Framework SDK
documentation.
Note
22 Module 13: Remoting and XML Web Services


Demonstration: Remoting

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This demonstration shows how a client application can use .NET Remoting to
make a method call on an object in a server application.

Before running the application, ensure that all currently running
server applications that may be using the same port number have been closed.
Running more than one server that uses the same port number will generate an
error.


You can display your machines current TCP/IP network connections by
running the program netstat in a command prompt window. The following
example shows that port 7 is in use by a process with PID 1504:
C:\>netstat -o -n -a

Active Connections

Proto Local Address Foreign Address State PID
TCP 0.0.0.0:7 0.0.0.0:0 LISTENING 1504

Topic Objective
To demonstrate how a client
application uses .NET
Remoting to make a method
call on an object in a server
application.
Lead-in
This demonstration shows
how a client application
uses .NET Remoting to
make a method call on an
object in a server
application.
Delivery Tip
Although this demonstration
includes detailed
instructions, this is not a
guided practice. Present the
demonstration and suggest
that the students try the
demonstration for
themselves later.
Important
Tip
Module 13: Remoting and XML Web Services 23


Server-Side
The following sample code is the server source code, which is named Server.cs:
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels.Http;

namespace RemotingSamples
{

public class HelloServer : MarshalByRefObject
{

public int callCounter = 0;

public static int Main(string [] args)
{

TcpChannel chan1 = new TcpChannel(8085);
HttpChannel chan2 = new HttpChannel(8086);

ChannelServices.RegisterChannel(chan1);
ChannelServices.RegisterChannel(chan2);

/*
RemotingConfiguration.RegisterWellKnownServiceType
(
typeof(HelloServer),
"SayHello",
WellKnownObjectMode.Singleton
);
*/

RemotingConfiguration.RegisterWellKnownServiceType(
typeof(HelloServer),
"SayHello",
WellKnownObjectMode.SingleCall
);


System.Console.WriteLine("Press Enter key to exit");
System.Console.ReadLine();
return 0;
}

(Code continued on the following page.)
24 Module 13: Remoting and XML Web Services


public HelloServer()
{
Console.WriteLine("HelloServer activated");
}

public String HelloMethod(String name,
out int counter)
{
counter = ++callCounter;
Console.WriteLine(
"Server Hello.HelloMethod : {0} Counter :{1}",
name, callCounter);
return "Hi there " + name;
}
}
}

The RemotingSamples.HelloServer class is derived from
MarshalByRefObject, so HelloServer is remotable. When the server is
started, you create and register a TCP channel that listens for clients to connect
on port 8085 and an HTTP channel that listens for clients to connect on port
8086. You also register the remote object with the remoting framework by
calling RegisterWellKnownServiceType.
The parameters for this call include the following:
! The full type of the object that is being registered, such as
RemotingSamples.HelloServer in the preceding example
! The name of the endpoint where the object will be published
To connect to the object, clients must know the name of the endpoint. Any
string can be used. In the preceding example, you use SayHello.
You can also connect to remote objects through ASP.NET. In the preceding
example, if you were connecting to remote objects through ASP.NET, the
endpoint would be RemotingSamples/HelloServer.soap.
! The object mode, which can be SingleCall or Singleton
In the preceding example, you initially specify SingleCall. The object mode
specifies the lifetime of the object when it is activated on the server. In the
case of SingleCall objects, a new instance of the class is created for each
call that is made from a client, even if the same client calls the same method
more than once.
Singleton objects, on the other hand, are created only once, and all clients
communicate with the same object.
Module 13: Remoting and XML Web Services 25


Client-Side
The following sample code is the client source code, which is named Client.cs:
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels.Http;
using System.IO;

namespace RemotingSamples
{
public class Client
{
public static int Main(string [] args)
{
int counter;

TcpChannel chan1 = new TcpChannel();
ChannelServices.RegisterChannel(chan1);
HelloServer obj1 = (HelloServer)Activator.GetObject(
typeof(RemotingSamples.HelloServer),
"tcp://localhost:8085/SayHello");
if (obj1 == null)
{
System.Console.WriteLine(
"Could not locate TCP server");
return 1;
}


HttpChannel chan2 = new HttpChannel();
ChannelServices.RegisterChannel(chan2);
HelloServer obj2 =(HelloServer)Activator.GetObject(
typeof(RemotingSamples.HelloServer),
"http://localhost:8086/SayHello");
if (obj2 == null)
{
System.Console.WriteLine(
"Could not locate HTTP server");
return 1;
}

(Code continued on the following page.)
26 Module 13: Remoting and XML Web Services


try
{
Console.WriteLine(
"Client1 TCP HelloMethod {0} Counter {1}",
obj1.HelloMethod("Caveman", out counter),
counter);
Console.WriteLine(
"Client2 HTTP HelloMethod {0} Counter {1}",
obj2.HelloMethod("Caveman", out counter),
counter);
}
catch (IOException ioExcep)
{
Console.WriteLine("Remote IO Error" +
"\nException:\n" + ioExcep.ToString());
return 1;
}
return 0;
}
}
}

When the client starts up, it registers a TCP channel and an HTTP channel and
proceeds to activate an object on each channel by calling the GetObject
method on the Activator class.
The parameters for this call are the type of the name of the class that you need
to activate, RemotingSamples.HelloServer, and the endpoint Uniform
Resource Identifier.
For the clients TCP connection, the Uniform Resource Identifier is
tcp://localhost:8085/SayHello.
For the clients HTTP connection, the Uniform Resource Identifier is
http://localhost:8086/SayHello.

The Uniform Resource Identifier includes the protocol, computer
name, and port number, as well as the endpoint. If the server is deployed on a
host that is named Sunshine, clients can connect to the server that is using TCP
by specifying tcp://sunshine:8085/SayHello.

When you run the client, it locates and connects to the server, retrieves a proxy
for the remote objects, and calls the HelloMethod on the remote objects,
passing the string Caveman as a parameter and the counter as an out parameter.
The server returns Hi there Caveman and the count of the number of times that
the server objects method has been called.
Important
Module 13: Remoting and XML Web Services 27


Building and Executing the Server and Client
To build the server and the client in the same directory, type the following
command at the Microsoft Visual Studio .NET Command Prompt command
prompt:

To use Microsoft Visual Studio .NET tools within a command
prompt window, the command prompt window must have the proper
environment settings. The Visual Studio .NET Command Prompt window
provides such an environment. To run a Visual Studio .NET Command Prompt
window: on the Start menu point to All Programs, point to Microsoft
Visual Studio .NET, Visual Studio .NET Tools, and then click Visual Studio
.NET Command Prompt.

csc server.cs
csc /r:server.exe client.cs

To execute the application, start the server application from a console window,
and then start the client application from another console window.

The counter values on the two client calls should have the same value
because the server objects activation mode is SingleCall.

Changing the Activation Mode
To change the servers activation mode from SingleCall to Singleton, change
the RegisterWellKnownServiceType call in the server as follows:
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(HelloServer),
"SayHello",
WellKnownObjectMode.Singleton);

Rebuild and execute as in the preceding example. Note the counter values on
both calls. They should increase after each call.
Important
Note
28 Module 13: Remoting and XML Web Services


Lab 13.1: Building an Order-Processing Application by
Using Remoted Servers

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives
After completing this lab, you will be able to:
! Create an XML Web service hosted in a .NET executable file.
! Create a TCP server.
! Create a client that uses .NET Remoting to access both an XML Web
service and a TCP server.

Lab Setup
Only solution files are associated with this lab. The solution files for this lab are
in the folder <install folder>\Labs\Lab13.1\Solution.
Topic Objective
To introduce the lab.
Lead-in
In this lab, you will create an
XML Web service hosted in
a .NET executable file,
create a TCP server, and
create a client that uses
.NET Remoting to access
an XML Web service and a
TCP server.
Module 13: Remoting and XML Web Services 29


Scenario
This lab is based on a scenario of a simple distributed order-processing
application, in which a customer specifies a customer ID and an item number
for an item that the customer wants to purchase, and the application processes
the order. The order processing involves authenticating the customers ID and
arranging order fulfillment that is to say, having the ordered item sent to the
customer.
In this lab, you will create a distributed solution that uses .NET Remoting. You
will use .NET Remoting support of SOAP over HTTP to create a server
application that exports an authenticate method as an open standards-based
XML Web service. You will also use .NET Remoting support of TCP with
binary formatting to create a remote order fulfillment server application that
trades off the advantages of the open standards-based flexible XML Web
services protocol for improved performance.
In addition, you will create a simple test client to exercise these servers.
Because the focus of this lab is on .NET Remoting, the functionality that is
specific to authentication and fulfillment will be minimal.
Estimated time to complete this lab: 50 minutes
30 Module 13: Remoting and XML Web Services


Exercise 1
Creating an Authentication XML Web Service
In this exercise, you will create an XML Web service that provides simple
authentication. Simple authentication will consist of using a random-number
generator to authenticate the user successfully 50 percent of the time.
! Create the authentication server
1. Using the editor of your choice, create a C# source file named
Authenticate.cs, and save it in the Lab13.1 directory.
2. To simplify coding, add using statements to import the namespaces System,
System.Runtime.Remoting, System.Runtime.Remoting.Channels, and
System.Runtime.Remoting.Channels.Http.
3. Specify the namespace Lab13, and in it, create a public class named
AServer that is marshal-by-reference.
4. Add a field named aRandom to hold a reference to a random number
generator object of class System.Random.
5. In the Main method of AServer:
a. Create and register a channel that supports the protocol and format for an
XML Web service. Specify port 8086.
b. Invoke the RemotingConfiguration.RegisterWellKnownServiceType
method to register the authentication server whose full type name is
Lab13.AServer, and whose endpoint is DoAuthentication.
To retain the objects state, that is to say, the random-number generator
object, over multiple client calls, the well-known objects mode should
be stateful.
c. Print to the console a message that tells the user to press any key so the
server exits.
The program should wait for this user action before continuing.
6. Add a public default constructor that creates a new instance of the Random
class, assigns it to the field that was created in step 4, and prints a message
to the console that states that AServer is running.
Module 13: Remoting and XML Web Services 31


7. Add a public method named Authenticate that takes a customer ID of type
int and returns a bool value.
a. Assign to a variable named aRandomNumber of type double a random
number that is greater than or equal to 0 and is less than 1, by using the
following code:
double aRandomNumber = aRandom.NextDouble();

b. Using the value from step a, assign to a variable of type bool named
passed the value true for 50 percent of the time and the value false for
50 percent of the time.
c. Print to the console a message that identifies the customer and states
whether the customer is authenticated on the basis of the value of
passed.
d. Return passed.
8. In a Visual Studio .NET Command Prompt window, build Authenticate.exe.

To use Microsoft Visual Studio .NET tools within a command
prompt window, the command prompt window must have the proper
environment settings. The Visual Studio .NET Command Prompt window
provides such an environment. To run a Visual Studio .NET Command
Prompt window: on the Start menu point to All Programs, point to
Microsoft Visual Studio .NET, Visual Studio .NET Tools, and then click
Visual Studio .NET Command Prompt.


Important
32 Module 13: Remoting and XML Web Services


Exercise 2
Creating a Fulfillment Server
In this exercise, you will create a server that provides simple order fulfillment
functionality by using TCP and a binary format to provide fast communication.
Simple fulfillment will consist of using a random-number generator to
successfully fulfill the order 50 percent of the time.
! Create the fulfillment server
1. Using the editor of your choice, create a C# source file that is named
Fulfillment.cs, and save it in the Lab13.1 directory.
2. To simplify coding, add using statements to import the namespaces System,
System.Runtime.Remoting, System.Runtime.Remoting.Channels, and
System.Runtime.Remoting.Channels.Tcp.
3. Specify the namespace Lab13, and in it, create a public class named
FServer that is marshal-by-reference.
4. Add a field named aRandom to hold a reference to a random-number
generator object of class System.Random.
5. In the Main method of FServer:
a. Create and register a channel that uses the TCP protocol and binary
formatting. Specify port 8085.

You cannot have two channels on one computer that are using the
same port number; therefore you cannot use port 8086.

b. Invoke the RemotingConfiguration.RegisterWellKnownServiceType
method to register the fulfillment server whose full type name is
Lab13.FServer, and whose endpoint is DoFulfillment.
To retain the objects state, that is to say, the random-number generator
object, over multiple client calls, the well-known objects mode should
be stateful.
c. Print to the console a message that tells the user to press any key so the
server exits.
The program should wait for this user action before continuing.
6. Add a public default constructor that creates a new instance of the Random
class, assigns it to the field that was created in step 4, and prints a message
to the console that states that FServer is running.
Note
Module 13: Remoting and XML Web Services 33


7. Add a public method named Fulfill that takes a customer ID of type int, an
item number of type int, and returns a bool value.
a. Assign to a variable named aRandomNumber of type double a random
number that is greater than or equal to 0 and less than 1, by using the
following code:
double aRandomNumber = aRandom.NextDouble();

b. Using the value from step a, assign to a variable of type bool named
shipped the value true for 50 percent of the time and the value false for
50 percent of the time.
c. Print to the console a message that identifies the customer and the item
number and states whether the item was shipped on the basis of the
value of shipped.
d. Return shipped.
8. In a Visual Studio .NET Command Prompt window, build Fulfillment.exe.

34 Module 13: Remoting and XML Web Services


Exercise 3
Creating a Test Client
In this exercise, you will create a test client application to test the order-
processing servers. The client calls the authentication XML Web service. If the
user is authenticated, the client calls the order fulfillment server.
! Create the test client
1. Using the editor of your choice , create a C# source file named Testclient.cs,
and save it in the Lab13.1 directory.
2. To simplify coding, add using statements to import the namespaces System,
System.Runtime.Remoting, System.Runtime.Remoting.Channels,
System.Runtime.Remoting.Channels.Http,
System.Runtime.Remoting.Channels.Tcp, and System.IO.
3. Specify the namespace Lab13, and in it, create a class named TestClient.
In subsequent steps of this exercise, you will code the Main method.
4. In the Main method of TestClient:
Create and register an HTTP channel and a TCP channel.
5. Invoke the Activator.GetObject method to create an AServer instance of
type Lab13.AServer named aServer at the following URL:
http://localhost:8086/DoAuthentication

6. Invoke the Activator.GetObject method to create an FServer instance of
type Lab13.FServer named fServer at the URL:
tcp://localhost:8085/DoFulfillment

7. In the try section of a try/catch block:
a. Invoke the Authenticate method of the aServer object, and use 1234 as
the customers ID. Print a message with the result to the console.
b. If authentication passes, call the fServer objects Fulfill method. Use
1234 as the customers ID and 5678 as the item number. Print a message
with the result to the console.
8. In the catch section of the try/catch block:
a. Catch any exceptions of type IOException, and print out their content.
b. Return a value of 1.
9. In a Visual Studio .NET Command Prompt window, build Testclient.exe.

Do not forget to reference Authenticate.exe, and
Fulfillment.exe in the command line.

Important
Module 13: Remoting and XML Web Services 35


10. Test the order system.
a. Ensure that no server application is running and listening on ports 8085
and 8086.

You can display your machines current TCP/IP network connections
by running the program netstat in a command prompt window. The
following example shows that port 7 is in use by a process with PID 1504:
C:\>netstat -o -n -a

Active Connections

Proto Local Address Foreign Address State PID
TCP 0.0.0.0:7 0.0.0.0:0 LISTENING 1504


b. Open two separate console windows. In one window, run
Fulfillment.exe, and in the other window, run Authenticate.exe.
c. Open a third console window and repeatedly run Testclient.exe.
The console output will vary because the random-number generator
values cause authentication and fulfillment to succeed or fail 50 percent
of the time.
A typical series of test client invocations would produce output similar
to the following:
>testclient
Authentication Server Customer 1234 Authorization: False

>testclient
Authentication Server Customer 1234 Authorization: False

>testclient
Authentication Server Customer 1234 Authorization: False

>testclient
Authentication Server Customer 1234 Authorization: True
Fulfillment Server Customer 1234 Item Number 5678
Shipped: False

>testclient
Authentication Server Customer 1234 Authorization: True
Fulfillment Server Customer 1234 Item Number 5678
Shipped: False

>testclient
Authentication Server Customer 1234 Authorization: True
Fulfillment Server Customer 1234 Item Number 5678
Shipped: True


Tip
36 Module 13: Remoting and XML Web Services


" "" " XML Web Services
! ASP.NET XML Web Services Overview
! ASP.NET Features
! Consuming an XML Web Service
! XML Web Service Discovery

*****************************ILLEGAL FOR NON-TRAINER USE******************************
XML Web services can be provided by and accessed from applications that
reside in a variety of hosts. Hosts include, but are not limited to, ASP.NET,
Microsoft Internet Explorer, executable files, Microsoft Windows NT Server,
and Microsoft Windows 2000 Component Services, also known as COM+
Services.
In the preceding sections, you have learned how to use .NET Remoting to
create executable files that host XML Web services by using the default HTTP
channel with SOAP formatting.
In this section, you will learn how to use Visual Studio .NET to implement an
ASP.NET XML Web service and how to access this XML Web service from
both a Web browser and a client application.
Topic Objective
To provide an overview of
the topics covered in this
section.
Lead-in
In this section, you will learn
how to use Visual Studio
.NET to implement an
ASP.NET XML Web service
and how to access the XML
Web service from a Web
browser and a client
application.
Module 13: Remoting and XML Web Services 37


ASP.NET XML Web Services Overview
! ASP.NET Files with .asmx Extensions Are Web Services
! Example of HelloWorld .asmx File
! If File Is Placed on Server Foo Inside a Virtual Directory Bar
# Access service by using:
# Access WSDL of service by using:
<%@ WebService Language="C#" Class="HelloWorld" %>
using System;
using System.Web.Services;
public class HelloWorld : WebService {
[WebMethod] public String SayHelloWorld() {
return "Hello World";
}
}
<%@ WebService Language="C#" Class="HelloWorld" %>
using System;
using System.Web.Services;
public class HelloWorld : WebService {
[WebMethod] public String SayHelloWorld() {
return "Hello World";
}
}
http://Foo/Bar/HelloWorld.asmx?wsdl
http://Foo/Bar/HelloWorld.asmx?wsdl
http://Foo/Bar/HelloWorld.asmx
http://Foo/Bar/HelloWorld.asmx

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This section introduces some of the basic features of ASP.NET that enable
developers to host and use XML Web services. The Internet is quickly evolving
from its present stage where Web sites deliver user interface (UI) pages to Web
browsers to a next generation of programmable Web sites that directly link
organizations, applications, services, and devices. These programmable Web
sites are more than passively accessed sites; they are reusable, intelligent XML
Web services.
Using .asmx Files
ASP.NET provides support for XML Web services with the .asmx file. An
.asmx file is a text file that is similar to an .aspx file. An .asmx file can be part
of an ASP.NET application that includes .aspx files. Like .aspx files, .asmx files
are automatically compiled by the ASP.NET runtime when a request to the
service is made. Subsequent requests are serviced by a cached pre-compiled
type object. Like .aspx files, .asmx files are then Uniform Resource Identifier-
addressable.
The following example shows the code that is contained in the
HelloWorld.asmx file. It is a simple example of a ASP.NET-hosted XML Web
service.
<%@ WebService Language="C#" Class="HelloWorld" %>

using System;
using System.Web.Services;

public class HelloWorld : WebService {

[WebMethod] public String SayHelloWorld() {
return "Hello World";
}

}
Topic Objective
To provide an overview of
ASP.NET XML Web
services.
Lead-in
ASP.NET provides support
for XML Web services with
the .asmx file, which is a
text file that is similar to an
.aspx file.
Delivery Tip
You can demonstrate the
SayHelloWorld service by
cutting and pasting the
HelloWorld.asmx code into
Notepad and saving it as a
file named HelloWorld.asmx
in the source directory for an
existing IIS virtual directory.
You then type the
appropriate URL in the
Internet Explorer Address
bar. To make the Address
bar visible, click the View
menu, point to Toolbars,
and then click Address Bar.
38 Module 13: Remoting and XML Web Services


The HelloWorld.asmx file starts with an ASP.NET directive, WebService, and
sets the language to C#. You can also set the language to Microsoft
Visual Basic.
The class HelloWorld contains the XML Web service and therefore is derived
from the base class WebService in the System.Web.Services namespace. All
of the methods that will be accessible as part of the service must have the
custom attribute, [WebMethod], in front of their signatures. In the preceding
example, the SayHelloWorld method is the services only method.
Using a Virtual Directory
To create an ASP.NET application, you can use an existing virtual directory or
create a new one. For example, if you installed Microsoft Windows 2000 Server
and Internet Information Server (IIS) on a computer, that computer probably
now has the directory C:\InetPub\WWWRoot.
To configure IIS, start the Internet Information Services tool. Click Start,
click Control Panel, Performance and Maintenance, Administrative Tools,
and then click Internet Information Services. In the Internet Information
Services tool, you can create a new virtual directory or promote an existing
directory.
! To create a new virtual directory, right-click an existing directory, and then
click New.
! To promote an existing directory, right-click a virtual directory, click
Properties and then set the Local Path.

If you place the HelloWorld.asmx file on a server called Foo inside a virtual
directory called Bar, you can use a URL in Internet Explorer to test the
application. For example, if you type http://Foo/Bar/HelloWorld.asmx in the
Address bar, the resulting page shows the public methods for this XML Web
service and the protocols, such as SOAP or HTTP GET, that you can use to
invoke these methods.
The public methods for the XML Web service are marked with the
[WebMethod] attribute.
The Web Services Description Language XML file for this service is produced
when you type http://Foo/Bar/HelloWorld.asmx?WSDL in the
Internet Explorer Address bar. This WSDL file is important and can be used
by clients to access the service.
Module 13: Remoting and XML Web Services 39


ASP.NET Features
! ASP.NET Can Expose Web Services Defined in a .NET Class
# Class derives from WebService, method attribute [WebMethod]
#
# Class source file is compiled into a library DLL and placed in \Bin
# The .asmx file contains a single line that names the class
! Advanced ASP.NET Features
# Data sets, Global.asax, Session and Application objects, pattern matching
<%@ WebService Class="MyNameSpace.HelloWorld" %>
<%@ WebService Class="MyNameSpace.HelloWorld" %>
namespace MyNameSpace {
public class HelloWorld : WebService {
[WebMethod] public String SayHelloWorld() {
return "Hello World"; }
}
}
namespace MyNameSpace {
public class HelloWorld : WebService {
[WebMethod] public String SayHelloWorld() {
return "Hello World"; }
}
}
csc /out:bin\helloworld.dll /t:library helloworld.cs
csc /out:bin\helloworld.dll /t:library helloworld.cs

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In ASP.NET, you can create an XML Web service by simply exposing a .NET
class that inherits from the WebService class. You compile the classs source
file into a library DLL and place this DLL in the ASP.NET applications \Bin
subdirectory. You then create in the ASP.NET applications directory an .asmx
file that contains only the following single line of code:
<%@ WebService Class="<namespace>.<classname>" %>

The HelloWorld.cs source code defines a HelloWorld service with an exported
SayHelloWorld method in the MyNameSpace namespace, as shown in the
following example:
using System;
using System.Web.Services;

namespace MyNameSpace {

public class HelloWorld : WebService {
[WebMethod] public String SayHelloWorld() {
return "Hello World";
}
}

}

To compile this HelloWorld.cs file in the ASP.NET application directory, you
type the following command in a Visual Studio .NET Command Prompt
window:
>csc /out:bin\helloworld.dll /t:library helloworld.cs

Topic Objective
To explain how to create an
XML Web service by
exposing a .NET class that
inherits from the
WebService class, and to
introduce advanced
ASP.NET features.
Lead-in
In ASP.NET, you can create
an XML Web service by
simply exposing a .NET
class that inherits from the
WebService class.
40 Module 13: Remoting and XML Web Services


You then create the file named HelloWorld.asmx in the ASP.NET application
directory. HelloWorld.asmx contains the following single line of code:
<%@ WebService Class="MyNameSpace.HelloWorld" %>

The only methods that are exposed from a service are those class methods that
are flagged with a [WebMethod] custom attribute. Without this attribute, the
method is not exposed from the service. Not exposing a method from a service
is useful when you want to hide implementation details that are called by public
XML Web service methods or when the WebService class is also used in local
applications.

A local application can use any public method, but only [WebMethod]
methods are remotely accessible through SOAP.

Advanced ASP.NET Features
ASP.NET is also useful for building complex XML Web services. Advanced
ASP.NET features include:
! Data sets
Data sets are a powerful new XML-based technique to represent
disconnected data. Data sets can be returned from an XML Web service
method. Because Data sets can store complex information and relationships
in an intelligent structure, they enable you to take full advantage of XML
Web services. When you expose Data sets through a service, you can limit
the database connections to your data server.
! Global.asax file
The Global.asax file adds application-level logic and event-handling code to
Web applications.
! Session and Application objects
You can use ASP.NET intrinsics, such as the Session and Application
objects, to manage an ASP.NET applications state.
! Text Pattern Matching
Text Pattern Matching is a technology that can be used to address any
Uniform Resource Identifier that returns text as if it were an XML Web
service.

Further discussion of these and other advanced ASP.NET features is beyond the
scope of this course.
Note
Module 13: Remoting and XML Web Services 41


Demonstration: Using Visual Studio .NET to Create an XML Web
Service

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this section, you have learned how to create an XML Web service by using
ASP.NET. Visual Studio .NET provides an even easier way to build ASP.NET-
hosted XML Web services.
In this demonstration, you will see how to create a service called MathService
that has a single method named SalesTax. The SalesTax method takes two
parameters: The first parameter represents the sales amount; and the second
parameter represents the fractional sales tax rate. For example, a sales tax rate
of 8.5 percent is represented as .085. SalesTax returns a total amount that is
equal to the sales amount plus the sales tax.
! To create a service called MathService that has a single method named
SalesTax
1. Start Visual Studio, and create a new project.
a. In the New Project dialog box, select Visual C# Projects as the type.
b. Select ASP.NET Web Service as the template. You may need to scroll
down to see ASP.NET Web Service.
c. In the Location box, type the name of the Web server,
http://localhost/MathService. The grayed out Name box will now
contain the text MathService. Then click OK.
2. On the View menu, click Code.
3. Because the XML Web service in this demonstration is simple, you can
work on the code directly. For more complex XML Web services, you can
use the Service1.asmx.cs Design palette that is displayed when you create a
new project of type XML Web service. The Service1.asmx.cs Design palette
enables the drag-and-drop operation of rapid application development that
can make complex services easier to set up.
4. In the Service1 class, examine the HelloWorld method in the commented-
out WEB SERVICE EXAMPLE.
Topic Objective
To demonstrate how to use
Visual Studio .NET to
create an XML Web service.
Lead-in
In this demonstration, you
will learn how to create a
service called MathService
that has a single method
named SalesTax.
Delivery Tip
Although this demonstration
includes detailed
instructions, it is not a
guided practice. Present the
demonstration and suggest
that students try the
demonstration for
themselves later.
42 Module 13: Remoting and XML Web Services


5. In Service1.asmx.cs, implement the SalesTax method by inserting the
following code after the comments for the HelloWorld method. You should
leave the HelloWorld code commented out.
[WebMethod] public float SalesTax(
float salesAmount, float fractionalTaxRate) {
return salesAmount +
(salesAmount * fractionalTaxRate);
}

6. On the Build menu, click Build Solution to create the XML Web service.
7. Use Internet Explorer to access the XML Web service at the following
URL:
http://localhost/MathService/Service1.asmx

To use Internet Explorer to view an XML Web service from within the
Visual Studio environment, right-click the .asmx file in the Solution
Explorer window, and then click View in Browser.

8. In the Internet Explorer page that is returned in step 7, click the link for the
Service1 operation labeled SalesTax.
9. In the Internet Explorer page that is returned in step 8, enter some
parameters, and then click Invoke to verify that the correct value is returned
and displayed in SOAP/XML format.
10. Use Internet Explorer to access and verify that the XML Web services
description is obtained from the following URL:
http://localhost/MathService/Service1.asmx?wsdl


Tip
Module 13: Remoting and XML Web Services 43


Consuming an XML Web Service
! To Consume a Web Service, Use Wsdl.exe
# Identify the object or service URI to be used
# Run Wsdl.exe on the services URI
# Write the client code that calls the remote object
# Build the client and proxy code
http://localhost/MathService/Service1.asmx
http://localhost/MathService/Service1.asmx
wsdl http://localhost/MathService/Service1.asmx?wsdl
wsdl http://localhost/MathService/Service1.asmx?wsdl
csc salestaxclient.cs service1.cs
csc salestaxclient.cs service1.cs
Service1 salesTaxService = new Service1();
float totalAmount = salesTaxService.SalesTax(
amount,taxRate);
Service1 salesTaxService = new Service1();
float totalAmount = salesTaxService.SalesTax(
amount,taxRate);

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In addition to providing technology that enables you to create XML Web
services, the .NET Framework provides a sophisticated set of tools and code to
consume XML Web services. Consuming an XML Web service means
accessing it as a client. Because XML Web services are based on open
protocols, such as SOAP and HTTP, this client technology can also be used to
consume XML Web services that are not built by using the .NET Framework.
Using the Web Services Description Language Tool
The .NET Framework SDK provides a tool that is called the Web Services
Description Language tool (Wsdl.exe). You can use this tool to download the
WSDL description of an XML Web service and create a proxy class that
addresses this service. For XML Web services that are created by using
ASP.NET, the proxy class is similar to the class that is defined in the .asmx file.
However, the proxy class contains only methods with the [WebMethod]
custom attribute.
An application that uses an XML Web service creates an instance of the
services proxy class and invokes the services methods as it would with any
local object. You compile and build your code with this proxy class included.
Alternatively, you can use features of the Visual Studio environment to build
applications that consume XML Web services. These Visual Studio features are
beyond the scope of this course.
Topic Objective
To explain how XML Web
services are consumed and
how to use the Web
Services Description
Language tool.
Lead-in
In addition to providing
technology that enables you
to create XML Web
services, the .NET
Framework provides a
sophisticated set of tools
and code to consume XML
Web services.
44 Module 13: Remoting and XML Web Services


The following process shows how to create a simple application that uses the
XML Web service that is created in the Using Visual Studio to Create an XML
Web service demonstration in this module.
! To use the Service1 Web Service
1. Identify the object and the service that should be used.
The Salestaxclient application will use the SalesTax method of the Service1
service whose URL is:
http://localhost/MathService/Service1.asmx

Typically a client uses an XML Web service that is located on a
remote computer. If Service1 were located in a MathService virtual
directory on a Web server named foo, the URL for the service would be:
http://foo/MathService/Service1.asmx


2. Run the WSDL tool, Wsdl.exe, and point it at the URL where the server
object is located.
You can request that the Web Service Utility retrieves the schema and, from
it, generates a source file that contains a proxy for the services class.
To generate a proxy for the Service1 class in a file named Service1.cs, type
the following command in a Visual Studio .NET Command Prompt
window:
wsdl http://localhost/MathService/service1.asmx?wsdl

Delivery Tip
The use of the Web
Services Description
Language tool (Wsdl.exe) is
covered in detail because
the students will need this
information to do Lab 13.2,
Using an XML Web service.
If you feel that students may
benefit from a full
demonstration, you can
demonstrate the steps here.
Note
Module 13: Remoting and XML Web Services 45


The proxy, Service1.cs, contains code similar to the following:
//...
using System.Diagnostics;
using System.Xml.Serialization;
using System;
using System.Web.Services.Protocols;
using System.ComponentModel;
using System.Web.Services;
//...
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Web.Services.WebServiceBindingAttribute!
(Name="Service1Soap", Namespace="http://tempuri.org/")]
public class Service1 :
System.Web.Services.Protocols.SoapHttpClientProtocol {
//...
public Service1() {
this.Url =
"http://localhost/mathservice/service1.asmx";
}
//...
[System.Web.Services.Protocols.SoapDocumentMethodAttribute
!
("http://tempuri.org/SalesTax",
Use=System.Web.Services.Description.SoapBindingUse.Literal,
ParameterStyle=
System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
public System.Single SalesTax(System.Single salesAmount,
System.Single fractionalTaxRate) {
object[] results =
this.Invoke("SalesTax",
new object[] {salesAmount,
fractionalTaxRate});
return ((System.Single)(results[0]));
}
//...

46 Module 13: Remoting and XML Web Services


3. Write the client code that calls the remote object. Create a text file named
Salestaxclient.cs in the folder <install folder>\DemoCode\Mod13\
Demo13.2 containing the following code:
using System;

public class Class1 {
public Class1() {}
public static int Main(string[] args) {
Console.WriteLine("Enter Sales Amount:");
float amount = Single.Parse(Console.ReadLine());
Console.WriteLine(
"Enter Fractional Sales Tax Rate:");
float taxRate = Single.Parse(Console.ReadLine());
Service1 salesTaxService = new Service1();
float totalAmount =
salesTaxService.SalesTax(amount,taxRate);
Console.WriteLine(
"Total: {0}",totalAmount.ToString());
return 0;
}
}

4. Build the client executable file by using the client code and proxy code. You
must include references to the assemblies that are used by the client and
proxy code.
5. To build Salestaxclient.exe, type the following command in a Visual Studio
.NET Command Prompt window:
csc salestaxclient.cs service1.cs

6. Run the client, and enter parameters as prompted.
Running Salestaxclient.exe produces output that should be similar to the
following:
salestaxclient
Enter Sales Amount:
100
Enter Fractional Sales Tax Rate:
.08
Total: 108


Module 13: Remoting and XML Web Services 47


XML Web Service Discovery
! Discovery The Process of Locating and Interrogating XML Web
Service Descriptions
! Discovery Services Are Evolving and Changing Rapidly
! Disco Microsoft XML Web Services Discovery Tool
# Disco file is an XML document that links to descriptions of XML Web
Services
# Disco file is currently created and used by Visual Studio
! Universal Description, Discovery, and Integration Project
# Open framework for describing services, discovering businesses,
and integrating business services that use the Internet
# Cross-industry support and platform independence
# For more information, see http://www.uddi.org

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Web Service discovery is the process of dynamically locating and interrogating
Web Service descriptions, which is a preliminary step for accessing an XML
Web service. The discovery process allows an XML Web service client that
may not know the Web Services Uniform Resource Identifier to locate an
XML Web service, to determine the capabilities of that Web Service, and to
properly interact with it.
Web Service discovery is currently evolving, and the information that is
presented in this topic is subject to change.
Disco Microsoft Web Services Discovery Tool
In the current version of the .NET Framework, you can enable programmatic
discovery when an XML Web service publishes a file with a .disco extension.
Disco is the Microsoft XML Web services discovery tool. A .disco file is an
XML document that contains links to other resources that describe the XML
Web service. Currently, when you create an ASP.NET-hosted XML Web
service with Visual Studio .NET, .disco files are automatically generated and
used.
For more information about Disco, see the Visual Studio .NET and .NET
Framework SDK documentation.
Universal Description, Discovery, and Integration
The Universal Description, Discovery and Integration (UDDI) project is a
cross-industry project that is dedicated to creating a platform-independent, open
framework for describing Web Services, discovering businesses, and
integrating business services that use the Internet. The project is driven by all of
the major platform and software providers, marketplace operators, and
e-business leaders.
For more information about UDDI, see the UDDI Web site at
http://www.uddi.org/.
Topic Objective
To introduce the Web
Service discovery process
and the tools that are
available for discovery.
Lead-in
Web Service discovery is
the process of dynamically
locating and interrogating
Web Service descriptions,
which is a preliminary step
for accessing an XML Web
service.
48 Module 13: Remoting and XML Web Services


Lab 13.2: Using an XML Web Service

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives
After completing this lab, you will be able to:
Access an XML Web service by creating a proxy using the Web Services
Description Language tool.

Lab Setup
Starter and solution files are associated with this lab. The starter files are in the
folder <install folder>\Labs\Lab13.2\Starter. The solution files for this lab are
in the folder <install folder>\Labs\Lab13.2\Solution.
Topic Objective
To introduce the lab.
Lead-in
In this lab, you will access
an XML Web service by
creating a proxy using the
Web Services Description
Language tool.
Module 13: Remoting and XML Web Services 49


Scenario
This lab is similar to the scenario used in Lab 13.1, Building an Order-
Processing Application by Using Remoted Servers. That scenario was based on
a simple distributed order-processing application, in which a customer specifies
a customer ID and an item number for an item that the customer wants to
purchase, and the application processes the order. The order processing
involves authenticating the customers ID and arranging order fulfillment that is
to say, having the ordered item sent to the customer.
This lab extends Lab 13.1 by having the client application calculate the cost of
the ordered item. Your client will use an XML Web service to calculate the
items cost including sales tax. This scenario simulates the case where you do
not have access to the XML Web services source code or assembly.
You will build a MathService XML Web service to calculate the total cost of an
item given the pretax cost and fractional tax rate. You will then use the Web
Services Description Language tool (Wsdl.exe) to access and create a .NET
proxy class for this XML Web service. Then you will build a client called
Testclient2.exe that uses this proxy to access the XML Web services
Service1.SalesTax method to calculate the cost of the ordered item.
Estimated time to complete this lab: 55 minutes
50 Module 13: Remoting and XML Web Services


Exercise 1
Creating the XML Web Service Proxy
In this exercise, you will create the MathService XML Web service and use the
Web Services Description Language tool (Wsdl.exe) to create a proxy class to
this XML Web service.
! Create the MathService XML Web service using Visual Studio .NET
1. Start Visual Studio, and create a new project.
a. In the New Project dialog box, select Visual C# Projects as the type.
b. Select ASP.NET Web Service as the template. You may need to scroll
down to see ASP.NET Web Service.
c. In the Location box, type the name of the Web server,
http://localhost/MathService. The grayed out Name box will now
contain the text MathService. Then click OK.
2. On the View menu, click Code.
Because the XML Web service in this demonstration is simple, you can
work on the code directly. For more complex XML Web services, you can
use the Service1.asmx.cs Design palette that is displayed when you create a
new project of type XML Web service. The Service1.asmx.cs Design palette
enables the drag-and-drop operation of rapid application development that
can make complex services easier to set up.
3. In the Service1 class, examine the HelloWorld method in the commented-
out WEB SERVICE EXAMPLE.
4. In Service1.asmx.cs, implement the SalesTax method by inserting the
following code after the comments for the HelloWorld method. You should
leave the HelloWorld code commented out.
[WebMethod] public float SalesTax(
float salesAmount, float fractionalTaxRate) {
return salesAmount +
(salesAmount * fractionalTaxRate);
}

5. On the Build menu, click Build Solution to create the XML Web service.
6. Use Internet Explorer to access the XML Web service at the following
URL:
http://localhost/MathService/Service1.asmx

To use Internet Explorer to view an XML Web service from within the
Visual Studio environment, right-click the .asmx file in the Solution
Explorer window, and then click View in Browser.

Tip
Module 13: Remoting and XML Web Services 51


7. In the Internet Explorer page that is returned in step 6, click the link for the
Service1 operation labeled SalesTax.
8. In the Internet Explorer page that is returned in step 7, type some
parameters, and then click Invoke to verify that the correct value is returned
and displayed in SOAP/XML format.
9. Use Internet Explorer to access and verify that the XML Web services
description is obtained from the following URL:
http://localhost/MathService/Service1.asmx?wsdl


! Create the XML Web service proxy

To use Microsoft Visual Studio .NET tools within a command
prompt window, the command prompt window must have the proper
environment settings. The Visual Studio .NET Command Prompt window
provides such an environment. To run a Visual Studio .NET Command Prompt
window: on the Start menu, point to All Programs, point to Microsoft Visual
Studio .NET, Visual Studio .NET Tools, and then click Visual Studio .NET
Command Prompt.

In a Visual Studio .NET Command Prompt window, from the Lab 13.2
Starter directory, create a proxy to the authentication XML Web service
named Service1.cs by running the following command:
wsdl http://localhost/MathService/service1.asmx?wsdl


Important
52 Module 13: Remoting and XML Web Services


Exercise 2
Creating the Test Client
In this exercise, you will create a test client. The client calls the authentication
server. If the user is authenticated, then the client calls the order fulfillment
server. If the order is fulfilled, then the client calls the MathService XML Web
service to calculate the total cost including sales tax. For simplicity, the client
will assume that all items cost $100 and that the sales tax rate is always 0.1.
! Create the test client
1. Use the editor of your choice to examine the C# source file that is named
Testclient2.cs located in the Lab13.2 Starter directory.
2. Add code to Testclient2.cs after the following command that outputs the
results from calling the fulfillment service:
Console.WriteLine(
"Fulfillment Server Customer 1234 Item Number 5678
Shipped: {0}", shipped.ToString() );

The code should check the status of fulfillment. If shipped is true, then
perform the following steps:
a. Instantiate a new Service1 object named salesTaxService.
b. Call the salesTaxService.SalesTax method, passing 100.0F as its first
argument and 0.1F as its second argument.
c. Print out to the console the returned total cost value.
3. Build Testclient2.exe in a Visual Studio .NET Command Prompt window as
follows:
a. Reference Fulfillment.exe, and Authenticate.exe.
b. Specify the C# source files, Service1.cs and Testclient2.cs.
c. Specify that the output file should be named Testclient2.exe.
Module 13: Remoting and XML Web Services 53


4. Test the order fulfillment system, by performing the following steps:
a. In a console window, from the Lab 13.2 Starter directory, run
Authenticate.exe.
b. In a second console window, from the Lab 13.2 Starter directory, run
Fulfillment.exe.
c. In a third console window, repeatedly run Testclient2.exe.
The console output will vary because the random-number generator
values cause authentication and fulfillment to succeed or fail 50 percent
of the time.
A typical series of test client invocations would produce output similar
to the following:
>testclient2
Authentication Server Customer 1234 Authorization: True
Fulfillment Server Customer 1234 Item Number 5678!
Shipped: True
Total Cost: 110

>testclient2
Authentication Server Customer 1234 Authorization: True
Fulfillment Server Customer 1234 Item Number 5678!
Shipped: False

54 Module 13: Remoting and XML Web Services


Review
! Remoting
! Remoting Configuration Files
! XML Web Services

*****************************ILLEGAL FOR NON-TRAINER USE******************************
1. Can two application domains that are on the same computer each have a
channel that listens on the same port number?
Ports are a machine-wide resource; therefore, on one computer, it is
illegal to register multiple channels that listen on the same port
number, even if the channels are registered in different application
domains.


2. What is the purpose of a proxy?
The proxy object acts as a representative of the remote object and
ensures that all calls that are made on the proxy are forwarded to the
correct remote object instance. All methods that are called on the proxy
are automatically forwarded to the remote class, and any results are
returned to the client.


3. Can a remotely instantiated object be returned by value?
No. All objects that are instantiated remotely are returned by reference.


4. What determines whether a remotely instantiated objects parameters and
return values are passed by reference or by value?
Objects whose classes are marked with the SerializableAttribute are
marshal-by-value, and objects that inherit from
System.MarshalByRefObject are marshal-by-reference.


Topic Objective
To reinforce module
objectives by reviewing
key points.
Lead-in
The review questions cover
some of the key concepts
taught in the module.
Module 13: Remoting and XML Web Services 55


5. What file extension is typically used by ASP.NET-hosted XML Web
services?
ASP.NET provides support for XML Web services with the .asmx file.


6. In ASP.NET, how do you specify that a service is defined in a prebuilt
assembly, and where should that assemblys DLL be located in relation to
the ASP.NET application?
The .asmx file should contain the single line:
<%@ WebService Class="<namespace>.<classname>" %>
The assembly library DLL should be in the applications \Bin
subdirectory.


7. How can a client invoke an XML Web service that is not implemented by
using the .NET Framework or in which the XML Web services assembly or
source code is not available?
The Web Services Description Language tool (Wsdl.exe) can be used to
read the WSDL description of an XML Web service and create a proxy
class. The client can use the proxy class to invoke the methods of the
XML Web service.



56 Module 13: Remoting and XML Web Services


Course Evaluation

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Your evaluation of this course will help Microsoft understand the quality of
your learning experience.
To complete a course evaluation, go to http://www.metricsthatmatter.com/
survey/.
Microsoft will keep your evaluation strictly confidential and will use your
responses to improve your future learning experience.



THIS PAGE INTENTIONALLY LEFT BLANK

Topic Objective
To direct students to a Web
site to complete a course
evaluation.
Lead-in
Between now and the end of
the course, you can go to
the Web site listed on this
page to complete a course
evaluation.








Contents
Overview 1
Introduction to Threading 2
Using Threads in .NET 9
Thread Safety 29
Special Thread Topics 51
Asynchronous Programming in .NET 74
Lab 14: Working With Multithreaded
Applications 92
Review 108

Module 14 (Optional):
Threading and
Asynchronous
Programming



Information in this document, including URL and other Internet Web site references, is subject to
change without notice. Unless otherwise noted, the example companies, organizations, products,
domain names, e-mail addresses, logos, people, places, and events depicted herein are fictitious,
and no association with any real company, organization, product, domain name, e-mail address,
logo, person, places or events is intended or should be inferred. Complying with all applicable
copyright laws is the responsibility of the user. Without limiting the rights under copyright, no
part of this document may be reproduced, stored in or introduced into a retrieval system, or
transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or
otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual
property rights covering subject matter in this document. Except as expressly provided in any
written license agreement from Microsoft, the furnishing of this document does not give you any
license to these patents, trademarks, copyrights, or other intellectual property.

2001-2002 Microsoft Corporation. All rights reserved.

Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint,
Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and
Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A.
and/or other countries.

The names of actual companies and products mentioned herein may be the trademarks of their
respective owners.


Module 14 (Optional): Threading and Asynchronous Programming iii


Instructor Notes Module 14
This module provides students with knowledge about the support that the
Microsoft .NET Framework provides for working with multithreaded
applications and asynchronous programming.
After completing this module, students will be able to:
! Create and manage threads.
! Create thread-safe code.
! Create and use timers.
! Create threads using thread pools.
! Create managed threads that interact well with COM components.
! Create Microsoft Windows Forms applications with background threads.
! Make asynchronous calls using delegates.

Materials and Preparation
This section provides the materials and preparation tasks that you need to teach
this module.
Required Materials
To teach this module, you need the Microsoft PowerPoint file 2349B_14.ppt.
Preparation Tasks
To prepare for this module:
! Read all of the materials for this module.
! Practice the demonstrations.
! Complete the lab.

Presentation:
150 Minutes

Lab:
60 Minutes
iv Module 14 (Optional): Threading and Asynchronous Programming


Demonstrations
This section provides demonstration procedures that will not fit in the margin
notes or are not appropriate for the student notes.
The code for each of the following demonstrations is contained in project
folders that are located in <install folder>\Democode\Mod14.
Use the debugger to step through the code while you point out features and ask
students what they think will happen next.
Managing Threads
In this demonstration, you will show students how to create and manage threads
in the .NET Framework by using some of the classes and methods that were
covered in Using Threads in .NET.
The code for this demonstration is contained in one project and is located in
<install folder>\Democode\Mod14\Demo14.1.
Interrupt and Abort
In this demonstration, you will show students how to terminate threads by using
the Thread.Interrupt and Thread.Abort methods.
The code for this demonstration is contained in one project and is located in
<install folder>\Democode\Mod14\Demo14.2.
Using Synchronization Contexts and Synchronized Code
Regions to Provide Thread Safety
In this demonstration, you will use a series of tests to show students how to
achieve thread safety through the use of synchronization contexts and
synchronized code regions.
The code for this demonstration is contained in one project and is located in
<install folder>\Democode\Mod14\Demo14.3.
Using Synchronization Techniques and Thread Pooling
In this demonstration, you will show students how to perform synchronization
by using the C# lock keyword and manual synchronization primitives, and also
how to obtain multiple threads by using a thread pool.
The code for this demonstration is contained in one project and is located in
<install folder>\Democode\Mod14\Demo14.4.
Module 14 (Optional): Threading and Asynchronous Programming v


Windows Forms Threading
In this demonstration, you will show students how to use a background thread
in a Windows Forms application.
The code for this demonstration is contained in one project and is located in
<install folder>\Democode\Mod14\Demo14.5.
Asynchronous File Stream Read
In this demonstration, you will show students how to use synchronous and
asynchronous read methods on a file stream. For the asynchronous case, you
will show four different ways to complete the operation: callback, poll, end
method call, and wait with timeout.
The code for this demonstration is contained in one project and is located in
<install folder>\Democode\Mod14\Demo14.6.
Using a Delegate
In this demonstration, you will show students how to use a delegate object to
make asynchronous calls.
The code for this demonstration is contained in one project and is located in
<install folder>\Democode\Mod14\Demo14.7.
Multimedia Presentation
This section lists the multimedia items that are part of this module. Instructions
for launching and playing the multimedia are included with the relevant slides.
Asynchronous Programming
This animation illustrates the .NET Framework common language runtime
support for asynchronous programming using Delegate objects.
vi Module 14 (Optional): Threading and Asynchronous Programming


Module Strategy
Use the following strategy to present this module:
! Introduction to Threading
Provide a general introduction to the concept of threads and discuss
advantages and disadvantages of using them. As a simple illustration of the
concept of threading, use Microsoft Internet Explorer to show how you can
still do work while waiting for a download operation to complete.
Provide a high level overview of the System.Threading namespace and
introduce the asynchronous design pattern, which you will cover in more
detail in Asynchronous Programming in .NET.
Explain how application domains play a key role in .NET threading
architecture.
! Using Threads in .NET
Focus on the classes in the System.Threading namespace that are used to
start, manage, and terminate threads.
In addition to the preceding operations, discuss the role of managed thread
local storage.
! Thread Safety
Focus on the issues that students may encounter in multithreaded
programming from sharing data and resources between threads, as a result
of thread synchronization.
Introduce strategies that the .NET Framework provides for dealing with
synchronization, in particular classes and interfaces in System.Threading.
! Special Thread Topics
Introduce the Thread.Timer class, which provides a mechanism for
executing methods at specified intervals. Explain how a TimerCallback
delegate is used in conjunction with a Timer.
Discuss the use of thread pools in making multiple threads operate more
efficiently.
Discuss how managed threads call into a COM object.
Outline best practices for implementing thread-safe code.
Module 14 (Optional): Threading and Asynchronous Programming vii


! Asynchronous Programming in .NET
Provide a brief definition of the concept of asynchronous programming as
the ability to issue method calls to other components, and to carry on with
other work without waiting for the operation to complete.
Introduce the asynchronous design pattern, and emphasize that one of its
innovations is that the caller can decide whether a particular call should be
asynchronous.
Spend most of the time in this section on the Asynchronous File Stream
Read Example. In addition to the code on the slides and information in the
Student Notes, there is an accompanying demonstration.
Discuss the use of Asynchronous delegates to call a synchronous method in
an asynchronous manner.
Play the Asynchronous Programming animation to illustrate the .NET
Framework common language runtime support for asynchronous
programming using Delegate objects.

Module 14 (Optional): Threading and Asynchronous Programming 1


Overview
! Introduction to Threading
! Using Threads in .NET
! Thread Safety
! Special Thread Topics
! Asynchronous Programming in .NET

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this module, you will learn about the support that the Microsoft .NET
Framework provides for working with multithreaded applications and
asynchronous programming. The common language runtime abstracts much of
the threading support into classes that greatly simplify most threading tasks.
Even if you do not create your own threads explicitly, you need to understand
how your code should handle multiple threads if it is run in a multithreaded
environment.
You will also learn how to handle thread synchronization to maintain
application responsiveness and avoid potential data corruption and other
problems.
In the .NET Framework, asynchronous programming is a feature that is
supported by Remoting, Networking: HTTP, TCP, File I/O, ASP.NET, and
Microsoft Message Queue Server (MSMQ). Because asynchronous
programming is a core concept, the .NET Framework provides a common
design pattern for handling asynchronous execution. This module introduces the
.NET Framework asynchronous Design Pattern and gives examples of its use.
After completing this module, you will be able to:
! Create and manage threads.
! Create thread-safe code.
! Create and use timers.
! Create threads using thread pools.
! Create managed threads that interact well with COM components.
! Create Microsoft Windows Forms applications with background threads.
! Make asynchronous calls using delegates.

Topic Objective
To provide an overview of
the module topics and
objectives.
Lead-in
In this module, you will learn
about the support that the
.NET Framework provides
for working with
multithreaded applications
and asynchronous
programming.
2 Module 14 (Optional): Threading and Asynchronous Programming



" "" " Introduction to Threading
! Overview of Threads
! Overview of Support for Threading in .NET
! Thread Architecture in .NET

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this section, you will learn about the advantages and disadvantages of using
threads and the support for threads that is provided by the .NET Framework.
Topic Objective
To provide an overview of
the topics that you will cover
in this section.
Lead-in
In this section, you will learn
about the advantages and
disadvantages of using
threads and the support for
threads that is provided by
the .NET Framework.
Module 14 (Optional): Threading and Asynchronous Programming 3


Overview of Threads
! Threads
# The basic unit to which an operating system allocates processor time
# Enable multiple activities to appear to occur simultaneously
! Advantages of using multiple threads
# Application does background processing while keeping the UI responsive
# Distinguish tasks of varying priority
# Communicate over a network, to a Web server, and to a database
! Potential disadvantages of using threads
# Diminished performance due to increased operating system overhead, for
example, thread context switching
# Controlling code execution with many threads is complex, and can be a
source of many difficult to find and fix bugs

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Whether you are developing for computers with one processor or several, you
want your application to provide the most responsive interaction with the user,
even if the application is currently doing other work. Using multiple threads of
execution is one of the most powerful ways to keep your application responsive
to the user and at the same time make use of the processor in between or even
during user events.
Threads
Threads are the basic unit to which an operating system allocates processor
time, and more than one thread can execute code inside a process. Each thread
maintains exception handlers, a scheduling priority, and a set of structures that
the system uses to save the thread context until it is scheduled. The thread
context includes all of the information that the thread needs to smoothly resume
execution, including the threads set of CPU registers and stack, in the address
space of the threads host process.
Operating systems use processes to separate the different applications that they
are executing. The .NET Framework further subdivides an operating system
process into lightweight, managed subprocesses, called application domains,
represented by System.AppDomain. One or more managed threads,
represented by System.Threading.Thread, can run in one or any number of
application domains within the same process. A preemptive multitasking
operating system allocates a processor time slice to each thread that it executes.
Because each time slice is small, multiple threads appear to execute at the same
time.
Topic Objective
To define threads in the
context of general
multitasking and state the
primary advantages and
disadvantages of using
threads.
Lead-in
Whether you are developing
for computers with one
processor or several, you
want your application to
provide the most responsive
interaction with the user,
even if the application is
currently doing other work.
Delivery Tip
You can quickly illustrate
how multiple threads are
used to create nonblocking
UI by running the Microsoft
Internet Explorer Web
browser. Start the browser
and type a URL address or
click a link to a site that
takes some time to
download. Show how you
can still interact with the
application during this
download operation, for
example, canceling the
current download by clicking
Stop.
4 Module 14 (Optional): Threading and Asynchronous Programming


Advantages of using multiple threads
Threads enable multiple activities to appear to occur simultaneously in an
application. For example, a user can edit a worksheet while another thread
recalculates other parts of the worksheet within the same application. Threading
maintains the responsiveness of the user interface while background processing
is occurring. Threads can be used to enable users to receive notification of a
tasks progress, and even to cancel the task at any time.
You can also use threads to distinguish tasks of varying priority. For example,
you can use a high-priority thread to manage time-critical tasks, and a low-
priority thread to perform other tasks.
Threads are also useful when an application must wait for an event, such as user
input or a read from the network or from a file, before continuing to execute.
Multiple threads enable the processor to handle a separate task while waiting
for the completion of the event.
Potential disadvantages of using threads
In some circumstances, threading may cause application performance to
degrade. On a single-processor computer, a compute-bound task, such as
calculating a series of values by using multiple threads, would be slower
because of the overhead caused by thread-switching. Keeping track of a large
number of threads consumes significant processor time. If there are too many
threads, most of them will not make significant progress.
Controlling code execution with many threads is complex, and can be a source
of many bugs. You run the risk of data corruption or other problems, such as
deadlocks and race conditions. To protect an applications data from possible
corruption, you must ensure that access to shared data is properly synchronized.
For more information about deadlocks and race conditions, see Thread Safety in
this module.

While this module focuses on threading, there are other ways of
achieving concurrency that include using multiple processes/AppDomains,
messaging, and database stored procedures.

Note
Module 14 (Optional): Threading and Asynchronous Programming 5


Overview of Support for Threading in .NET
! Basic thread namespace
# System.Threading
! Standard design pattern for asynchronous
programming
# Hides thread creation and synchronization details
# Supported by .NET Framework delegate classes and/or
the IAsyncResult interface

*****************************ILLEGAL FOR NON-TRAINER USE******************************
To provide support for multithreaded programming, the .NET Framework
supplies its own namespace for thread creation and management and a
programming model to handle asynchronous operations.
Basic thread namespace
The System.Threading namespace provides classes and interfaces that enable
multithreaded programming. System.Threading includes classes that are used
to create and manage threads, protect shared data, and improve system
responsiveness.
The System.Threading.Thread class is an abstraction of a managed thread that
executes within the runtime. This includes threads that are created by the
runtime and those that are created outside the runtime but that interact with the
runtime environment to execute some managed code.
The System.Threading namespace includes classes that assist with thread
synchronization and protection of shared data. For example, the
System.Threading.Interlocked class provides atomic operations for variables
that are shared by multiple threads. The System.Threading.Monitor class
provides a synchronization mechanism to ensure that where multiple threads
access a shared resource, only one thread can access the resource at a particular
time.
Topic Objective
To provide a high-level
overview of the
System.Threading
namespace and introduce
the asynchronous design
pattern.
Lead-in
Before examining how
threads work in the .NET
Framework, lets briefly
review important ways in
which the .NET Framework
provides support for
threading.
6 Module 14 (Optional): Threading and Asynchronous Programming


Standard design pattern for asynchronous programming
The standard asynchronous design pattern in the .NET Framework provides an
efficient model to manage asynchronous operations and provide a consistent
programming model. The .NET Framework provides support for asynchronous
programming using the IAsyncResult interface and delegate classes that enable
a programmer to avoid some of the implementation details of threading. The
asynchronous design pattern is covered in more detail later in this module.
For more information about the System.Threading namespace and its classes,
see System.Threading Namespace in the .NET Framework SDK
documentation.
Module 14 (Optional): Threading and Asynchronous Programming 7


Thread Architecture in .NET
Example of a Process Hosting AppDomains
AppDomain A AppDomain B
Shared Data
Shared Data
Shared Data
Shared Data
Thread 1 Thread 2 Thread 3
Thread
Specific
Data
Thread
Specific
Data
Thread
Specific
Data
Thread
Specific
Data
Thread
Specific
Data
Thread
Specific
Data

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Application domains, also known as AppDomains, play a key role in how
threads work in the .NET Framework. An application domain is a runtime
representation of a logical process within a physical process. A single process
can contain multiple application domains, each of which is completely isolated
from other application domains within this or any other process. One or more
threads run in an application domain.
Although hosting threads within application domains is conceptually similar to
the COM threading model with its use of apartments, there is an important
difference: application domains are managed types whereas the COM threading
model is built on an unmanaged architecture.
For more information about application domains, see Module 2, Introduction
to a Managed Execution Environment, in Course 2349B, Programming with
the Microsoft .NET Framework (Microsoft Visual C# .NET).
Topic Objective
To explain thread
architecture in .NET in terms
of application domains.
Lead-in
Lets look at the different
architectural components
that make up a single .NET
process.
8 Module 14 (Optional): Threading and Asynchronous Programming


Mapping from Win32 Threading to Managed Threading
The following table maps Microsoft Win32 threading elements to their
approximate runtime equivalent. Note that this mapping does not represent
identical functionality. For example, TerminateThread does not execute
finally clauses or free up resources, and cannot be prevented. However,
Thread.Abort executes all of your rollback code, reclaims all of the resources,
and can be denied by using ResetAbort. Be sure to read the .NET Framework
SDK closely before making assumptions about functionality.
In Win32 In the common language runtime

CreateThread Combination of new Thread() and
Thread.Start
TerminateThread Thread.Abort
SuspendThread Thread.Suspend
ResumeThread Thread.Resume
Sleep Thread.Sleep
WaitForSingleObject on the thread handle Thread.Join
ExitThread No equivalent
GetCurrentThread Thread.CurrentThread
SetThreadPriority Thread.Priority
No equivalent Thread.Name
No equivalent Thread.IsBackground
Close to CoInitializeEx (Ole32.dll) Thread.ApartmentState

Module 14 (Optional): Threading and Asynchronous Programming 9


" "" " Using Threads in .NET
! Starting Threads
! Thread Properties and Parameters
! Managing Threads
! Thread Local Storage
! Interrupting and Terminating Threads

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this section you will learn how to start, manage, and terminate threads.
The System.Threading namespace includes classes for starting managing, and
terminating threads.
Being able to perform basic threading operations is only the first requirement in
creating multithreaded applications. On a more advanced level, you must
consider issues of data protection and performance. These issues are covered in
the topic Thread Safety in this module.
Topic Objective
To provide an overview of
the topics that you will cover
in this section.
Lead-in
Having briefly reviewed how
an operating system uses
threads to perform
multitasking, lets look at
how threading works in the
.NET Framework.
10 Module 14 (Optional): Threading and Asynchronous Programming


Starting Threads
! Create a new instance of a Thread Object
# Constructor takes a ThreadStart delegate as its only
parameter
# ThreadStart references the method that will be
executed by the new thread
! Thread is not executed until the Thread.Start method is
invoked
Thread t = new
Thread(new ThreadStart(MyClass.AStaticMethod));
t.Start();
Thread t = new
Thread(new ThreadStart(MyClass.AStaticMethod));
t.Start();

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Creating a new instance of a Thread class creates a new managed thread. The
constructor for Thread takes, as its only parameter, a ThreadStart delegate
that references the method that will be executed by the new thread. The Thread
function does not begin executing until the Start method is called.
Topic Objective
To explain how to instantiate
and start a new thread by
using the .NET Framework
classes.
Lead-in
Creating a new instance of a
Thread class creates a new
managed thread.
Module 14 (Optional): Threading and Asynchronous Programming 11


The following code shows how to create and start new threads:
using System;
using System.Threading;

class MyClass
{
//
public static void AStaticMethod()
{
//
}
public void AnInstanceMethod()
{
//
}
}

class App
{

static void Main()
{
// Create and start thread on an instance method
MyClass aMyClass = new MyClass();
Thread t1 = new
Thread(new ThreadStart(aMyClass.AnInstanceMethod));
t1.Start();
// Create and start thread on a static method
Thread t2 = new
Thread(new ThreadStart(MyClass.AStaticMethod));
t2.Start();

//
}
}

In the preceding example, the calls to t1.Start and t2.Start place the t1 and t2
threads in the running state, and the operating system can schedule them for
execution. The Start method submits an asynchronous request to the system,
and the call returns immediately, possibly before the new thread has started.
The threads execution begins at the first line of the method that is referred to
by the thread delegate. Calling Start more than once on the same thread causes
the runtime to throw a ThreadStateException.
12 Module 14 (Optional): Threading and Asynchronous Programming


Thread Properties and Parameters
! Use the Thread.Name and Thread.Priority properties to
get or set the name and priority of the thread
! Designate a thread as a background or a foreground
thread by setting the Thread.IsBackground property
# A background thread will not keep the managed
execution environment alive
! Encapsulate thread parameters in an object
t.Name = "My Background Thread";
t.Priority = ThreadPriority.AboveNormal;
t.IsBackground = true;
t.Name = "My Background Thread";
t.Priority = ThreadPriority.AboveNormal;
t.IsBackground = true;

*****************************ILLEGAL FOR NON-TRAINER USE******************************
To get or set the name of a thread and its priority, use the Thread.Name and
Thread.Priority properties. Thread.Priority gets or sets the following values
that indicate the scheduling priority of a thread:
! Highest
! AboveNormal
! Normal
! BelowNormal
! Lowest

For example, to set the name of a thread t to My Background Thread and
make its priority AboveNormal, you use the following code:
t.Name = "My Background Thread";
t.Priority = ThreadPriority.AboveNormal;

Because the details of scheduling algorithms vary with each operating
system, operating systems are not required to honor the priority of a thread.

Topic Objective
To describe some key
Thread class properties and
how to pass parameters to
threads.
Lead-in
To get or set the name of a
thread and its priority, use
the Thread.Name and
Thread.Priority properties.
Note
Module 14 (Optional): Threading and Asynchronous Programming 13


Background and Foreground Threads
A managed thread runs as a background thread or a foreground thread.
Background threads are identical to foreground threads except that a
background thread does not keep the managed execution environment alive.
After all foreground threads have been stopped in a managed process (where
the .exe file is a managed assembly), the system stops all background threads
and shuts down. You can designate a thread as a background or foreground
thread by setting the Thread.IsBackground property.
For example, to designate a thread as a background thread, you set
Thread.IsBackground to true. Likewise, to designate a thread as foreground
thread, set IsBackground to false.
All threads that enter the managed execution environment from unmanaged
code are marked as background threads. All threads that are generated by
creating and starting a new Thread object are foreground threads. If you create
a thread that you want to listen for some activity, such as a socket connection,
you should set Thread.IsBackground to true, so that your process can
terminate.
For example, to set a thread t to be a background thread:
t.IsBackground = true;

14 Module 14 (Optional): Threading and Asynchronous Programming


Encapsulating thread parameters in an object
It is sometimes important to supply parameters to a thread. However, the
ThreadStart delegate takes as its only parameter a ThreadStart delegate. The
following code demonstrates how to use an object to encapsulate thread
parameters:
using System;
using System.Threading;

class MyClassWithThreadState
{
int sleepTime;

public MyClassWithThreadState(int sleepTime)
{
this.sleepTime = sleepTime;
}

public void ThreadMethod()
{
// method can reference sleepTime
Thread.Sleep(sleepTime);
}
}

class Class1
{
static void Main()
{
int sleepTime = 1000; // time for the thread to sleep
MyClassWithThreadState myClassWithThreadState =
new MyClassWithThreadState(sleepTime);
Thread ts = new Thread(new
ThreadStart(myClassWithThreadState.ThreadMethod));
ts.Start();
//
}
}

Module 14 (Optional): Threading and Asynchronous Programming 15


Managing Threads
! Thread.Sleep causes the current thread to block
! Suspend and Resume methods are not generally useful
# Can result in serious application problems like deadlocks
! Thread.Join waits for another thread to stop
! Thread.WaitHandle methods wait for one or more events
! Thread.ThreadState property - bit mask of the thread's state
Thread.Sleep(3000); // blocks for 3 seconds
Thread.Sleep(3000); // blocks for 3 seconds
t.Start();
t.Join(); // Wait for the thread to exit
t.Start();
t.Join(); // Wait for the thread to exit
WaitHandle.WaitAll(waitEvents);
WaitHandle.WaitAll(waitEvents);

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Typically, thread management includes tasks such as suspending a thread for a
period of time, waiting until another thread completes, or waiting until one or
more specific events occur.
Pausing and Resuming Threads
After starting a thread, you sometimes need to pause that thread for a fixed
period of time. Calling Thread.Sleep causes the current thread to immediately
block for the number of milliseconds you pass to Sleep, yielding the remainder
of its time slice to another thread. One thread cannot call Sleep on another
thread. For example, to cause the current thread to suspend for three seconds,
call the static method of Thread.Sleep as follows:
Thread.Sleep(3000);

Calling Thread.Sleep(Timeout.Infinite) causes a thread to sleep until it is
interrupted by another thread that calls Thread.Interrupt or is aborted by
Thread.Abort.
Using Thread.Suspend to pause a thread
You can also pause a thread by calling Thread.Suspend. When a thread calls
Thread.Suspend on itself, the call blocks until the thread is resumed by
another thread.
When one thread calls Thread.Suspend on another thread, the call is a
nonblocking call that causes the other thread to pause. Calling Thread.Resume
breaks another thread out of the suspended state and causes the thread to resume
execution, regardless of how many times Thread.Suspend was called. For
example, if you call Thread.Suspend five consecutive times and then call
Thread.Resume, the thread resumes execution immediately following the call
to Resume.
Topic Objective
To describe the classes that
the runtime uses to pause,
resume, and force threads
to wait.
Lead-in
Lets look at how the
runtime provides classes to
manage threads.
16 Module 14 (Optional): Threading and Asynchronous Programming


Unlike Thread.Sleep, Thread.Suspend does not cause a thread to immediately
stop execution. The runtime must wait until the thread has reached a safe point
before it can suspend the thread. A thread cannot be suspended if it has not been
started or if it has stopped.
For more information about safe points, see the .NET Framework SDK.
The Suspend and Resume methods are not generally useful for applications,
and you should not confuse them with synchronization mechanisms. Because
Suspend and Resume do not rely on the cooperation of the thread that is being
controlled, they are highly intrusive and can cause serious application problems.
For example, if you suspend a thread that holds a resource needed by another
thread, this causes a deadlock condition.
Some applications do need to control the priority of threads for better
performance. To do this, you should use Thread.Priority rather than
Thread.Suspend in your application.
Using Thread.Join to pause a thread
You can force a thread to wait for another thread to stop by calling
Thread.Join, as shown in the following code:
using System;
using System.Threading;

class MyApp
{//
static void MyThreadMethod()
{
//
}
static void Main()
{
// create, start and join a simple background thread
// MyThreadMethod is the secondary thread's entry point.
Thread t = new Thread(new ThreadStart(MyThreadMethod));
// Start the thread
t.Start();
// Wait for the thread to exit
t.Join();
}
}

Module 14 (Optional): Threading and Asynchronous Programming 17


Using the WaitHandle class to wait for events
You can force a thread to wait for one or more events to occur by calling
methods in the Thread.WaitHandle class.
The following table describes some of the methods in the WaitHandle class.
Method Description

WaitAll This method waits for all of the elements in the specified array to
receive a signal.
WaitAny This method waits for any of the elements in the specified array to
receive a signal.
WaitOne This method blocks the current thread until the current WaitHandle
receives a signal.

The WaitHandle class encapsulates Win32 synchronization handles. While
WaitHandle objects represent operating system synchronization objects and
expose advanced functionality, they are less portable than the Monitor.Wait
method, which is fully managed and, in some circumstances, is more efficient
in its use of operating system resources.
Using Classes that are derived from WaitHandle
Examples of classes that derive from WaitHandle include AutoResetEvent,
ManualResetEvent, and Mutex. You use the AutoResetEvent class to make a
thread wait until some event puts it in the signaled state by calling
AutoResetEvent.Set. Unlike ManualResetEvent, AutoResetEvent is
automatically reset to nonsignaled by the system after a single waiting thread
has been released. If no threads are waiting, the event objects state remains
signaled. The AutoResetEvent corresponds to a Win32 CreateEvent call,
false specified by the bManualReset argument.
18 Module 14 (Optional): Threading and Asynchronous Programming


For example, the following code shows how to force the main thread to wait
until the threads that it has created signal that they have finished executing:
using System;
using System.Threading;

class MyClassWithThreadState
{
AutoResetEvent done;
public MyClassWithThreadState(AutoResetEvent done)
{
this.done = done;
}
public void ThreadMethod()
{
//
done.Set(); // signal that we are finished
}
}

class MyApp
{
//
static void Main()
{
//
int numberOfThreads = 3; // number of threads to create
AutoResetEvent[] waitEvents = new
AutoResetEvent[numberOfThreads];
Thread ts;
MyClassWithThreadState myClassWithThreadState;
for (int i=0; i<numberOfThreads; i++)
{
waitEvents[i] = new AutoResetEvent(false);
myClassWithThreadState = new
MyClassWithThreadState(waitEvents[i]);
ts = new
Thread( new
ThreadStart(myClassWithThreadState.ThreadMethod));
ts.Start();
}
// Wait for all threads to indicate that they are done.
WaitHandle.WaitAll(waitEvents);
//
}
}

You can also use multiple calls to the Thread.Join method to wait for
multiple threads to exit.

In this module, thread safety describes how to force a thread to wait for access
to a synchronized object, such as a Mutex method call.
Also in this module, Terminating Threads describes how to interrupt a waiting
thread by calling the Thread.Interrupt or Thread.Abort method.
Note
Module 14 (Optional): Threading and Asynchronous Programming 19


Thread Activity States
The Thread.ThreadState property provides a bit mask that indicates a threads
current state. A thread is always in at least one of the possible states in the
ThreadState enumeration, and can be in multiple states at the same time.
When you create a managed thread, it is in the Unstarted state, and remains in
this state until it is moved into the started state by calling Thread.Start.
Unmanaged threads that enter the managed environment are already in the
started state.
When in the started state, some actions can cause the thread to change states.
The following table lists the actions that cause a change of state and the
corresponding new state.
Action ThreadState

A thread is created within the common language runtime. Unstarted
A thread calls Start. Running
The thread starts running. Running
The thread calls Sleep. WaitSleepJoin
The thread calls Wait on another object. WaitSleepJoin
The thread calls Join on another thread. WaitSleepJoin
Another thread calls Interrupt. Running
Another thread calls Suspend. SuspendRequested
The thread responds to a Suspend request. Suspended
Another thread calls Resume. Running
Another thread calls Abort. AbortRequested
The thread responds to an Abort request. Stopped
A thread is terminated. Stopped

Because the Running state has a value of 0, you cannot perform a bit test to
discover this state. Instead, you can use the following test (in pseudo-code):
if ((tState & (Unstarted | Stopped)) == 0) // implies Running

Threads are often in more than one state at any particular time. For example, if
a thread is blocked from a Wait call and another thread calls Abort on that
same thread, the thread is in both the WaitSleepJoin and the AbortRequested
state at the same time. In that case, as soon as the thread returns from the call to
Wait or is interrupted, it receives the ThreadAbortException.
After a thread leaves the Unstarted state as the result of a call to Thread.Start,
it can never return to the Unstarted state. A thread can never leave the Stopped
state, either.
20 Module 14 (Optional): Threading and Asynchronous Programming


Thread Local Storage
! Managed thread local storage - data unique to a thread
! Thread-Relative Static Fields
# Constructor code runs only on first thread, other threads
thread static variables are initialized to null
class AClassWithThreadState
{
[ThreadStatic] public static int count = 42;
public void ThreadMethod() {
count++;
}
}
class AClassWithThreadState
{
[ThreadStatic] public static int count = 42;
public void ThreadMethod() {
count++;
}
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Managed thread local storage (TLS) provides dynamic data slots that are unique
to a thread and an application domain combination. The two types of data slots
are named slots and unnamed slots. Named slots can be convenient, because
you can use a mnemonic identifier. But other components can intentionally or
unintentionally modify a named slot by using the same name for the
components own thread-relative storage. However, if you do not expose an
unnamed data slot to other code, it cannot be used by any other component.
To use managed TLS, create your data slot by using
Thread.AllocateNamedDataSlot or Thread.AllocateDataSlot, and use the
appropriate methods to set or retrieve the information that is placed there.
Topic Objective
To show how to use
managed thread local
storage.
Lead-in
Lets discuss how to
manage thread local
storage.
Module 14 (Optional): Threading and Asynchronous Programming 21


Thread-Relative Static Fields
If you know that a field of your type should always be unique to a thread and
application domain combination, decorate a static field with the
ThreadStaticAttribute attribute. Any class constructor code runs on the first
thread in the first context that accesses the field. In all other threads or contexts,
the field is initialized to null, or Nothing in Microsoft Visual Basic.
Therefore, you should not rely on class constructors to initialize thread-relative
static fields. Instead, you should always assume that thread-relative static fields
are initialized to null.
For example, the following code shows how to use thread relative static fields
to store a thread specific counter:
class AClassWithThreadState
{
// NOTE: Only the first thread instance's field is
// initialized, other threads have the field zeroed
[ThreadStatic] public static int count = 42;

public void ThreadMethod()
{
count++;
//
}
//
}

22 Module 14 (Optional): Threading and Asynchronous Programming


Demonstration: Managing Threads

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This demonstration shows first how to create, manage, and join with a simple
background thread. Then a more complex example is shown where parameters
and thread static values for a thread instance are encapsulated in a class,
MyClassWithThreadState. After starting multiple threads, the main thread
calls the WaitHandle.WaitAll method to wait until all those threads indicate
that they have finished executing by setting their AutoResetEvent objects.

The code for this demonstration is located in <install folder>\Democode\
Mod14\Demo14.1.

Topic Objective
To demonstrate how to
create and manage threads
in the .NET Framework.
Lead-in
In this demonstration, you
will see how to create and
manage threads in .NET, by
using some of the classes
and methods that we have
talked about in this section.
Delivery Tip
You should run this demo
with breakpoints set in the
Main, MyThreadMethod,
and
MyClassWithThreadState.
ThreadMethod methods
and observe the
Visual Studio .NET
Threads window.
To make the Visual Studio
.NET Threads window
visible, when the application
is running, on the Debug
menu, click Windows, and
then click Threads.
Module 14 (Optional): Threading and Asynchronous Programming 23


Interrupting and Terminating Threads
! Thread.Interrupt method
# Wakes a thread from any wait state and causes a
ThreadInterruptedException
! Thread.Abort method to permanently terminate thread
# Wakes a thread from any wait state and causes a
ThreadAbortException
# ThreadAbortException is a special exception that can
be caught by application code, but is rethrown at the end
of the catch block unless ResetAbort is called
# Immediate abort not guaranteed, therefore use
Thread.Join to block until thread terminates

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In addition to starting, pausing, and resuming threads, the .NET Framework
also provides classes for terminating threads.
Thread.Interrupt wakes a thread from any wait state and causes a
ThreadInterruptedException to be thrown in the destination thread.
Thread.Abort is used to permanently terminate a thread.
Using Thread.Interrupt
When you call Thread.Interrupt, you wake a thread from any wait state and
cause a ThreadInterruptedException to be thrown in the destination thread.
The thread should catch the ThreadInterruptedException and do whatever is
appropriate to continue working. If the thread ignores the exception, the runtime
catches the exception and stops the thread.
For example, you may want to terminate a thread in response to a user-initiated
event, such as clicking a Cancel button. The Cancel buttons event handler may
run in one thread (thread1), while the event to which the Cancel action applies
may run in a separate thread (thread2). In your event handler, you can call the
Interrupt method on the second thread, as follows:
// inside the Cancel button's event handler
if ( thread2 != null)
{
thread2.Interrupt();
thread2 = null;
}

Topic Objective
To explain how to terminate
a thread by using the .NET
Framework classes.
Lead-in
Lets look at how to
terminate a thread in the
.NET Framework.
24 Module 14 (Optional): Threading and Asynchronous Programming


Using Thread.Abort
To terminate a thread permanently, you use the Thread.Abort method. When
you call Abort to terminate a thread, the system wakes the thread from any wait
state and throws a ThreadAbortException. The runtime executes the catch
block and any finally block.
ThreadAbortException is a special exception that can be caught by
application code, but is rethrown at the end of the catch block unless
ResetAbort is called. Only code with the proper permissions can call
ResetAbort method. ResetAbort cancels the request to abort, and prevents the
ThreadAbortException from terminating the thread. If the finally blocks
contain any unbounded computations, the threads termination may be delayed
indefinitely.
Because the call to Thread.Abort does not always result in the immediate
termination of a thread, you can ensure the threads destruction by calling the
Join method on the thread after you call Abort. Join blocks the calling thread
until the thread stops executing.
After a thread terminates, you cannot restart it by calling Start again. For
example, if code creates a thread, lets it run, and then aborts the thread, any
attempt to restart the thread causes the runtime to throw a
ThreadStateException.
Blocking Issues
If a thread makes an unmanaged call into the operating system, and the system
has blocked the thread in unmanaged code, the runtime will not take control of
the blocked thread for Thread.Interrupt or Thread.Abort.
In the case of Thread.Abort, the runtime marks the thread for Abort and takes
control of the thread when it re-enters managed code. Where possible, you
should use managed blocking rather than unmanaged blocking. The following
methods are all responsive to Thread.Interrupt and Thread.Abort:
! WaitHandle
! WaitOne
! WaitAny
! WaitAll
! Monitor.Enter
! Monitor.Block
! Thread.Join
! GC.WaitForPendingFinalizers

Also, if a thread is in a single-threaded apartment, all of these managed
blocking operations will correctly pump messages in the apartment while the
thread is blocked.
Module 14 (Optional): Threading and Asynchronous Programming 25


Demonstration: Interrupt and Abort

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This demonstration shows how to use Thread.Interrupt and Thread.Abort to
terminate threads.
Pay careful attention to what happens in the catch and finally blocks when the
Interrupt and Abort methods are called. Notice whether the thread resumes
working after the method calls as indicated by the output string Thread - still
alive and working.
Topic Objective
To demonstrate how to
terminate threads by using
the Thread.Interrupt and
Thread.Abort methods.
Lead-in
In this demonstration, you
will see how to use the
Thread.Interrupt and
Thread.Abort methods to
terminate threads.
26 Module 14 (Optional): Threading and Asynchronous Programming


The following code is used in the demonstration:
using System;
using System.Threading;

public class ThreadWork
{
static public bool resetAbort;

public static void DoWork()
{
try
{
for(int i=0; i<100; i++)
{
Console.WriteLine(
"Thread - working.");
Thread.Sleep(100);
}
}

catch(ThreadInterruptedException e)
{
Console.WriteLine(
"Thread - caught ThreadInterruptedException");
Console.WriteLine(
"Exception message: {0}", e.Message);
}

catch(ThreadAbortException e)
{
Console.WriteLine(
"Thread - caught ThreadAbortException");
Console.WriteLine(
"Exception message: {0}", e.Message);
if (resetAbort == true) Thread.ResetAbort();
}
finally
{
Console.WriteLine("Thread - finally block");
}
Console.WriteLine(
"Thread - still alive and working.");
Thread.Sleep(1000);
Console.WriteLine(
"Thread - finished working.");
}
}

(Code continued on the following page.)
Module 14 (Optional): Threading and Asynchronous Programming 27


class ThreadAbortTest
{
public static void Main()
{
ThreadStart myThreadDelegate = new
ThreadStart(ThreadWork.DoWork);
Thread myThread;

Console.WriteLine(
"Main - interrupting my thread.");
myThread = new Thread(myThreadDelegate);
myThread.Start();
Thread.Sleep(100);
myThread.Interrupt();
Thread.Sleep(2000);

ThreadWork.resetAbort = true;
Console.WriteLine(
"Main - aborting my thread with resetAbort: {0}.",
ThreadWork.resetAbort);
myThread = new Thread(myThreadDelegate);
myThread.Start();
Thread.Sleep(100);
myThread.Abort();
Thread.Sleep(2000);

ThreadWork.resetAbort = false;
Console.WriteLine(
"Main - aborting my thread with resetAbort: {0}.",
ThreadWork.resetAbort);
myThread = new Thread(myThreadDelegate);
myThread.Start();
Thread.Sleep(100);
myThread.Abort();
myThread.Join();
Console.WriteLine("Main ending.");
}
}

28 Module 14 (Optional): Threading and Asynchronous Programming


The output should be similar to the following:
Main - interrupting my thread.
Thread - working.
Thread - caught ThreadInterruptedException
Exception message: !
Thread has been interrupted from a waiting state.
Thread - finally block
Thread - still alive and working.
Thread - finished working.
Main - aborting my thread with resetAbort: True.
Thread - working.
Thread - caught ThreadAbortException
Exception message: Thread was being aborted.
Thread - finally block
Thread - still alive and working.
Thread - finished working.
Main - aborting my thread with resetAbort: False.
Thread - working.
Thread - caught ThreadAbortException
Exception message: Thread was being aborted.
Thread - finally block
Main ending.

Module 14 (Optional): Threading and Asynchronous Programming 29


" "" " Thread Safety
! Overview of Thread Safety
! Synchronization Context
! Synchronized Code Regions
! Manual Synchronization
! Thread Safety and the .Net Framework Classes

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The System.Threading namespace provides classes and interfaces that enable
multithreaded programming. This namespace includes classes for synchronizing
access to data to provide thread safety.
This section focuses on the issues that you may encounter from sharing data
and resources between threads in multithreaded programming. The section also
introduces .NET Framework synchronization strategies.
Topic Objective
To provide an overview of
the topics that you will cover
in this section.
Lead-in
In this section, well look at
the importance of thread
safety and the mechanisms
that the .NET Framework
supports to provide thread
safety.
30 Module 14 (Optional): Threading and Asynchronous Programming


Overview of Thread Safety
! Common problems sharing data and resources between threads:
# Race condition uncontrolled order of execution causing errors
# Deadlocks threads waiting for each other so that they cannot
proceed
! Best approach is to avoid sharing when possible
# Encapsulate data into instances that are not shared across requests
! The .NET Framework provides three strategies to synchronize
access to instance and static methods and instance fields:
# Synchronized contexts
# Synchronized code regions
# Manual synchronization

*****************************ILLEGAL FOR NON-TRAINER USE******************************
When writing multithreaded applications, you may encounter common
problems, such as race conditions and deadlock.
Race condition
Two or more threads that simultaneously access the same data can cause
undesirable and unpredictable results. For example, one thread may update the
contents of a structure while another thread reads the contents of the same
structure. In this scenario, it is unknown what data the reading thread will
receive: the old data, the newly written data, or possibly a mixture of both.
When the proper operation of a program depends on the uncontrolled order of
execution of multiple threads, then a race condition exists.
Deadlock
A multithreaded application can also encounter problems with thread
synchronization if multiple threads are waiting for each other to release
resources. This blocking of thread execution is known as a deadlock.
For example, consider two threads that transfer money between accounts A and
B. The first thread, Thread 1, is coded to do the following tasks in the following
order:
! Wait for and acquire a lock on account A
! Wait for and acquire a lock on account B
! Transfer funds
! Release both locks

The second thread, Thread 2, is coded in the same way, except that it first
acquires a lock on account B, then on account A.
Topic Objective
To discuss problems that
result from sharing data and
resources between threads,
and to introduce .NET
Framework synchronization
strategies.
Lead-in
Lets talk about how sharing
data and resources between
threads can lead to
compromised thread safety.
Module 14 (Optional): Threading and Asynchronous Programming 31


Consider the case where Thread 1 acquires lock A, but before it can acquire
lock B, Thread 2 runs and acquires lock B. In this case, both threads block
while waiting for the other lock. Because both threads are blocked, neither
thread releases the lock that is required by the other thread to proceed. This
condition is referred to as a deadlock situation.
You can best achieve thread safety by not sharing data or resources between
threads. If possible, use a design pattern that encapsulates data and resources
into instances that are not shared across requests. When this is not possible you
should use the .NET Framework thread synchronization classes to provide
thread safety.
.NET Framework strategies for synchronization
When you need to share data, the .NET Framework provides three strategies to
synchronize access to instance methods, static methods, and instance fields:
! Synchronized contexts
You can use the SynchronizationAttribute attribute to enable simple,
automatic synchronization for ContextBoundObject objects.
! Synchronized code regions
You can use the MethodImplAttribute class (passing
MethodImplOptions.Synchronized), the Monitor class, or compiler
support to synchronize only the code blocks that need it.
! Manual synchronization
You can use the various synchronization objects to create your own
synchronization mechanisms.

The runtime provides a thread model in which classes fall into a number of
categories that can be synchronized in a variety of different ways, depending on
the requirements.
No Synchronization
No synchronization support is the default for objects. Any thread can access
any method or field at any time.
Synchronized Context
You can use the SynchronizationAttribute on any class that is derived from
ContextBoundObject to synchronize all instance methods and fields. All
objects in the same context domain share the same lock. Multiple threads are
allowed to access the instance methods and fields, but only a single thread is
allowed at any one time. Static members are not protected from concurrent
access by multiple threads.
The following table shows the support provided for fields and methods under
the synchronization context category.
Synchronization context supports Synchronization context does not support

Instance fields and instance methods Global fields, static fields, and static methods
Specific code blocks

32 Module 14 (Optional): Threading and Asynchronous Programming


Synchronized Code Regions
You can use the Monitor class or a compiler keyword to synchronize blocks of
code, instance methods, and static methods. In Microsoft Visual C#

, the
keyword is lock. You can also decorate a method with a MethodImplAttribute
(passing MethodImplOptions.Synchronized), which has a similar result to
using Monitor or one of the compiler keywords for a Monitor code block
where the code block is the entire method.
The following table shows the support provided for fields and methods under
the synchronized code regions category.
Synchronized code regions supports
(only if marked)
Synchronized code regions does not
support

Static methods, instance methods, and
specific code blocks
Global fields, static fields, and instance
fields

Manual Synchronization
You can use the Interlocked, Mutex, ManualResetEvent, AutoResetEvent,
and ReaderWriterLock classes to acquire and release a lock to protect global,
static, and instance fields and global, static, and instance methods.
Module 14 (Optional): Threading and Asynchronous Programming 33


Synchronization Context
! SynchronizationAttribute enables simple, automatic
synchronization for ContextBoundObject objects
# Only instance fields and methods are synchronized
# Static fields and methods are not protected from
concurrent access by multiple threads
[Synchronization()]
class CounterSynchronizedContext : ContextBoundObject
{
static int sCount = 0; //multiple threads can access
int iCount = 0; //only one thread can access at a time
public void Increment() {
// only one thread can access at a time
}
}
[Synchronization()]
class CounterSynchronizedContext : ContextBoundObject
{
static int sCount = 0; //multiple threads can access
int iCount = 0; //only one thread can access at a time
public void Increment() {
// only one thread can access at a time
}
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
A context is an ordered sequence of properties that defines an environment for
the objects within it. Contexts are created during the activation process for
objects that are configured to require certain automatic services such as
synchronization. Multiple objects can reside inside a context.
Using the SynchronizationAttribute attribute
You can use the SynchronizationAttribute attribute to enable simple,
automatic synchronization for objects whose class derives from
ContextBoundObject. The attribute forces a synchronization domain for the
current context and all contexts that share the same instance. When you apply
this attribute to an object, only one thread can be executing in all contexts that
share an instance of this property. Multiple threads may access the methods and
fields, but only a single thread is allowed at any one time. Allowing only a
single thread at a time to access instance fields and methods may make these
fields and methods thread-safe.

A synchronization context does not protect static fields and methods
from concurrent access by multiple threads.


Topic Objective
To describe how to
synchronize access to
instance fields and methods
by using synchronization
contexts.
Lead-in
Lets look at using
synchronization contexts in
more detail.
Caution
34 Module 14 (Optional): Threading and Asynchronous Programming


For example, consider the CounterSynchronizedContext class. This classs
instanceCount field and Increment method are protected from concurrent
access by multiple threads; only one thread at any one time can gain access to
these members. However, multiple threads may access its staticCount member,
as shown in the following code:
using System;
using System.Threading;
using System.Runtime.Remoting.Contexts;

[Synchronization()]
class CounterSynchronizedContext : ContextBoundObject
{
// Caution! multiple concurrent threads allowed for
// static field can use manual synchronization to protect
static int staticCount = 0;

// only one thread at a time allowed for instance field
int instanceCount = 0;

// only one thread at a time can execute instance method
public void Increment()
{
Console.WriteLine(
"Start Object:{0} Thread:{1}!
Resource writing count, static:{2} instance:{3}",
this.GetHashCode(),
Thread.CurrentThread.GetHashCode(),
staticCount, instanceCount);
int tempStaticCount = staticCount;
int tempInstanceCount = instanceCount;
Thread.Sleep(50);
tempStaticCount++;
tempInstanceCount++;
staticCount = tempStaticCount;
instanceCount = tempInstanceCount;
Console.WriteLine(
"Stop Object:{0} Thread:{1}!
Resource writing count, static:{2} instance:{3}",
this.GetHashCode(),
Thread.CurrentThread.GetHashCode(),
staticCount, instanceCount);
}
}

Module 14 (Optional): Threading and Asynchronous Programming 35



There are two classes named SynchronizationAttribute: one in the
System.EnterpriseServices namespace, and the other in the
System.Runtime.Remoting.Contexts namespace. The
System.EnterpriseServices.SynchronizationAttribute class supports only
synchronous calls, and can be used only with serviced components. (For more
information about serviced components, see Serviced Component Overview in
the .NET Framework SDK documentation.) The
System.Runtime.Remoting.Contexts.SynchronizationAttribute supports
both synchronous and asynchronous calls, and can be used only with context
bound objects. This topic discusses only the
System.Runtime.Remoting.Contexts.SynchronizationAttribute.

Note
36 Module 14 (Optional): Threading and Asynchronous Programming


Synchronized Code Regions
! MethodImplAttribute with MethodImplOptions.Synchronized
# Method executed by only one thread at a time
# Can be used to synchronize instance and static methods
# Similar to using Monitor or Visual C# lock on the entire method
! Monitor - synchronize access to a region of code
# Code region can be a portion of, or the entire method
# Monitor.Enter, Monitor.TryEnter, and Monitor.Exit
# C# lock keyword uses Monitor class methods
lock(this) to protect instance, lock(typeof(class)) to protect static
[MethodImplAttribute(MethodImplOptions.Synchronized)]
[MethodImplAttribute(MethodImplOptions.Synchronized)]
lock (typeof(Counter)) {
//block of protected code
}
lock (typeof(Counter)) {
//block of protected code
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
You can decorate a method with a MethodImplAttribute, passing
MethodImplOptions.Synchronized, which indicates that only one thread at a
time may execute the method. You can use this attribute to synchronize both
instance methods and static methods. Using this attribute provides the same
result essentially as using Monitor or the Visual C# lock keyword where the
synchronized code region encompasses the entire method.
Topic Objective
To explain and contrast
different options for
synchronizing blocks of
code.
Lead-in
In addition to synchronizing
methods and fields, we also
have several options for
synchronizing blocks of
code:
MethodImplAttribute, the
Monitor class, and the lock
keyword.
Module 14 (Optional): Threading and Asynchronous Programming 37


[MethodImplAttribute(MethodImplOptions.Synchronized)]
In the following example, the class CounterSynchronizedCodeRegion has
both its instance method IncrementInstance and static method
IncrementStatic attributed for synchronized access. This allows only one
thread to execute either method at one time.
using System;
using System.Threading;
using System.Runtime.CompilerServices;

class ASynchronizedClassExample
{
static int staticValue = 0;
int instanceValue = 0;

[MethodImplAttribute(MethodImplOptions.Synchronized)]
public void ChangeInstance()
{
// thread safe changes to instanceValue
}

[MethodImplAttribute(MethodImplOptions.Synchronized)]
public static void ChangeStatic()
{
// thread safe changes to staticValue
}
}
//

You can use Thread.Interrupt to break a thread out of blocking
operations, such as waiting for access to a synchronized region of code.

Monitor Class and the C# Compiler lock Keyword
Using the Monitor class or Visual C# lock keyword is a little more complex
than using MethodImplAttribute, but these approaches give you the flexibility
to synchronize access to a specific region of code and not just to the entire
method. By synchronizing the least amount of code required for thread safety,
you maximize the amount of possible concurrency.
The Monitor class supports the following code-block synchronization:
! Synchronized instance methods
On instance methods, the current object is used for synchronization.
Visual C# uses the this keyword to indicate the current object, while
Visual Basic .NET use the Me keyword.
! Synchronized static methods
On static methods, the class is used for synchronization.

The Monitor class provides methods that you can use to synchronize access to
a specific region of code by taking and releasing a lock on a particular object.
After you obtain a lock on a code region, you can use the Monitor.Wait,
Monitor.Pulse, and Monitor.PulseAll methods.
Note
38 Module 14 (Optional): Threading and Asynchronous Programming


Monitor.Wait
The Monitor.Wait method releases the lock on an object and blocks the current
thread until it reacquires the lock. The thread that currently owns the lock on the
specified object invokes this method to release the object so that another thread
can access it. The caller is blocked while waiting to reacquire the lock. This
method is called when the calling thread is waiting for a change in the state of
the object that will occur as a result of another threads operations on the object.
When a thread calls Wait, it releases the lock on the object and enters the
objects waiting queue. The next thread in the objects ready queue, if there is
one, acquires the lock and has exclusive use of the object. All threads that call
Wait remain in the waiting queue until they receive a signal from Pulse or
PulseAll, sent by the owner of the lock. If Pulse is sent, only the thread at the
head of the waiting queue is affected. If PulseAll is sent, all threads that are
waiting for the object are affected. When the signal is received, one or more
threads leave the waiting queue and enter the ready queue. A thread in the ready
queue is permitted to reacquire the lock.
This method returns when the calling thread reacquires the lock on the object.
Note that this method blocks indefinitely if the holder of the lock does not call
Pulse or PulseAll.

You should check whether the Pulse method is ever called before its
corresponding Wait method is called. A thread must have already issued a
Wait for the Pulse to affect it. You can use the Monitor.Wait methods with a
timeout parameter to prevent the caller of the Wait from blocking indefinitely
when such a condition occurs.

Monitor.Pulse
The Monitor.Pulse method notifies a thread in the waiting queue of a change in
the locked object's state. Only the current owner of the lock can signal a waiting
object by using Pulse. The thread that currently owns the lock on the specified
object invokes this method to signal the next thread in line for the lock. Upon
receiving the pulse, the waiting thread is moved to the ready queue. When the
thread that invoked Pulse releases the lock, the next thread in the ready queue,
which is not necessarily the thread that was pulsed, acquires the lock.

Be aware of the distinction between the use of Monitor and WaitHandle
objects. Monitor objects are purely managed, fully portable, and may be more
efficient in terms of operating system resource requirements. WaitHandle
objects represent operating system waitable objects, are useful for
synchronizing between managed and unmanaged code, and expose some
advanced operating system features like the ability to wait on many objects at
once.

Caution
Note
Module 14 (Optional): Threading and Asynchronous Programming 39


Compiler Support
Both Visual Basic and Visual C# support a language keyword that uses
Monitor.Enter and Monitor.Exit to lock the object. Visual Basic supports the
SyncLock keyword; Visual C# supports the lock keyword.
When you use the lock or SyncLock keyword, the compiler generates code.
The Visual C# and Visual Basic compilers emit a try/finally block with
Monitor.Enter at the beginning of the try, and Monitor.Exit in the finally
block. If an exception is thrown inside of the lock or SyncLock block, the
finally handler runs to allow you to perform any clean-up work.
The C# statement, of the form lock(x) where x is an expression of a reference-
type, is equivalent to the following except that x is only evaluated once:
System.Threading.Monitor.Enter(x);
try {
...
}
finally {
System.Threading.Monitor.Exit(x);
}

The lock keyword marks a statement block as a critical section.
lock(expression) statement_block,
where expression specifies the object that you want to lock on. expression must
be a reference type.
Typically, expression is this if you want to protect an instance variable, or
typeof(class) if you want to protect a static variable or if the critical section
occurs in a static method in the specified class. The statement_block includes
the statements of the critical section.
40 Module 14 (Optional): Threading and Asynchronous Programming


For example, to synchronize access in a static method:
using System;
using System.Threading;

class Cache
{
public static void Add(object x)
{
// method code that doesn't require exclusive access
lock (typeof(Cache))
{
// code requiring exclusive access to static data
}
// method code that doesn't require exclusive access
}

public static void Remove(object x)
{
// method code that doesn't require exclusive access
lock (typeof(Cache))
{
// code requiring exclusive access to static data
}
// method code that doesn't require exclusive access
}
}

For example, to synchronize access in an instance method:
class Counter
{
public override int Read(int threadNum)
{
// ...
// method code that doesn't require exclusive access
lock(this)
{
// code requiring exclusive access to instance data
}
// method code that doesn't require exclusive access
}
//
}

Module 14 (Optional): Threading and Asynchronous Programming 41


Demonstration: Using Synchronization Contexts and Synchronized
Code Regions to Provide Thread Safety

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This demonstration shows the use of synchronization contexts and
synchronized code regions to provide thread safety. The demonstration consists
of several test sequences, which are described in this topic.
The code for this demonstration is located in <install folder>\Democode\
Mod14\Demo14.3.
The Unsafe test
The Unsafe test shows the race condition that occurs when multiple threads
concurrently execute a method on a class that is not thread-safe.
The CounterUnsafe class has a method that is named Increment, which reads
an objects instance variable that is named count, increments its value, and
stores the value back in count. The class and its method do not synchronize
access. Therefore, the condition can arise where one thread executing the
method has read the value of count but before it can increment it and store it
back in count, another concurrently executing thread executing the same
method reads the same starting value as the first thread.
Topic Objective
To demonstrate the use of
synchronization contexts
and synchronized code
regions to provide thread
safety.
Lead-in
In this demonstration, you
will see how to achieve
thread safety by using
synchronization contexts
and synchronized code
regions.
42 Module 14 (Optional): Threading and Asynchronous Programming


The Unsafe test should result in output that is similar to the following example.
Notice that the Increment method has been called three times but the count has
only been incremented by one.
Starting Unsafe Test
Start Resource writing count: 0
Start Resource writing count: 0
Start Resource writing count: 0
Stop Resource writing count: 1
Stop Resource writing count: 1
Stop Resource writing count: 1
All Unsafe threads have completed.

The Static Method Synchronized Context test
The Static Method Synchronized Context test calls the static IncrementStatic
method on the CounterSynchronizedContext class, which inherits from
ContextBoundObject and has a [Synchronization()] attribute.
Because synchronization contexts do not synchronize static methods, the
following undesirable result occurs:
Starting Static Method Synchronized Context Test
Start Thread:7 Resource writing count, static:0
Start Thread:8 Resource writing count, static:0
Start Thread:9 Resource writing count, static:0
Stop Thread:7 Resource writing count, static:1
Stop Thread:8 Resource writing count, static:1
Stop Thread:9 Resource writing count, static:1
All Static Method Synchronized Context threads have completed.

The Instance Method Synchronized Context test
The Instance Method Synchronized Context test calls the IncrementInstance
instance method on the CounterSynchronizedContext class, which inherits
from ContextBoundObject and has a [Synchronization()] attribute.
Because synchronization contexts do synchronize instance methods, the
following correct result occurs:
Starting Instance Method Synchronized Context Test
Start Object:39 Thread:31 Resource writing count, instance:0
Stop Object:39 Thread:31 Resource writing count, instance:1
Start Object:39 Thread:32 Resource writing count, instance:1
Stop Object:39 Thread:32 Resource writing count, instance:2
Start Object:39 Thread:33 Resource writing count, instance:2
Stop Object:39 Thread:33 Resource writing count, instance:3
All Instance Method Synchronized Context threads have!
completed.

Unlike a COM STA, a synchronized context is entered by many different
threads. Like a COM STA, only one thread at a time can execute the instance
methods.

Note
Module 14 (Optional): Threading and Asynchronous Programming 43


The Static Method Synchronized Code Region test
The Static Method Synchronized Code Region test calls the
CounterSynchronizedCodeRegion classs static method, IncrementStatic,
that has the [MethodImplAttribute(MethodImplOptions.Synchronized)]
attribute. Because synchronized code regions can be used to synchronize static
methods, the following correct result occurs:
Starting Static Method Synchronized Code Region Test
Start Thread:40 Resource writing count, static:0
Stop Thread:40 Resource writing count, static:1
Start Thread:41 Resource writing count, static:1
Stop Thread:41 Resource writing count, static:2
Start Thread:42 Resource writing count, static:2
Stop Thread:42 Resource writing count, static:3
All Static Method Synchronized Code Region threads have!
completed.

The Instance Method Synchronized Code Region test
The Instance Method Synchronized Code Region test calls the
CounterSynchronizedCodeRegion classs instance method,
IncrementInstance, that has the
[MethodImplAttribute(MethodImplOptions.Synchronized)] attribute.
Because synchronized code regions can be used to synchronize instance
methods, the following correct result occurs:
Starting Instance Method Synchronized Code Region Test
Start Object:47 Thread:44 Resource writing count, instance:0
Stop Object:47 Thread:44 Resource writing count, instance:1
Start Object:47 Thread:45 Resource writing count, instance:1
Stop Object:47 Thread:45 Resource writing count, instance:2
Start Object:47 Thread:46 Resource writing count, instance:2
Stop Object:47 Thread:46 Resource writing count, instance:3
All Static Method Synchronized Code Region threads have!
completed.

44 Module 14 (Optional): Threading and Asynchronous Programming


Manual Synchronization
! Interlocked methods synchronize access to a variable
that is shared by multiple threads
# CompareExchange, Decrement, Exchange, Increment
# Threads of different processes can use this mechanism
to safely handle variables in
shared memory
! Mutex synchronization between threads
across processes
! ReaderWriterLock single-writer/multiple-reader

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The .NET Framework provides several synchronization classes, which you can
use to create your own synchronization mechanisms. Manual synchronization
may be useful for the following operations:
! Synchronizing access to a variable that is shared by multiple threads
! Synchronizing between threads and across processes
! Defining a lock that implements single-writer/multiple-reader semantics

Using methods of the Interlocked class
The Interlocked class methods Increment, Decrement, Exchange, and
CompareExchange provide a simple mechanism for synchronizing access to a
variable that is shared by multiple threads. The threads of different processes
can use this mechanism if the variable is in shared memory.
On modern processors, the methods of the Interlocked class can often be
implemented by a single instruction. Thus, the methods of the Interlocked
class provide very high-performance synchronization, and can be used to build
higher-level synchronization mechanisms, like spin locks.
Topic Objective
To describe the use of the
Interlocked class methods,
the Mutex class, and the
ReaderWriterLock class in
manual synchronization.
Lead-in
Lets look at some options
that the .NET Framework
provides to create
synchronization
mechanisms.
Module 14 (Optional): Threading and Asynchronous Programming 45


Increment and Decrement
The Increment and Decrement methods combine the operations of
incrementing or decrementing the variable and checking the resulting value.
This atomic operation is useful in a multitasking operating system, in which the
system can interrupt one threads execution to grant a slice of processor time to
another thread.
Without such synchronization, one thread can increment a variable but be
interrupted by the system before it can check the resulting value of the variable.
A second thread can then increment the same variable. When the first thread
receives its next time slice, it will check the value of the variable, which has
now been incremented not once, but twice. The Interlocked variable access
methods protect against this kind of error.

The C# increment operator (++) and decrement operator (--) are not
implemented by atomic operations and therefore do not provide the thread
safety that the Increment and Decrement methods do.

Exchange and CompareExchange
The Exchange method atomically exchanges the values of the specified
variables. The CompareExchange method combines two operations:
comparing the first and third parameter for equality, and if they are equal,
storing the second value in the first variable.
The Exchange and CompareExchange methods that are exposed by the
Interlocked class take arguments of type Object that can store references.
However, type safety requires that all of the arguments be typed strictly as
Object; you cannot simply cast an object to Object in the call to one of these
methods. Instead, you must create variables of type Object, assign a custom
object to that variable, and then pass that variable.
Note
46 Module 14 (Optional): Threading and Asynchronous Programming


The following Visual C# code example only allows a single call to set the
property X. The property X is only set once because the CompareExchange
method changes the value of _x only when its first and third parameters are
equal, that is to say when _x is equal to null. After _x has been set to ovalue, it
no longer equals null and the CompareExchange method will no longer
modify it.
using System;
using System.Threading;

namespace CompareandExchange
{
class PropertyTest
{
private Object _x;
public int X
{
set {
Object ovalue = value;
Interlocked.CompareExchange(
ref _x, ovalue, null);
}
get {
return (int) _x;
}
}
}

class Class1
{
static void Main(string[] args)
{
PropertyTest pt = new PropertyTest();
pt.X = 1;
pt.X = 2;
Console.WriteLine("pt.X: {0}", pt.X);
}
}
}

This code outputs:
pt.X: 1

Module 14 (Optional): Threading and Asynchronous Programming 47


Mutex
You can use a Mutex object to synchronize between threads and across
processes. The Mutex class provides a synchronization primitive that grants
exclusive access to the shared resource to only one thread. If a thread acquires a
mutex, the second thread that wants to acquire that mutex is suspended until the
first thread releases the mutex. Although Mutex does not have all of the wait
and pulse functionality of the Monitor class, it does offer the creation of named
mutexes that can be used between processes.
You call WaitOne, WaitAll, or WaitAny to request ownership of the Mutex.
The state of the Mutex is signaled if no thread owns it.
If a thread owns a Mutex, that thread can specify the same Mutex in repeated
wait-request calls without blocking its execution. However, it must release the
Mutex as many times as it called wait to release ownership. If a thread
terminates normally while owning a Mutex, the state of the Mutex is set to
signaled and the next waiting thread gets ownership. The Mutex class
corresponds to a Win32 CreateMutex call.
ReaderWriterLock
The ReaderWriterLock defines a lock that implements single-writer/multiple-
reader semantics. In terms of resources, ReaderWriterLock objects are
sufficiently inexpensive enough to be used in large numbers such as with per-
object synchronization.
The ReaderWriterLock provides equity between readers and writers. After a
writer-lock is requested, no new readers are accepted until the writer has access.
Neither readers nor writers are perpetually denied access. The
ReaderWriterLock class supports functionality for upgrading to a writer lock
with a return argument that indicates intermediate writes, and for downgrading
from a writer lock, which restores the state of the lock. The class recovers from
most common failures such as the creation of events. The lock maintains a
consistent internal state and remains usable.
48 Module 14 (Optional): Threading and Asynchronous Programming


The following example shows how a class can use a ReaderWriterLock object
to allow multiple threads to execute its Read method or a single thread to
execute its Write method at any time.
using System;
using System.Threading;

class Resource
{
ReaderWriterLock rwl = new ReaderWriterLock();

public void Read(Int32 threadNum)
{
rwl.AcquireReaderLock(Timeout.Infinite);
try
{
// many can do read processing, writers blocked
}
finally
{
rwl.ReleaseReaderLock();
}
}

public void Write(Int32 threadNum)
{
rwl.AcquireWriterLock(Timeout.Infinite);
try
{
// one can write processing, readers blocked
}
finally
{
rwl.ReleaseWriterLock();
}
}
}

Module 14 (Optional): Threading and Asynchronous Programming 49


Thread Safety and the .Net Framework Classes
! Public static members of .NET Framework classes are
thread-safe other public members see .NET SDK
! .NET Framework Collections classes, by default, are
generally not thread-safe for modification
# Hashtable is thread safe for one writer and
multiple readers
! To obtain a thread-safe collection class
# Use or implement a thread-safe wrapper object obtained
from a Synchronized method
# Use a locking mechanism

*****************************ILLEGAL FOR NON-TRAINER USE******************************
All public static members (methods, properties, and fields) within the .NET
Framework support concurrent access within a multithreaded environment.
Therefore, you can invoke any .NET Framework static member simultaneously
from two threads without encountering race conditions, deadlocks, or crashes.
To determine whether a .NET Framework class or structure is thread-safe, see
the Thread Safety section for that class or structure in the .NET Framework
SDK documentation.
If you want to use a nonthread-safe class in a multithreaded environment, you
must wrap an instance of the class with code that supplies the necessary
synchronization constructs.
Topic Objective
To introduce .NET
Framework classes thread
safety issues.
Lead-in
Lets look at the thread
safety issues of the .NET
Framework classes.
50 Module 14 (Optional): Threading and Asynchronous Programming


Collections
By default, Collections classes are generally not thread-safe. Multiple readers
can safely read the collection; however, any modification to the collection
produces undefined results for all threads that access the collection, including
the reader threads.
You can make Collections classes thread safe by using any of the following
techniques:
! Create a thread-safe wrapper by using the Synchronized method and access
the collection exclusively through that wrapper. When implementing the
Synchronized method, derived classes must override the IsReadOnly
property to return the correct value.
! If the class does not have a Synchronized method, derive from the class and
implement a Synchronized method by using the SyncRoot property.
! Use a locking mechanism, such as the lock statement in Visual C#
(SyncLock in Visual Basic), on the SyncRoot property when accessing the
collection.

By default, only the Hashtable class guarantees thread safety when one writer
and multiple readers simultaneously access the collection. You can make the
collection thread-safe for multiple writers by using any of the preceding
techniques.
The Array class does not include a Synchronized method and, although it has
a SyncRoot property, you cannot derive from the class. Therefore, you can
make an array thread safe only through the locking mechanism.
Module 14 (Optional): Threading and Asynchronous Programming 51


" "" " Special Thread Topics
! Timer Class
! Thread Pools
! Threads and COM Interoperability
! Using Threads with Windows Forms Controls
! Best Practices for Working with Threads

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In addition to the basic thread creation and management operations and the
thread safety support through synchronization mechanisms, the .NET
Framework provides other interesting areas of threading functionality.
In this section, you will learn about timers and thread pools. The
System.Threading namespace includes:
! A Timer class that enables a delegate to be called after a specified amount
of time
! A ThreadPool class that manages groups of threads
! An IOCompletionCallback class to enable notification when an I/O
operation completes

You will also learn how the runtime synchronizes access to shared resources
between unmanaged COM objects and managed objects in a way that ensures
thread safety.
In addition, you will learn about the requirements for dealing with nonthread-
safe Windows Forms controls, and how you can optimize the performance
effects that are associated with marshaling method calls.
Finally, you will learn the recommended best practices for implementing
thread-safe code.
Topic Objective
To provide an overview of
the topics that you will cover
in this section.
Lead-in
Having looked at threading
in general and within the
context of the .NET
Framework, lets focus now
on some specialized thread
topics.
52 Module 14 (Optional): Threading and Asynchronous Programming


Timer Class
! Thread.Timer class - periodically executes a method
# Threading.TimerCallback delegate for callback method
# Change method changes timer values
# Dispose method frees the resources held by the timer
Timer timer = new Timer(
new TimerCallback(CheckStatus), null, 0, 2000);
//
static void CheckStatus(Object state) {
Console.WriteLine("Checking Status.");
}
Timer timer = new Timer(
new TimerCallback(CheckStatus), null, 0, 2000);
//
static void CheckStatus(Object state) {
Console.WriteLine("Checking Status.");
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The Thread.Timer class provides a mechanism for executing methods at
specified intervals. You cannot inherit from this class.
To specify the methods associated with a Timer, use a TimerCallback
delegate. Specify the timer delegate when the timer is constructed. The timer
delegate cannot be changed. The method calls do not execute in the thread that
creates the timer; they execute in a separate thread that is automatically
allocated by the system.
When you create a timer, you specify an amount of time to wait before the first
invocation of the delegate method (due time), and an amount of time to wait
between subsequent invocations (period). A timer invokes its methods when its
due time elapses, and invokes its methods once per period thereafter. You can
change these values, or you can disable the timer, by using the Change method.
When a timer is no longer needed, use the Dispose method to free the resources
held by the timer.

There are multiple Timer classes in the .NET Framework. Be careful not
to confuse the Thread.Timer class with the System.Timers.Timer and
System.Windows.Forms.Timer classes. For more information about these
classes, see the .NET Framework SDK documentation.

Topic Objective
To describe how to use the
Thread.Timer class with a
delegate to execute
methods periodically.
Lead-in
The Thread.Timer class
provides functionality to
execute a method at
specified intervals.
Note
Module 14 (Optional): Threading and Asynchronous Programming 53


The following example shows how to use a timer and a delegate to execute
methods at specified intervals of two seconds:
using System;
using System.Threading;


class App {
public static void Main() {
Console.WriteLine(
"Checking for status updates every 2 seconds.");
Console.WriteLine(
" (Hit Enter to terminate the sample)");
Timer timer = new Timer(new TimerCallback(CheckStatus),
null, 0, 2000);
Console.ReadLine();
timer.Dispose();
}


// The callback method's signature MUST
// match that of a System.Threading.TimerCallback
// delegate (it takes an Object parameter and returns void)
static void CheckStatus(Object state) {
Console.WriteLine("Checking Status.");
// ...
}
}

54 Module 14 (Optional): Threading and Asynchronous Programming


Thread Pools
! Thread pooling is used to improve efficiency
# System optimizes based on all of computers processes
! Do not use thread pooling when you
# Need a task to have a particular priority
# Have a task that might run for a long time
# Need to place threads into a single-threaded apartment
# Need a stable identity to be associated with the thread
! IOCompletionCallback delegate for asynchronous I/O
completion events
# A thread from the thread pool will process data when received

*****************************ILLEGAL FOR NON-TRAINER USE******************************
You can often use thread pooling to make the use multiple threads more
efficient. Many applications use multiple threads, but often those threads spend
a great deal of time in the sleeping state waiting for an event to occur. Other
threads may enter a sleeping state and be awakened only periodically to poll for
a change or update status information before going to sleep again.
Advantages of thread pools
Using thread pooling provides your application with a pool of worker threads
that are managed by the system, allowing you to concentrate on application
tasks rather than thread management. In fact, if you have a number of short
tasks that require more than one thread, using the ThreadPool class is the
easiest and typically the most efficient way to take advantage of multiple
threads.
Thread pooling enables the system to optimize the use of multiple threads for
better throughput, not only for your applications process but also with respect
to other processes on the computer. This optimization is transparent to your
application. When you use a thread pool, the system can optimize thread time
slices, taking into account all of the current processes on your computer.
Topic Objective
To introduce thread pooling.
Lead-in
You can often use thread
pooling to make the use of
multiple threads more
efficient.
Module 14 (Optional): Threading and Asynchronous Programming 55


Using thread pools in the .NET Framework
The .NET Framework uses thread pools for several purposes: asynchronous
calls, System.Net socket connections, asynchronous I/O completion, timers,
and registered wait operations, among others.
To use a thread pool, call ThreadPool.QueueUserWorkItem from managed
code, or CorQueueUserWorkItem from unmanaged code, and pass a
WaitCallback delegate that wraps the method that you want to add to the
queue.
You can also queue work items that need to be executed when a wait operation
completes. You queue these work items by using
ThreadPool.RegisterWaitForSingleObject and passing a WaitHandle and a
WaitOrTimerCallback delegate. When the WaitHandle is signaled or when
timed out, it raises a call to the method wrapped by the WaitOrTimerCallback
delegate. In both cases the thread pool uses or creates a background thread to
invoke the callback method.
Reasons Not to Use Thread Pools
There are several scenarios in which it is appropriate to create and manage your
own threads instead of using the ThreadPool class. You should do so when:
! You need a task to have a particular priority.
! You have a task that may run a long time and, therefore, block other tasks.
! You need to place threads into a single-threaded apartment (all ThreadPool
threads are in the multithreaded apartment).
! You need a stable identity to be associated with a particular thread. For
example, you may want to use a dedicated thread to abort that thread,
suspend it, or discover it by name.

56 Module 14 (Optional): Threading and Asynchronous Programming


The following example shows one way to use the ThreadPool class methods to
start multiple threads and monitor their completion:
using System;
using System.Threading;

class Counter
{
public void Read(int threadNum)
{
//
}
public void Increment(int threadNum)
{
//
}
}

class App
{
static Counter counter = null;
static int totalNumberOfAsyncOps = 10;
static int numAsyncOps = totalNumberOfAsyncOps;
static AutoResetEvent asyncOpsAreDone =
new AutoResetEvent(false);

public static void Main()
{
counter = new Counter();
for (int threadNum = 0;
threadNum < totalNumberOfAsyncOps;
threadNum++)
{
ThreadPool.QueueUserWorkItem(
new WaitCallback(UpdateResource), threadNum);
}
asyncOpsAreDone.WaitOne();
}
//
// The callback method's signature MUST match that of a
// System.Threading.TimerCallback delegate
// (it takes an Object parameter and returns void)
static void UpdateResource(Object state)
{
int threadNum = (int) state;
if ((threadNum % 2) != 0) counter.Read(threadNum);
else counter.Increment(threadNum);

if (( Interlocked.Decrement(ref numAsyncOps)) == 0)
asyncOpsAreDone.Set();
}
}

Module 14 (Optional): Threading and Asynchronous Programming 57


IOCompletionCallback
Using asynchronous I/O completion events, a thread from the thread pool
processes data only when the data is received. After the data has been
processed, the thread returns to the thread pool.
To make an asynchronous I/O call, you must associate an operating system I/O
handle with the thread pool and specify a callback method. When the I/O
operation completes, a thread from the thread pool invokes the callback method.
For more information about using asynchronous I/O completion events, see the
.NET Framework SDK documentation.
58 Module 14 (Optional): Threading and Asynchronous Programming


Demonstration: Using Synchronization Techniques and Thread
Pooling

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This demonstration shows how to perform synchronization using the C# lock
keyword and manual synchronization primitives and how to use the thread pool
to obtain multiple threads. The demonstration consists of several test sequences,
which are described in this topic.
The classes in the demonstration derive from the class Counter and differ by
the technique for synchronization that they implement. For each classs test, the
even number threads execute the classs Read method, which simply examines
and outputs the counters value, but does not modify it. The odd number threads
execute the classs Write method, which reads the counter, increments the
value, and stores the new value back in the counter.
The code for this demonstration is located in <install folder>\Democode\
Mod14\Demo14.4.
Topic Objective
To demonstrate various
synchronization techniques
and the use of the thread
pool.
Lead-in
In this demonstration, well
look at various
synchronization techniques
and the use of the thread
pool.
Module 14 (Optional): Threading and Asynchronous Programming 59


The Unsafe test
The Unsafe test uses an instance of the CounterUnsafe class which does no
synchronization. As a result of the race condition, the five Write method
invocations fail to increase the value of count by 5.
The test results in output that is similar to the following:
Unsafe test:
Start Resource writing (Thread=0) count: 0
Start Resource reading (Thread=1)count: 0
Stop Resource reading (Thread=1) count: 0
Start Resource writing (Thread=2) count: 0
Start Resource reading (Thread=3)count: 0
Stop Resource writing (Thread=0) count: 1
Start Resource writing (Thread=4) count: 1
Stop Resource reading (Thread=3) count: 1
Start Resource reading (Thread=5)count: 1
Stop Resource reading (Thread=5) count: 1
Start Resource writing (Thread=6) count: 1
Start Resource reading (Thread=7)count: 1
Stop Resource reading (Thread=7) count: 1
Start Resource writing (Thread=8) count: 1
Stop Resource writing (Thread=2) count: 1
Start Resource reading (Thread=9)count: 1
Stop Resource reading (Thread=9) count: 1
Stop Resource writing (Thread=4) count: 2
Stop Resource writing (Thread=6) count: 2
Stop Resource writing (Thread=8) count: 2
All Unsafe threads have completed.

60 Module 14 (Optional): Threading and Asynchronous Programming


The Interlocked test
The Interlocked test uses an instance of the CounterUsingInterlocked class,
which uses an Interlocked.Increment instruction to atomically update the
count. The five Write method invocations now increment count by 5.
The test results in output that is similar to the following:
Interlocked test:
Start Resource writing (Thread=0) count: 0
Stop Resource writing (Thread=0) count: 1
Start Resource reading (Thread=1)count: 1
Start Resource writing (Thread=2) count: 1
Stop Resource writing (Thread=2) count: 2
Start Resource reading (Thread=5)count: 2
Start Resource reading (Thread=3)count: 2
Start Resource writing (Thread=4) count: 2
Stop Resource writing (Thread=4) count: 3
Start Resource writing (Thread=6) count: 3
Stop Resource writing (Thread=6) count: 4
Start Resource reading (Thread=7)count: 4
Start Resource writing (Thread=8) count: 4
Stop Resource writing (Thread=8) count: 5
Start Resource reading (Thread=9)count: 5
Stop Resource reading (Thread=1) count: 5
Stop Resource reading (Thread=5) count: 5
Stop Resource reading (Thread=3) count: 5
Stop Resource reading (Thread=7) count: 5
Stop Resource reading (Thread=9) count: 5
All Interlocked threads have completed.

In the preceding example, the Interlock.Increment method
synchronized the concurrent threads Write method updates the count.
However, this approach does not prevent the value of count from changing its
value during another threads execution of the Read method. The manual
synchronization techniques that are demonstrated in the remaining test
sequences will synchronize execution to provide a consistent value for count
during each Read operation.

Note
Module 14 (Optional): Threading and Asynchronous Programming 61


The Lock test
The Lock test uses an instance of the CounterUsingLock class, which uses the
Visual C# lock keyword to synchronize access to the object so that only one
thread at a time executes either of the instance methods. The five Write method
invocations now increment count by 5, and all of the Read operations have a
consistent value for count.
The test results in output that is similar to the following:
Lock test:
Start Resource writing (Thread=0) count: 0
Stop Resource writing (Thread=0) count: 1
Start Resource writing (Thread=6) count: 1
Stop Resource writing (Thread=6) count: 2
Start Resource reading (Thread=9)count: 2
Stop Resource reading (Thread=9) count: 2
Start Resource reading (Thread=7)count: 2
Stop Resource reading (Thread=7) count: 2
Start Resource writing (Thread=4) count: 2
Stop Resource writing (Thread=4) count: 3
Start Resource writing (Thread=2) count: 3
Stop Resource writing (Thread=2) count: 4
Start Resource reading (Thread=3)count: 4
Stop Resource reading (Thread=3) count: 4
Start Resource reading (Thread=1)count: 4
Stop Resource reading (Thread=1) count: 4
Start Resource reading (Thread=5)count: 4
Stop Resource reading (Thread=5) count: 4
Start Resource writing (Thread=8) count: 4
Stop Resource writing (Thread=8) count: 5
All Lock threads have completed.

62 Module 14 (Optional): Threading and Asynchronous Programming


The Mutex test
The Mutex test uses an instance of the CounterUsingMutex class, with the
Mutex class to synchronize access to the object so that only one thread at a time
executes either of the instance methods. The five Write method calls now
increment count by 5, and all the Read operations have a consistent value for
count.
The test results in output that is similar to the following:
Mutex test:
Start Resource writing (Thread=0) count: 0
Stop Resource writing (Thread=0) count: 1
Start Resource reading (Thread=3)count: 1
Stop Resource reading (Thread=3) count: 1
Start Resource writing (Thread=4) count: 1
Stop Resource writing (Thread=4) count: 2
Start Resource reading (Thread=5)count: 2
Stop Resource reading (Thread=5) count: 2
Start Resource writing (Thread=6) count: 2
Stop Resource writing (Thread=6) count: 3
Start Resource reading (Thread=7)count: 3
Stop Resource reading (Thread=7) count: 3
Start Resource writing (Thread=8) count: 3
Stop Resource writing (Thread=8) count: 4
Start Resource writing (Thread=2) count: 4
Stop Resource writing (Thread=2) count: 5
Start Resource reading (Thread=1)count: 5
Stop Resource reading (Thread=1) count: 5
Start Resource reading (Thread=9)count: 5
Stop Resource reading (Thread=9) count: 5
All Mutex threads have completed.

Module 14 (Optional): Threading and Asynchronous Programming 63


The ReadWriteLock test
The ReadWriteLock test uses an instance of the
CounterUsingReaderWriterLock class, which uses the ReaderWriterLock
class to synchronize access to the object. Note that multiple readers or a single
writer can execute concurrently. The five Write method calls now increment
count by 5, and all of the Read operations have a consistent value for count.
Because a thread that is executing a Read does not block other threads that are
executing a Read, this technique enables the Read operations to complete
sooner than in the previous tests.
The test results in output that is similar to the following:
ReadWriteLock test:
Start Resource writing (Thread=0) count: 0
Stop Resource writing (Thread=0) count: 1
Start Resource reading (Thread=1)count: 1
Start Resource reading (Thread=3)count: 1
Start Resource reading (Thread=5)count: 1
Start Resource reading (Thread=7)count: 1
Start Resource reading (Thread=9)count: 1
Stop Resource reading (Thread=1) count: 1
Stop Resource reading (Thread=3) count: 1
Stop Resource reading (Thread=5) count: 1
Stop Resource reading (Thread=7) count: 1
Stop Resource reading (Thread=9) count: 1
Start Resource writing (Thread=2) count: 1
Stop Resource writing (Thread=2) count: 2
Start Resource writing (Thread=4) count: 2
Stop Resource writing (Thread=4) count: 3
Start Resource writing (Thread=6) count: 3
Stop Resource writing (Thread=6) count: 4
Start Resource writing (Thread=8) count: 4
Stop Resource writing (Thread=8) count: 5
All ReadWriteLock threads have completed.

64 Module 14 (Optional): Threading and Asynchronous Programming


Threads and COM Interoperability
! Thread calls to compatible COM apartment objects reduce costs
# Calls go directly to the COM objects, costly proxy-stub is eliminated
! A managed thread can indicate it will host an MTA or STA COM apartment
using
# Thread.ApartmentState property or [STAThread] [MTAThread] attributes,
for example:
! The finalizer thread and all ThreadPool threads are MTA
! Managed objects can be called from any COM apartment in a free-threaded
manner
# Except managed objects derived from ContextBoundObject
! Managed code calls out to COM objects following COM rules
[STAThread]
public static void Main(string[] args) {//}
[STAThread]
public static void Main(string[] args) {//}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
COM components use apartments to synchronize access to resources. In
contrast, managed objects use synchronized regions, synchronization primitives
such as mutexes, locks and completion ports, and synchronized contexts to
ensure that all shared resources are used in a thread-safe manner.
Calling a COM object from a managed thread
For interoperability, the runtime creates and initializes an apartment when a
managed thread creates a COM object. A managed thread can create and enter a
single-threaded apartment (STA) that contains only one thread, or a multi-
threaded apartment (MTA) that contains one or more threads. When a COM
apartment and a thread-generated apartment are compatible, COM allows the
calling thread to make calls directly to the COM object. If the apartments are
incompatible, COM creates a compatible apartment and marshals all calls
through a proxy in the new apartment.
On the first call to unmanaged code, the runtime calls CoInitializeEx to
initialize the COM apartment as an MTA or STA apartment. As long as the
proxy and stub are registered or the type library is registered, you do not have to
set this property.
Topic Objective
To explain how managed
threads call into a COM
component.
Lead-in
COM components use
apartments to synchronize
access to resources.
Module 14 (Optional): Threading and Asynchronous Programming 65


Setting the Thread.ApartmentState property
You can mark a managed thread to indicate that it will host a single-threaded or
multithreaded apartment. The Thread.ApartmentState property returns and
assigns the apartment state of a thread. If the property has not been set, the
property returns ApartmentState.Unknown.
Whenever the COM object and the managed thread are in incompatible
apartments, all calls on the object are made through a COM-created proxy.
The following example shows how to create an STA apartment-threaded COM
object, AptSimple, from managed code:
using System.Threading;
using APTOBJLib;
//
AptSimple obj = new AptSimple ();
obj.Counter = 1;

To eliminate the proxy and stub mechanism and significantly enhance
performance, set the ApartmentState on the thread before creating the object,
as follows:
using System.Threading;
using APTOBJLib;
//
Thread.CurrentThread.ApartmentState = ApartmentState.STA;
AptSimple obj = new AptSimple ();
obj.Counter = 1;

You can set the ApartmentState property when the thread is in the
ThreadState.Unstarted or ThreadState.Running state; however, you can
only set the property once for a thread. The two valid property states are single-
threaded apartment (STA) or multithreaded apartment (MTA).
66 Module 14 (Optional): Threading and Asynchronous Programming


Using the [STAThread] and [MTAThread] attributes
In some situations, a thread may already have called into unmanaged code
before the ApartmentState could be set. As an alternative to setting the
ApartmentState enumeration, you can apply the
System.STAThreadAttribute or System.MTAThreadAttribute to the main
entry point of the application. By applying these attributes, you ensure that the
main thread of an application is in the proper state.
For example, to ensure that the main thread of a Windows Form is apartment
compatible with the Forms control objects that must be created in a single
threaded COM apartment, you can use the following code:
//
[STAThread]
public static void Main(string[] args)
{
//
}

Other COM vs. managed threading issues
Either apartment state has little effect on the managed portion of your
application. The finalizer thread and all threads that are controlled by
ThreadPool are MTA.
Managed objects that are exposed to COM behave as if they had aggregated the
free-threaded marshaler. In other words, they can be called from any COM
apartment in a free-threaded manner. The only managed objects that do not
exhibit this free-threaded behavior are those objects that derive from the
ContextBoundObject class.
In the managed execution environment, there is no support for the
SynchronizationAttribute, unless you use contexts and context-bound
managed instances. If you are using EnterpriseServices, your object must
derive from ServicedComponent, which is itself derived from
ContextBoundObject.
When managed code calls out to COM objects, it always follows COM rules. In
other words, it calls through COM apartment proxies and COM+ 1.0 context
wrappers as dictated by OLE32.
Module 14 (Optional): Threading and Asynchronous Programming 67


Using Threads with Windows Forms Controls
! Controls can only execute methods on the thread on
which they are created
# Except for calls to their thread-safe methods
! Background thread calls to a control must be marshaled
# Use thread-safe methods - Invoke, BeginInvoke, and
EndInvoke
# Methods take a reference to a delegate, typically an
instance of the MethodInvoker delegate
! For example, a call to the aFormControl objects
UpdateProgress method
MethodInvoker mi = new
MethodInvoker(aFormControl.UpdateProgress);
aFormControl.BeginInvoke(mi);
MethodInvoker mi = new
MethodInvoker(aFormControl.UpdateProgress);
aFormControl.BeginInvoke(mi);

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Windows Forms controls can only execute on the thread on which they are
created. That is, they are not thread-safe. If you want to get or set properties, or
call methods, on a control from a background thread, you must marshal the call
to the thread that created the control.
There are five methods on a control that are safe to call from any thread:
InvokeRequired, Invoke, BeginInvoke, EndInvoke, and CreateGraphics.
For all other method calls on a control, you should pass a delegate to that
method as a parameter to the controls Invoke or BeginInvoke methods. The
Invoke and BeginInvoke methods ensure that their delegate parameters
method is executed on the thread that owns this controls underlying window
handle.
Topic Objective
To describe how to use a
Windows Forms controls
thread-safe method to make
cross thread calls on the
control.
Lead-in
Because Windows Forms
controls can only execute on
the thread on which they are
created, they are not
considered thread-safe.
68 Module 14 (Optional): Threading and Asynchronous Programming


The following table describes the invoke methods and specifies the method
signatures for those methods.
Method Signature and description

InvokeRequired public bool InvokeRequired { get ; }

Returns true if the caller must call Invoke when making method
calls to this control.
BeginInvoke public IAsyncResult BeginInvoke(Delegate
method)
public IAsyncResult BeginInvoke(Delegate
method, Object[] args)

Executes the specified delegate on the thread that owns this
controls underlying window handle. The delegate is called
asynchronously, and this method returns immediately. You can
call this method from any thread, even the thread that owns the
controls handle. If the controls handle does not exist yet, this
method will follow up the controls parent chain until it finds a
control or form that has a window handle. If no appropriate
handle can be found, BeginInvoke throws an exception.
Exceptions within the delegate method are considered
untrapped and are sent to the applications untrapped exception
handler.
EndInvoke public Object EndInvoke(IAsyncResult
asyncResult)

Retrieves the return value of the asynchronous operation
represented by the IAsyncResult interface that is passed. If the
asynchronous operation has not completed, this method blocks
until the result is available.
Invoke public Object Invoke(Delegate method)
public Object Invoke(Delegate method,
Object[] args)

Executes the specified delegate on the thread that owns this
controls underlying window handle. The delegate is called
synchronously and this method returns after the invoked
method has returned. The return value is the result of the
invoked method. It is an error to call this method on the same
thread to which the control belongs.

The following code demonstrates how to create a background thread that uses a
MethodInvoker to update a ProgressBar control at regular intervals:
Module 14 (Optional): Threading and Asynchronous Programming 69


//
//Start the background thread
timerThread = new Thread(new ThreadStart(ThreadProc));
timerThread.IsBackground = true;
timerThread.Start();

//

//This function is executed on a background thread
// it marshalls calls to update the UI back to
// the foreground thread
public void ThreadProc() {

try {
MethodInvoker mi = new
MethodInvoker(this.UpdateProgress);
while (true)
{
//Call BeginInvoke on the Form
this.BeginInvoke(mi);
Thread.Sleep(500) ;
}
}
//Thrown when the thread is interupted by the main
// thread - exiting the loop
catch (ThreadInterruptedException e)
{
//Simply exit....
}
catch (Exception we)
{
}
}

//This function is called from the background thread
private void UpdateProgress()
{
//Reset to start if required
if (progressBar1.Value == progressBar1.Maximum)
{
progressBar1.Value = progressBar1.Minimum ;
}
progressBar1.PerformStep() ;
}

//

//Make sure to clean up the background thread in Dispose
public override void Dispose()
{
if (timerThread != null)
{
timerThread.Interrupt();
timerThread = null;
}
}
70 Module 14 (Optional): Threading and Asynchronous Programming



If you are making multiple cross-thread calls to a control, it is much more
efficient to create a new method that executes those calls and make a single
cross-thread call to the new method.

Note
Module 14 (Optional): Threading and Asynchronous Programming 71


Demonstration: Windows Forms Threading

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This demonstration shows how to use a background thread in a Windows Form
application. The background thread will periodically advance the Windows
Form progress bar.
The code for this demonstration is located in <install folder>\Democode\
Mod14\Demo14.5.
You should run this demonstration with breakpoints set on the ThreadProc
methods statement:
this.BeginInvoke(mi);

and on the UpdateProgress methods statement:
progressBar1.PerformStep();

Start the application and observe the Microsoft Visual Studio .NET Threads
window.

To make the Visual Studio .NET Threads window visible, while the
application is running, on the Debug menu, click Windows, and click Threads.

Click the forms Start button, and when the program breaks, note the active
thread. Continue to the next breakpoint and again note the active thread. You
should observe that the BeginInvoke method marshaled the call to the
Windows Forms main thread.
Topic Objective
To demonstrate how to use
a background thread in a
Windows Forms application.
Lead-in
In this demonstration, you
will see how to use a
background thread in a
Windows Forms application.
Note
72 Module 14 (Optional): Threading and Asynchronous Programming


Best Practices for Working with Threads
! Avoid providing static methods that alter static state
! Static state must be thread safe
! Instance state does not need to be thread safe
! Avoid taking locks whenever possible
! Be aware that deadlocks can result from calls in locked
sections
! Use the System.Threading.Interlocked classes in
preference to the lock statement where possible
! Avoid the need for synchronization if possible

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Use the following best practices and rules as design guidelines for
implementing threading in your applications:
! Avoid providing static methods that alter static state.
In common server scenarios, static state is shared across requests, which
means that multiple threads can execute requests that access the same static
state at the same time. This creates the potential for threading bugs.
Consider using a design pattern that encapsulates data into instances that are
not shared across requests.
! Static state must be thread-safe.
! Instance state does not need to be thread safe.
By default, a library is not thread safe. Adding locks to create thread-safe
code decreases performance, increases lock contention, and creates the
possibility for deadlock bugs to occur.
In common application models, only one thread at a time executes user
code, which minimizes the need for thread safety. For this reason, the .NET
Framework is not thread safe by default. In cases where you want to provide
a thread-safe version of a class, use a GetSynchronized method to return a
thread-safe instance of a type. For examples, see the System.Collections
namespace in the .NET Framework SDK documentation.
! Design your library with consideration for the efficiency costs of running in
a server scenario. Avoid taking locks whenever possible.
! Be aware of method calls in locked sections.
Deadlocks can result when a static method in class A calls static methods in
class B and vice versa. If A and B both synchronize their static methods,
this will cause a deadlock. You may discover this deadlock only under
heavy threading stress.
Topic Objective
To provide a brief review of
guidelines for working with
threads.
Lead-in
If you need to implement
threading in your
applications, there are some
guidelines and best
practices of which you
should be aware.
Module 14 (Optional): Threading and Asynchronous Programming 73


! Performance issues can result when a static method in a class calls a static
method in the same class.
If these methods are not factored correctly, performance will suffer because
there will be a large amount of redundant synchronization. Excessive use of
synchronization may negatively affect performance. In addition, it may have
a significant negative effect on scalability.
! Be aware of issues with the lock statement (SyncLock in Visual Basic).
It is tempting to use the lock statement to solve all threading problems.
However, the System.Threading.Interlocked class is superior for updates
that must be made automatically. It executes a single lock prefix if there is
no contention.
In a code review, you should look for instances like the one shown in the
following example.
lock(this)
{
myField++;
}

! Avoid the need for synchronization if possible.
For high traffic pathways, it is best to avoid synchronization. Sometimes
you can adjust the algorithm to tolerate race conditions rather than eliminate
them.

74 Module 14 (Optional): Threading and Asynchronous Programming


" "" " Asynchronous Programming in .NET
! Support for Asynchronous Programming in .NET
! Design Pattern for Asynchronous Programming
! Asynchronous File Stream Read Example
! Asynchronous Delegates

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The central idea behind asynchronous programming is to be able to issue
method calls to other components and to carry on with other work, without
waiting for the operation to complete. The runtime provides rich support for
asynchronous programming and handles the details of threading and data
exchange.
In this section, you will learn about how the .NET Framework supports
asynchronous programming.
Topic Objective
To provide an overview of
the topics that you will cover
in this section.
Lead-in
In this section, we will look
at how the .NET Framework
enables asynchronous
programming.
Module 14 (Optional): Threading and Asynchronous Programming 75


Support for Asynchronous Programming in .NET
! Supported in Many Areas of .NET
# I/O, sockets, networking, remoting, ASP.NET and Web
Services, messaging (MSMQ), delegates
! Offers a design pattern for asynchronous programming
# Consistent and type-safe programming model
# User-created classes should conform to this
design pattern

*****************************ILLEGAL FOR NON-TRAINER USE******************************
With asynchronous programming in the .NET Framework, you make a call to a
.NET Framework class method while the program continues execution until a
specified callback is made. If no callback is specified, the program continues
execution until it encounters blocking, polling, or waiting for the call to
complete.
For example, a program can call a method which enumerates a large list, while
the main program continues to execute. When the enumeration is complete, a
callback is made and the program addresses it.
Many areas of the .NET Framework support asynchronous programming,
including:
! File IO, Stream IO, Socket IO
! Networking: HTTP, TCP
! Remoting channels (HTTP, TCP) and proxies
! XML Web services created by using ASP.NET
! ASP.NET Web Forms
! Messaging message queues over MSMQ
! Asynchronous delegates

The .NET Framework provides a design pattern that makes asynchronous calls
uniform across the different parts of the framework. This pattern is very useful
for making complex calls that take a considerable amount of time to complete.
User-created classes that support asynchronous calls should conform to this
design pattern.
Topic Objective
To provide a high level
overview of the .NET
Framework support for
asynchronous programming.
Lead-in
In addition to support for
asynchronous programming
in many areas of .NET, the
.NET Framework offers an
asynchronous programming
design pattern.
76 Module 14 (Optional): Threading and Asynchronous Programming


Design Pattern for Asynchronous Programming
! Caller decides whether to make a synchronous or asynchronous call
# Server may explicitly implement asynchronous methods, or client can use
delegate objects asynchronous support
! Caller does asynchronous operation in two parts
# Call begin method to supply parameters and start operation
- an object implementing IAsyncResult is returned
# When operation is done, call end method to obtain the results
! Caller has several options to know when operation is done
# Callback method - if specified in the begin method call, callback is invoked
# Poll check IAsyncResult.IsCompleted property
# Call end method if called before operation is done, automatically waits
# Wait wait on IAsyncResult.WaitHandle property, can use timeouts

*****************************ILLEGAL FOR NON-TRAINER USE******************************
One of the innovations that is provided by the asynchronous design pattern is
that the caller can decide whether a particular call should be asynchronous. You
do not need to do additional programming for the called object for it to support
asynchronous client calls. The client can make an asynchronous call on another
object whose class does not explicitly support such calls by instantiating a
delegate object that refers to the objects method.
The runtime and language compilers encapsulate methods that provide
asynchronous operation into delegate classes. The called object can choose to
explicitly support asynchronous behavior; either because it can implement
asynchronous behavior more efficiently than by using a general architecture, or
because the called object is designed to support only asynchronous behavior by
its callers. The level of asynchronous support of the called object is up to the
objects author.
However, if an object is to explicitly support asynchronous methods, you
should follow the asynchronous design pattern for exposing these methods.
Details of the asynchronous design pattern
In the .NET Framework asynchronous design pattern, the asynchronous
operation is split into two logical parts: the part where the client calls a begin
operation method and provides input to start the asynchronous operation, and
the part where the results of the asynchronous operation are obtained by the
client through a call to an end operation method.
Topic Objective
To provide details of the
asynchronous design
pattern.
Lead-in
One of the innovations that
is provided by the
asynchronous design
pattern is that the caller can
decide whether a particular
call should be
asynchronous.
Module 14 (Optional): Threading and Asynchronous Programming 77


Beginning the asynchronous operation
In the first part of an asynchronous call, the caller upon invoking the begin
operation method can also supply an optional AsyncCallback delegate, in
addition to providing the input needed for the operation. This delegate refers to
the method to be called when the asynchronous operation is completed. The
begin method synchronously returns an object that implements the
IAsyncResult interface. The caller can use the methods of this interface to
determine the status of the asynchronous operation. The server typically
maintains any state that is associated with an asynchronous operation in this
waitable object.
The IAsyncResult interface specifies the properties that are listed in the
following table.
IAsyncResult interface property Description

AsyncState AsyncState returns the object that was provided as
the last parameter, as part of the begin operation
method call.
AsyncWaitHandle The AsyncWaitHandle property returns the
WaitHandle that is set after the server has
completed processing of the call.
CompletedSynchronously The CompletedSynchronously property is set to
true if the begin operation call completes
synchronously.
IsCompleted The IsCompleted property is set to true after the
server completes processing of the call.

Obtaining the results of the asynchronous operation
In the second part of an asynchronous call, the caller obtains the results of the
operation in one of the four following ways:
! Callback Method
If the caller supplied the optional AsyncCallback delegate, the callback
method referred to by this delegate will be called when the operation
completes.
! Polling
The caller can poll the returned IAsyncResult interfaces IsCompleted
property to determine if the call has completed.
! End Operation Method
The caller can attempt to complete the operation by calling the end
operation method, thereby blocking until the operation completes.
! WaitHandle, End Operation Method
The caller can wait on the IAsyncResult interfaces WaitHandle property.
One difference between this and the previous option is that the client can
use time outs to wake up periodically. Another difference is that the caller
can choose to wait for multiple events to occur by calling the WaitAll
method in the Thread.WaitHandle class and specifying an array of events,
see the topic, Using the WaitHandle class to wait for events in this module.

78 Module 14 (Optional): Threading and Asynchronous Programming



Cancel is not provided on IAsyncResult, because in many
implementations, there is no guarantee that an asynchronous operation will be
canceled.

Exceptions
If the begin operation method throws an exception, the caller can assume that
an asynchronous operation was not started and that the callback delegate will
not be called.
After an asynchronous operation starts, the client is notified of any exceptions
that are raised by the server when the client calls the end operation method.
Note
Module 14 (Optional): Threading and Asynchronous Programming 79


Asynchronous File Stream Read Example
! Asynchronous read with callback
# Create callback method delegate
# Begin the operation
# In the callback method, complete the operations
AsyncCallback myCallback = new
AsyncCallback(this.OnReadDone);
AsyncCallback myCallback = new
AsyncCallback(this.OnReadDone);
IAsyncResult ar = aStream.BeginRead(buffer,0,
buffer.Length, myCallback,(object)myState);
IAsyncResult ar = aStream.BeginRead(buffer,0,
buffer.Length, myCallback,(object)myState);
int byteCount = aStream.EndRead(ar); //data in buffer
int byteCount = aStream.EndRead(ar); //data in buffer

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The examples in this topic show how to implement a client that uses the
System.IO.Stream classs methods to read a sequence of bytes from a file. The
System.IO.Stream classs synchronous read method is named Read and the
asynchronous methods are named BeginRead and EndRead.
For the complete running code of these examples, see <install folder>\
Democode\Mod14\Demo14.6.
The following code uses a byte array named buffer to store the bytes returned
from the read operation:
byte[] buffer = new byte [512];
string filename = "test.txt"
Stream aStream = File.OpenRead(filename);

The following code uses a synchronized call to read data from the file:
int byteCount = aStream.Read(buffer, 0, buffer.Length);
aStream.Close();

Details of the asynchronous read operation
The asynchronous operation consists of two logical parts: the part that takes
input from the client and calls the asynchronous operation, and the part that
supplies results of the asynchronous operation to the client.
Topic Objective
To show the first part of the
Asynchronous File Stream
Read example by explaining
how to do an asynchronous
read using a callback.
Lead-in
Lets first look at how to do
an asynchronous read by
using a callback.
80 Module 14 (Optional): Threading and Asynchronous Programming


Asynchronous Read With Callback
In this example the callback method is an instance method named
OnReadDone, which returns void and takes a single parameter of type
IAsyncResult. The System namespace defines a delegate class AsyncCallback
that matches the signature for the instance method so that there is no need to
declare a new delegate type.
You create the callback delegate object as follows:
AsyncCallback myCallback = new AsyncCallback(this.OnReadDone);

If the caller may issue multiple asynchronous calls, the caller must be able to
correlate each of the results to the original call. You can use the state parameter
of the method that makes the asynchronous call to store this information.
In this example, you store the name of the file being read in an instance of
MyState:
class MyState
{
public string filename;
public MyState(string filename) {
this.filename = filename;
}
}
// ...
MyState myState = new MyState(filename);

You can now make the asynchronous call as follows:
IAsyncResult ar = aStream.BeginRead(
buffer, 0, buffer.Length, myCallback, (object)myState);
// continue to execute

The BeginRead method returns and the thread can continue to execute while
the read operation proceeds. When the read operation completes, the
OnReadDone method is called, possibly on a separate thread. This method
performs the second part of the asynchronous operation, obtaining the results of
the operation as follows:
int byteCount = aStream.EndRead(ar); // data now in buffer
Console.WriteLine("Filename read: {0},
((MyState)(ar.AsyncState)).filename);
aStream.Close();

The next topic, Asynchronous File Stream Read Example (Continued),
completes the example, by showing how to do an asynchronous read with
polling.
Module 14 (Optional): Threading and Asynchronous Programming 81


Asynchronous File Stream Read Example (continued)
! Asynchonous read with polling
# Begin the operation
# Poll and complete
while (!ar.IsCompleted){ // do whatever
}
int byteCount = aStream.EndRead(ar); // data in buffer
while (!ar.IsCompleted){ // do whatever
}
int byteCount = aStream.EndRead(ar); // data in buffer
IAsyncResult ar = aStream.BeginRead(buffer,0,
buffer.Length, null,(object)myState);
IAsyncResult ar = aStream.BeginRead(buffer,0,
buffer.Length, null,(object)myState);

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The preceding topic showed how to complete an asynchronous read operation
by using a callback delegate. In this topic, you will see how to obtain results of
the asynchronous read with polling.
Asynchronous Read with Polling
To handle the second part of the asynchronous operation by polling for
completion rather than using a callback method, you make the initial
asynchronous call by supplying a null value for the callback method.
IAsyncResult ar = aStream.BeginRead(
buffer, 0, buffer.Length,null, (object)myState);

The BeginRead method returns and the thread can continue to execute while
the read operation proceeds. In this case, the thread periodically polls the
IsCompleted property of the IAsyncResult to determine when the read
operation is complete.
while (!ar.IsCompleted)
{
// do whatever
}
int byteCount = aStream.EndRead(ar); // data now in buffer
Console.WriteLine("Filename read: {0},
((MyState)(ar.AsyncState)).filename);
aStream.Close();

Topic Objective
To continue the
Asynchronous File Stream
Read example by explaining
how to do an asynchronous
read with polling.
Lead-in
Now lets see how to do an
asynchronous read with
polling.
82 Module 14 (Optional): Threading and Asynchronous Programming


Demonstration: Asynchronous File Stream Read

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This demonstration shows how to use synchronous and asynchronous read
methods on a file stream. For the asynchronous case, the four different ways to
complete the operation are shown: callback, poll, end method call, and wait
with timeout.
Notice that in the asynchronous case that uses a callback method, the callback
method may be executed on a different thread than the main code.
The code for this demonstration is located in <install folder>\Democode\
Mod14\Demo14.6.
Topic Objective
To demonstrate how to use
synchronous and
asynchronous read methods
on a file stream.
Lead-in
In this demonstration, you
will see how to use
synchronous and
asynchronous read methods
on a file stream.
Module 14 (Optional): Threading and Asynchronous Programming 83


The output that is produced by running the code is similar to the following:
Doing Synchronous Read

Synchronous Read Data:!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
01234567890123456789012345678901

Doing Callback Asynchronous Read

Asynchronous Callback Read
Filename: test.txt Data:!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
01234567890123456789012345678901

Doing Poll Asynchronous Read

Asynchronous Poll Read
Filename: test.txt Data:!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
01234567890123456789012345678901

(Code continued on the following page.)
84 Module 14 (Optional): Threading and Asynchronous Programming


Doing End Method Asynchronous Read

Asynchronous End Read
Filename: test.txt Data:!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
01234567890123456789012345678901

Doing Wait Asynchronous Read

Asynchronous Wait Read
Filename: test.txt Data:!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
0123456789012345678901234567890123456789!
01234567890123456789012345678901

Module 14 (Optional): Threading and Asynchronous Programming 85


Asynchronous Delegates
! Delegate object provides the ability to call a
synchronous method in an asynchronous manner
# Compiler generates synchronous invoke method and
asynchronous methods: BeginInvoke and EndInvoke
! Asynchronous calls follow the design pattern
# Start operation with BeginInvoke
# Use callback, polling, call to EndInvoke, or wait to
determine completion
# Call EndInvoke to get results

*****************************ILLEGAL FOR NON-TRAINER USE******************************
You can make an asynchronous method call on an object even if the objects
class does not provide explicit support for asynchronous operations by using the
asynchronous methods of a delegate object.
Calling a synchronous method asynchronously
For each delegate class the Visual C# compiler generates BeginInvoke and
EndInvoke methods that provide the ability to call a synchronous target
method in an asynchronous manner.
If the BeginInvoke method is called, the runtime queues the request and returns
immediately to the caller. The target method will be called on a thread from the
thread pool. The original thread that submitted the request is free to continue
executing in parallel to the target method, which is running on a thread pool
thread.
If a callback has been specified on BeginInvoke, it is called when the target
method returns. In the callback, the EndInvoke method is used to obtain the
return value and the in/out parameters. If the callback is not specified on
BeginInvoke, you can use the other asynchronous design pattern techniques,
for example polling, on the original thread that submitted a request.
Topic Objective
To describe how to use
delegate objects to make
asynchronous calls.
Lead-in
You can make an
asynchronous method call
on an object even if the
objects class does not
provide explicit support for
asynchronous operations by
using the asynchronous
methods of a delegate
object.
86 Module 14 (Optional): Threading and Asynchronous Programming


Using an asynchronous delegate
In the following example, which factorizes a number, you can see how to use a
delegate to make an asynchronous call.
The code for this demonstration is located in <install folder>\Democode\
Mod14\Demo14.7.
In this example, an asynchronous call is invoked on the Factorize method in
the PrimeFactorizer class, which is declared as follows:
public class PrimeFactorizer
{
public bool Factorize(
int factorizableNum,
ref int primefactor1,
ref int primefactor2)
{
// code to factorize the number
}
}

First a delegate for the Factorize is defined:
public delegate bool FactorizeDelegate(
int factorizableNum,
ref int primefactor1,
ref int primefactor2);

When the compiler emits the FactorizeDelegate delegate class after parsing its
definition, it generates the BeginInvoke and EndInvoke methods, in addition
to the Invoke method. The BeginInvoke and EndInvoke methods follow the
asynchronous design patterns method signatures, as follows:
public class FactorizeDelegate: delegate
{
public bool Invoke(
int factorizableNum,
ref int primefactor1,
ref int primefactor2);

// The following code was supplied by the compiler.
public IAsyncResult BeginInvoke(
int factorizableNum,
ref int primefactor1,
ref int primefactor2,
AsyncCallback cb,
Object AsyncState
);

// The following code was supplied by the compiler.
public bool EndInvoke(
ref int primefactor1,
ref int primefactor2,
IAsyncResult ar);
}

Module 14 (Optional): Threading and Asynchronous Programming 87


Next, the method to be called when the operation completes is defined:
public class ProcessFactorizedNumber
{
private int _ulNumber;

public ProcessFactorizedNumber(int number)
{
_ulNumber = number;
}

// the qualifier is one-way, see Note
[OneWayAttribute()]
public void FactorizedResults(IAsyncResult ar)
{
int factor1=0, factor2=0;

// Extract the delegate from the AsyncResult.
FactorizeDelegate fd =
(FactorizeDelegate)((AsyncResult)ar).AsyncDelegate;

// Obtain the result.
fd.EndInvoke(ref factor1, ref factor2, ar);

// Output results.
Console.WriteLine(
"On CallBack: Factors of {0} : {1} {2}",
_ulNumber, factor1, factor2);
}
}

The OneWayAttribute marks a method as one way, without a return
value and out or ref parameters. When one-way methods are called, no reply
message, status, or other information is expected.

Note
88 Module 14 (Optional): Threading and Asynchronous Programming


The asynchronous operation is started by calling BeginInvoke as follows:
public void FactorizeNumber1()
{
//create the necessary arguments
// delegate to method to be called asynchronously
PrimeFactorizer pf = new PrimeFactorizer();
FactorizeDelegate fd = new
FactorizeDelegate(pf.Factorize);

// arguments for the Factorize method
int factorizableNum = 1000589023, temp=0;

// create the callback delegate
// to be called when the call completes

// first create an instance of the class which
// contains the callback method
ProcessFactorizedNumber fc = new
ProcessFactorizedNumber(factorizableNum);
// then create an AsyncCallback delegate to the method
AsyncCallback cb = new
AsyncCallback(fc.FactorizedResults);

// Asynchronously invoke the Factorize method.
IAsyncResult ar = fd.BeginInvoke(
factorizableNum,
ref temp,
ref temp,
cb,
null);

//
// Do some other useful work.
//. . .
}

Module 14 (Optional): Threading and Asynchronous Programming 89


An alternative approach to using a callback function is to wait for completion,
as shown in the following code:
public void FactorizeNumber2()
{
PrimeFactorizer pf = new PrimeFactorizer();
FactorizeDelegate fd = new
FactorizeDelegate(pf.Factorize);

int factorizableNum = 1000589023, temp=0;

// Asynchronously invoke the Factorize method on pf.
IAsyncResult ar = fd.BeginInvoke(
factorizableNum,
ref temp,
ref temp,
null,
null);

ar.AsyncWaitHandle.WaitOne(10000, false);

if (ar.IsCompleted)
{
int factor1=0, factor2=0;

// Obtain the result.
fd.EndInvoke(ref factor1, ref factor2, ar);

// Output results.

Console.WriteLine(
"After Using Wait Handle: Factors of {0}: {1} {2}",
factorizableNum, factor1, factor2);
}
}

90 Module 14 (Optional): Threading and Asynchronous Programming


Multimedia: Asynchronous Programming

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This animation illustrates the .NET Framework common language runtime
support for asynchronous programming using Delegate objects.
Topic Objective
To illustrate asynchronous
programming using
Delegate objects.
Lead-in
This animation illustrates the
.NET Framework common
language runtimes support
for asynchronous
programming using
Delegate objects.
To launch the animation,
click the button in the lower-
left corner of the slide.
Module 14 (Optional): Threading and Asynchronous Programming 91


Demonstration: Using a Delegate

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This demonstration shows how to use a delegate object to make asynchronous
calls. The asynchronous call is made by using a callback method and by waiting
for the completion event.
The code for this demonstration is located in <install folder>\Democode\
Mod14\Demo14.7.
The output is similar to the following:
On CallBack: Factors of 1000589023 : 7 142941289
After Using Wait Handle : Factors of 1000589023 : 7 142941289

Topic Objective
To demonstrate how to use
a delegate object to make
asynchronous calls.
Lead-in
In this demonstration, you
will see how to use a
delegate object to make
asynchronous calls.
92 Module 14 (Optional): Threading and Asynchronous Programming


Lab 14: Working With Multithreaded Applications

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives
After completing this lab, you will be able to:
! Create thread-safe methods using the C# lock keyword.
! Create, start, and dispose of a System.Thread.Timer object to periodically
perform an action.
! Create, start, and interrupt a background thread.
! Make an asynchronous method call by using delegates and a callback
method according to the .NET Framework asynchronous design pattern.

Lab Setup
Starter and solution files are associated with this lab. The starter files are in the
folder <install folder>\Labs\Lab14\Starter. The solution files for this lab are in
the folder <install folder>\Labs\Lab14\Solution.
Topic Objective
To introduce the lab.
Lead-in
In this lab, you will create
thread-safe code that has
both System.Thread.Timer
and background thread
objects. In addition you will
make asynchronous calls
using delegates.
Explain the lab objectives.
Module 14 (Optional): Threading and Asynchronous Programming 93


Scenario
In this scenario, you use a Windows Forms client to invoke methods on a
logger server that writes messages to a log file. When starting the logger server
the client specifies the filename to use as a log file. After the logger server
starts, it opens the log file and begins two concurrent operations. In the first
operation, the server periodically writes the date and time to the log file; the
server does this by using a System.Thread.Timer object that uses a thread
from the thread pool.
In the second operation, the server periodically updates the clients progress bar
to indicate to the user that the logger service is running. The server does this by
creating and starting a background thread. When the user stops the logger
server, the server terminates both concurrent operations and closes the log file.
While the logger server is running, the client can invoke a synchronous or an
asynchronous write of a text string to the log file. The asynchronous write is
done according to the .NET Framework asynchronous design pattern. The client
starts the asynchronous write by calling the BeginInvoke method of a delegate
to the logger servers write method. The client, as a parameter to this call,
supplies an AsyncCallback delegate object that refers to the clients method
that is called when the write operation completes.
Because multiple client calls to the server can execute concurrently, the servers
methods must be implemented in a thread-safe manner.
Estimated time to complete this lab: 50 minutes
94 Module 14 (Optional): Threading and Asynchronous Programming


Exercise 1
Creating Thread-safe Methods
In this exercise, you will add thread-safe code to the MyLogger static methods
to start the logger, stop the logger, and write to the log file.
! Implement the StartLogger method
1. In Visual Studio .NET, open the Multithreading project which is located in
<install folder>\Labs\Lab14\Starter.
2. Open the file Form1.cs and locate the following comment for the
StartLogger method of the MyLogger class:
// TODO - Exercise 1.1: Add code to start logger

You can use the Visual Studio .NET Task List window to quickly
locate this code. To display this window, on the View menu, click Show
Tasks, and click All.

3. Use a lock statement to ensure that only one thread at a time executes the
methods code.
Because StartLogger is a static method, you lock on the System.Type
object of the MyLogger class. You can obtain this object by using the
following expression:
typeof(MyLogger)

Place the remainder of the StartLogger methods code within the lock
statement block.
4. Create a conditional expression that evaluates whether the logger is running.
If the logger is running, throw an InvalidOperationException exception. If
the logger is not running (loggerRunning is false):
a. Initialize numberOfWrites to zero.
b. Create a FileStream object for the file that is specified in the filename
parameter of the StartLogger method. The new FileStream object also
takes the filename, FileMode.OpenOrCreate and FileAccess.Write
parameters.
c. Create a StreamWriter object and pass in the new FileStream object.
This new object should be assigned to the existing static member
variable w.
d. Set the file pointer to the end of the new stream by calling the
StreamWriter objects seek method as follows:
w.BaseStream.Seek(0, SeekOrigin.End);

e. Write to the stream by using the following format and supplying the
current time and date as the parameters:
"\nStartLogger Time: {0} {1} \n\n"

Note
Module 14 (Optional): Threading and Asynchronous Programming 95


f. Flush the stream.
g. Set the existing static member variable form to the value of the
parameter aForm in the StartLogger method.
h. Set the existing static member variable mi to a new delegate object of
type MethodInvoker that refers to the form objects UpdateProgress
method.
i. Set loggerRunning to true.

! Implement the StopLogger method
1. In Form1.cs, locate the following comment for the StopLogger method of
the MyLogger class:
// TODO - Exercise 1.2: Add code to stop logger
2. Use a lock statement to ensure that only one thread at a time executes the
static methods code.
Place the remainder of the StopLogger methods code within the lock
statement block.
3. Create a conditional expression that evaluates whether the logger is running.
If the logger is not running, throw an InvalidOperationException
exception. If the logger is running (loggerRunning is true):
a. Write to the stream by using the following format and supplying the
current time and date as the parameters:
"\nStopLogger Time: {0} {1} \n\n"

b. Close the stream.
c. Set loggerRunning to false.

96 Module 14 (Optional): Threading and Asynchronous Programming


! Implement the WriteLog method
1. In Form1.cs, locate the following comment for the WriteLog method of the
MyLogger class:
// TODO - Exercise 1.3: Add code to write to logger
2. Use a lock statement to ensure that only one thread at a time executes the
static methods code.
Place the remainder of the WriteLog methods code within the lock
statement block.
3. Create a variable of type int that is named writeCount and initialize it to
the value of numberOfWrites.
4. Create a conditional expression that evaluates whether the logger is running.
If the logger is not running, throw an InvalidOperationException
exception. If the logger is running (loggerRunning is true):
a. Make the thread sleep for 2 seconds.
b. Increment the value of writeCount by 1.
c. Write to the stream by using the following format and supplying
writeCount and logText as the parameters:
"\nCount: {0} Text: {1} - Time:"

d. Write to the stream by using the following format and supplying the
current time and date as the parameters:
"{0} {1} \n\n"

e. Flush the stream.
f. Set numberOfWrites to the value of writeCount.
5. Return writeCount.

Module 14 (Optional): Threading and Asynchronous Programming 97


! Test the application
1. Build and run the application by using Visual Studio .NET. Ignore warning
messages about fields that are never used.
2. In the Text To Log field, type Hello World.
3. To check the error reporting, click Synchronous Write, and note the
following message in the status bar at the bottom of the form:
Invalid Operation - Write failed because logger was stopped
4. Click Start Logger, and note the Logger State Indicator progress bar and
the following message in the status bar:
Logger started
5. Click Synchronous Write, and note that after a pause of 2 seconds the
following message is displayed in the status bar:
Log written, count = 1
6. Click Synchronous Write, and note that after a pause of 2 seconds the
following message is displayed in the status bar:
Log written, count = 2
7. Click Stop Logger, and note that the message in the status bar now says:
Logger stopped
8. Use Visual Studio .NET to view the log file Log.txt, that was created in the
same folder as the multithreading applications executable program:
<install folder>\Labs\Lab14\Starter\Multithreading\bin\Debug
You should see output that is similar to the following:
StartLogger - Time: 11:09:48 AM Wednesday, September 19,
2001


Count:1 Text: Hello World - Time:11:10:40 AM Wednesday,
September 19, 2001


Count:2 Text: Hello World - Time:11:11:30 AM Wednesday,
September 19, 2001


StopLogger - Time:11:11:56 AM Wednesday, September 19, 2001

9. Stop the application and delete Log.txt.

98 Module 14 (Optional): Threading and Asynchronous Programming


Exercise 2
Periodically Writing to a Log File
In this exercise, you will add code to the MyLogger class to use a
System.Thread.Timer object. This object is used to periodically write a
message to the log file indicating that the logger is alive when the logger is
running.
! Create the Timer object
1. In Form1.cs, locate the following comment for the StartLogger method of
the MyLogger class:
// TODO - Exercise 2.1: Add code to periodically call !
a method using a System.Threading.Timer object

2. Locate the line of code that sets loggerRunning to true, and after this line
assign to the static member variable timer a new object of type
System.Threading.Timer.
The new object should call the MyLogger classs LogStatus method every
5 seconds. LogStatus is a static method whose delegate type is
TimerCallback.

! Dispose of the Timer object
1. In Form1.cs, locate the following comment for the StopLogger method of
the MyLogger class:
// TODO - Exercise 2.2: Add code to dispose of the!
System.Threading.Timer object

2. Locate the line of code that writes the string "\nStopLogger - Time:"
to the log file and before this line add code that invokes the Dispose
method on the timer object.

! Add the Timer object that writes logger alive messages periodically to
the log file
1. In Form1.cs, locate the following comment for the LogStatus method of the
MyLogger class:
// TODO - Exercise 2.3: Add Timer callback method code to!
write the date and time to the log

2. Write to the stream the following string:
"\nLogger Alive: "

3. Once again, write to the stream by using the following format and supplying
the current time and date as the parameters:
"{0} {1} \n\n"

Module 14 (Optional): Threading and Asynchronous Programming 99


! Test the application
1. Build and run the application by using Visual Studio .NET. Ignore warning
messages about fields that are never used. Ensure that you have deleted the
file Log.txt that was created in the preceding exercise.
2. In the Text To Log field, type Hello World.
3. Click Start Logger, and note the following message in the status bar:
Logger started
4. Click Synchronous Write, and note that after a pause of 2 seconds the
following message is displayed in the status bar:
Log written, count = 1
5. Click Synchronous Write, and note that after a pause of 2 seconds the
message in the status bar changes to:
Log written, count = 2
6. Click the Stop Logger button and note that the message in the status bar
now says;
Logger stopped
7. Use Visual Studio .NET to view the file Log.txt that was created in the same
folder as the multithreading applications executable program:
<install folder>\Labs\Lab14\Starter\Multithreading\bin\Debug
You should see output that is similar to the following:

StartLogger - Time: 11:47:21 AM Wednesday, September 19,
2001


Logger Alive: 11:47:21 AM Wednesday, September 19, 2001


Count:1 Text: Hello World - Time:11:47:24 AM Wednesday,
September 19, 2001


Logger Alive: 11:47:26 AM Wednesday, September 19, 2001


Count:2 Text: Hello World - Time:11:47:27 AM Wednesday,
September 19, 2001


StopLogger - Time:11:47:28 AM Wednesday, September 19, 2001


8. Stop the application and delete Log.txt.

100 Module 14 (Optional): Threading and Asynchronous Programming


Exercise 3
Providing the User with Status
In this exercise, you will add code to the MyLogger class to use a background
thread that periodically advances the Progress Bar in the user interface
whenever the logger is running.
! Create the background thread
1. In Form1.cs, locate the following comment for the StartLogger method of
the MyLogger class:
// TODO - Exercise 3.1: Add code to start a background!
thread

2. Locate the line of code that assigns to the static member variable timer a
new object of type System.Threading.Timer and after this line, add code
to do the following steps.
3. Create a new object of type Thread
The Thread object is initialized with a delegate object of type ThreadStart
that refers to the ThreadProc method of the MyLogger class. This new
object should be assigned to the existing static member variable
timerThread.
4. Make timerThread a background thread by using its IsBackground
property.
5. Start the timerThread background thread.

! Interrupt the background thread
1. In Form1.cs, locate the following comment for the StopLogger method of
the MyLogger class:
// TODO - Exercise 3.2: Add code to stop a background!
thread

2. Locate the line of code that invokes the Dispose method on the timer object
and before this line, set up a conditional expression that checks whether the
background thread is not null. If the background thread named
timerThread is not null:
a. Call the timerThread Interrupt method.
b. Assign timerThread, form, and mi to null.

Module 14 (Optional): Threading and Asynchronous Programming 101


! Advance the forms progress bar periodically
1. In Form1.cs, locate the following comment for the ThreadProc method of
the MyLogger class:
// TODO - Exercise 3.3: Add code to update the client's!
progress indicator

2. In a try/catch blocks try section, insert a while(true) statement whose
block contains the following steps:
a. Call the form objects BeginInvoke method.
Pass as the BeginInvoke methods parameter the delegate object of type
MethodInvoker that refers to the form objects UpdateProgress
method. This object was created in the StartLogger method and is
named mi.
b. Make the thread sleep for second.
3. In the try/catch blocks catch section add code to do the following:
a. Catch exceptions of the type ThreadInterruptedException.
b. Within the catch block, do nothing.
This will resolve the exception and the thread will terminate gracefully.

! Test the application
1. Build and run the application by using Visual Studio .NET. Ensure that you
have deleted the file Log.txt that was created in the preceding exercise.
2. In the Text To Log field, type Hello World.
3. Click Start Logger.
Note that the Logger State Indicator progress bar is advancing and note
the following message in the status bar.
Logger started
4. Click Synchronous Write, and note that the progress bar stops advancing.
After a pause of 2 seconds, the progress bar jumps ahead and the following
message is displayed in the status bar.
Log written, count = 1
5. Click Synchronous Write, and note that the progress bar stops advancing.
After a pause of 2 seconds, the progress bar jumps ahead and the following
message is displayed in the status bar.
Log written, count = 2
6. Click Stop Logger.
Note that the Logger State Indicator progress bar has stopped advancing
and note that the message in the status bar now says:
Logger stopped
102 Module 14 (Optional): Threading and Asynchronous Programming


7. Use Visual Studio .NET to view the file Log.txt that was created in the same
folder as the multithreading applications executable program:
<install folder>\Labs\Lab14\Starter\Multithreading\bin\Debug
You should see output that is similar to the following:
StartLogger - Time: 11:56:44 AM Wednesday, September 19,
2001


Logger Alive: 11:56:44 AM Wednesday, September 19, 2001


Count:1 Text: Hello World - Time:11:56:47 AM Wednesday,
September 19, 2001


Logger Alive: 11:56:49 AM Wednesday, September 19, 2001


Count:2 Text: Hello World - Time:11:56:50 AM Wednesday,
September 19, 2001


StopLogger - Time:11:56:53 AM Wednesday, September 19, 2001

8. Stop the application and delete the Log.txt.

Module 14 (Optional): Threading and Asynchronous Programming 103


Exercise 4
Making an Asynchronous Method Call
In this exercise, you will add code that uses a delegate object to begin an
asynchronous invocation of the WriteLog method of the MyLogger class. The
code will specify a method to be called when the WriteLog method completes.
You will add code to this method to obtain the results of the WriteLog method
and post the results to the forms status bar.
! Create the event handler code that initiates the asynchronous write
1. In Form1.cs, locate the following comment for the button4_Click method
of the Form1 class:
// TODO - Exercise 4.1: Add code to begin asynchronous!
log write

2. Set the statusBar1.Text property to an empty string.
3. Create a delegate named wld of type WriteLogDelegate that refers to the
WriteLog static method of the MyLogger class.
4. Create a delegate object named cb of type AsyncCallback that refers to the
current Form1 objects AsynchronousWriteResults method.
5. Initialize a variable named ar of type IAsyncResult to the value that is
returned by invoking the wld objects BeginInvoke method.
BeginInvoke should include the following three parameters:
a. The textBox2 Text property
b. The cb delegate object
c. null

104 Module 14 (Optional): Threading and Asynchronous Programming


! Create the code that is called when the asynchronous method call
completes
1. In Form1.cs, locate the following comment for the
AsynchronousWriteResults method of the Form1 class:
// TODO - Exercise 4.2: Add code to this callback method to handle !
the results of asynchronous log write
2. Create a delegate object name wld of type WriteLogDelegate and set wld
to refer to the method that was previously specified in the button4_Click
methods BeginInvoke method call by doing the following:
a. Explicitly cast the parameter that is named ar of type IAsyncResult to
type AsyncResult and get its AsyncDelegate property.
b. Explicitly cast the result of the preceding step to type
WriteLogDelegate and assign this value to wld.
3. Create a try/catch block and in the try section, add code to do the
following:
a. Initialize a variable named count of type int to the result of calling the
wld objects EndInvoke method with the single parameter named ar.
b. Assign to the existing variable named resultString of type string the
following value:
"Log written asynchronously, " + count.ToString()

4. In the try/catch blocks catch section, add code to do the following:
a. Catch exceptions of the type InvalidOperationException.
b. Assign to the existing variable named resultString of type string the
following value:
"Invalid Operation - Asynchronous write failed because !
logger was stopped"

5. To update the forms control on the thread that originally created the
control, call the BeginInvoke method of the current Form1 object.
BeginInvoke takes a single parameter that is a delegate of type
MethodInvoker that refers to the current Form1 objects
UpdateAsynchronousWriteStatus method.

! Test the application
1. Build and run the application by using Visual Studio .NET. Ensure that you
have deleted the file Log.txt that was created in the preceding exercise.
2. In the Text To Log field, type Hello Asynchronous World.
3. Click Start Logger.
Note that the Logger State Indicator progress bar is advancing and note
the following message in the status bar.
Logger started
Module 14 (Optional): Threading and Asynchronous Programming 105


4. Click Asynchronous Write, and note that the progress bar continues to
advance.
After a pause of 2 seconds, the following message is displayed in the status
bar:
Log written asynchronously, 1
5. Click Asynchronous Write, and note that after a pause of 2 seconds the
message in the status bar changes to:
Log written asynchronously, 2
6. Click Asynchronous Write twice in quick succession, and after the
message Log written asynchronously, 4 is displayed in the status bar.
Click Stop Logger.
Note that the Logger State Indicator progress bar stops advancing and note
that the following message is displayed in the status bar:
Logger stopped
7. Use Visual Studio .NET to view the file Log.txt that was created in the same
folder as the multithreading applications executable program:
<install folder>\Labs\Lab14\Starter\Multithreading\bin\Debug
You should see output that is similar to the following:
StartLogger - Time: 12:04:27 PM Wednesday, September 19,
2001


Logger Alive: 12:04:27 PM Wednesday, September 19, 2001


Count:1 Text: Hello Asynchronous World - Time:12:04:30 PM
Wednesday, September 19, 2001


Logger Alive: 12:04:32 PM Wednesday, September 19, 2001


Count:2 Text: Hello Asynchronous World - Time:12:04:33 PM
Wednesday, September 19, 2001


Count:3 Text: Hello Asynchronous World - Time:12:04:36 PM
Wednesday, September 19, 2001


Logger Alive: 12:04:37 PM Wednesday, September 19, 2001


Count:4 Text: Hello Asynchronous World - Time:12:04:38 PM
Wednesday, September 19, 2001


StopLogger - Time:12:04:41 PM Wednesday, September 19, 2001


8. Stop the application and delete Log.txt.
106 Module 14 (Optional): Threading and Asynchronous Programming



If Time Permits
Demonstrating a Race Condition
In this exercise, you will remove the lock in the WriteLog method of the
MyLogger class. You will note the errors resulting from the existence of a race
condition that occurs when multiple asynchronous calls cause multiple threads
to concurrently access the static variables of the MyLogger class.
! Create the potential for a race condition by removing the WriteLog
method lock
1. In Form1.cs, locate the WriteLog method of the MyLogger class and
comment out the line(s) that contain the lock statement and the opening
brace of the lock statement block.
2. Just before the end of the method, comment out the closing brace of the lock
statement.

! Test the application
1. Build and run the application by using Visual Studio .NET. Ensure that you
have deleted the file Log.txt that was created in the preceding exercise.
2. In the Text To Log field, type Hello Asynchronous World.
3. Click Start Logger.
Note that the Logger State Indicator progress bar is advancing and note
the following message in the status bar:
Logger started
4. Click Asynchronous Write, and note that the progress bar continues to
advance.
After a pause of 2 seconds, the following message is displayed in the status
bar:
Log written asynchronously, 1
5. Click Asynchronous Write, and note that after a pause of 2 seconds the
message in the status bar changes to:
Log written asynchronously, 2
6. Click Asynchronous Write twice in quick succession, wait for about 10
seconds, and then click Stop Logger.
Note that the Logger State Indicator progress bar stops advancing and note
the following message is displayed in the status bar:
Logger stopped
Module 14 (Optional): Threading and Asynchronous Programming 107


7. Use Visual Studio .NET to view the file Log.txt that was created in the same
folder as the multithreading applications executable program:
<install folder>\Labs\Lab14\Starter\Multithreading\bin\Debug
You should see output that is similar to the following:
StartLogger - Time: 12:40:24 PM Wednesday, September 19,
2001


Logger Alive: 12:40:24 PM Wednesday, September 19, 2001


Count:1 Text: Hello Asynchronous World - Time:12:40:28 PM
Wednesday, September 19, 2001


Logger Alive: 12:40:30 PM Wednesday, September 19, 2001


Count:2 Text: Hello Asynchronous World - Time:12:40:30 PM
Wednesday, September 19, 2001


Logger Alive: 12:40:35 PM Wednesday, September 19, 2001


Count:3 Text: Hello Asynchronous World - Time:12:40:35 PM
Wednesday, September 19, 2001


Count:3 Text: Hello Asynchronous World - Time:12:40:35 PM
Wednesday, September 19, 2001


Logger Alive: 12:40:39 PM Wednesday, September 19, 2001


Logger Alive: 12:40:44 PM Wednesday, September 19, 2001


StopLogger - Time:12:40:44 PM Wednesday, September 19, 2001

Because of the race condition during the last two asynchronous
writes, the write counter is the same for both of these writes.

8. Stop the application and delete Log.txt.

Note
108 Module 14 (Optional): Threading and Asynchronous Programming


Review
! Introduction to Threading
! Using Threads in .NET
! Thread Safety
! Special Thread Topics
! Asynchronous Programming in .NET

*****************************ILLEGAL FOR NON-TRAINER USE******************************
1. State one advantage and one disadvantage of using multiple threads in an
application.
Advantages: Threading maintains the responsiveness of the user
interface while background processing is occurring, allows for subtasks
to be assigned different priorities, and allows a long-running task to be
done in the background.
Disadvantages: Overhead with thread creation and running may
decrease performance; sharing data between threads is complex and
prone to errors.


2. Write the code to execute a thread on the static method in a class named
MyClass that is declared: static void MyMethod().
Thread t1 = new
Thread(new ThreadStart(MyClass.MyMethod));
t1.Start();


3. Name the three .NET Framework categories of techniques for providing
thread safety when data must be shared between concurrently executing
threads.
Synchronized Context, Synchronized Code Regions, Manual
Synchronization.


Topic Objective
To reinforce module
objectives by reviewing
key points.
Lead-in
The review questions cover
some of the key concepts
taught in the module.
Module 14 (Optional): Threading and Asynchronous Programming 109


4. If you want to get or set properties, or call methods on a control from a
background thread, what must you do?
You must marshal the call to the thread on which the control was
created by using one of the controls thread safe methods: Invoke,
BeginInvoke, and EndInvoke.


5. In the second part of an asynchronous call operation, name the four ways
that the caller can know when the asynchronous operation has completed.
Callback method, poll, call end method, wait for event.






THIS PAGE INTENTIONALLY LEFT BLANK









Contents
Overview 1
Integration Services 2
Platform Invoke 7
Lab 15.1: Calling Win32 APIs 16
Calling COM Objects from Managed Code 20
Lab 15.2: Calling COM Objects 39
Calling .NET Objects from COM Objects 43
Review 55

Module 15 (Optional):
Interoperating Between
Managed and
Unmanaged Code


Information in this document, including URL and other Internet Web site references, is subject to
change without notice. Unless otherwise noted, the example companies, organizations, products,
domain names, e-mail addresses, logos, people, places, and events depicted herein are fictitious,
and no association with any real company, organization, product, domain name, e-mail address,
logo, person, places or events is intended or should be inferred. Complying with all applicable
copyright laws is the responsibility of the user. Without limiting the rights under copyright, no
part of this document may be reproduced, stored in or introduced into a retrieval system, or
transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or
otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual
property rights covering subject matter in this document. Except as expressly provided in any
written license agreement from Microsoft, the furnishing of this document does not give you any
license to these patents, trademarks, copyrights, or other intellectual property.

2001-2002 Microsoft Corporation. All rights reserved.

Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint,
Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and
Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A.
and/or other countries.

The names of actual companies and products mentioned herein may be the trademarks of their
respective owners.


Module 15 (Optional): Interoperating Between Managed and Unmanaged Code iii


Instructor Notes
After completing this module, students will be able to:
! State the need for interoperability between Microsoft .NET assemblies and
COM components.
! Use platform invoke to call a function in a dynamic-link library (DLL).
! Expose the methods and properties of a COM object to an assembly.
! Describe the three ways to generate runtime callable wrappers.
! Use Microsoft Visual Studio .NET to call a COM object.
! Use the Type Library Importer to generate metadata from a type library.
! Expose the methods and properties of a .NET Framework class to a COM
client.
! Use the Type Library Exporter to generate a type library for an assembly.
! Use the ClassInterfaceAttribute to control and modify the type of interface
that is generated for a .NET Framework class.

Materials and Preparation
This section provides the materials and preparation tasks that you need to teach
this module.
Required Materials
To teach this module, you need the Microsoft PowerPoint file 2349B_15.ppt.
Preparation Tasks
To prepare for this module, you should:
! Read all of the materials for this module.
! Complete the lab.

Presentation:
90 Minutes

Lab:
60 Minutes
iv Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


Demonstrations
The following demonstrations are used to show how to create a managed
assembly from an unmanaged DLL file, and how to create a COM component
from a managed assembly.
In this module, use a Visual Studio .NET Command Prompt window to obtain a
command prompt window with the appropriate environment values that are
required for .NET tools.
Demonstration: Using the Type Library Importer
Before running the demo, the COM object needs to be registered in the system
registry.

To use Microsoft Visual Studio .NET tools within a command
prompt window, the command prompt window must have the proper
environment settings. The Visual Studio .NET Command Prompt window
provides such an environment. To run a Visual Studio .NET Command Prompt
window, click Start, All Programs, Microsoft Visual Studio .NET, Visual
Studio .NET Tools, and Visual Studio .NET Command Prompt.

! To register the COM object
1. At the command prompt, change the current folder to <install folder>\
Democode\Mod15\Demo15.1.
2. Run regsvr32 TLBIMPDemo.dll to register the COM object.
3. A dialog box appears informing you that the registration succeeded. To
dismiss the dialog box, click OK.

In this demonstration, students will see how to use the Type Library Importer to
generate metadata for a DLL, and then use the metadata to create a managed
wrapper for the component.
! To create a managed wrapper for the TLBIMPDemo component and
generate metadata by using the Type Library Importer
1. Run the Type Library Importer on TLBIMPDemo.dll, by typing the
following command in a Visual Studio .NET Command Prompt window:
tlbimp TLBIMPDemo.dll /out:Calculator.dll

The resulting DLL is a .NET assembly and is named Calculator.dll.
2. Build the calculator application by typing the following command:
csc /reference:Calculator.dll CalcUI.cs


Important
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code v


! To run the calculator application and view the metadata of
Calculator.dll
1. Run CalcUI.exe from the command prompt by typing the following
command:
CalcUI

2. Exit CalcUI.exe.
3. In a Visual Studio .NET Command Prompt window, run the Microsoft
intermediate language (MSIL) Disassembler on Calculator.dll by typing the
following command:
ildasm /adv Calculator.dll

4. To display the contents of the manifest, double-click MANIFEST.
5. Notice the assembly Calculator that appears near the end of the manifest. It
should look similar to the following example:
.assembly Calculator
{
.custom instance void!
[mscorlib]System.Runtime.InteropServices.GuidAttribute::
.ctor(string) = (
.custom instance void!
[mscorlib]System.Runtime.InteropServices.!
ImportedFromTypeLibAttribute::.ctor(string) = !
// ...TLBIMPDemo..
.hash algorithm 0x00008004
.ver 1:0:0:0
}

6. This entry in the manifest demonstrates the referencing of an unmanaged
COM component from managed code through the interoperability services
provided by the .NET Framework.

vi Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


Demonstration: Using the Type Library Exporter
In this demonstration, students will see how to use the Type Library Exporter to
generate a type library that describes the types defined in a common language
runtime assembly.
! To generate a type library from an assembly by using the Type Library
Exporter
1. In a Visual Studio .NET Command Prompt window, change the current
folder to <install folder>\Democode\Mod15\Demo15.2.
2. To create a type library for Calculator.dll, type the following command:
tlbexp Calculator.dll /out:Calculator.tlb

3. The Type Library Exporter creates the Calculator.tlb file.

! To view the contents of the type library
1. Start a Visual Studio .NET Command Prompt.
2. Type oleview and press Enter.
3. In the OLE/COM Object Viewer window, on the File menu, click View
TypeLib.
4. In the Open dialog box, move to <install_folder>\Democode\
Mod15\Demo15.2, select Calculator.tlb, and then click Open. The
ITypeLib Viewer window appears with the information that is found in the
type library.

Module 15 (Optional): Interoperating Between Managed and Unmanaged Code vii


Module Strategy
Use the following strategy to present this module:
! Integration Services
This module provides an introduction to COM interoperability and platform
invoke. Ensure that students understand the difference between calling an
API function that is implemented in a DLL and calling a COM component.
! Platform Invoke
Concentrate on the information in the slides titled How Platform Invoke
Works and Calling Unmanaged Functions.
! Calling COM Objects from Managed Code
Concentrate on the procedure for generating runtime callable wrappers, the
use of the type library importer, and the process for signature translation and
error handling.
! Calling .NET Objects from COM Objects
Ensure that students understand the export process when the Type Library
Exporter is used to generate a type library.

Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 1


Overview
! Integration Services
! Platform Invoke
! Calling COM Objects from Managed Code
! Calling .NET Objects from COM Objects

*****************************ILLEGAL FOR NON-TRAINER USE******************************
After completing this module, you will be able to:
! State the need for interoperability between Microsoft .NET assemblies and
COM components.
! Use platform invoke to call a function in a dynamic-link library (DLL).
! Expose the methods and properties of a COM object to an assembly.
! Describe the three ways to generate runtime callable wrappers.
! Use Microsoft Visual Studio .NET to call a COM object.
! Use the Type Library Importer to generate metadata from a type library.
! Expose the methods and properties of a .NET Framework class to a COM
client.
! Use the Type Library Exporter to generate a type library for an assembly.
! Use the ClassInterfaceAttribute to control and modify the type of interface
that is generated for a .NET Framework class.

Topic Objective
To provide an overview of
the module topics and
objectives.
Lead-in
In this module, you will learn
about interoperating
between managed and
unmanaged code.
2 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


" "" " Integration Services
! Introduction to Platform Invoke
! Introduction to COM Interoperability
! Interop Marshaling Overview

*****************************ILLEGAL FOR NON-TRAINER USE******************************
When you adopt the .NET Framework to develop your applications, you may
still have a lot of unmanaged code libraries implemented as DLLs and COM
components. Code executing under the control of the runtime is called managed
code. Conversely, code that runs outside the runtime is called unmanaged code.
COM components, Microsoft ActiveX interfaces, and Microsoft Win32 API
functions are examples of unmanaged code.
You do not have to convert all of your existing unmanaged code to managed
code before you can use it with .NET Framework applications. The Microsoft
.NET Framework promotes interaction with COM components, COM+
services, external type libraries, and many operating system services. Data
types, method signatures, and error-handling mechanisms vary between
managed and unmanaged object models. The .NET Framework offers two
servicesplatform invoke and COM interoperability servicesthat allow
managed code to interoperate with existing unmanaged code.

The ActiveX Control Importer (Aximp.exe) can be used to convert type
definitions in a COM type library for an ActiveX control into a Microsoft
Windows Forms control. For additional information, see Tools and
Debuggers; Windows Forms ActiveX Control Importer (Aximp.exe) in the
.NET Framework SDK documentation.

Topic Objective
To introduce the services
provided by the .NET
Framework that allow
managed code to
interoperate with
unmanaged code.
Lead-in
The .NET Framework
provides two services that
enable managed code to
interoperate with
unmanaged code. This
section provides an
overview of these two
services.
Note
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 3


Introduction to Platform Invoke
! Allows Managed Code to Call Unmanaged Functions
That Are Implemented in a DLL
! Provides the Mechanism for
# Finding and invoking unmanaged functions
# Marshaling managed arguments to and from
unmanaged code

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Platform invoke allows managed code to call unmanaged functions that are
implemented in a DLL. Platform invoke provides the mechanism for finding
and invoking unmanaged functions, as well as marshaling managed arguments
to and from unmanaged code.
When managed code calls an unmanaged function that is implemented in a
DLL, platform invoke locates the DLL that implements the function, loads the
DLL into memory, and locates the function address in memory. It then pushes
the functions arguments onto the stack, marshals any data that has to be
marshaled, enables pre-emptive garbage collection, and transfers control to the
address of the unmanaged code.
Topic Objective
To provide an overview of
platform invoke.
Lead-in
Platform invoke allows
managed code to
interoperate with
unmanaged APIs.
4 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


Introduction to COM Interoperability
! COM and .NET Differences Include:
# Lifetime management - reference counting versus
garbage collection
# Service discovery - QueryInterface versus Reflection
# Object memory fixed versus movable
! Runtime Callable Wrapper (RCW)
# Used by managed clients to call a method on a COM object
! COM Callable Wrapper (CCW)
# Used by COM clients to call a method on a managed object

*****************************ILLEGAL FOR NON-TRAINER USE******************************
COM differs from the .NET Framework object model in several important
ways:
! Clients of COM objects must manage the lifetime of those objects; the
common language runtime manages the lifetime of objects in its
environment.
! Clients of COM objects discover whether a service is available by
requesting an interface that provides that service and getting back an
interface pointer, or not. Clients of .NET objects can obtain a description of
an object's functionality using Reflection.
! Managed objects reside in memory managed by the .NET Framework
execution environment. The execution environment can move objects
around in memory for performance reasons and update all references to the
objects it moves. Unmanaged clients, having obtained a pointer to an object,
rely on the object to remain at the same location. These clients have no
mechanism for dealing with an object whose location is not fixed.

To overcome these differences, the runtime provides wrapper classes to make
both managed and unmanaged clients think they are calling objects within their
respective environment. Whenever your managed client calls a method on a
COM object, the runtime creates a runtime callable wrapper (RCW). RCWs
abstract the differences between managed and unmanaged reference
mechanisms, among other things. The runtime also creates a COM callable
wrapper (CCW) to reverse the process, enabling a COM client to call a method
on a .NET object.
Topic Objective
To provide an overview of
COM interoperability
services provided by the
.NET Framework.
Lead-in
COM interoperability
services allow managed
code to interoperate with
COM components.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 5


Interop Marshaling Overview
! Interop marshaling governs how data is passed
between managed and unmanaged memory during calls
# Is a run time activity performed by the common language
runtime marshaling service
! Blittable data types are common to managed and
unmanaged memory and require no conversion data
types (C# keyword) :
# System.Byte (byte) System.SByte (sbyte)
# System.Int16 (short) System.UInt16 (ushort)
# System.Int32 (int) System.UInt32 (uint)
# System.Int64 (long) System.IntPtr System.UIntPtr

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Interop marshaling governs how data is passed in method arguments and return
values between managed and unmanaged memory during calls. Interop
marshaling is a run-time activity performed by the common language runtime's
marshaling service.
Most data types have a common representation in both managed and
unmanaged memory and do not require special handling by the interop
marshaler. These types are called blittable types because they do not require
conversion when passed between managed and unmanaged code.
The following types from the System namespace are blittable types:
! System.Byte
! System.SByte
! System.Int16
! System.UInt16
! System.Int32
! System.UInt32
! System.Int64
! System.IntPtr
! System.UintPtr

Topic Objective
To provide an overview of
interop marshalling.
Lead-in
Interop marshaling governs
how data is passed in
method arguments and
return values between
managed and unmanaged
memory during calls.
6 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


Interop Marshaling Overview (continued)
! Non-blittable types have different or ambiguous
representations in managed and unmanaged languages
and may require conversion
# For example, managed strings are non-blittable types
! The standard RCW or CCW usually provides adequate
marshaling for calls that cross the boundary between
COM and the .NET Framework
# Custom attributes can optionally adjust the way the
runtime represents managed and unmanaged code

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Non-blittable types have different or ambiguous representations in managed and
unmanaged languages. These types may require conversion when they are
marshaled between managed and unmanaged code. For example, managed
strings are non-blittable types because they can have several different
unmanaged representations, some of which can require conversion.
The following table lists non-blittable types from the System namespace.
Delegates, which are data structures that refer to a static method or to a class
instance, are also non-blittable.
Non-blittable type Description

System.Array Converts to a C-style or a SAFEARRAY.
System.Boolean Converts to a 1, 2, or 4-byte value with true as 1 or -1.
System.Char Converts to a Unicode or ANSI character.
System.Class Converts to a class interface.
System.Object Converts to a variant or an interface.
System.Mdarray Converts to a C-style array or a SAFEARRAY.
System.String Converts to a null-terminated string or to a BSTR.
System.Valuetype Converts to a structure with a fixed memory layout.
System.Szarray Converts to a C-style array or a SAFEARRAY.

In most cases, the standard RCW or CCW that is generated by the runtime
provides adequate marshaling for calls that cross the boundary between COM
and the .NET Framework. Using custom attributes, you can optionally adjust
the way the runtime represents managed and unmanaged code.
Topic Objective
To complete the overiew of
interop marshalling.
Lead-in
Lets discuss how non-
blittable types differ from
blittable types with regard to
Interop marshaling and how
custom attributes may be
used to in place of the
standard marshaling
operations.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 7


" "" " Platform Invoke
! How Platform Invoke Works
! Calling a Win32 API From Managed Code
! Calling Unmanaged Functions
! Pinning
! Marshaling
! Performance Considerations and Limitations of
Platform Invoke

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Platform invoke allows managed code to call unmanaged functions that are
implemented in a DLL. Platform invoke is primarily used to call C functions
that are part of the Win32 API. To use these functions, you must import the
DLL that hosts the functions. The .NET Framework provides default
marshaling to marshal data between managed and unmanaged code. You can,
however, override the marshaling provided by the .NET Framework whenever
you require custom marshaling to be performed.
Topic Objective
To introduce platform
invoke.
Lead-in
In this section you will learn
how platform invoke can be
used to call unmanaged
code.
8 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


How Platform Invoke Works

*****************************ILLEGAL FOR NON-TRAINER USE******************************
When platform invoke calls an unmanaged function, it performs the following
sequence of actions:
1. Platform invoke locates the DLL containing the function.
2. It loads the DLL into memory.
3. It locates the address of the function in memory and pushes its arguments
onto the stack, marshaling data as required.
4. Platform invoke then transfers control to the unmanaged function.

You provide the DLL and function information to platform invoke by
annotating your code. This information is used by platform invoke to locate the
DLL and load it into the memory of the process, after which the address of the
function is located and control is transferred.
Topic Objective
To describe how platform
invoke executes methods
that are implemented in a
DLL.
Lead-in
This is how platform invoke
loads and executes a
method that is implemented
in a DLL.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 9


Calling a Win32 API From Managed Code
! Declare the Method with the static and extern
C# Keywords
! Import the DLL That Implements the Unmanaged
Function That You Wish to Call
! Optionally, Specify Custom Marshaling Information to
Override the .NET Framework Default Marshaling
[DllImport(<DLL name>, EntryPoint=<function name>,
CharSet=<type of characterset>]
[DllImport(<DLL name>, EntryPoint=<function name>,
CharSet=<type of characterset>]

*****************************ILLEGAL FOR NON-TRAINER USE******************************
To call an unmanaged function, the first step is to annotate your code by
importing the DLL file in which the function is implemented. You use the
DllImport attribute to import the DLL. The DllImport attribute requires the
DLL_name argument, and also takes any of the following additional arguments.
Argument Description

EntryPoint Specifies the DLL entry point to be called.
CharSet Controls name mangling and the way that string arguments
should be marshaled to the function. The default is
CharSet.Ansi.
ExactSpelling Prevents an entry point from being modified to correspond to
the character set. If CharSet.Auto is enabled, the default is
False. Otherwise, the default is True.
CallingConvention Specifies the calling-convention values used in passing method
arguments. The default is Winapi.
PreserveSig Indicates that the managed method signature should not be
transformed into an unmanaged signature that returns an
HRESULT, and might have an additional [out, retval]
argument for the return value.
The default is True (the signature should not be transformed).
SetLastError Enables the caller to use the GetLastError Win32 API
function to determine whether an error occurred while
executing the method. In Microsoft Visual Basic, the default is
True; in C# and C++, the default is False.

If the DLL has both an ANSI and a Unicode implementation of the same
function, then the CharSet parameter specifies which version of the function is
called.
Topic Objective
To describe how to call a
Win32 API from managed
code.
Lead-in
To use an unmanaged
function that is implemented
in a DLL, you must first
import the DLL into your
code.
10 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


Calling Unmanaged Functions
! To Call a Function That Is Implemented in an Unmanaged DLL
# Add a using statement for System.Runtime.InteropServices
# Add a DllImport attribute to a method, specifying the name of the
unmanaged DLL that exports the function to be called
# Declare with the static and extern C# keywords the method that is
used to call unmanaged code without providing any implementation
for the method
using System.Runtime.InteropServices;

[DllImport("msvcrt.dll", CharSet=CharSet.Ansi)]
public static extern int puts(String str);
using System.Runtime.InteropServices;

[DllImport("msvcrt.dll", CharSet=CharSet.Ansi)]
public static extern int puts(String str);

*****************************ILLEGAL FOR NON-TRAINER USE******************************
When you call unmanaged functions from .NET Framework applications, you
must add a DllImport attribute to a method that is to be used by platform
invoke. The DllImport attribute is defined in
System.Runtime.InteropServices. Platform invoke uses the information in the
attribute to call the appropriate underlying DLL function, marshal parameters to
the function, and return values from the function when the method is called.
The example on the slide shows the use of the DllImport attribute.
To annotate a method in managed code that is used to call a function in an
unmanaged DLL, you must specify the name of the DLL that exports the
unmanaged function. You must also specify with the static and extern C#
keywords the name of the exported function or its equivalent entry point
ordinal, if the exported function name is different than the name of the method
that you declared in the managed module. Optionally, you can specify a flag to
indicate the underlying calling convention and to automatically marshal string
data types correctly. The calling convention flag can be set to Cdecl, FastCall,
StdCall, ThisCall, or Winapi. If no calling convention is specified, Winapi is
the default. You can also optionally specify the character set to use as part of
the function call. The values that you can specify for the character set are ansi,
unicode, or auto.
Topic Objective
To describe how to annotate
unmanaged method calls so
that they can be called from
.NET Framework
applications.
Lead-in
Unmanaged methods that
are implemented as a DLL
and called from managed
code must be annotated
before they can be executed
by the runtime.
Delivery Tip
Refer to the .NET SDK for
more information about
DllImport, calling
conventions, marshaling,
and character sets.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 11


There are two general approaches when declaring methods that match
unmanaged functions:
! You can declare the method to call the unmanaged DLL without providing
any implementation for the method, as shown in the following example:
using System;
using System.Runtime.InteropServices;

[DllImport("msvcrt.dll", CharSet=CharSet.Ansi)]
public static extern int puts(String str);

static void Main()
{
String Str = "Hello World!";
puts(Str);
}

The compiler uses the method declaration to generate the metadata that the
runtime needs to locate and correctly marshal the call to the DLL. When
declaring the method in your managed code, you can substitute the
equivalent runtime types for the functions types. In the previous example, a
string object is substituted for a character string.
! You can also declare a .NET Framework class that is a collection of
declarations for an API exported by an unmanaged DLL. The advantage of
creating such a class is that you do not have to declare and annotate the
methods for the unmanaged DLL that you want to call in all the applications
that you create. After it is created, you can include the class in your
application, and you can call the unmanaged functions that are declared in
the class as if they were .NET Framework methods.

Annotated methods behave like managed .NET Framework methods. When an
annotated method is called, platform invoke calls the unmanaged function,
marshaling any data that needs to be marshaled to and from the unmanaged
function.
12 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


Pinning
! Data Is Temporarily Locked in Its Current Memory
Location to Keep It from Being Relocated by the
Common Language Runtimes Garbage Collector
! Pinning Is Performed When Data Has to Be Passed
Between Managed and Unmanaged Code and
# Has a fixed layout and common data representation in
both managed and unmanaged memory
- or -
# Has a fixed layout but the data representation is different
in managed and unmanaged memory and the class is
marshaled by reference

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Pinning is a technique in which data is temporarily locked in its current
memory location to keep it from being relocated during garbage collection.
Whether data is copied or pinned during the marshaling process depends on the
type of the data and its InAttribute and OutAttribute.
When classes that have a fixed layout and common data representation in both
managed and unmanaged memory require marshaling, a pointer to the actual
object is passed to the called function. The called function is then free to change
the contents of the memory location being referenced by the pointer.
When classes have a fixed layout but the data representation is different in
managed and unmanaged memory and the class is marshaled by reference, the
called function receives a pointer to a copy of the data structure. If the
InAttribute is set, the copy is always initialized with the instances state. If the
OutAttribute is set, the state is always copied back in to the instance upon
return. If both the InAttribute and OutAttribute are set, the copy is always
initialized with the instances state, and the last state is always copied back into
the instance on return. If either attribute is omitted, the runtime may choose to
eliminate either copy as an optimization.
When classes that do not have a fixed layoutsuch as System.String and
System.Text.StringBuilderhave to be marshaled by value or by reference to
unmanaged code, the runtime typically copies the data of either type to a
secondary buffer and passes a reference to the buffer to the called function. The
reference is always allocated with CoTaskMemAlloc.
Topic Objective
To define pinning.
Lead-in
When data is marshaled
between managed and
unmanaged code, it can
either be copied from one
memory location to another,
or it can be pinned.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 13


Marshaling
! Platform Invoke Marshals Simple Data Types Between
Managed and Unmanaged Code
! Custom Marshalling Can Be Specified by Using the
MarshalAs Attribute
public static extern int MessageBoxW(
int h,
[MarshalAs(UnmanagedType.LPWStr)] string m,
);
public static extern int MessageBoxW(
int h,
[MarshalAs(UnmanagedType.LPWStr)] string m,
);

*****************************ILLEGAL FOR NON-TRAINER USE******************************
For arguments that are simple data typessuch as bytes or integersplatform
invoke marshals the managed argument to the corresponding unmanaged data
type. The following table shows how the data types used in the Win32 API
(wtypes.h) should be expressed in C#.
Data Type in wtypes.h C# Data Type

HANDLE int
BYTE byte
SHORT short
WORD ushort
INT int
UINT uint
LONG int
ULONG uint
BOOLEAN int
CHAR char
LPSTR (and most other string types) String for in, StringBuilder for inout
FLOAT float
DOUBLE double

If any parameter is passed by reference, the ref keyword in C# should be added
to the method declaration used to call unmanaged code.
Topic Objective
To describe how
marshalling is performed by
platform invoke.
Lead-in
Platform invoke
automatically marshals data
between unmanaged types
and managed types.
14 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


For example, the GetUserNameEx Win32 API function has the following
declaration:
BOOLEAN GetUserNameEx(
EXTENDED_NAME_FORMAT NameFormat, // name format
LPTSTR lpNameBuffer, // name buffer
PULONG nSize // size of name buffer
);

To call this from managed code, you would use the following declaration:
[DllImport("secur32.dll", CharSet=CharSet.Auto)]
public static extern int GetUserNameEx (int nameFormat,
StringBuilder userName, ref uint userNameSize);

Notice that an integer is substituted for the enumeration parameter, a
StringBuilder object is substituted for the string parameter, and the ref
keyword is added to the last parameter, because it is passed by reference.
Your choice of data types may affect applications when the data types are
marshaled to and from managed code. For example, in Win32 APIs, a LONG
data type is 32 bits, whereas the C# long and Visual Basic .NET Long data
types are 64 bits.
When calling API functions, you can specify custom marshaling attributes by
adding a MarshalAsAttribute to the parameters of the function. You can also
use the MarshalAsAttribute with structures. When using the
MarshalAsAttribute with structures, you must also use the StructLayout
attribute to set the native layout of the structure.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 15


Performance Considerations and Limitations of Platform Invoke
! Platform Invoke Supports Only Integer Arguments to
Callback Functions
! Platform Invoke Does Not Support All Data Types
! Platform Invoke Supports Calling Only Global Functions
Exported from the DLL
! Array Arguments That Are Passed by Reference and
Copied Back Are Resized to 1

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Some of the limitations that you must keep in mind when calling unmanaged
functions by using platform invoke are:
! Platform invoke supports the callback mechanism. However, only integers
are currently permitted as the arguments to the callback function.
! Platform invoke does not support all data types.
! Platform invoke supports calling only global functions exported from the
DLL.
! When you call an unmanaged function, passing a reference to an array
argument, all the array elements are copied to an unmanaged buffer. When
the values are copied back, the size of that unmanaged buffer is no longer
known. Therefore, only one element is copied back. The array is resized
to 1.

Topic Objective
To list some performance
considerations and
limitations of using platform
invoke.
Lead-in
When calling unmanaged
code from managed code by
using platform invoke, you
must keep the following
limitations and performance
considerations in mind.
16 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


Lab 15.1: Calling Win32 APIs

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives
After completing this lab, you will be able to call a Win32 API from managed
code.
Estimated time to complete this lab: 30 minutes
Topic Objective
To introduce the lab.
Lead-in
In this lab, you will call a
Win32 API from managed
code.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 17


Exercise 1
Calling a Win32 API from Managed Code
In this exercise, you will call Win32 APIs from managed code. This includes
adding DllImport attributes to methods in your class, and then invoking those
methods. The first unmanaged function you call retrieves the user name, and the
second unmanaged function you call displays the user name in a message box.
! Open the Visual Studio .NET project for this exercise and examine the
application UI
1. In Microsoft Windows Explorer, move to <install folder>\Labs\
Lab15\Lab15.1\Starter.
2. Double-click CallingUnmanagedCode.csproj to open the project for this
exercise in Visual Studio .NET.
3. In the Solution Explorer pane, double-click ManagedCode.cs. The UI
design for the Windows Forms application appears. Examine the UI for a
moment to become familiar with it.
4. Press F7 to edit the code that implements the Windows Form application.

! Add the DllImport attribute for the method used to retrieve the user
name
GetUserNameEx is the Win32 API to be called. The signature for this
function is as follows:
BOOLEAN GetUserNameEx(
EXTENDED_NAME_FORMAT NameFormat, // name format
LPTSTR lpNameBuffer, // name buffer
PULONG nSize // size of name buffer
);

In the ManagedCode.cs file, notice the TODO comment near the
beginning, instructing you to add the declaration for a method that is used to
call GetUserNameEx. Add the declaration for the method, including the
DllImport attribute. Your method should also be named GetUserNameEx.
Some key information you need in order to add the declaration is listed in
the following table.
Information Value

DLL containing the GetUserNameEx function secur32.dll
Character set to use CharSet.Auto

For information about the managed data types to use in your method
declaration, see Marshaling earlier in this module. Notice that you can use
the task list tab in the lower-left hand pane to find the TODO comments.
Click View, click Show Tasks, and then click All if the TODO comments
do not appear in the task list.

18 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


! Add the DllImport attribute for the method used to display the user
name
MessageBox is the Win32 API to be called. The signature for this function
is as follows:
int MessageBox(
HWND hWnd, // handle to owner window
LPCTSTR lpText, // text in message box
LPCTSTR lpCaption, // message box title
UINT uType // message box style);

In the ManagedCode.cs file, notice the next TODO comment near the
beginning, instructing you to add the declaration for a method that is used to
call MessageBox. Add the declaration for the method, including the
DllImport attribute. Your method should also be named MessageBox.
Some key information you need in order to add the declaration is listed in
the following table.
Information Value

DLL containing the MessageBox function user32.dll
Character set to use CharSet.Auto

For information about the managed data types to use in your method
declaration, see Marshaling earlier in this module. Notice that the strings
passed to MessageBox are input-only parameters.

! Implement the methods that call unmanaged code
1. Scroll down in the ManagedCode.cs file to find the next TODO comment
near the end of the file in the buttonRetrieveUserName_Click method.
The comment instructs you to add the code to retrieve the user name and
display it in a message box. Replace the comment with a call to
GetUserNameEx followed by a call to MessageBox.
The SamCompatibleNameFormat constant represents the format of the
username that will be returned from GetUserNameEx. Note that
GetUserNameEx returns a non-zero value if it succeeds. Otherwise it
returns a value of zero.
For the uType parameter to MessageBox, you should pass in
(uint)MessageBoxIcon.Information if GetUserNameEx succeeds, or
(uint)MessageBoxIcon.Error if GetUserNameEx fails. MessageBox
returns a value for the button clicked on the message box, but this should
not affect the application.
2. Scroll down to the catch block to find the next TODO comment
instructing you to add a MessageBox call to report the exception as an error.
Replace the comment with a call to MessageBox after the exception
message is created. Pass in (uint)MessageBoxIcon.Error for the uType
parameter.

Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 19


! Build and test your application
1. On the File menu, click Save All.
2. On the Build menu, click Build Solution. Your application should build
successfully. The following statement should appear as the last line of the
output window:
Build: 1 succeeded, 0 failed, 0 skipped.

3. On the Debug menu, click Start, and then click Retrieve User Name. The
user name should be displayed in a message box. Click OK to dismiss the
message box.
4. To close the application, click Exit.

20 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


" "" " Calling COM Objects from Managed Code
! Runtime Callable Wrappers
! Generating Runtime Callable Wrappers
! Threading Models
! Signature Translation and Error Handling
! Marshaling
! Performance and Security Issues
! Best Practices

*****************************ILLEGAL FOR NON-TRAINER USE******************************
COM objects are exposed to managed code by creating runtime callable
wrappers for the objects. The purpose of the wrappers is to marshal calls
between a .NET Framework client and a COM object.
Topic Objective
To introduce how COM
objects are exposed to
managed code.
Lead-in
In this section you will learn
how to expose COM objects
to managed code by using
runtime callable wrappers.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 21


Runtime Callable Wrappers

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In order to call a COM object, the runtime uses a runtime callable wrapper.
Runtime callable wrappers contain all of the information needed to call the
COM object. The runtime callable wrapper maintains a cache of interface
pointers on the COM object it wraps and releases its reference on the COM
object when the wrapper is no longer needed. The runtime performs garbage
collection on the runtime callable wrapper after the COM object has been
released.
One of the primary functions of the runtime callable wrapper is to marshal data
between managed and unmanaged code. The runtime callable wrapper provides
marshaling for method arguments and method return values whenever the client
and server have different representations of the data passed between them. For
example, when a .NET Framework client passes a String type as part of an
argument to a managed object, the wrapper converts the String type to a BSTR
type. Should the COM object return a BSTR type to its managed caller, the
caller receives a String type. Both the client and the server send and receive
data types that are familiar to each of them. Some other types require no
conversion. For instance, a standard wrapper will always pass a 4-byte integer
between managed and unmanaged code without converting the type.
The runtime callable wrapper implements the interfaces that the COM object
implements and exposes the methods, properties, and events from the objects
interfaces. In the illustration, the wrapper exposes the INew interface and all its
methods and properties to the client, but consumes the IUnknown and
IDispatch interfaces. The IUnknown interface is used by the runtime to
identify the COM object, provide type coercion, and control lifetime
management. The runtime distinguishes between COM objects by comparing
the value of the IUnknown interface for each object. The wrapper uses the
QueryInterface method to get and hold a reference to an unmanaged object.
The reference is retained until the runtime performs garbage collection on the
wrapper. The wrapper releases the unmanaged object during garbage collection.
Topic Objective
To describe the purpose of
runtime callable wrappers.
Lead-in
The primary goal of runtime
callable wrappers is to hide
the differences between the
managed and unmanaged
programming models.
22 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


The runtime callable wrapper also consumes the interfaces listed in the
following table.
Interface Description

IErrorInfo If the COM object being wrapped implements the IErrorInfo
interface, the exceptions generated by the wrapper contain the
information provided by the interface
IProvideClassInfo If the COM object being wrapped implements the
IProvideClassInfo interface, the wrapper extracts the type
information from this interface to provide better type identity

Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 23


Generating Runtime Callable Wrappers
! Runtime Callable Wrappers Are Generated in One of
Three Ways:
# Adding a reference to a COM component in a
Visual Studio .NET project
# Employing the Type Library Importer
# Creating custom wrappers

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Runtime callable wrappers are generated in one of three ways:
! Adding a reference to a COM component in a Visual Studio .NET project
This automatically converts COM types in a type library to metadata in an
assembly.
! By using the Type Library Importer
This provides command-line switches to adjust metadata in the resulting
assembly file, imports types from an existing type library, and generates an
assembly and a namespace.
! Creating custom wrappers
As a less-desirable option, you can create type definitions. This requires
advanced programming skills.

Visual Studio .NET generates an assembly containing metadata when you add a
reference to a specific type library.
! To add a reference to a type library in Visual Studio .NET
1. Install the COM DLL or EXE file manually on your computer and use
Regsrv32.exe to add a component to the registry, unless a Microsoft
Windows installation program performs the installation for you.
2. In Visual Studio .NET, on the Project menu, click Add Reference.
3. Click the COM tab.
4. In the Component Name list, double-click the type library, and then click
OK.

Topic Objective
To define the three ways to
generate runtime callable
wrappers.
Lead-in
Runtime callable wrappers
are generated in one of
three ways.
Delivery Tip
You may want to create an
RCW and use the Microsoft
Intermediate Language
disassembler to show what
the metadata looks like.
24 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


The Type Library Importer (Tlbimp.exe) is a command-line tool that converts
the coclasses and interfaces contained in a COM type library to metadata. This
tool creates an assembly and namespace for the type information automatically.
After the metadata of a class is available, managed clients can create an
instance of the COM type and call its methods, as if it were a .NET Framework
instance. The Type Library Importer converts an entire type library to metadata
at once but cannot generate type information for a subset of the types defined in
a type library.
! To generate an assembly from a type library
To produce the Loanlib.dll assembly in the Loanlib namespace, type the
following command:
tlbimp Loanlib.tlb


Adding the /out: switch produces an assembly with an altered name, such as
Loanlib_RCW.dll. Altering the runtime callable wrapper assembly name can
help distinguish it from the original COM DLL. The following example uses
the /out: switch of the Type Library Importer to alter the generated assemblys
name:
tlbimp Loanlib.dll /out: Loanlib_RCW.dll

When a type library is unavailable or incorrect, one option is to create a
duplicate definition of the class or interface in managed source code. You then
compile the source code with a compiler that targets the runtime in order to
produce metadata in an assembly.
To define COM types manually, you must have access to the following items:
! Precise descriptions of the coclasses and interfaces being defined.
! A compiler, such as the C# compiler, that can generate the appropriate .NET
Framework class definitions.
! Knowledge of the rules for conversion from a type library to an assembly.

Writing a custom wrapper is an advanced technique that you seldom perform.
However, if you need to create custom wrappers, there are two methods to do
this:
! If you have access to the Interface Definition Language (IDL) source, you
can modify the source by applying type library file attributes and import the
type library.
! You can apply interop-specific attributes to imported types and generate a
new assembly.

For additional information on generating a custom wrapper, see Customizing
Standard Wrappers in the .NET Framework SDK documentation.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 25


Demonstration: Using the Type Library Importer

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The Type Library Importer (Tlbimp.exe) can be used to convert the coclasses
and interfaces found within a COM type library into equivalent definitions in a
.NET assembly. The output of the Type Library Importer is an assembly that
contains metadata for the types defined within the original type library. The
Type Library Importer performs the following tasks:
! Converts unmanaged COM coclasses to C# classes with a constructor
(without parameters) and no other methods.
! Converts unmanaged COM vtable interfaces to C# interfaces.
! Converts unmanaged COM structures to C# structures with public fields.

The syntax for using the Type Library Importer is as follows:
tlbimp inputFile [options]

In the previous example, inputFile is the name of the file that contains the COM
type library. This file can be either a stand-alone type library (.TLB) file or a
DLL. The following command imports a type library file and generates a .NET
assembly named myTest.dll:
tlbimp myTest.tlb

Topic Objective
To demonstrate how to use
the Type Library Importer to
convert the type definitions
found within a COM type
library into equivalent
definitions in a .NET
assembly.
Lead-in
In this demonstration you
will learn how to use the
Type Library Importer to
convert the type definitions
found within a COM type
library into equivalent
definitions in a .NET
assembly.
26 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


The /out: option specifies the name of the assembly that is created as a result of
running the Type Library Importer. The following command creates
myNewTest as a DLL:
tlbimp myTest.tlb /out:myNewTest.dll

If the output assemblys file name is equivalent to the input type librarys file
name, the Type Library Importer will generate an error, because the input file
cannot be overwritten.
The /delaysign option delays signing of the assembly. The /delaysign option
must be used with the /keycontainer:, /keyfile:, or /publickey: option. For a
complete list of all the options that can be used with the Type Library Importer,
see Type Library Importer (Tlbimp.exe) in the .NET Framework SDK
documentation.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 27


Threading Models
! COM Components Use Apartments to Synchronize Access to
Managed Resources
! Runtime Creates/Initializes Apartment When Calling a COM Object
! To ensure that the main thread of an application is STA
using System.Threading;
using APTOBJLib;

Thread.CurrentThread.ApartmentState =
ApartmentState.STA;
AptSimple obj = new AptSimple ();
obj.Counter = 1;
using System.Threading;
using APTOBJLib;

Thread.CurrentThread.ApartmentState =
ApartmentState.STA;
AptSimple obj = new AptSimple ();
obj.Counter = 1;

[STAThread]
static void Main()

[STAThread]
static void Main()


*****************************ILLEGAL FOR NON-TRAINER USE******************************
COM components use apartments to synchronize access to managed resources.
In contrast, managed objects use synchronized regions; synchronization
primitives such as mutexes, locks, and completion ports; and synchronized
contexts to ensure that all shared resources are used in a thread-safe manner.
For interoperability, the common language runtime creates and initializes an
apartment when calling a COM object. A managed thread can create and enter a
single-threaded apartment (STA) that contains only one thread, or a multi-
threaded apartment (MTA) that contains one or more threads. When a COM
apartment and a thread-generated apartment are compatible, COM permits the
calling thread to make calls directly to the COM object. If the apartments are
incompatible, COM creates a compatible apartment and marshals all calls
through a proxy in the new apartment.
On the first call to unmanaged code, the runtime calls CoInitializeEx to
initialize the COM apartment as either an MTA or an STA. You can control the
type of apartment created by setting the System.Threading.ApartmentState
property on the thread to MTA, STA, or Unknown. As long as the proxy and
stub are registered or the type library is registered, you do not have to set this
property. If neither the proxy and stub nor the type library is registered, an
InvalidCastException can occur when calling a COM object from managed
code.
Topic Objective
To describe the threading
models in managed and
unmanaged code.
Lead-in
The threading models that
are supported by COM
components and the .NET
Framework are different.
While managed objects use
synchronized regions
28 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


The following table lists the ApartmentState enumeration values and shows
the comparable COM apartment initialization call.
ApartmentState
enumeration value

COM apartment initialization

MTA CoInitializeEx(NULL, COINIT_MULTITHREADED)
STA CoIntializeEx(NULL,
COINIT_APARTMENTTHREADED)
Unknown CoInitializeEx(NULL, COINIT_MULTITHREADED)

Whenever the COM object and the managed thread are in incompatible
apartments, all calls on the object are made through a COM created proxy. For
example, calls between Windows Forms controls whose COM objects must
reside in an STA and managed code whose threads ApartmentState is MTA.
The following example shows how to create an STA apartment-threaded COM
object, AptSimple, from managed code:
using System.Threading;
using APTOBJLib;

AptSimple obj = new AptSimple ();
obj.Counter = 1;

To eliminate the use of proxies and stubs and significantly enhance
performance, set the ApartmentState on the thread before creating the object,
as shown in the following example:
using System.Threading;
using APTOBJLib;

Thread.CurrentThread.ApartmentState = ApartmentState.STA;
AptSimple obj = new AptSimple ();
obj.Counter = 1;

Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 29


A thread can only initialize a COM apartment once. Since the runtime
initializes the apartment before making the very first call to unmanaged code on
that thread, you should set the ApartmentState as early as possible. Changing
the ApartmentState after the apartment has been initialized has no effect. You
can neither un-initialize nor re-initialize an apartment. In some situations, the
thread may already have called into unmanaged code before the ApartmentState
could be set. In such cases, you cannot change the apartment type after the
thread is initialized. Your only option is to create a new thread.
As an alternative to setting the ApartmentState enumeration, you can apply
the System.STAThreadAttribute or System.MTAThreadAttribute to the
main entry point of the application. By applying these attributes you ensure that
the main thread of an application is in the proper state. For example:

[STAThread]
static void Main()


After setting the apartment state, you can check the state programmatically, as
in the following example:
Thread.CurrentThread.ApartmentState = ApartmentState.STA;
if (Thread.CurrentThread.ApartmentState == ApartmentState.STA)
//STA apartment state
else
//incompatible apartment state

30 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


Signature Translation and Error Handling

*****************************ILLEGAL FOR NON-TRAINER USE******************************
COM components accept COM data types and return HRESULTs. However,
classes in the .NET Framework do not use HRESULTs. In order to satisfy both
models, every method of a managed type has a .NET Framework signature and
an implied COM signature.
When COM interoperability services imports a COM type, it produces a
.NET Framework method signature equivalent to the original COM method
signature. This is referred to as signature translation. During signature
translation, COM parameters, return values, and HRESULTs are mapped to
corresponding entities in the .NET Framework method signature, as shown in
the illustration on the slide.
When the COM object returns an error HRESULT, the runtime converts the
HRESULT to an exception, which is thrown to the caller. The type of exception
that is generated depends on the error returned from the COM object. Note that
any non-error HRESULTs will be lost in this conversion. For example, if a
COM object returns any success HRESULT other than S_OK, such as
S_FALSE, no exception is thrown, but the managed caller does not receive the
HRESULT. You must manually generate the runtime callable wrappers to
avoid this issue.
A managed signature is converted to an unmanaged signature by changing the
managed return value to an [out, retval] parameter and changing the type of the
unmanaged return value to HRESULT. The COM signature returns an
HRESULT and has an additional output parameter for the return value. The
return value from the managed implementation always returns as an [out,
retval] parameter added to the end of the unmanaged signature, whereas the
unmanaged signature always returns an HRESULT. If the managed method has
a void return, the runtime omits the [out, retval] parameter.
Topic Objective
To describe how signature
translation works.
Lead-in
Signature translation is the
mechanism by which
managed return values are
converted into COM
HRESULTs.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 31


Under some circumstances, it is preferable to leave the managed signature
unchanged. You can use the PreserveSig attribute to do this. The following
examples show the translation from a managed signature to an unmanaged
signature.
Managed signature:
[PreserveSig] short DoSomething(short i);

Unmanaged signature:
short DoSomething ([in] short i);


32 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


Marshaling
! Wrappers perform data marshaling
# Support for marshaling data to and from COM almost
always provides the correct marshaling behavior
! Table shows corresponding COM and C# types
! MarshalAsAttribute to change the marshaling behavior
! Two ways to customize the RCW to handle types
# Edit the interop assembly
# Create a wrapper manually
public void M1
([MarshalAs(UnmanagedType.LPWStr)]String msg);
public void M1
([MarshalAs(UnmanagedType.LPWStr)]String msg);

*****************************ILLEGAL FOR NON-TRAINER USE******************************
When marshaling data between managed and unmanaged code, the interop
marshaler must recognize the representations of the data being passed.
! For blittable types, managed and unmanaged representations are always the
same: a 4-byte integer is always marshaled to a 4-byte integer. The interop
marshaler uses the managed signature to determine the data representation.
! For non-blittable types, the interop marshaler recognizes the managed
representation from its method signature, but is unable to do the same for
the unmanaged representation. To marshal non-blittable types, you can use
one of the following techniques:
Allow the marshaler to infer the representation from the managed
representation.
Supply the unmanaged data representation explicitly.

Topic Objective
To describe how COM types
relate to .NET Framework
types and classes.
Lead-in
Types used in COM have
corresponding .NET
Framework built-in value
types or classes.
Direct students to the
Student Notes and explain
that the table that is referred
to in the slide may be found
in the Student Notes.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 33


The following table shows data types used in COM and their corresponding
.NET Framework built-in value types or classes. Any type not explicitly
identified in this table is converted to an Int32 system type.
COM value type COM reference type C# Data Type

bool bool * int
char, small char *, small * System.SByte
short short * short
long, int long *, int * int
Hyper hyper * long
unsigned char, byte unsigned char *, byte * byte
wchar_t, unsigned short wchar_t *, unsigned short * ushort
unsigned long, unsigned int unsigned long *, unsigned int * uint
unsigned hyper unsigned hyper * ulong
float float * float
double double * double
VARIANT_BOOL VARIANT_BOOL * bool
void * void ** System.IntPtr
HRESULT HRESULT * System.IntPtr
SCODE SCODE * int
BSTR BSTR * string
LPSTR or [string, ]
char *
LPSTR * string
LPWSTR or [string, ]
wchar_t *
LPWSTR * string
VARIANT VARIANT * object
DECIMAL DECIMAL * System.Decimal
DATE DATE * System.DateTime
GUID GUID * System.Guid
CURRENCY CURRENCY * System.Decimal
IUnknown * IUnknown ** object
IDispatch * IDispatch ** object
SAFEARRAY(type) SAFEARRAY(type) * type[ ]

34 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


The following table lists COM value and reference types that convert to
corresponding element types. For example, a COM coclass automatically maps
to a managed class with the same name.
COM value type COM Reference type Element type

typedef BaseType
MyType
ByRef BaseType BaseType
MyStruct ByRef VALUETYPE<MyStruct> valuetype<MyStruct>
MyEnum ByRef VALUETYPE<MyEnum> valuetype<MyEnum>
MyInterface * ByRef CLASS <MyInterface> Class<MyInterface>
MyCoClass ByRef CLASS <_Class> class <_Class>

You can apply the System.Runtime.InteropServices.MarshalAsAttribute to
a method parameter, class field, or return value to change the marshaling
behavior. For example, a string is converted to a BSTR type when marshaled
from managed to unmanaged code, unless you explicitly apply the
MarshalAsAttribute to marshal the string to another type, such as LPWSTR.
You can apply this attribute to a parameter, field, or return value within the
source of the type definition, as shown in the following examples where it is
specified that msg is to be marshaled as a null-terminated buffer of Unicode
characters (LPWStr).
Apply the MarshalAsAttribute to a parameter:
public void M1
([MarshalAs(UnmanagedType.LPWStr)]String msg);

Apply the MarshalAsAttribute to a field within a class:
class MsgText {
[MarshalAs(UnmanagedType.LPWStr)] Public String msg;
}

Apply the MarshalAsAttribute to a return value:
[return: MarshalAs(UnmanagedType.LPWStr)]
public String GetMessage();

Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 35


Although the import process generally produces accurate interop assemblies,
there are cases when you can (or must) modify the interop assembly to produce
a custom RCW.
There are several reasons why you might customize an RCW, such as:
! One or more types require additional marshaling information.
! A type library contains many specialized types that are unrecognizable to
the marshaler.
! A large type library can include types that are unnecessary for an
application.
You can eliminate the need to deploy unnecessary types by creating an
interop assembly from managed source code.

If you must customize the runtime callable wrapper with additional or different
marshaling instructions, you can either:
1. Edit the interop assembly, searching for problematic syntax and replacing it
with alternative syntax.
This option is best for minor marshaling changes.
2. Create a wrapper manually, based on an existing Interface Definition
Language (IDL) file or type library.
Declaring COM types manually is a difficult activity that requires working
knowledge of the Type Library Importer (Tlbimp.exe), the default behavior
of the interop marshaler, and COM. This approach is best used when you
have an entire library of specialized types or require the RCW source code.

For more information about implementing these choices, see the .NET
Framework Software Development Kit (SDK) documentation.
36 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


Performance and Security Issues
! Recognize that transitions that occur when managed
code must interact with unmanaged code result in an
overhead of 10 to 40 instructions per call.
# Limit the number of transitions that appear in your code
# Instead, use API calls that perform several actions
at once
! Eliminate the creation of the proxy and stub by setting
the ApartmentState on the thread before creating a
COM object
! For trusted code, suppress security checks that are
performed when managed code calls unmanaged code

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Transitions occur when managed code must interact with unmanaged code. A
slight overhead is associated with every transition. These overheads require 10
to 40 instructions per call. The best practice is to limit the number of transitions
that appear in your code. If you must use transitions, use API calls that perform
several actions from the same call instead of ones that only perform few actions
and must be repeatedly called.
Threading models that are used in managed and unmanaged code affect the
performance of applications. Whenever a COM object and a managed thread
are in incompatible apartments, all calls on the object are made through a
COM-created proxy. To eliminate the use of proxies and stubs, and
significantly enhance performance, set the ApartmentState on the thread
before creating the object.
Security checks that are performed when unmanaged code is run, also lead to a
decrease in application performance. You can suppress these security checks
and increase the performance of your application.
When managed code calls unmanaged COM code by using COM
interoperability services, a demand for the UnmanagedCode permission is
made to ensure that all callers have the necessary permission to allow the call to
proceed. You can suppress the demand for the UnmanagedCode permission at
run time by using the SuppressUnmanagedCodeSecurityAttribute attribute.
The demand for the UnmanagedCode permission will still occur at link time.
Topic Objective
To list some performance
issues that arise from
running unmanaged code
from managed code.
Lead-in
Running unmanaged code
from managed code
decreases the performance
of your application.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 37


The SuppressUnmanagedCodeSecurityAttribute attribute helps improve
code performance by suppressing asserts and demands at run time. These run
time asserts and demands are the usual security checks performed when calling
unmanaged code. Only code that has been granted the UnmanagedCode
permission can use the SuppressUnmanagedCodeSecurityAttribute attribute,
however. Using this attribute in a class or module applies to all methods
contained in the class or module.
When you use the SuppressUnmanagedCodeSecurityAttribute attribute in a
class, code that does not have permission to access unmanaged code can call
methods in the class to access unmanaged code. Therefore, you must make sure
that when you use the SuppressUnmanagedCodeSecurityAttribute attribute,
you write secure code. Otherwise, your code can be misused by malicious code.
38 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


Best Practices
! The MSIL Disassembler can be used on the generated
assembly to determine the exact names of the
namespace and classes that are generated
! Any parameter passed by reference in C++ or
Visual Basic 6.0 will generate a parameter that needs
to be passed by using the ref keyword in C#

*****************************ILLEGAL FOR NON-TRAINER USE******************************
If you create your runtime callable wrapper assembly by adding a reference to a
COM object in Visual Studio .NET or by using the Type Library Importer, the
namespace and class names that are generated might not always be obvious. To
view these names, you can run the MSIL Disassembler on the particular
assembly. If you create the runtime callable wrapper assembly with
Visual Studio .NET, you must attempt to build the project once to generate the
assembly.
Parameters passed by reference in unmanaged code frequently require the use
of the ref keyword when that code is called by using C#. For C or C++, this
includes most parameters passed as pointers. For Visual Basic 6.0, keep in mind
that, by default, all parameters are passed by reference. The most notable
exception to this is for passing string types, which can usually be passed as
String or StringBuilder objects from managed code. For an example in C or
C++ of passing a string type, see Marshaling in this module.
Topic Objective
To list some best practices
to be followed when calling
COM components from
managed code.
Lead-in
Lets look at some best
practices that you should
use when calling COM
components from managed
code.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 39


Lab 15.2: Calling COM Objects

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives
After completing this lab, you will be able to call a COM object from managed
code.
Estimated time to complete this lab: 30 minutes
Topic Objective
To introduce the lab.
Lead-in
In this lab, you will call a
COM object from managed
code.
40 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


Exercise 1
Calling a COM Object From Managed Code
In this exercise, you will call a COM object from managed code. This includes
registering the COM DLL, adding a reference to the COM object in a
Visual Studio .NET project, and then adding code to call the COM object.

To use Microsoft Visual Studio .NET tools within a command
prompt window, the command prompt window must have the proper
environment settings. The Visual Studio .NET Command Prompt window
provides such an environment. To run a Visual Studio .NET Command Prompt
window, click Start, All Programs, Microsoft Visual Studio .NET,
Visual Studio .NET Tools, and Visual Studio .NET Command Prompt.

! Register the COM object
1. At the command prompt, change the current folder to <install folder>\Labs\
Lab15\Lab15.2\Starter.
2. To register the COM object, run regsvr32 VBUnmanagedCalculator.dll.
3. A dialog box appears informing you that the registration succeeded. Click
OK to dismiss the dialog box.

! Open the Visual Studio .NET project for this exercise and examine the
application UI
1. In Windows Explorer, move to <install folder>\Labs\Lab15\Lab15.2\
Starter.
2. Double-click CallingComFromDotNet.csproj to open the project for this
exercise in Visual Studio .NET.
3. In the Solution Explorer pane, double click CalcUI.cs. The UI design for
the Windows Forms application appears. Examine the UI for a moment to
become familiar with it.
4. Press F7 to edit the code that implements the Windows Form application.

Important
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 41


! Add the reference to the COM object and attempt to build the project
1. On the Project menu, click Add Reference.
2. In the Add Reference dialog box, click the COM tab. In the list of
registered COM objects, click VBUnmanagedCalculator.
3. To add VBUnmanagedCalculator to the Selected Components window,
click Select.
4. To add the reference, click OK. Notice that the VBUnmanagedCalculator
DLL is added to the project references.
5. On the Build menu, click Build Solution. Your application does not build
successfully yet, but the runtime callable wrapper assembly for the
VBUnmanagedCalculator.dll is generated.
6. In a Visual Studio .NET Command Prompt window, change the current
folder to <install folder>\Labs\Lab15\Lab15.2\ Starter\bin\Debug.
7. To view the metadata in the runtime callable wrapper assembly, at the
command prompt, type ildasm /adv Interop.VBUnmanagedCalculator.dll
Use the MSIL Disassembler to view the namespace and classes created for
the COM object.

! Modify the code in the project to call the COM object
1. In the CalcUI.cs file, find the first TODO comment that instructs you to
add a using statement for VBUnmanagedCalculator. Add the correct using
statement. Notice that you can use the Task List tab in the lower-left hand
pane to find the TODO comments. Click View, select Show Tasks, and
then click All if the TODO comments do not appear in the task list.
2. Find the next TODO comment that instructs you to instantiate a
CalcEngine object. Add the code to instantiate a CalcEngine object and
assign it to the calcEngine private member variable.
3. Scroll down to the next TODO comment that instructs you to call
GetVersion in CalcEngine. Add the code to make the call described and
assign the results of the call to the VersionInfo text box.
4. Scroll down to the KeyDate_Click method. Complete the code in this
method by replacing the TODO comment with code to obtain the date by
calling the GetDate method in CalcEngine and assign the results of the call
to the OutputDisplay text box. Then, add code to reset the engine for the
next equation by calling the CalcReset method in CalcEngine.
5. In the KeyClear_Click method, complete the code by replacing the
TODO comment with code that calls the CalcReset method in
CalcEngine and clears the OutputDisplay text box.

42 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


! Build and test your application
1. On the File menu, click Save All.
2. On the Build menu, click Build Solution. Your application should build
successfully. The following statement should appear as the last line of the
output window:
Build: 1 succeeded, 0 failed, 0 skipped.

3. On the Debug menu, click Start. When the calculator appears, check the
version number. It should read Calculator VB6 Unmanaged Component.
Enter some equations to test the application.
4. Close the application.


If you wish to test the solution project, you will first need to follow the
steps in the Register the COM object, Open the Visual Studio .NET project
for this exercise and examine the application UI, and Add the reference to the
COM object and attempt to build the project procedures above.


Note
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 43


" "" " Calling .NET Objects from COM Objects
! COM Callable Wrappers
! The Export Process
! Registering a .NET Framework Class with the System
Registry
! Using the ClassInterfaceAttribute
! Using .NET Framework Types from COM
! Managed and Unmanaged Events
! Best Practices

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The .NET Framework allows existing COM applications to use managed code
transparently by generating COM callable wrappers for .NET Framework
classes. These wrappers are generated at run time. COM callable wrappers are
proxy objects that are generated from the metadata that is stored in an assembly,
so COM applications can use the .NET Framework classes in the same manner
as they would use COM components.
Topic Objective
To introduce how COM
callable wrappers are used
to expose .NET Framework
classes to COM clients.
Lead-in
In this section, you will learn
how to create COM callable
wrappers to use .NET
Framework types for a COM
client.
44 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


COM Callable Wrappers

*****************************ILLEGAL FOR NON-TRAINER USE******************************
COM clients cannot reference a .NET Framework object directly. When a COM
client calls a .NET Framework object, the common language runtime creates
the managed object and a proxy for the managed object called the COM
callable wrapper.
The purpose of a COM callable wrapper is to marshal calls between managed
and unmanaged code, and to manage the identity and lifetime of the managed
object it wraps. COM callable wrappers are invisible to other classes running
within the .NET Framework. Unlike the managed object it wraps, the COM
callable wrapper is allocated memory from a non-collected heap. This makes it
possible for COM clients to reference the wrapper directly.
COM callable wrappers are reference-counted in the traditional COM fashion.
When the reference count on the wrapper reaches zero, the wrapper releases its
reference on the managed object. A managed object with no remaining
references is collected during the next garbage-collection cycle.
The runtime creates one COM callable wrapper for a managed object,
regardless of the number of COM clients requesting its services. The COM
callable wrapper, in turn, holds a single reference to the managed object that
implements the interface. Both COM clients and .NET Framework clients can
make requests on the same managed object simultaneously.
When you compile your managed code into an assembly, metadata describing
each type in the assembly is generated. The Type Library Exporter can be used
to create a type library file from the metadata for your assembly. COM clients
can then import the type library and use the managed object like any other
COM object. When the COM client tries to create and access a managed object
from the assembly, the runtime uses the assemblys metadata to generate a
COM callable wrapper. The wrapper marshals the call between the COM client
and the managed object.
Topic Objective
To describe how the runtime
uses COM callable
wrappers to expose
managed classes to COM
clients.
Lead-in
To allow COM clients to use
a managed class, the .NET
Framework creates a COM
callable wrapper.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 45


You can customize the COM callable wrapper by applying interop-specific
attributes to your managed source code and compiling the source code into an
assembly.

If you plan to export a managed object, be aware that the COM client
determines the apartment of the object. A managed object called by a COM
client initialized in an MTA must ensure thread safety.

Caution
46 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


Demonstration: Using the Type Library Exporter

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The Type Library Exporter (Tlbexp.exe ) is a command-line tool that builds a
type library for .NET Framework classes so that they can be used from
unmanaged clients. The Type Library Exporter uses the metadata that is stored
in the assembly to generate the type library. The type library contains
definitions of the types defined in an assembly. Applications that use
unmanaged code can use the generated type library to bind to the .NET
Framework types defined in the assembly.

You cannot use the Type Library Exporter to produce a type library from
an assembly that was imported by using the Type Library Importer
(Tlbimp.exe). Instead, you should refer to the original type library that was
imported with the Type Library Importer. You can export a type library from an
assembly that references assemblies that were imported by using the Type
Library Importer.

You must use the Type Library Exporter to generate a type library for an entire
assembly and not just a subset of the types that are defined in the assembly. A
single assembly may cause several type libraries to be generated, and these type
library files are placed in the current working directory or the directory
specified for the output file when running the Type Library Exporter.
The Type Library Exporter generates a type library but does not register the
types it exports with the system registry. To generate and register a type library
with COM, use the Assembly Registration tool (Regasm.exe). For information
about using the Assembly Registration tool to register a type library, see
Registering a .NET Framework class with the System Registry in this module.
Topic Objective
To describe the use of the
Type Library Exporter.
Lead-in
The Type Library Exporter is
a command-line tool that
exports the type information
to a type library so that it
can be used from a COM
client.
Note
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 47


The Export Process
! Every public class in an assembly is converted to a
coclass in a type library
! Interfaces that are explicitly implemented are exported
to the type library
Managed Code Type Library
public interface IShape
{
void Draw();
void Move(int x, int y);
}
class Circle : IShape
{
void Draw();
void Move(int x, int y);
void Enlarge(int x);
}
public interface IShape
{
void Draw();
void Move(int x, int y);
}
class Circle : IShape
{
void Draw();
void Move(int x, int y);
void Enlarge(int x);
}
[ uuid(), dual, odl, oleautomation ]
interface IShape : IDispatch {
HRESULT Draw();
HRESULT Move(int x, int y);
}
[ uuid() ]
coclass Circle {
interface IShape;
}
}
[ uuid(), dual, odl, oleautomation ]
interface IShape : IDispatch {
HRESULT Draw();
HRESULT Move(int x, int y);
}
[ uuid() ]
coclass Circle {
interface IShape;
}
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
When you run the Type Library Exporter on your assembly to generate a type
library, the export process converts the classes and interfaces defined in your
assembly, and creates representations for them in the type library.
The export process converts each public class in an assembly to a coclass in a
type library, and gives the coclass the same name as the managed class. All the
interfaces that are explicitly implemented by the managed class are exported to
the type library. Methods and properties are not exported.
When the class in the example in the slide is exported, the type library will
contain a representation for the IShape interface and a coclass definition for the
Circle class. The Draw, Move, and Enlarge methods of the Circle class will
not be exported.
When an assembly is exported, all managed interfaces and their methods and
properties are converted to COM interfaces. By default, the COM interface that
is generated in the type library file is a dual interface. You can use the
ClassInterfaceAttribute to determine the type of interface that is defined when
the type library is exported.
Each coclass can implement one other interface, called the class interface,
which the export process generates automatically. The class interface exposes
all methods and properties available in the original managed class, thereby
enabling COM clients to access them by calling through the class interface.
Topic Objective
To describe how the export
process converts classes
and interfaces.
Lead-in
The export process creates
definitions for the classes
and interfaces that are
defined in an assembly.
48 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


Registering a .NET Framework Class with the System Registry
! To register a .NET Framework class with the system registry, use
the Assembly Registration tool (Regasm.exe)
! Registering a .NET Framework class creates the following entries in
the system registry:
# The CLSID of the .NET Framework class under the
Hkey_Classes_Root\CLSID registry key
# Two sub keys: Implemented Categories and InprocServer32 under
the HKCR\CLSID\<class ID> key
! The default value of the HKCR\CLSID\<class ID> key is set to the
ProgID of the class
! The default value of the InprocServer32 is set to the name of the
DLL that contains the common language runtime (Mscoree.dll)

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Before you can use a .NET Framework class from unmanaged code, you must
register the class with the system registry. To register a .NET Framework class
with the system registry, you use the command-line Assembly Registration tool
(Regasm.exe). The Assembly Registration tool adds information about the class
to the system registry so COM clients can use the .NET Framework class
transparently. You can also use the RegistrationServices class from within
your managed code, to register your .NET Framework class with the system
registry.
When you register a .NET Framework class, the following entries are made in
the system registry:
! A new entry for the class identifier (CLSID) of the .NET Framework class
is created under the HKEY_CLASSES_ROOT\CLSID registry key. After
the CLSID is generated for the .NET Framework class, the Assembly
Registration tool always uses the same CLSID, regardless of the number of
times it is used on the class.
! Under the HKEY_CLASSES_ROOT\CLSID\<class ID> key, are two sub
keys: ImplementedCategories and InprocServer32. The default value of
the HKEY_CLASSES_ROOT\CLSID\<class ID> key is set to the
ProgID of the class. The default value of the InprocServer32 is set to the
name of the DLL that contains the common language runtime (Mscoree.dll).

When a COM client attempts to load the assembly in which the class is
implemented, the runtime reads the assembly value from the registry and passes
it on to the runtime assembly resolver. The assembly resolver attempts to locate
the assembly based on the assembly information, such as the name and version
number. Before the assembly resolver can locate the assembly, however, the
assembly must be signed and installed in the global assembly cache, or it must
be in the applications root directory or a subdirectory of the applications root
directory.
Topic Objective
To describe the procedure
for registering a .NET
Framework class with the
system registry.
Lead-in
Before you can use a .NET
Framework class from
unmanaged code, you must
register the class with the
system registry.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 49


Using the ClassInterfaceAttribute
! The class interface is an interface that exposes all
public methods, properties, fields, and events that are
explicitly exposed on the .NET Framework object
! The [ClassInterface(ClassInterfaceType.None)] attribute
prevents the class interface from being created
! The [ClassInterface(ClassInterfaceType.AutoDispatch)]
attribute creates a dispatch interface
! The [ClassInterface(ClassInterfaceType.AutoDual)]
attribute creates a dual interface

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The class interface is an interface that exposes all public methods, properties,
fields, and events that are explicitly exposed on a .NET Framework object. This
interface can be a dual or dispatch-only interface. The class interface receives
the name of the .NET Framework class, preceded by an underscore.
When you use the Type Library Exporter to generate a type library for a
managed class, a dispatch-only interface for each class you export is created by
default. You can use the ClassInterfaceAttribute with your class to prevent or
modify the automatic creation of this interface. When you use the
ClassInterfaceAttribute, you must add the Sytem.Runtime.InteropServices
namespace to your code.
You use the ClassInterfaceAttribute and pass the value
ClassInterfaceType.None to the constructor, to prevent the class interface
from being generated when the class metadata is exported to a type library, as
shown in the following example:
[ClassInterface(ClassInterfaceType.None)]
public class LoanApp : IExplicit
{
void M();
}

In the preceding example, COM clients can access the LoanApp class only
through the IExplicit interface.
Topic Objective
To describe the use of the
ClassInterfaceAttribute to
control the type of interface
that is created for a
managed class.
Lead-in
When used with a .NET
Framework class, the
ClassInterfaceAttribute
can be used to control and
modify the type of interface
that is generated when that
.NET Framework class is
exported.
50 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


To avoid breaking late-bound COM clients when using the class interface, set
the Value property of the ClassInterfaceAttribute to
ClassInterfaceType.AutoDispatch. This value implements a dispatch-only
class interface, but omits the interface description from the type library.
Without an interface description, clients are unable to cache dispatch identifiers
(DISPIDs) at compile time. Although this is the default interface type for the
class interface, you can apply the attribute value explicitly, as shown in the
following example:
[ClassInterface(ClassInterfaceType.AutoDispatch)]
public class LoanApp : IAnother
{
void M();
}

To get the DISPID of an interface member at run time, COM clients can call
IDispatch.GetIdsOfNames. To invoke a method on the interface, pass the
returned DISPID as an argument to IDispatch.Invoke.
A dual interface that is automatically generated (ClassInterface.AutoDual)
might be appropriate in rare cases. However, a dual interface that is
automatically generated often creates version-related complexities. For
example, COM clients that are using the class interface of a derived class can
easily break when there are changes to the base class. When a third party
provides the base class, the layout of the class interface is out of your control.
Further, unlike a dispatch-only interface, a dual interface provides a description
of the class interface in the exported type library. Such a description encourages
late-bound clients to cache DISPIDs at run time.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 51


Using .NET Framework Types from COM
! To Use a .NET Framework Class from Unmanaged Code
# Set a reference to the type library file that describes the
interferences of the .NET Framework class
! In Visual Basic 6.0, Use the References Dialog Box to
Set the Reference
! In Visual C++ 6.0, Use the #import Directive to
Reference to the Type Library
! Create and Use Objects of the Classes That Are Defined
in the Type Library

*****************************ILLEGAL FOR NON-TRAINER USE******************************
After you have exported the metadata from an assembly to a type library file,
you can use the .NET Framework classes that are implemented in the assembly
as you would create and use a COM object.
! To use a .NET Framework class that has been exported to a type
library from a Visual Basic 6.0 application
1. In Visual Studio 6.0, on the Project menu, click References.
The References dialog box appears.
2. To add the reference to your project, scroll down Available References list,
click the reference you want to add, and then click OK.
3. Create an object of the .NET Framework class (as you would create a COM
object), as shown in the following example:
Dim Server As New ExcelServer.ExcelServer

4. Use the methods and properties of the class, as shown in the following
example:
Dim Count As Integer
Count = Server.Count


To use a .NET Framework class from a Microsoft Visual C++ 6.0 application,
use the #import directive to reference the type library, as shown in the
following example:
#import "c:\Samples\SampleClassLibrary\SampleClassLib.tlb"

Topic Objective
To describe the procedure
for using .NET Framework
types from COM clients.
Lead-in
After you have generated a
type library for your .NET
assembly, you can use the
assembly from unmanaged
code.
52 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


Managed and Unmanaged Events
! .NET event model differs from COM event model
# Managed events are based on delegates, whereas
unmanaged events (in COM) are based on connection
points.
! See the .NET SDK for details on the topics:
# Handling Events Raised by a COM Source
# Raising Events Handled by a COM Sink

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The .NET Framework event model differs from the traditional COM event
model. Managed events are based on delegates, whereas unmanaged events in
COM are based on connection points. Both models are tightly coupled event
systems, because the client (event receiver) and server (event sender) must run
simultaneously.
The SDK explains in detail how to bridge the managed and unmanaged event
systems, enabling objects to send and receive events across the interoperation
boundary. Topics include Handling Events Raised by a COM Source and
Raising Events Handled by a COM Sink.
Topic Objective
To understand at a high
level interoperability
between .NET and COM
events
Lead-in
Lets look at how the NET
Framework event model
differs from the traditional
COM event model.
Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 53


Best Practices
! Define an Explicit Interface for COM Clients to Use
Rather Than Generating the Class Interface
! Avoid Caching Dispatch Identifiers
! Restrict Using the Dual Interface Option for the Class
Interface

*****************************ILLEGAL FOR NON-TRAINER USE******************************
When exposing managed code to COM clients, use the following best practices:
! Define an explicit interface for COM clients to use rather than generating
the class interface.
Because COM interop services generate class interfaces automatically, post-
version changes to your class can alter the layout of the class interface
exposed by the common language runtime. Since COM clients are typically
unprepared to handle changes in the layout of an interface, they break if you
change the member layout of the class.
To reduce the risk of breaking COM clients by inadvertently reordering the
interface layout, isolate all changes to the class from the interface layout by
explicitly defining interfaces.
Set the Value property of ClassInterfaceAttribute to
ClassInterfaceType.None to disengage the automatic generation of the
class interface and implement an explicit interface for the class.
! Avoid caching dispatch identifiers (DISPIDs).
Using the class interface is an acceptable option for scripted clients,
Visual Basic 6.0 clients, or any late-bound client that does not cache the
DISPIDs of interface members. DISPIDs identify interface members and
enable late binding.
For the class interface, generation of DISPIDs is based on the position of the
member in the interface. If you change the order of the member and export
the class to a type library, you will alter the DISPIDs generated in the class
interface.
Topic Objective
To list some best practices
when using managed
classes from COM clients.
Lead-in
When exposing managed
code to COM clients, follow
the best practices listed on
the slide.
54 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


To avoid breaking late-bound COM clients when using the class interface,
set the Value property of the ClassInterfaceAttribute to
ClassInterfaceType.AutoDispatch. This value implements a dispatch-only
class interface, but it omits the interface description from the type library.
Without an interface description, clients are unable to cache DISPIDs at
compile time. Although this is the default interface type for the class
interface, you can apply the attribute value explicitly.
! Restrict using the dual interface option for the class interface.
Dual interfaces enable early and late binding to interface members by COM
clients. At design time and during testing, you might find it useful to set the
class interface to dual. For a managed class and its base classes that will
never be modified, this option is also acceptable. In all other cases, avoid
setting the class interface to dual.

Module 15 (Optional): Interoperating Between Managed and Unmanaged Code 55


Review
! Integration Services
! Platform Invoke
! Calling COM Components from Managed Code
! Calling .NET Objects from COM Objects

*****************************ILLEGAL FOR NON-TRAINER USE******************************
1. List the steps for calling an API function that is implemented in a DLL.
Declare the API function with the static and extern C# keywords,
attach the DllImport attribute to the function specifying the name of the
DLL that exports the unmanaged function, and optionally specify
marshaling information.


2. What is pinning?
Pinning is a technique in which data, in its current memory location, is
temporarily locked to keep it from being relocated by the common
language runtimes garbage collector.


3. List the tasks that are performed when the Type Library Importer is run on a
type library file.
The Type Library Importer converts unmanaged COM coclasses to C#
classes with a constructor (that does not have parameters) and no other
methods, converts unmanaged COM vtable interfaces to C# interfaces,
and converts unmanaged COM structures to C# structures with public
fields.


4. Which attribute must you use to suppress runtime security checks that are
preformed when managed code calls unmanaged code?
SuppressUnmanagedCodeSecurityAttribute


Topic Objective
To reinforce module
objectives by reviewing
key points.
Lead-in
The review questions cover
some of the key concepts
taught in the module.
56 Module 15 (Optional): Interoperating Between Managed and Unmanaged Code


5. List some best practices when using .NET code from COM clients.
Define an explicit interface for COM clients to use rather than
generating the class interface, and avoid caching dispatch identifiers
(DISPIDs).


6. Which must you use to prevent the class interface from being generated?
Set the Value property of the ClassInterfaceAttribute to
ClassInterfaceType.None













Contents
Overview 1
Overview of ADO.NET 2
Connecting to a Data Source 10
Accessing Data with DataSets 12
Using Stored Procedures 26
Lab 16: Using ADO.NET to Access Data 34
Accessing Data with DataReaders 42
Binding to XML Data 50
Review 56

Module 16 (Optional):
Using Microsoft
ADO.NET to Access
Data



Information in this document, including URL and other Internet Web site references, is subject to
change without notice. Unless otherwise noted, the example companies, organizations, products,
domain names, e-mail addresses, logos, people, places, and events depicted herein are fictitious,
and no association with any real company, organization, product, domain name, e-mail address,
logo, person, places or events is intended or should be inferred. Complying with all applicable
copyright laws is the responsibility of the user. Without limiting the rights under copyright, no
part of this document may be reproduced, stored in or introduced into a retrieval system, or
transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or
otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual
property rights covering subject matter in this document. Except as expressly provided in any
written license agreement from Microsoft, the furnishing of this document does not give you any
license to these patents, trademarks, copyrights, or other intellectual property.

2001-2002 Microsoft Corporation. All rights reserved.

Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint,
Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and
Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A.
and/or other countries.

The names of actual companies and products mentioned herein may be the trademarks of their
respective owners.


Module 16 (Optional): Using Microsoft ADO.NET to Access Data iii


Instructor Notes
This module focuses on using ADO.NET to access data from various data
sources.
After completing this module, students will be able to:
! Describe the ADO.NET object model.
! Connect to a data source by using ADO.NET.
! Retrieve data from a database by using DataReaders and DataSets.
! Display the data from a database on the client by using DataGrid controls.
! Use stored procedures to read data from a data source.
! Read data from an XML file into DataSets.

Materials and Preparation
This section provides the materials and preparation tasks that you need to teach
this module.
Required Materials
To teach this module, you need the Microsoft PowerPoint file 2349B_16.ppt.
Preparation Tasks
To prepare for this module, you should:
! Read all of the materials for this module.
! Complete all of the demonstrations.
! Complete the lab.
! Go through the animation.

Presentation:
120 Minutes

Lab:
60 Minutes
iv Module 16 (Optional): Using Microsoft ADO.NET to Access Data


Multimedia Presentation
This section provides multimedia presentation procedures that do not fit in the
margin notes or are not appropriate for the student notes.
Using ADO.NET to Access Data
! To present the animation
Action Say this

Start animation There are two ways to access data from a database by using
ADO.NET: by using a DataSet or by using a DataReader.
This animation demonstrates how these two methods work
and highlights their differences.
Click Start
Click DataSet The DataSet method is a disconnected way to access data
from a database.
In this method, when a user requests data from a database, the
DataAdapter object is used to create a DataSet, which is
basically a collection of data tables from the database that
also retains the relationships between these tables. Notice
that, after a DataSet is populated, it is disconnected from the
database.
To display the data from the DataSet, you set up a DataView
for the desired table. The DataView is then bound to a list-
bound control for displaying purposes. You can use any of
the three list-bound controls, DataGrid, Repeater, or
DataList, to display data.
The data in the list-bound control is then displayed on the
client.
An important point to make here is that the use of a
DataView to display data is necessary only in ASP.NET
Beta 1. From the Beta 2 version onward, you can directly
bind the DataSet to a list-bound control.
Click DataReader This method is similar to the Microsoft ActiveX Data
Objects (ADO) way of accessing data by using recordsets.
In this method, when a user requests data from a database, the
Command object retrieves the data into a DataReader. A
DataReader is a read-only/forward-only view of the data. A
DataReader works similarly to a Recordset in ADO,
allowing you to simply loop through the records. Like the
ADO Recordset, the DataReader is connected to the
database. You must explicitly close the connection when you
are finished reading data.

Module 16 (Optional): Using Microsoft ADO.NET to Access Data v


Module Strategy
Use the following strategy to present this module:
! Overview of ADO.NET
This section provides students with an overview of ADO.NET. The section
begins a description of the objects used when connecting to a database both
with a DataReader and a DataSet. Point out to students that there are
Microsoft SQL Server

and ADO versions of many of these objects. After


describing the process of accessing data through a DataReader and a
DataSet, show the animation. Because students may be familiar with ADO,
this may be an ideal time to discuss some of the main differences between
ADO and ADO.NET.
When talking about using namespaces, explain their significance to the
students.
! Connecting to a Data Source
From this point onward, students will actually start working with
ADO.NET. Tell them that all of the examples in this module use
SqlConnection objects rather than OleDbConnection objects. Direct the
students to the Microsoft .NET Framework software development kit (SDK)
documentation for more information.
! Accessing Data with DataSets
ADO.NET provides two ways to access data, the DataSet and the
DataReader. This section focuses on accessing data by using the DataSet.
The DataSet represents a new concept, so spend additional time on this
section. The demonstrations actually show every aspect of data access with
ADO.NET. Go through the demonstrations carefully, and make sure that the
students understand the details.
! Using Stored Procedures
Most students who have worked with a SQL Server database and ADO will
have experience with using stored procedures. This section provides the
students with information about how to use stored procedures and
parameterized stored procedures with ADO.NET.
! Lab16: Using ADO.NET to Access Data
The lab for this module is encountered in the middle of the module. This is
because the module is long and also because the lab does not use material
from the last two sections in the module.
vi Module 16 (Optional): Using Microsoft ADO.NET to Access Data


! Accessing Data with DataReaders
This section focuses on accessing data by using a DataReader. Point out to
the students that, when they use a DataReader, the database connection is
always open. When they are finished reading data, they must explicitly close
the connection.
! Binding to XML Data
XML is fast emerging as the most popular language for exchanging data.
This section provides students with information on how to read XML data
by using ADO.NET.
Most students will already know about XML documents. However, for
students who are not familiar with XML, it will be useful to show an
example of an XML document and how it is displayed on the client.

Module 16 (Optional): Using Microsoft ADO.NET to Access Data 1


Overview
! Overview of ADO.NET
! Connecting to a Data Source
! Accessing Data with DataSets
! Using Stored Procedures
! Accessing Data with DataReaders
! Binding to XML Data

*****************************ILLEGAL FOR NON-TRAINER USE******************************
ADO.NET, offers a rich suite of data handling and data binding functions for
manipulating all types of data. ADO.NET is an evolution of the ADO data
access model that directly addresses user requirements for developing scalable
applications. It was designed specifically for the Web with scalability,
statelessness, and XML in mind.
After completing this module, you will be able to:
! Describe the ADO.NET object model.
! Connect to a data source by using ADO.NET.
! Retrieve data from a database by using DataReaders and DataSets.
! Display the data from a database on the client by DataGrid controls.
! Use stored procedures.
! Read data from an XML file into DataSets.

Topic Objective
To provide an overview of
the module topics and
objectives.
Lead-in
In this module, you will learn
about the data binding
features in ASP.NET.
2 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


" "" " Overview of ADO.NET
! The ADO.NET Object Model
! RecordSets vs. DataSets
! Using Namespaces

*****************************ILLEGAL FOR NON-TRAINER USE******************************
ADO.NET is not a revision of Microsoft ActiveX Data Objects (ADO), but a
new way to manipulate data that is based on disconnected data and XML.
Although ADO is an important data access tool it is connected by default, relies
on an OLE DB provider to access data, and it is entirely Component Object
Model (COM)-based.
ADO.NET has been designed to work with disconnected datasets. Disconnected
datasets reduce network traffic.
ADO.NET uses XML as the universal transmission format. This guarantees
interoperability as long as the receiving component runs on a platform where an
XML parser is available. When the transmission occurs through XML, it is no
longer necessary that the receiver be a COM object. The receiving component
has no architectural restrictions whatsoever. Any software component can share
ADO.NET data, as long as it uses the same XML schema for the format of the
transmitted data.
In this section, you will learn about ADO.NET. You will learn about the new
and modified objects in ADO.NET. You will also learn about some of the new
namespaces.
Topic Objective
To introduce the topics
included in this section.
Lead-in
ASP.NET offers a new
means to retrieve data with
the introduction of
ADO.NET.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 3


The ADO.NET Object Model
DataAdapter
Connection
Database Database
Command
.ASPX Page
List-Bound
Control
List-Bound
Control
DataReader
Company:
Northwind Traders
Company:
Northwind Traders
.ASPX Page
DataView
DataView
DataSet

*****************************ILLEGAL FOR NON-TRAINER USE******************************
ADO.NET evolved from the ADO data access model. By using ADO.NET, you
can develop applications that are robust and scalable, and that can use XML.
ADO.NET has some of the same objects as ADO (like the Connection and
Command objects), and introduces new objects, such as the Dataset,
DataReader, and DataAdapter.
Connection Objects
Connection objects are used to talk to databases. They have properties, such as
DataSource, UserID, and Password, which are needed to access a particular
DataSource. Commands travel over connections, and result sets are returned in
the form of streams that can be read by DataReaders or pushed into DataSet
objects.
There are two kinds of connection objects in ADO.NET: SqlConnection and
OleDbConnection.
Command Objects
Command objects contain the information that is submitted to a database. A
command can be a stored procedure call, an update statement, or a statement
that returns results. You can also use input and output parameters and return
values. In ADO.NET, you can use two kinds of command objects:
SqlCommand and OleDbCommand.
Topic Objective
To describe the ADO.NET
object model.
Lead-in
ADO.NET includes some of
the same objects as ADO
(like Connection and
Command), and introduces
new objects, such as
DataSets, DataReaders
and DataAdapters.
Delivery Tip
Point out that there are ADO
and SQL Server versions of
the Connection,
Command, DataAdapter,
and DataReader objects.
4 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


DataReader Objects
A DataReader is somewhat synonymous with a read-only/forward-only view
of the data. The DataReader API supports flat as well as hierarchical data. A
DataReader object is returned after executing a command against a database. It
works similarly to a recordset in ADO; however the format of the returned
DataReader object is different from a recordset. For example, you may use the
DataReader to show the results of a search list in a Web page.
ADO.NET includes two types of DataReader objects: the SqlDataReader for
Microsoft SQL Server

version 7.0 (or later) data, and the OleDbDataReader


for ADO data. The DataReader object is database-specific. The behavior of the
SqlDataReader may differ from the behavior of the OleDbDataReader and
additional DataReader objects that are introduced in the future.
You use the OleDbCommand and SqlCommand objects and the
ExecuteReader method to transfer data into a DataReader.
DataSet Objects
The DataSet object is similar to the ADO Recordset object, but more
powerful, and with one other important distinction: the DataSet is always
disconnected. The DataSet provides a rich object model to work with when
passing data between various components of an enterprise solution. The
DataSet object represents a cache of data, with database-like behavior. It
contains tables, columns, relationships, constraints, and data. Data coming from
a database, an XML file, code, or user input can be entered into DataSet objects
and converted into files, forms, or databases. The behavior of a DataSet is
completely consistent regardless of the underlying database, SQL Server or
OLE DB. As changes are made to the DataSet, they are tracked in a way
similar to the way changes are tracked in a word processing document.
The DataSet object has a collection of DataTable objects. A DataTable
represents one table of in-memory data. It contains a collection of columns that
represents the table's schema. A DataTable also contains a collection of rows,
representing the data contained in the table.
You use the OleDbDataAdapter and SqlDataAdapter objects and the Fill
method to get data into a DataSet.
DataView Objects
A DataView enables you to create different views of the data stored in a
DataTable, a capability that is often used in data-binding applications. By
using a DataView, you can expose the data in a table with different sort orders,
and you can filter the data by row state or based on a filter expression.
A DataView provides a dynamic view of data whose content, ordering, and
membership reflect changes to the underlying DataTable as they occur. This is
different from the Select method of the DataTable, which returns a DataRow
array from a table per a particular filter and/or sort order, and whose content
reflects changes to the underlying table, but whose membership and ordering
remain static. The dynamic capabilities of the DataView make it ideal for data-
binding applications.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 5


DataAdapter Object
While the DataSet object provides a tool for in-memory data storage, you need
another tool to create and initialize the various tables. This tool is the
DataAdapter object. It represents a centralized console that hides the details of
working with connections and commands. The DataAdapter object allows for
the retrieval and saving of data between a DataSet object and the source data
store. It is responsible for pulling out data from the physical store and pushing it
into data tables and relations. The DataAdapter object is also responsible for
transmitting any update, insertion, or deletion to the physical database. You can
use four command objects to make any updates: UpdateCommand,
InsertCommand, DeleteCommand, and SelectCommand.
The DataAdapter object exists in two forms: SqlDataAdapter objects and
OleDbDataAdapter objects. The data source is SQL Server for
SqlDataAdapter objects and any other OLE DB provider for
OleDbDataAdapter objects.
The following illustration shows the use of a SQLDataAdapter object to
transfer data between a SQL Server database and a DataSet object.

6 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


Multimedia: Using ADO.NET to Access Data

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this animation, you will learn how to access data by using ADO.NET and
how you can display that data in an ASP.NET page. To view the animation,
open the file 2349B_16A001.htm file from the Media folder.
Topic Objective
To provide a high-level
overview of how to access
data by using ADO.NET.
Lead-in
In this animation, you will
learn about how ADO.NET
accesses data and how you
can display that data in an
ASP.NET page.
Delivery Tip
Tell students that they can
view the animation again
later for themselves by
opening the
2349B_mod3.htm file from
the Media folder.

For details about how to run
and describe the animation,
see the Multimedia
Presentation section in the
Instructor Notes for this
module.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 7


Recordsets vs. DataSets
Transmit XML file COM marshalling Transmitting data
Disconnected Connected or
disconnected
Data connections
Navigate via
relationships
Move row-by-row Moving through data
Includes relationships Based on join Relationships
Multiple tables One table Number of tables
DataSet Recordset Feature

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In ADO, the in-memory representation of database data is the recordset. In
ADO.NET, it is the DataSet. The DataSet contains a collection of tables and
knowledge of relationships between those tables. Each table contains a
collection of columns. These objects represent the schema of the DataSet. Each
table can then have multiple rows, representing the data held by the DataSet.
These rows track their original state along with their current state, so that the
DataSet tracks what kinds of changes have occurred. Additionally, the DataSet
provides persistence and de-persistence through XML.
There are important differences between recordsets and DataSets, which are
highlighted in the following table and detailed in the text that follows.
Feature Recordset DataSet

Number of tables One table Multiple tables
Relationships Based on join Includes relationships
Moving through data Move row-by-row Navigate via relationships
Data connections Connected or disconnected Disconnected
Transmitting data COM marshalling Transmit XML file

Number of Tables
An ADO recordset looks like a single table. If a recordset is to contain data
from multiple database tables, it must use a JOIN query, which assembles the
data from the various database tables into a single result table.
In contrast, an ADO.NET DataSet is a collection of one or more tables. The
tables within a data set are called data tables; specifically, they are DataTable
objects.
Topic Objective
To highlight the differences
between DataSets and
Recordsets.
Lead-in
In ADO, you used
Recordsets.
8 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


Relationships
Typically, a DataSet also contains relationships. A relationship within a
DataSet is analogous to a foreign-key relationship in a database. In ADO.NET,
a DataRelation represents the relationship.
Moving Through Data
In ADO.NET, the methods you use to read or modify data differ from the
programming methods you use in ADO in the following ways:
! In ADO, you scan sequentially through the rows of the recordset.
! In ADO.NET, rows are represented as collections, so you can loop through
a table as you would through any collection or access particular rows
through ordinal or primary key index. DataRelation objects maintain
information about master and detail records and provide a method that
allows you to get records related to the one you are working with. For
example, starting from the row of the Investor table for "Jose Lugo", you
can navigate to the set of rows of the Purchase table that describes the
purchases made by Jose Lugo.

Data Connections
In ADO.NET, the DataSet provides disconnected access to database data. In
ADO, the recordset can provide disconnected access, but is typically used to
provide connected access.
Transmitting Data
To transmit an ADO disconnected recordset from one component to another,
you use COM marshalling. To transmit an ADO.NET data set, you simply
transmit an XML file. Because components exchange ADO.NET datasets by
using XML, firewalls can allow datasets to pass.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 9


Using Namespaces
! Namespaces Used with ADO.NET Include:
# System.Data
# System.Data.OleDb
# System.Data.SqlClient
# System.Data.SqlTypes

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The Microsoft .NET Framework is an object-oriented system. When using
specific parts of the framework, you must include references to the appropriate
namespace.
When using ADO.NET from either Microsoft Visual Basic or Microsoft
Visual

C#

, you must reference the System.Data namespace, plus either the


System.Data.OleDb or System.Data.SqlClient namespace, depending on the
data source you choose to use. System.Data provides the code facilities, while
System.Data.OleDb and System.Data.SqlClient are the namespaces for the
two managed providers. The System.Data.Common namespace contains
classes that are shared by the .NET data providers. The System.Data.SqlTypes
namespace provides classes for native data types within SQL Server. These
classes provide a safer, faster alternative to other data types.
The following table summarizes the list of available namespaces with
ADO.NET.
Namespace Contains

System.Data Base objects and types for ADO.NET
System.Data.OleDb Managed OLE DB data store objects
System.Data.SqlClient SQL Server specific implementations of ADO.NET objects
System.Data.Common Classes shared by the .NET data providers
System.Data.SqlTypes SQL data types

Topic Objective
To describe the new
namespaces used with
ADO.NET.
Lead-in
In.NET, you must specify
the namespace that you
want to use.
10 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


Connecting to a Data Source
! Using SqlConnection
! Using OleDbConnection
SqlConnection mySqlConnection = new
SqlConnection("server=(local)\\NetSDK;!
Trusted_Connection=yes;database=northwind");
SqlConnection mySqlConnection = new
SqlConnection("server=(local)\\NetSDK;!
Trusted_Connection=yes;database=northwind");
OleDbConnection myOleDbConnection = new
OleDbConnection("server=(local)\\NetSDK; !
Trusted_Connection=yes;database=northwind; !
provider=sqloledb");
OleDbConnection myOleDbConnection = new
OleDbConnection("server=(local)\\NetSDK; !
Trusted_Connection=yes;database=northwind; !
provider=sqloledb");

*****************************ILLEGAL FOR NON-TRAINER USE******************************
When you connect to a database, there are two routes that you can take: use
OLE DB or use the native SQL provider. The native SQL provider is faster, but
you must use Microsoft SQL Server as your database. If you are using
Microsoft Access, Microsoft Excel, a comma-delimited file, or some other data
source, you must use the OLE DB provider. You can use the OLE DB provider
with a Microsoft SQL Server database; however, it is not as fast as using the
native SQL provider.
The Connection object defines how to connect to a specific data store. The
.NET Framework provides two Connection objects: SqlConnection and
OleDbConnection. The SqlConnection object defines how to connect to
SQL Server databases and the OleDbConnection object allows you to establish
a connection to a database through an OLE DB provider.
Using SqlConnection
The following code illustrates how to create and open a connection to a
Microsoft SQL Server database by using the SqlConnection object.
SqlConnection mySqlConnection = new
SqlConnection("server=(local)\\NetSDK;!
Trusted_Connection=yes;database=northwind");

Embedded usernames/passwords in database connection strings are
inherently unsecure, and should only be used on platforms, such as Microsoft
Windows 9x, that do not support Integrated Security.
For more information about Integrated Security, see the .NET Framework SDK
documentation.

Topic Objective
To describe how to connect
to a data source by using
ADO.NET.
Lead-in
Connecting to a data source
is the first step in data
access.
Delivery Tip
Point out to students that in
ADO.NET you do not
always need to explicitly
open and close the
connection to the database.
Warning
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 11


Using OleDbConnection
For the OLE DB Managed Provider, the connection string format is quite
similar to the connection string format used in OLE DB.
The following code illustrates how to create and open a connection to a
Microsoft SQL Server database by using OleDbConnection.
OleDbConnection myOleDbConnection = new
OleDbConnection("server=(local)\\NetSDK;!
Trusted_Connection=yes;database=northwind;!
provider=sqloledb");

The examples in this module, use SqlConnection objects. Implementation is
slightly different for using OleDbConnection objects. For more information
about using OleDbConnection objects, search for OleDbConnection in the
Microsoft .NET Framework SDK documentation.
Choosing a .NET Data Provider
Depending on the design and data source for your application, your choice of
.NET data provider can improve the performance, capability, and integrity of
your application. The following table discusses the advantages and limitations
of each .NET data provider.
Provider Notes

SQL Server
.NET Data Provider
Recommended for middle-tier applications that use Microsoft
SQL Server 7.0 or later.
Recommended for single-tier applications that use Microsoft
Data Engine (MSDE) or Microsoft SQL Server 7.0 or later.
Recommended over use of the OLE DB Provider for
SQL Server (SQLOLEDB) with the OLE DB .NET Data
Provider.
For Microsoft SQL Server version 6.5 and earlier, you must
use the OLE DB Provider for SQL Server with the OLE DB
.NET Data Provider.
OLE DB .NET
Data Provider
Recommended for middle-tier applications that use Microsoft
SQL Server 6.5 or earlier, or any OLE DB provider that
supports the OLE DB interfaces listed in OLE DB Interfaces
Used by the OLE DB .NET Data Provider (OLE DB 2.5
interfaces are not required).
For Microsoft SQL Server 7.0 or later, the SQL Server .NET
Data Provider is recommended.
Recommended for single-tier applications that use Microsoft
Access databases. Use of a Microsoft Access database for a
middle-tier application is not recommended.
Support for the OLE DB Provider for ODBC (MSDASQL) is
disabled. For access to Open Database Connectivity (ODBC)
data sources, an ODBC .NET Data Provider is available as a
separate download at http://msdn.microsoft.com/downloads.

Delivery Tip
Stress to students that all
examples in the module and
lab will use SqlConnection
objects.
12 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


" "" " Accessing Data with DataSets
! Using DataSets to Read Data
! Storing Multiple Tables in a DataSet
! Using DataViews
! Updating a Database from a DataSet
! Displaying Data in the DataGrid Control

*****************************ILLEGAL FOR NON-TRAINER USE******************************
ADO.NET provides two ways to access data, DataSets and DataReaders.
In this section, you will learn how to access data by using DataSets. You will
also learn about DataViews and displaying data in DataGrid controls.
Topic Objective
To introduce the topics
included in the section.
Lead-in
After connecting to a
database, the next step is to
access data from it.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 13


Using DataSets to Read Data
! Create the Database Connection
! Store the Query in a SqlDataAdapter
! Create and Populate the DataSet with DataTables
SqlDataAdapter mySqlDataAdapter =
new SqlDataAdapter(
"select * from customers", mySqlConnection);
SqlDataAdapter mySqlDataAdapter =
new SqlDataAdapter(
"select * from customers", mySqlConnection);
DataSet myDataSet = new DataSet();
mySqlDataAdapter.Fill(myDataSet,"Customers");
DataSet myDataSet = new DataSet();
mySqlDataAdapter.Fill(myDataSet,"Customers");

*****************************ILLEGAL FOR NON-TRAINER USE******************************
After you establish a connection to a database, you can access its data.
ADO.NET provides multiple ways to access data.
Using DataSets
The DataSet object is the centerpiece of ADO.NET. It represents a complete
set of data, including multiple, related tables, and constraints.
Although a DataSet stores data, you need DataAdapter objects to create and
initialize the various tables. You also need the Fill method to populate a
DataSet with the results from a query.
The Fill method takes two parameters: a DataSet instance and a string. The
DataSet instance represents the DataSet to be filled, and the string identifies
the DataTable that will be created inside the DataSet. A DataSet can contain
many DataTables. You use the string supplied to the Fill method to reference
the DataTable after it is created.
Topic Objective
To describe how to retrieve
data from a database by
using DataSets.
Lead-in
Now that we can establish a
connection, we need to be
able to execute statements
against the database to
retrieve data.
Delivery Tip
Explain the parameters to
the Fill method. The second
parameter is the name that
will be given to the
DataTable created in the
DataSet. You use this name
when reading data from the
DataSet.
14 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


The following code example illustrates how to create a SqlDataAdapter object
that contains the query statement. The Fill method then populates the DataSet
with the results from the query.
// Create a connection
SqlConnection mySqlConnection = new
SqlConnection("server=(local)\\NetSDK;!
Trusted_Connection=yes;database=northwind");
// Create the DataAdapter
SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter(
"select * from customers", mySqlConnection);
// Create and populate the DataSet
DataSet myDataSet = new DataSet();
mySqlDataAdapter.Fill(myDataSet,"Customers");

Displaying Data In A DataSet
Because the data is stored in a collection of rows in the table, you can easily use
a foreach statement to iterate through the rows:
foreach (DataRow myDataRow in
myDataSet.Tables["Customers"].Rows)
{
Console.WriteLine(
" CustomerID: {0}",
myDataRow["CustomerID"].ToString);
}

Typed DataSet
Along with late bound access to values through weakly typed variables, the
DataSet provides access to data through a strongly typed metaphor. By using
user-friendly names and strongly typed variables, you can access tables and
columns that are part of the DataSet. You can also transport a strongly typed
DataSet by using an XML Web service.
A typed DataSet is a class that derives from a DataSet. As such, it inherits all
of the methods, events, and properties of a DataSet. Additionally, a typed
DataSet provides strongly typed methods, events, and properties. This means
that you can access tables and columns by name, instead of using collection-
based methods. Aside from the improved readability of the code, a typed
DataSet also allows the Microsoft Visual Studio .NET code editor to
automatically complete lines as you type.
Additionally, a strongly typed DataSet provides access to the correct types for
values at compile time. With a strongly typed DataSet, type mismatch errors
are caught when the code is compiled rather than at run time.
Using an XML Schema that complies with the XML Schema definition
language (XSD) standard, you can generate a strongly typed DataSet by using
the XSD.exe tool that is provided with the .NET Framework SDK. The use of
this tool is outside the scope of this module.
You will see how to easily create and use a typed DataSet using Visual Studio
.NET in this modules lab.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 15


Storing Multiple Tables in a DataSet
! Add the First Table
! Add the Subsequent Table(s)
SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter(
"select * from customers",mySqlConnection);
DataSet myDataSet = new DataSet();
mySqlDataAdapter.Fill(myDataSet,"Customers");
SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter(
"select * from customers",mySqlConnection);
DataSet myDataSet = new DataSet();
mySqlDataAdapter.Fill(myDataSet,"Customers");
mySqlDataAdapter.SelectCommand.CommandText =
"select * from orders";
mySqlDataAdapter.Fill(myDataSet,"Orders");
mySqlDataAdapter.SelectCommand.CommandText =
"select * from orders";
mySqlDataAdapter.Fill(myDataSet,"Orders");
Orders
Customers
DataSet:
Data Tables

*****************************ILLEGAL FOR NON-TRAINER USE******************************
A DataSet can contain multiple tables. You can retrieve multiple tables from a
database and store them in a DataSet.

You can store tables from different databases in the same DataSet.

! To retrieve and store multiple tables in a DataSet
1. Create and populate the first DataSet.
DataSet myDataSet = new DataSet();
string strSql ="select * from customers";
SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter(
strSql, mySqlConnection);
mySqlDataAdapter.Fill(myDataSet,"Customers");

2. Reset the SelectCommand, InsertCommand, or DeleteCommand
property of the DataAdapter object to a new command string.
strSql = "select * from orders";
mySqlDataAdapter.SelectCommand.CommandText = strSql;

3. Call Fill again.
mySqlDataAdapter.Fill(myDataSet,"Orders");


Topic Objective
To explain how to retrieve
and store multiple tables in
a DataSet.
Lead-in
Unlike a disconnected
recordset, a DataSet can
hold more than one table.
Note
Delivery Tip
Tell students that they can
create new relationships
between the tables in a
DataSet.
16 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


The following code shows how you can add two tables from two different
queries, one for authors and the other for titles, to the same DataSet.
// create connection to database
string strConn =
"server=(local)\\NetSDK;!
Trusted_Connection=yes;database=northwind";
SqlConnection mySqlConnection = new SqlConnection(strConn);

string strSql = "select * from customers"
SqlDataAdapter mySqlDataAdapter =
new SqlDataAdapter(strSql, mySqlConnection);
//fill DataSet with first set of data
DataSet myDataSet = new DataSet();
mySqlDataAdapter.Fill(myDataSet,"Customers");
//fill DataSet with first set of data
strSql = "select * from orders"
mySqlDataAdapter.SelectCommand.CommandText = strSql;
mySqlDataAdapter.Fill(myDataSet,"Orders");

Module 16 (Optional): Using Microsoft ADO.NET to Access Data 17


Using DataViews
! DataViews Can be Customized to Present a Subset of
Data from a DataTable
! The DefaultView Property Returns the Default DataView
for the Table
! Setting Up Different Views
myDataView.Sort = "Country";
myDataView.RowFilter = "Country = 'Argentina'";
myDataView.Sort = "Country";
myDataView.RowFilter = "Country = 'Argentina'";
DataView myDataView =
myDataSet.Tables["Customers"].DefaultView;
DataView myDataView =
myDataSet.Tables["Customers"].DefaultView;

*****************************ILLEGAL FOR NON-TRAINER USE******************************
There are many ways to filter data. One way is to filter data at the database
command level, by using a where clause in your query. A second way is to
filter the data after it is in the DataSet. This topic covers filtering in the
DataSet.
Filtering and Sorting with DataViews
To display the data held in a DataSet, you can use a DataView.
DataViews can be customized to present a subset of data from the DataTable.
This capability allows you to have two controls bound to the same DataTable,
but showing different versions of the data. For example, one control may be
bound to a DataView that shows all the rows in the table, and a second may be
configured to display only the rows that have been deleted from the DataTable.
Each DataTable in a DataSet has a DefaultView property, which returns the
default view for the table. You can access the default DataView of a DataSet
as follows:
DataView myDataView =
myDataSet.Tables["Customers"].DefaultView;

The DataSet object contains a Tables collection. You reference the
DataTable you are interested in by name.

You can sort the data. For example, you can sort the customers by country.
myDataView.Sort = "Country";

Topic Objective
To explain the role of
DataViews in accessing
data from a database by
using DataSets.
Lead-in
To display data in a
DataSet, you can use a
DataView.
Note
18 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


Setting up a Different View of a DataSet
You can also create a view of a subset of the data in a DataTable. For example,
you can set the RowFilter property on a DataView to retrieve only customers
from Argentina.
myDataView.RowFilter = "Country = 'Argentina'";

For more information about the properties of the DataView object, see the
Microsoft .NET Framework SDK documentation.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 19


Updating a Database From a DataSet
! SQLCommandBuilder generates Update command
! Set the MissingSchemaAction property
! Add a row
! To submit the data
mySqlDataAdapter.MissingSchemaAction =
MissingSchemaAction.AddWithKey;
mySqlDataAdapter.MissingSchemaAction =
MissingSchemaAction.AddWithKey;
SqlCommandBuilder mySqlCommandBuilder = new
SqlCommandBuilder(mySqlDataAdapter);
SqlCommandBuilder mySqlCommandBuilder = new
SqlCommandBuilder(mySqlDataAdapter);
DataRow myDataRow =
myDataSet.Tables["Customers"].NewRow();
myDataRow["CustomerId"] = "NewID";
// ...
myDataSet.Tables["Customers"].Rows.Add(myDataRow);
DataRow myDataRow =
myDataSet.Tables["Customers"].NewRow();
myDataRow["CustomerId"] = "NewID";
// ...
myDataSet.Tables["Customers"].Rows.Add(myDataRow);
mySqlDataAdapter.Update(myDataSet, "Customers");
mySqlDataAdapter.Update(myDataSet, "Customers");

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This topic illustrates how to update data in a database by using a DataSet. You
can also insert, update, and delete data in a database directly by using a
SqlCommand as described in the .NET Framework SDK documentation.
After a DataSet is loaded, you can modify the data, and the DataSet will track
the changes. The DataSet may be considered an in-memory cache of data that
is retrieved from a database. In this topic, you will see how to use the Add
method on the DataTable to add new data to a DataSet. The Add method takes
either an array of the expected data columns, or a DataRow.
To load the DataSet from the database:
SqlConnection myConnection = new SqlConnection(
"server=(local)\\NetSDK;!
Trusted_Connection=yes;database=northwind");
SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter(
"Select * from Customers",
myConnection);
DataSet myDataSet = new DataSet();

Before you can submit the update back to the database, you must set up the
InsertCommand, UpdateCommand, and DeleteCommand to reconcile the
changes to the database. For limited scenarios you can use the
SqlCommandBuilder to automatically generate those for you:
SqlCommandBuilder mySqlCommandBuilder = new
SqlCommandBuilder(mySqlDataAdapter);

Topic Objective
To show how to update data
in a Database from a
DataSet.
Lead-in
This topic illustrates how to
update data in a database
using a DataSet. You can
also insert, update, and
delete data in a database
directly using a
SqlCommand as described
in the .NET Framework
SDK.
20 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


Because Fill will not cause primary key and unique key information to be
retrieved unless AddWithKey is specified, you must set the
MissingSchemaAction property to AddWithKey:
mySqlDataAdapter.MissingSchemaAction =
MissingSchemaAction.AddWithKey;

mySqlDataAdapter.Fill(myDataSet, "Customers");

DataRow myDataRow;
myDataRow = myDataSet.Tables["Customers"].NewRow();

The DataTable must return a DataRow through the NewRow method. The
method returns a DataRow object with the appropriate schema of the
DataTable. The new DataRow is independent of the table until it is added to
the RowsCollection.
myDataRow["CustomerId"] = "NewID";
myDataRow["ContactName"] = "New Name";
myDataRow["CompanyName"] = "New Company Name";

myDataSet.Tables["Customers"].Rows.Add(myDataRow);

To submit the data from the DataSet into the database, use the Update method
on the SqlDataAdapter.
mySqlDataAdapter.Update(myDataSet, "Customers");

You can change data in a DataRow by accessing the DataRow. You can use
the index of the row in the RowsCollection that is accessed through the Rows
property:
myDataSet.Tables["Customers"].Rows[0]["ContactName"]="Peach";

You can also access a specific row by the primary key value:
DataRow myDataRow1 =
myDataSet.Tables["Customers"].Rows.Find("ALFKI");
myDataRow1["ContactName"]="Peach";

In the preceding example, "ALFKI" is the value of the primary key
"CustomerID" in the Customers table. When using the SqlDataAdapter, the
key is established from the database. You can also set the key through the
PrimaryKey property if you are not using the database.
Use the Delete method to remove the Row. Note that a logical deletion occurs
in the DataSet, which only results in a hard deletion after the DataSet is
updated to the database. Similarly, you can use RejectChanges on the DataSet,
in which case the Row is restored.
myDataSet.Tables["Customers"].Rows[0].Delete();

The original and new values are maintained in the row. The RowChanging
event allows you to access both original and new values to decide whether you
want the edit to proceed. Because original and new values are maintained, you
can establish scenarios such as optimistic locking and key changes.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 21


Demonstration: Accessing Data with DataSets

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this demonstration, you will see how to access data with a DataSet.
Run the Visual Studio .NET Accessing Data with DataSets project from the
following location:
<install folder>\Democode\Mod16\Demo16.1
The code reads in data from database tables, creates views, and updates the
database.

You can examine the database tables and stored procedures by using the
Server Explorer window Data Connections entries.

Topic Objective
To demonstrate how to
open a database, create a
DataSet with multiple tables,
and create views on the
data.
Lead-in
In this demonstration, you
will see how to access data
with a DataSet.
Tip
22 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


Running this program produces output that is similar to the following:
Opened Connection to!
server=(local)\NetSDK;!
Trusted_Connection=yes;database=northwind
Total Number of Regions: 4
Total Number of Customers: 91

Regions:
ID: 1 Description: Eastern
ID: 2 Description: Western
ID: 3 Description: Northern
ID: 4 Description: Southern

First 5 Customer IDs:
Customer ID: ALFKI
Customer ID: ANATR
Customer ID: ANTON
Customer ID: AROUT
Customer ID: BERGS

View sorted by country
First 5 Customers in the view:
Customer ID: CACTU Country: Argentina
Customer ID: OCEAN Country: Argentina
Customer ID: RANCH Country: Argentina
Customer ID: ERNSH Country: Austria
Customer ID: PICCO Country: Austria

Filtered by country equals Argentina
Customers with Country = Argentina, number of entries 3
ID: CACTU Country: Argentina
ID: OCEAN Country: Argentina
ID: RANCH Country: Argentina

Module 16 (Optional): Using Microsoft ADO.NET to Access Data 23


Displaying Data in the DataGrid Control
! Create a Windows Forms DataGrid control
! Bind to a DataSet
! Using a custom view
dataGrid1 = new System.Windows.Forms.DataGrid();
dataGrid1 = new System.Windows.Forms.DataGrid();
dataGrid1.DataSource = myDataSet;
dataGrid1.DataMember = "Regions";
dataGrid1.DataSource = myDataSet;
dataGrid1.DataMember = "Regions";
DataView myDataView =
myDataSet.Tables["Customers"].DefaultView;
myDataView.RowFilter = "Country = 'Argentina'";
dataGrid2.DataSource = myDataView;
DataView myDataView =
myDataSet.Tables["Customers"].DefaultView;
myDataView.RowFilter = "Country = 'Argentina'";
dataGrid2.DataSource = myDataView;

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Displaying data from a data source is extremely simple and flexible in either
Windows Forms or ASP.NET. These application development environments
include a set of controls that perform the function of displaying data. You only
need to bind these controls to a data source.
To display data on the client, you can use any list-bound control, such as the
DataGrid.
Using the DataGrid Control
The DataGrid control is designed to produce output that resembles a
worksheet. In Visual Studio .NET, you can drag and drop a DataGrid from the
Toolbox onto the Design window. This generates code to create a DataGrid
object as follows:
dataGrid1 = new System.Windows.Forms.DataGrid();

To bind a DataSet to a DataGrid control, you first set the DataSource
property of the DataGrid to a DataTable, or DataVieweither the
DefaultView property of a DataSet or a custom DataView object.
To specify a DataTable, set the DataMember property of the DataGrid to the
name of the DataTable.
Topic Objective
To describe how to display
data on the client DataGrid
controls.
Lead-in
After you connect to a data
source and retrieve data
from it, the next step is to
display the data on the
client.
24 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


Example Binding to a DataSet
dataGrid1.DataSource = myDataSet;
dataGrid1.DataMember = "Regions";

Alternatively, you can use the Tables collection of the DataSet to assign the
DataTable directly to the DataSource of the DataGrid, as in the following
example:
dataGrid1.DataSource = myDataSet.Tables["Regions"];

If you want to display a different view of data in the DataGrid control, create a
new DataView object from the DataSet and bind that to the control. For
example, the following code will display only those customers in Argentina.
Example Using a Custom View
DataView myDataView =
myDataSet.Tables["Customers"].DefaultView;
myDataView.RowFilter = "Country = 'Argentina'";
dataGrid2.DataSource = myDataView;

Module 16 (Optional): Using Microsoft ADO.NET to Access Data 25


Demonstration: Displaying Data in a DataGrid

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this demonstration, you will see how to read data from a database into a
DataSet and then display it in a DataGrid control.
Run the Visual Studio .NET DataGrid project from the following location:
<install folder>\Democode\Mod16\Demo16.2
Running this program displays a button labeled Load Data and two DataGrid
controls. Clicking the button reads region and customer data from a database
and displays them in the grids. A DataView object is used to display only those
customers in Argentina.
Topic Objective
To introduce the Data Grid.
Lead-in
In this demonstration, you
will see how to create a
Windows Form that reads
data from a database and
displays it in a DataGrid
control.
26 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


" "" " Using Stored Procedures
! Calling Stored Procedures
! Passing Parameters
! Calling Action Stored Procedures

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In the past, data processing has been primarily connection-based. Now, in an
effort to make multi-tiered applications more efficient, data processing is
turning to a message-based approach that revolves around chunks of
information. In ADO.NET, this is accomplished by the DataAdapter object,
which provides a bridge to retrieve and save data between a DataSet object and
its source data store. The DataAdapter object accomplishes this by invoking
the appropriate SQL commands against the data store.
Both the SqlDataAdapter and the OleDbDataAdapter classes feature four
command objects, InsertCommand, UpdateCommand, DeleteCommand,
and SelectCommand. These objects provide the create, update, delete, and
read functions for a specific DataTable in the DataSet.
These command objects are used when you want to perform a number of
updates at the same time. Instead of having one stored procedure to do it all,
you can put SQL Statements in each object and call the Update method.
For more information about these command objects, see the .NET Framework
SDK documentation.
However, the easier and more commonly used method of updating data in a
database is to use stored procedures. You can use stored procedures to read and
modify data from a database. You can call stored procedures both from
DataAdapter and Command objects.
In this section, you will learn how to call stored procedures, pass parameterized
stored procedures, and call action stored procedures.
Topic Objective
To introduce the topics in
this section.
Lead-in
When working with data
from a data source, the
ability to use stored
procedures is important.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 27


Calling Stored Procedures
! Stored Procedures Provide Security for Database
! Set Up the DataAdapter
! Run the Stored Procedure and Store Returned Records
SqlDataAdapter mySqlDataAdapter = new
SqlDataAdapter();
mySqlDataAdapter.SelectCommand = new SqlCommand();
mySqlDataAdapter.SelectCommand.Connection =
mySqlConnection;
mySqlDataAdapter.SelectCommand.CommandText =
"GetProducts";
mySqlDataAdapter.SelectCommand.CommandType =
CommandType.StoredProcedure;
SqlDataAdapter mySqlDataAdapter = new
SqlDataAdapter();
mySqlDataAdapter.SelectCommand = new SqlCommand();
mySqlDataAdapter.SelectCommand.Connection =
mySqlConnection;
mySqlDataAdapter.SelectCommand.CommandText =
"GetProducts";
mySqlDataAdapter.SelectCommand.CommandType =
CommandType.StoredProcedure;
mySqlDataAdapter.Fill(myDataSet,"Products");
mySqlDataAdapter.Fill(myDataSet,"Products");

*****************************ILLEGAL FOR NON-TRAINER USE******************************
A stored procedure is a sequence of Transact-SQL (T-SQL) statements stored
on the database server. Stored procedures provide a level of security to a
database. The database designer can create stored procedures to retrieve and
modify data, and not allow developers access to the actual tables of the
database. In this way, the database designer can then change the structure of the
database without breaking applications that use it.
Though it is not always the case, stored procedures can return a set of records.
Stored procedures can encapsulate repetitive tasks and execute them efficiently.
When you call a stored procedure that returns a set of records, use a
DataAdapter and the Fill method. When you call a stored procedure that
performs some function on the database but does not return a set of records, use
a Command object and the ExecuteNonQuery method.
Topic Objective
To explain how to use
stored procedures to
retrieve data in a database.
Lead-in
Like ADO, ADO.NET allows
developers to use stored
procedures to modify data.
28 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


Calling a Select Stored Procedure
Using stored procedures in ADO.NET is similar to ADO. You create a
Command object and point it to the database connection. Next, you set the
CommandText property to the name of the stored procedure and the
CommandType property to CommandType.StoredProcedure.
The following is the GetProducts stored procedure. It returns a list of
ProductNames.
PROCEDURE GetProducts as select ProductName from Products

Using a Connection object named mySqlConnection, the following example
shows how to setup the SqlDataAdpator for the GetProducts stored
procedure:
SqlDataAdapter mySqlDataAdapter = new
SqlDataAdapter();
mySqlDataAdapter.SelectCommand = new SqlCommand();
mySqlDataAdapter.SelectCommand.Connection = mySqlConnection;
mySqlDataAdapter.SelectCommand.CommandText = "GetProducts";
mySqlDataAdapter.SelectCommand.CommandType =
CommandType.StoredProcedure;

You can directly set the connection and command text when creating the
SqlDataAdapter object:
SqlDataAdapter mySqlDataAdapter = new
SqlDataAdapter(
"GetProducts", mySqlConnection);

You then set the CommandType property before you call the Fill method.
mySqlDataAdapter.SelectCommand.CommandType =
CommandType.StoredProcedure;


To execute the stored procedure, call the Fill method of the
mySqlDataAdapter object. This fills a DataTable object with the returned
records of the stored procedure.
DataSet myDataSet = new DataSet();
mySqlDataAdapter.Fill(myDataSet,"Products");

After you have filled a DataTable with the results of a Select stored procedure,
you can bind it to a list-bound control to display the data. For example, to bind
to a Windows Forms DataGrid control named dataGrid1:
dataGrid1.DataSource =
myDataSet.Tables["Products"];

Note
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 29


Passing Parameters
! Create Parameter, Set Direction and Value, Add to the
Parameters Collection
! Run Stored Procedure
SqlParameter workParam = new
SqlParameter("@ProductName",SqlDbType.NChar, 40);
workParam.Direction =
ParameterDirection.Input; // Input is default
workParam.Value = textBox1.Text;
mySqlDataAdapter.SelectCommand.Parameters.Add(
workParam);
SqlParameter workParam = new
SqlParameter("@ProductName",SqlDbType.NChar, 40);
workParam.Direction =
ParameterDirection.Input; // Input is default
workParam.Value = textBox1.Text;
mySqlDataAdapter.SelectCommand.Parameters.Add(
workParam);
DataSet myDataSet = new DataSet();
mySqlDataAdapter.Fill(myDataSet,"ProductData");
DataSet myDataSet = new DataSet();
mySqlDataAdapter.Fill(myDataSet,"ProductData");

*****************************ILLEGAL FOR NON-TRAINER USE******************************
When using Microsoft SQL Server, or other procedure-based databases,
parameters can be used to pass and retrieve information from the database.
Using parameters in ADO.NET works just as it does in ADO. You can pass the
string in the command or use the parameters collection.
When using parameters, the names of the parameters added to the parameters
collection of the command must match the names of the parameter markers in
the stored procedure.
Parameters
Your application can pass specific data to a stored procedure by using
parameters. The following table describes the types of parameters that are
available to you.
Direction Use

Input Used by your application to send specific data values to a stored
procedure.
Output Used by a stored procedure to send specific values back to the calling
application.
InputOutput Used by a stored procedure to both retrieve information sent by your
application and to send specific values back to the application.
ReturnValue Used by a stored procedure to send a return value back to the calling
application.

Topic Objective
To show students how to
use parameterized stored
procedures.
Lead-in
When using Microsoft
SQL Server, or other
procedure-based
databases, you can use
parameters to pass and
retrieve information from the
database.
Delivery Tip
Stress that, when you create
a parameter, you must use
the same name that was
used in the stored
procedure. Also, stress that
the order in which you
create parameters does not
matter.
30 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


Creating a Parameter
To create a parameter for the SqlDataAdapter object, create a new
SqlParameter object with the name and data type of the parameter. Next, set
the Direction property of the new parameter to indicate how the parameter is
used by the stored procedure. If the parameter is an input parameter, set the
Value property to specify the data that should be sent to the server.
For example, the GetProductData stored procedure takes one input parameter:
PROCEDURE GetProductData !
@ProductName nchar(40) as select * from Products !
where @ProductName = ProductName

To call the GetProductData stored procedure, create an input parameter named
@ProductName and set its value to the Text property of a text box named
textBox1.
SqlDataAdapter mySqlDataAdapter = new
SqlDataAdapter();
mySqlDataAdapter.SelectCommand = new SqlCommand();
mySqlDataAdapter.SelectCommand.Connection = mySqlConnection;
mySqlDataAdapter.SelectCommand.CommandText = "GetProductData";
mySqlDataAdapter.SelectCommand.CommandType =
CommandType.StoredProcedure;

SqlParameter workParam = new SqlParameter("@ProductName",
SqlDbType.NChar, 40);
// ParameterDirection.Input is the default for the Direction
// property. Thus the following line is not needed here.
// To set the Direction property to its default value,
// use the following line.
// workParam.Direction = ParameterDirection.Input;
workParam.Value = textBox1.Text;

After you have created the parameter, use the Add method of the Parameters
collection of the SelectCommand object. The Add method takes a
SqlParameter as an argument. If a stored procedure has more than one
parameter, it does not matter in which order you add them because you create
them by name.
mySqlDataAdapter.SelectCommand.Parameters.Add(workParam);

Use the Fill method to run the stored procedure and retrieve the records.
DataSet myDataSet = new DataSet();
mySqlDataAdapter.Fill(myDataSet,"ProductData");
// bind to a DataGrid
dataGrid.DataSource = myDataSet.Tables["ProductData"];

Module 16 (Optional): Using Microsoft ADO.NET to Access Data 31


Calling Action Stored Procedures
! Use SQLCommand object
! Call the ExecuteNonQuery Method
! Retrieve Output Parameters
textBox2.Text =
mySqlCommand.Parameters["@ProductCount"].!
Value.ToString();
textBox2.Text =
mySqlCommand.Parameters["@ProductCount"].!
Value.ToString();
SqlCommand mySqlCommand = new
SqlCommand("GetProductCount", mySqlConnection);
mySqlCommand.CommandType =
CommandType.StoredProcedure;
SqlParameter workParam = new
SqlParameter("@ProductCount", SqlDbType.Int);
workParam.Direction = ParameterDirection.Output;
mySqlCommand.Parameters.Add(workParam);
SqlCommand mySqlCommand = new
SqlCommand("GetProductCount", mySqlConnection);
mySqlCommand.CommandType =
CommandType.StoredProcedure;
SqlParameter workParam = new
SqlParameter("@ProductCount", SqlDbType.Int);
workParam.Direction = ParameterDirection.Output;
mySqlCommand.Parameters.Add(workParam);
mySqlCommand.ExecuteNonQuery();
mySqlCommand.ExecuteNonQuery();

*****************************ILLEGAL FOR NON-TRAINER USE******************************
When calling a stored procedure that does not return records, you use the
SqlCommand directly and call the ExecuteNonQuery method to run it.
The GetProductCount stored procedure returns the number of products:
PROCEDURE GetProductCount @ProductCount int out as !
select @ProductCount=COUNT(ProductID) from Products

Because this stored procedure does not return a set of records, you do not need
to use a DataAdapter object. Instead, you can use a Command object directly,
and call the ExecuteNonQuery method to run the stored procedure.
To call this stored procedure, create an output parameter named
@ProductCount, add it to the Parameters collection of a Command object,
and then call ExecuteNonQuery to run the stored procedure:
SqlCommand mySqlCommand = new
SqlCommand("GetProductCount", mySqlConnection);
mySqlCommand.CommandType = CommandType.StoredProcedure;

SqlParameter workParam = new
SqlParameter("@ProductCount", SqlDbType.Int);
workParam.Direction = ParameterDirection.Output;
mySqlCommand.Parameters.Add(workParam);

// execute the stored procedure
mySqlCommand.ExecuteNonQuery();

Topic Objective
To show how to call a stored
procedure that does not
return records.
Lead-in
When calling a stored
procedure that does not
return records, you use the
SqlCommand directly and
call the ExecuteNonQuery
method to run it.
32 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


Retrieving an Output Parameter
If you need to retrieve a value from a stored procedure that returns a value or
sets an output parameter, use the Parameters collection to find the value of the
output parameter. You can reference the value of the output parameter by name
or index.
The following example code retrieves the value of the @ProductCount output
parameter by name and assigns it to a text box named textBox2:
textBox2.Text =
mySqlCommand.Parameters["@ProductCount"].Value.ToString();

Delivery Tip
If a stored procedure returns
records and an output
parameter, retrieve the
records in the usual way by
calling the Fill method.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 33


Demonstration: Calling Stored Procedures

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this demonstration, you will see how to create and call stored procedures
with and without parameters.
Run the Visual Studio .NET StoredProcedure project from the following
location:
<install folder>\Democode\Mod16\Demo16.3

You can examine the database tables and stored procedures by using the
Server Explorer window Data Connections entries.

To create and execute a stored procedure that returns the product names of all
the entries in the Products table, click the Load Product Names button.
Typing a name in the text box that is labeled Enter Product Name allows you
to click on the Load Product Data button. This button creates and executes a
stored procedure that takes the product name as an input parameter and returns
all the fields of that product.
To create and execute a stored procedure that has an output parameter, which
gets set to the number of products in the databases Products table, click the
Get Number of Products button.
Topic Objective
To introduce the
demonstration.
Lead-in
In this demonstration, you
will see how to call stored
procedures both with and
without parameters.
Tip
34 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


Lab 16: Using ADO.NET to Access Data

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives
After completing this lab, you will be able to:
! Use the rapid application design facilities of Visual Studio .NET to create an
XML Web service whose methods can be used to read and update a
database by using ADO.NET.
! Use Visual Studio .NET rapid application design facilitates to create a
Windows Forms application that displays and enables a user to update a
DataGrid control that is bound to a DataSet. The DataSet is obtained and
updated by using an XML Web service.

Lab Setup
Only solution files are associated with this lab. The solution files for this lab are
in the folder <installfolder>\Labs\Lab16\Solution.
Topic Objective
To introduce the lab.
Lead-in
In this lab, you will create an
XML Web service and a
Windows form that reads
and updates database
records by using ADO.NET.
Explain the lab objectives.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 35


Scenario
This lab is based on the Visual Studio .NET Framework SDK Walkthrough:
Creating a Distributed Application. In this lab, you will create a multi-tiered,
distributed application.
The application consists of three logical tiers: data, business object, and user
interface. The data tier is a SQL Server database. The business object tier
handles the tasks of accessing the data and distributing it to the clients. The user
interface tier consists of a Windows-based application.
You will create the Authors XML Web service and the Windows Client
Application. The following diagram shows the architecture of the distributed
application:

As part of the distributed application, you will build a simple data application
with look-up and update functionality. You will create an XML Web service to
retrieve data from the Authors table in the SQL Server Pubs sample database.
You will also build a Windows-based client application to display the results of
the query by the XML Web service. The communication between the client and
the XML Web service is handled by using HTTP and XML.
For this lab, the data has already been generated and is available in the Pubs
sample database. Therefore, you will begin by creating the business object, the
XML Web service, followed by building the Windows Form user interface.
Estimated time to complete this lab: 60 minutes
36 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


Exercise 1
Creating the Middle-Tier Business Object
In this exercise, you will create a business object that runs on a Web server.
You will implement the business object as an XML Web service that holds the
data connections and dataset definition.
After first creating an ASP.NET Web Service project in Visual Studio .NET,
you will use the Visual Studio .NET design environment to add a data
connection, a data adapter, and a dataset class definition to the XML Web
service. You will add two methods to the XML Web service: GetAuthors,
which returns a dataset from the database, and UpdateAuthors, which updates
the database with changes from the user.
! Create a ASP.NET Web Service project
1. Open Visual Studio .Net and on the File menu, click New, and then click
Project to display the New Project dialog box.
2. In the Project Types pane, click Visual C# Projects, and then select
ASP.NET Web Service in the Templates pane.
3. In the Location box, type the name of the Web server,
http://localhost/AuthorsWebService. The dimmed Name box will now
contain the text: AuthorsWebService. Click OK.
4. The AuthorsWebService project is added to the solution. The Component
Designer for Service1.asmx appears in the development environment. In this
component, you will create a connection to the data store and obtain an
instance of the data by using a dataset. In Solution Explorer, double-click
Service1.asmx.
5. In the Properties window, set the Name property of Service1 to
AuthorsService.
6. In Solution Explorer, right-click the Service1.asmx file, click Rename,
and rename the file AuthorsService.asmx, to match the service name.

! Create a database connection and data adapter
1. If Server Explorer is not visible, on the View menu, click Server
Explorer.
2. In Server Explorer, right-click the Data Connections node and on the
shortcut menu, click Add Connection.
3. In the Connection tab of the Data Link Properties dialog box, type the
name of the SQL Server where the pubs database is installed:
(local)\NETSDK
Select Use Windows NT Integrated security for the logon information.
4. Select the pubs database from the list. Click Test Connection to validate
the information that you provided, and then click OK to establish the
connection.
A new node appears in the Data Connections node of Server Explorer.
5. In Server Explorer, expand the node for the new connection node and then
expand the Tables node.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 37


6. Find the authors node and expand it to show the fields in the authors table.
7. Using CTRL+Click, select the au_id, au_lname, au_fname, and city fields.
8. Drag these fields from Server Explorer onto the design surface.
A SqlConnection that is paired with a SqlDataAdapter appears in the
designer. A connection has now been created to the database, with the
transfer of information to be handled by the SqlDataAdapter. These
components are configured to move a dataset with the selected fields of the
authors table in and out of the database.

! Configure Integrated Windows authentication
1. Start the Internet Information Services tool. It can be run from the Start
button, Control Panel, Performance and Maintenance, Administrative
Tools.
2. Expand the node for your server.
3. Expand the Default Web Site node.
4. Right-click the node for AuthorsWebService and click Properties.
5. Click the Directory Security tab.
6. Click the Edit button in the Anonymous access and authentication
control section, click the Edit button.
7. Clear the Anonymous access check box.
8. Select the Integrated Windows authentication check box. Click OK to
return to the AuthorsWebService Properties and click OK again. You
have now configured your XML Web service directory.
9. Returning to the project in Visual Studio, in Solution Explorer, double-
click the Web.config file.
10. Add the following tag on the line after the <system.web> tag to configure
integrated security for your XML Web service.
<identity impersonate="true"/>


! Create a dataset class definition
1. In Solution Explorer, double-click the AuthorsService.asmx file to open it
in the designer.
2. On the Data menu, click Generate DataSet. In the Generate Dataset
dialog box, select New and name the dataset authors1. Do not select the
Add this dataset to the designer check box. Click OK.

A dataset schema file, authors1.xsd, is created and added to the
project. This schema file contains a class definition for authors1. This class,
which inherits from DataSet, contains a typed dataset definition for the
authors table.

3. On the File menu, click Save All.

Note
38 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


! Add methods to the XML Web service
1. In Solution Explorer, double-click AuthorsService.asmx, if it is not
already open in the designer.
2. On the View menu, click Code.
3. Add a method named GetAuthors to deliver a dataset to the client.
This method creates a new authors1 dataset, and fills it by using the
SqlDataAdapter that is based on the authors table. The method then
returns the dataset:
The following code shows the method:
[WebMethod]
public authors1 GetAuthors()
{
authors1 authors = new authors1();
sqlDataAdapter1.Fill(authors);
return authors;
}

4. Add a method named UpdateAuthors to propagate changes from the client
back to the database.
This method has an authors1 dataset parameter (authorChanges) that
contains the changed data and updates the database through the
SqlDataAdapter.Update method. The Update method accepts the changes
in the dataset. The dataset is returned to the client. The client then uses this
returned dataset to update its own instance of the authors1 dataset.
The following code shows the method:
[WebMethod]
public authors1 UpdateAuthors(authors1 authorChanges)
{
if (authorChanges != null)
{
sqlDataAdapter1.Update(authorChanges);
return authorChanges;
}
else
{
return null;
}
}

In a production application, you would add error checking and
exception handling to these methods.

5. On the File menu, click Save All.
6. On the Build menu, click Build Solution.
7. To test the XML Web service, in the Solution Explorer right-click
AuthorsService.asmx and click View in Browser. In the browser, click
GetAuthors and in the resulting GetAuthors page, click the Invoke button.
You should see the list of authors displayed as XML.

Note
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 39


Exercise 2
Creating a Windows User Interface
In this exercise, you will create a Windows Forms client application. The
application consists of one Windows Form that contains a reference to
AuthorsWebService. The data in the database is displayed in a DataGrid
control when a Load button on the form is clicked.
To implement the display of data in the DataGrid, you call the XML Web
services GetAuthors method. The DataGrid control enables direct editing of
the data, with changes being passed directly to the underlying dataset.
In addition to a Load button, the form has a save button. The code for this
button calls the XML Web service's UpdateAuthors method to save the
changes back to the database.
! Create the Windows application
1. Open Visual Studio .NET and on the File menu, click New, and then click
Project to display the New Project dialog box.
2. Select Visual C# Projects in the Project Types pane, then select Windows
Application in the Templates pane.
3. Name the project AuthorsWinClient.
4. In the Location box, type the name of the Starter folder:
<install folder>\Labs\Lab16\Starter
5. Click OK.
The AuthorsWinClient project is created and Form1 is automatically
added to the project and appears in the Windows Forms Designer.
6. Add a reference to the XML Web service project that you created earlier.
a. In Solution Explorer, expand the AuthorsWinClient node, right-click
the References node, and on the shortcut menu, click Add Web
Reference.
b. In the Address box at the top of the Add Web Reference dialog box,
type the following and then press ENTER:
http://localhost/AuthorsWebService/AuthorsService.asmx
This is the location of the XML Web service file of your ASP.NET Web
Service project.
c. Click Add Reference.
You can now create an instance of the authors1 dataset in your
application.

40 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


! Add the controls to the form
1. Drag a DataGrid control from the Windows Forms tab of the Toolbox
onto the form.
2. Drag a Button control from the Windows Forms tab of the Toolbox onto
the form. Set the button's Name property to LoadData and its Text
property to Load.
3. Drag a second Button control from the Windows Forms tab of the
Toolbox onto the form. Set the button's Name property to SaveData and its
Text property to Save.
4. Drag a DataSet object from the Data tab of the Toolbox onto the form. In
the Add DataSet dialog box, select Typed dataset and then select
AuthorsWinClient.localhost.authors1 from the Name list. Click OK.
This action creates a DataSet object in the component tray that is based on
the authors1 dataset class definition.
5. Select the DataSet control and set the Name property to AuthorData.
6. Select the DataGrid control and select AuthorData from the DataSource
property list. Select authors from the DataMember property list.
The column headings of the DataGrid are set to the Authors table column
names.
You may want to adjust the width of the form and the DataGrid control to
accommodate all of the column headings. The user can then avoid having to
scroll horizontally to view all of the columns in the DataGrid.

! Add code for the LoadData and SaveData buttons
1. On the View menu, click Designer. Double-click the button that is labeled
Load to create an empty event handler for the Click event. Add the
following code to the LoadData_Click method:
private void LoadData_Click(
object sender, System.EventArgs e)
{
AuthorsWinClient.localhost.AuthorsService ws = new
AuthorsWinClient.localhost.AuthorsService();
ws.Credentials =
System.Net.CredentialCache.DefaultCredentials;
AuthorData.Merge(ws.GetAuthors());
}

XML Web service methods are called by first creating an instance of the
service class, and then calling the service methods. In this case, the
GetAuthors method is called. The dataset returned is merged with the
AuthorData dataset.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 41


2. On the View menu, click Designer. Double-click the button labeled Save to
create an empty event handler for the Click event. Add the following code
to the SaveData_Click method:
private void SaveData_Click(
object sender, System.EventArgs e)
{
if (AuthorData.HasChanges())
{
AuthorsWinClient.localhost.AuthorsService
ws = new
AuthorsWinClient.localhost.AuthorsService();
ws.Credentials =
System.Net.CredentialCache.DefaultCredentials;
AuthorsWinClient.localhost.authors1 diffAuthors =
new AuthorsWinClient.localhost.authors1();
diffAuthors.Merge(AuthorData.GetChanges());
ws.UpdateAuthors(diffAuthors);
AuthorData.Merge(diffAuthors);
}
}

If there are changes in the dataset, a new dataset of type authors1 is created
to hold just the changed data. This dataset is then passed to the XML Web
service's UpdateAuthors method. The dataset is returned with the changes
accepted, and the AuthorData dataset is updated to reflect these new
changes.

! Run the application
1. On the File menu, click Save All.
2. In Solution Explorer, right-click AuthorsWinClient, and click Set as
StartUp Project.
3. Press CTRL+F5 to run the application.
A window is displayed that contains an empty table with headers from the
authors table in the pubs database.
4. Click Load to populate the table. Make some changes to the data, and then
click Save.
You can click Load again to see that the changes that you made were stored
in the database. Also, close and reopen the application. The next time you
click Load, you will see that your changes have persisted.



42 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


" "" " Accessing Data with DataReaders
! Creating a DataReader
! Reading Data from a DataReader
! Using DataSets vs. DataReaders

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The benefit of using a DataSet is that it gives you a disconnected view of the
database. For long running applications, this is often the best approach. For
Web applications, developers usually perform short and simple operations with
each request, such as displaying data. For such operations, developers do not
have to maintain a DataSet object. In such cases, you can use a DataReader.
Choosing a DataReader or a DataSet
When deciding whether your application should use a DataReader or a
DataSet, you should consider the type of functionality that your application
requires. Use a DataSet when you need to do any of the following:
! Remote data between tiers or from an XML Web service.
! Interact with data dynamically such as binding to a Windows Forms control
or combining and relating data from multiple sources.
! Cache data locally in your application.
! Provide a hierarchical XML view of relational data and use tools like an
XSL Transformation or an XML Path Language (XPath) Query on your
data.
For more information, see XML and the DataSet in the .NET Framework
SDK documentation.
! Perform extensive processing on data without requiring an open connection
to the data source, which frees the connection to be used by other clients.

Topic Objective
To introduce the topics in
this section.
Lead-in
Another method of
accessing data from a data
source is to use
DataReaders.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 43


If you do not require the functionality that is provided by the DataSet, you can
improve the performance of your application by using the DataReader to
return your data in a forward-only read-only fashion. Although the
DataAdapter uses the DataReader to fill the contents of a DataSet, by using
the DataReader instead, you can receive the following performance gains:
! The saving of memory that would be consumed by the DataSet.
! The saving of the processing that would be required to create and fill the
contents of the DataSet.

In this section, you will learn how to read data from a data source by using
DataReaders.
44 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


Creating a DataReader
! Create and Open the Database Connection
! Create the DataReader From a Command Object
! Close the Reader and the Connection
SqlCommand mySqlCommand = new SqlCommand(
"select * from customers", mySqlConnection);
SqlDataReader myReader =
mySqlCommand.ExecuteReader();
SqlCommand mySqlCommand = new SqlCommand(
"select * from customers", mySqlConnection);
SqlDataReader myReader =
mySqlCommand.ExecuteReader();
SqlConnection mySqlConnection = new
SqlConnection("server=(local)\\NetSDK; !
Trusted_Connection=yes;database=northwind");
mySqlConnection.Open();
SqlConnection mySqlConnection = new
SqlConnection("server=(local)\\NetSDK; !
Trusted_Connection=yes;database=northwind");
mySqlConnection.Open();
If (myReader != null) myReader.Close();
if (mySqlConnection.State == ConnectionState.Open)
mySqlConnection.Close();
If (myReader != null) myReader.Close();
if (mySqlConnection.State == ConnectionState.Open)
mySqlConnection.Close();

*****************************ILLEGAL FOR NON-TRAINER USE******************************
When a large amount of data is being retrieved, holding memory open becomes
an issue. For example, reading 10,000 rows out of a database causes a
DataTable to allocate and maintain memory for those 10,000 rows for the
lifetime of the table. If 1,000 users do this against the same server at the same
time, memory usage becomes critical.
To address such situations, the DataReader is designed to produce a read-only,
forward-only stream returned from the database. Only one record at a time is
ever in memory. There are two DataReader objects, the SqlDataReader and
the OleDbDataReader.
A DataReader keeps the connection open until the DataReader is closed.
To use a SqlDataReader, declare a SqlCommand instead of a
SqlDataAdapter. The SqlCommand exposes an ExecuteReader method that
takes a SqlDataReader as a parameter. Note that you must explicitly open and
close the SqlConnection when you use a SQLCommand.
SqlConnection mySqlConnection = new
SqlConnection("server=(local)\\NetSDK;!
Trusted_Connection=yes;database=northwind");
mySqlConnection.Open();

SqlCommand mySqlCommand = new SqlCommand(
"select * from customers", mySqlConnection);

SqlDataReader myReader = mySqlCommand.ExecuteReader();
//...
if (myReader != null)
myReader.Close();

if (mySqlConnection.State == ConnectionState.Open)
mySqlConnection.Close();

Topic Objective
To describe how to retrieve
data from a database by
using a DataReader.
Lead-in
You can also use a
DataReader object to read
data from a database.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 45


Error Handling with a DataReader
When using connections with the DataReader object, you should always use a
try/finally block to ensure that, if anything fails, connections will be closed.
SqlDataReader myReader = null;

SqlConnection mySqlConnection = new SqlConnection(
"server=(local)\\NetSDK;!
Trusted_Connection=yes;database=northwind");
SqlCommand mySqlCommand = new SqlCommand(
"select * from customers", mySqlConnection);

try
{
mySqlConnection.Open();
myReader = mySqlCommand.ExecuteReader();
//
}
catch(Exception e)
{
Console.WriteLine(e.ToString());
}
finally
{
if (myReader != null)
myReader.Close();

if (mySqlConnection.State == ConnectionState.Open)
mySqlConnection.Close();
}

46 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


Reading Data from a DataReader
! Call Read for Each Record
# Returns false when there are no more records
! Get Field(s)
# Parameter is the ordinal position or name of the field
! Call Close to Free Up the Reader and the Connection
while (myReader.Read())
{
Console.Write(myReader["CustomerID"].ToString() +
" ");
Console.WriteLine(myReader["CompanyName"].ToString());
}
while (myReader.Read())
{
Console.Write(myReader["CustomerID"].ToString() +
" ");
Console.WriteLine(myReader["CompanyName"].ToString());
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
After you have called the ExecuteReader method of the Command object, you
access a record in the DataReader by calling the Read method. The default
positioning in the DataReader is before the first record, therefore you must call
Read before accessing any data. When no more records are available, the Read
method returns a null value.
Reading Fields from the Current Record
To get the data from fields in the current record, call an appropriate Get
method, for example, GetDateTime, GetDouble, GetInt32, or GetString. The
parameter of the Get method is the ordinal value of the field that you want to
read. For example, the following code reads the CustomerID and
CompanyName fields, from the current record of the DataReader, by using the
GetString method:
string customerID = myReader.GetString(0);
string companyName = myReader.GetString(1);

You can also reference the fields of data in the current record of the data reader
by name, and then call an appropriate conversion function, as shown in the
following example code:
string customerID = myReader["CustomerID"].ToString();
string companyName = myReader["CompanyName"].ToString();

Topic Objective
To explain how to read data
from a DataReader.
Lead-in
After you have called the
ExecuteReader method of
the Command object, you
access a record in the
DataReader by calling the
Read method.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 47


Looping Through All Records
To loop through and output to the Console all the records in a DataReader, you
can use a While loop, as shown in the following sample code:
while (myReader.Read())
{
Console.Write(myReader["CustomerID"].ToString() +
" ");
Console.WriteLine(myReader["CompanyName"].ToString());
}

Closing the DataReader
While the DataReader is in use, the associated connection is busy serving the
DataReader. Therefore, you must call Close to close the DataReader when
you are finished using it.
if (myReader != null)
myReader.Close();

if (mySqlConnection.State == ConnectionState.Open)
mySqlConnection.Close();

48 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


Demonstration: Accessing Data Using DataReaders

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this demonstration, you will see how to read data from a database by using a
SQLDataReader.
Run the Visual Studio .NET DataReader project from the following location:
<install folder>\Democode\Mod16\Demo16.4
Topic Objective
To introduce the
demonstration.
Lead-in
In this demonstration, you
will see how to read data
from a database by using a
SqlDataReader.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 49


Using DataSets vs. DataReaders
DataSet
1. Create a database
connection
2. Store query in DataAdapter
3. Populate DataSet with
Fill method
4. Create DataView
5. Bind DataView to
list-bound control
DataReader
1. Create a database connection
2. Open the database connection
3. Store query in SqlCommand
4. Populate DataReader with
ExecuteReader method
5. Call Read for each record,
and Get for each field
6. Manually display data
7. Close the DataReader and
the connection

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The general procedure for accessing databases from ASP.NET is different
depending on whether you will be using a DataSet or a DataReader:
Using DataSets Using DataReaders

1. Connect to the database by using
SqlConnection or OleDbConnection.
1. Connect to the database by using
SqlConnection or OleDbConnection.
2. Store the database query in
SqlDataAdapter or
OleDbDataAdapter objects.
2. Open the connection with the Open
method.
3. Populate a DataSet from the
DataAdapter by using Fill.
3. Store database query in SqlCommand
or OleDbCommand objects.
4. Set up a new DataView for the
desired table.
4. Populate a DataReader from the
Command by using ExecuteReader
method.
5. Bind a server control, such as the
DataGrid, to the DataView.
5. Call Read and Get methods of the
DataReader to read data.
6. Manually display data.
7. Close the DataReader. and the
connection.



Topic Objective
To describe the different
processes for getting data
with a DataSet versus a
DataReader.
Lead-in
The procedure for accessing
databases from ASP.NET is
different depending on
whether you use a DataSet
or a DataReader.
Delivery Tip
Discuss performance issues
in DataReaders and
DataSets.
50 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


" "" " Binding to XML Data
! Overview of XML
! Reading XML Data into a DataSet

*****************************ILLEGAL FOR NON-TRAINER USE******************************
HTML is widely used for presenting information on the Web. HTML works
well as a presentation language, but it is not suitable for representing data. For
example, you can easily format data in an HTML table, but you cannot describe
the individual components of the information. To share information between
applications, you must have a language that can describe data in a standardized
way so that any application, present or future, can understand and use this data
correctly. XML is one such standardized language. XML not only helps you
structure your data but acts as a common language between different business
applications.
In this section, you will get an overview of XML. You will also learn how to
read and display XML data by using ADO.NET.
Topic Objective
To introduce the topics
included in this section.
Lead-in
Another common source of
data for applications is XML
documents.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 51


Overview of XML
! Machine-Readable and Human-Readable Data
! Defines the Data Content and Structure
! Separates Structure from Presentation
! Allows You to Define Your Own Tags and Attributes
<employee>
<name>Jake</name>
<salary>25000</salary>
<region>Ohio</region>
</employee>
<employee>
<name>Jake</name>
<salary>25000</salary>
<region>Ohio</region>
</employee>

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Businesses today face many problems when it comes to organizing data. They
need to meet the following requirements:
! Data must be readable by both computers and users.
! Both the content and the structure of the data must be defined.
! The structure of the data needs to be separate from the presentation of the
data.
! The structure needs to be open and extensible.

XML fulfills all of these requirements.
XML defines the structure of data in an open and self-describing manner. This
allows data to be easily transferred over a network and consistently processed
by the receiver. XML describes how data is structured, not how it should be
displayed or used. XML documents contain tags that assign meaning to the
content of the document. These tags allow programmers to find the data they
need in the XML document.
For example, the following XML sample contains information about an
employee but does not specify how to display this information:
<employee>
<name>Jake</name>
<salary>25000</salary>
<region>Ohio</region>
</employee>

Topic Objective
To provide an overview of
XML data.
Lead-in
XML is a common language
used for exchanging
information between
business applications.
52 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


XML is considered a markup language because it allows you to define data
structure by using markup tags. You can define your own tags that describe the
data in whatever way you find useful.
XML data is held in a simple, open format that is easily parsed by other
applications. The fact that XML documents contain text rather than binary data
is another key advantage. Applications can parse an XML document, looking
for specific tags of interest to those applications. Unknown tags and their
associated data can be freely ignored.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 53


Reading XML Data into a DataSet
! Setup the XML file for reading
! Read the contents of the File into a DataSet
! Bind a DataGrid to the DataSet
FileStream fs = File.OpenRead("myFile.xml");
StreamReader myStreamReader = new StreamReader(fs);
FileStream fs = File.OpenRead("myFile.xml");
StreamReader myStreamReader = new StreamReader(fs);
DataSet myDataSet = new DataSet();
myDataSet.ReadXml(myStreamReader);
DataSet myDataSet = new DataSet();
myDataSet.ReadXml(myStreamReader);
dataGrid1.DataSource = myDataSet.Tables[0];
dataGrid1.DataSource = myDataSet.Tables[0];

*****************************ILLEGAL FOR NON-TRAINER USE******************************
DataSets in ADO.NET are designed to extract data in a way that is independent
of its data source. Therefore, reading data from an XML source is similar to
reading data from a database. To read XML data, you import the System.IO
namespace in your ASP.NET page.

You cannot read XML data into a DataReader. You can read it only into
a DataSet.

For XML data, the DataSet supports a ReadXml method that takes a
FileStream as its parameter. The DataSet expects data to be in the following
format:
<DocumentElement>
<TableName>
<ColumnName1>column value</ColumnName1>
<ColumnName2>column value</ColumnName2>
<ColumnName3>column value</ColumnName3>
</TableName>
<TableName>
<ColumnName1>column value</ColumnName1>
<ColumnName2>column value</ColumnName2>
<ColumnName3>column value</ColumnName3>
</TableName>
</DocumentElement>

Each TableName section corresponds to a single row in the table.
Topic Objective
To explain how to read XML
data into a DataSet.
Lead-in
Because XML is an
important part of ASP.NET,
there are various easy ways
to bind to XML data.
Note
54 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


The following example shows how to read the schema and data from an XML
file by using the ReadXml method, the FileStream object, and the
StreamReader object. Note that after the data is read into the DataSet, it is
indistinguishable from SQL datathe DataGrid binds to it in the same way.
You can perform the following steps to read a file named myFile.xml that
contains XML data and displays it in a DataGrid.
First open the XML file and create a StreamReader:
FileStream fs = File.OpenRead("myFile.xml");
StreamReader myStreamReader = new StreamReader(fs);

Next, read the XML data into the DataSet:
DataSet myDataSet = new DataSet();
myDataSet.ReadXml(myStreamReader);

Finally, bind a DataGrid to the DataSet:
dataGrid1.DataSource = myDataSet.Tables[0];

After the data has been read into a DataSet, the repeated elements in the XML
become the columns in the DataSet and can be bound to any control to be
displayed on the client.
The Windows Forms code, including try/catch/finally blocks, that is used in
Demonstration: Reading XML Data into a DataSet, is as follows:
StreamReader myStreamReader = null;
statusBar1.Text = "";

try
{
FileStream fs = File.OpenRead(textBox1.Text);
myStreamReader = new StreamReader(fs);
DataSet myDataSet = new DataSet();
myDataSet.ReadXml(myStreamReader);
dataGrid1.DataSource = myDataSet.Tables[0];
statusBar1.Text = "Read file: " + textBox1.Text;
}
catch(Exception exception)
{
statusBar1.Text = exception.ToString();
}
finally
{
if (myStreamReader != null) myStreamReader.Close();
}

Module 16 (Optional): Using Microsoft ADO.NET to Access Data 55


Demonstration: Reading XML Data into a DataSet

*****************************ILLEGAL FOR NON-TRAINER USE******************************
In this demonstration, you will see how to read information from an XML file
and display it in a DataGrid control.
Run the Visual Studio .NET XMLData project from the following location:
<install folder>\Democode\Mod16\Demo16.5
Enter the default filename books1.xml and click the Load Data button. The file
books1.xml is read into a DataSet and displayed in a DataGrid.

Topic Objective
To demonstrate how to
display information from an
XML file.
Lead-in
In this demonstration, you
will see how to read
information from an XML file
and display it in a DataGrid
control.
56 Module 16 (Optional): Using Microsoft ADO.NET to Access Data


Review
! Overview of ADO.NET
! Connecting to a Data Source
! Accessing Data with DataSets
! Using Stored Procedures
! Accessing Data with DataReaders
! Binding to XML Data

*****************************ILLEGAL FOR NON-TRAINER USE******************************
1. What are some of the new objects in the ADO.NET object model?
Some of the new objects in the ADO.NET object model are: DataSet,
DataAdapter, DataView, DataReader, and DataTable.


2. What is the difference between a DataSet and a DataView?
DataSet is a collection of DataTables and a DataView is a custom view
of the DataTables in a DataSet.


3. What is the difference between a DataSet and a DataReader?
The DataReader is designed to produce a read-only, forward-only
stream returned from the database.
The DataSet is designed to handle the actual data from a data store. It
represents a cache of data, with database-like behavior. It can contain
tables, columns, relationships, constraints, and data.


Topic Objective
To reinforce module
objectives by reviewing key
points.
Lead-in
The review questions cover
some of the key concepts
taught in the module.
Module 16 (Optional): Using Microsoft ADO.NET to Access Data 57


4. What is the purpose of the DataAdapter object?
A DataAdapter object is a tool that is used to create and initialize
various tables. It allows for the retrieval and saving of data between a
DataSet object and the source data store. It is responsible for pulling
out data from the physical store and pushing it into data tables and
relations.


5. Which method is used to populate a DataSet with results of a query?
The method that is used to populate the DataSet with results of a query
is the Fill method.






THIS PAGE INTENTIONALLY LEFT BLANK









Contents
Overview 1
Overview of Attributes 2
Defining Custom Attributes 13
Retrieving Attribute Values 22
Demonstration: Custom Attributes 26
Lab 17: Defining and Using Attributes 27
Review 36

Module 17 (Optional):
Attributes



Information in this document, including URL and other Internet Web site references, is subject to
change without notice. Unless otherwise noted, the example companies, organizations, products,
domain names, e-mail addresses, logos, people, places, and events depicted herein are fictitious,
and no association with any real company, organization, product, domain name, e-mail address,
logo, person, places or events is intended or should be inferred. Complying with all applicable
copyright laws is the responsibility of the user. Without limiting the rights under copyright, no
part of this document may be reproduced, stored in or introduced into a retrieval system, or
transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or
otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual
property rights covering subject matter in this document. Except as expressly provided in any
written license agreement from Microsoft, the furnishing of this document does not give you any
license to these patents, trademarks, copyrights, or other intellectual property.

2001-2002 Microsoft Corporation. All rights reserved.

Microsoft, ActiveX, BizTalk, IntelliMirror, Jscript, MSDN, MS-DOS, MSN, PowerPoint,
Visual Basic, Visual C++, Visual C#, Visual Studio, Win32, Windows, Windows Media, and
Window NT are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A.
and/or other countries.

The names of actual companies and products mentioned herein may be the trademarks of their
respective owners.


Module 17 (Optional): Attributes iii


Instructor Notes
Teach this module if time permits. Module 17 is a stand-alone module that is
not dependent upon any other module.
This module provides students with the details about how to use attributes in
code. It describes the predefined attributes that are provided by the Microsoft
.NET Framework and provides some simple examples of how to use some
common attributes. The concept of custom attributes is introduced. This
introduction is followed by a detailed explanation of how to define and use
custom attributes. The process used to compile code that has custom attributes
is also explained. Finally, the module describes how to retrieve attribute values
during run time by using reflection. The procedure that is used to retrieve
attribute information into an array and query the array to obtain the required
values is explained.
After completing this module, students will be able to:
! Use common predefined attributes.
! Create simple custom attributes.
! Query attribute information at run time.

Materials and Preparation
This section provides the materials and preparation tasks that you need to teach
this module.
Required Materials
To teach this module, you need Microsoft PowerPoint file 2349B_17.ppt.
Preparation Tasks
To prepare for this module, you should:
! Read all of the materials for this module.
! Complete the lab.
! Read the instructor notes and margin notes for the module.
! Practice using the Microsoft Intermediate Language disassembler to
examine the metadata of an assembly.

Presentation:
60 Minutes

Lab:
45 Minutes
iv Module 17 (Optional): Attributes


Demonstration
This section provides demonstration procedures that will not fit in the margin
notes or are not appropriate for the student notes.
Use the debugger to step through the code while you point out features and ask
students what they think will happen next.
Custom Attributes
In this demonstration, you will show students how to declare and apply custom
attributes and how to use reflection to retrieve custom attribute metadata.
The code for this demonstration is contained in one project and is located in
<install folder>\Democode\Mod17.
Module Strategy
Use the following strategy to present this module:
! Overview of Attributes
Begin by explaining that attributes are only annotations to classes and
perform no function themselves. Before attributes can cause an action,
certain code must be implemented. This code is in the runtime for the
predefined attributes and is written by the developer for custom attributes.
Explain the syntax used to apply an attribute, and, after covering the lists of
predefined attributes briefly, discuss the three attributesConditional,
DllImport, and Transactionusing examples.
! Defining Custom Attributes
Introduce the need for creating custom attributes, and explain the procedures
involved in defining a custom attribute. Explain the use of AttributeUsage
and how to create an attribute class. Then explain the details of the
procedure used to compile code that uses custom attributes. Finish the
section by explaining how to use multiple attributes in code.
! Retrieving Attribute Values
In this section, introduce the concept of retrieving attribute values at run
time by using reflection. Explain how to use the MemberInfo class and the
typeof operator to obtain attribute values. Finally, discuss how to iterate
through the stored attribute values in an array to retrieve the required values.
To end the discussion about attributes, use the review slide to recapitulate
the main concepts covered in the module.

Module 17 (Optional): Attributes 1


Overview
! Overview of Attributes
! Defining Custom Attributes
! Retrieving Attribute Values

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Attributes are a simple technique for adding metadata to classes. They can be
useful when you need to build components.
In this module, you will learn the purpose of attributes and the function that
they perform in Microsoft Visual C#

.NET applications. You will learn about


attribute syntax and how to use some of the predefined attributes in the
Microsoft .NET Framework environment. You will also learn to create custom
user-defined attributes. Finally, you will learn how classes and other object
types can implement and use these custom attributes to query attribute
information at run time.
After completing this module, you will be able to:
! Use common predefined attributes.
! Create simple custom attributes.
! Query attribute information at run time.

Topic Objective
To provide an overview of
the module topics and
objectives.
Lead-in
In this module, you will learn
about using attributes in
Microsoft Visual C# .NET.
Delivery Tip
Teach this module if time
permits. This is a stand-
alone module that is not
dependent upon any other
module.
2 Module 17 (Optional): Attributes


" "" " Overview of Attributes
! Introduction to Attributes
! Applying Attributes
! Common Predefined Attributes
! Using the Conditional Attribute
! Using the DllImport Attribute
! Using the Transaction Attribute

*****************************ILLEGAL FOR NON-TRAINER USE******************************
With the introduction of attributes, the C# language provides a convenient
technique that will help handle tasks such as changing the behavior of the
runtime, obtaining transaction information about an object, conveying
organizational information to a designer, and handling unmanaged code.
After completing this lesson, you will be able to:
! Identify which tasks you can perform with attributes.
! Use the syntax for using attributes in your code.
! Identify some of the predefined attributes that are available in the .NET
Framework.

Topic Objective
To introduce the topics
covered in this section.
Lead-in
In this section, you will learn
what attributes are and how
they are used.
Module 17 (Optional): Attributes 3


Introduction to Attributes
! Attributes Are:
# Declarative tags that convey information to the runtime
# Stored with the metadata of the element
! .NET Framework Provides Predefined Attributes
# The runtime contains code to examine values of
attributes and act on them

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The .NET Framework provides attributes so that you can extend the capabilities
of the C# language. An attribute is a declarative tag that you use to convey
information to the runtime about the behavior of programmatic elements such
as classes, enumerators, and assemblies.
You can think of attributes as annotations that your programs can store and use.
In most cases, you write the code that retrieves the values of an attribute in
addition to the code that performs a change in behavior at run time. In its
simplest form, an attribute is an extended way to document your code.
You can apply attributes to many elements of the source code. Information
about the attributes is stored with the metadata of the elements they are
associated with.
The .NET Framework is equipped with a number of predefined attributes. The
code to examine them and act upon the values they contain is also incorporated
as a part of the runtime and .NET Framework software development kit (SDK).
Topic Objective
To define attributes.
Lead-in
The concept of an attribute
is simple.
Delivery Tip
Stress that attributes are
fundamentally a very simple
ideathey are simply
annotations for your code
that are intended to convey
useful declarative
information.
4 Module 17 (Optional): Attributes


Applying Attributes
! Syntax: Use Square Brackets to Specify an Attribute
! To Apply Multiple Attributes to an Element, You Can:
# Specify multiple attributes in separate square brackets
# Use a single square bracket and separate attributes with
commas
# For some elements such as assemblies, specify the
element name associated with the attribute explicitly
[attribute(positional_parameters,named_parameter=value, ...)]
element
[attribute(positional_parameters,named_parameter=value, ...)]
element

*****************************ILLEGAL FOR NON-TRAINER USE******************************
You can apply attributes to different kinds of programming elements. These
elements include assemblies, modules, classes, structs, enums, constructors,
methods, properties, fields, events, interfaces, parameters, return values, and
delegates.
Attribute Syntax
To specify an attribute and associate it with a programming element, use the
following general syntax:
[attribute(positional_parameters,name_parameter=value, ...)]
element

You specify an attribute name and its values within square brackets ([ and ])
before the programmatic element to which you want to apply the attribute. Most
attributes take one or more parameters, which can be either positional or
named.
You specify a positional parameter in a defined position in the parameter list, as
you would specify parameters for methods. Any named parameter values
follow the positional parameters. Positional parameters are used to specify
essential information, whereas named parameters are used to convey optional
information in an attribute.

Before using an unfamiliar attribute, it is a good practice to check the
documentation for the attribute to find out which parameters are available and
whether they should be positional or named.

Topic Objective
To explain the syntax for
using attributes.
Lead-in
Attributes can be applied to
several different types of
programming elements.
Tip
Module 17 (Optional): Attributes 5


Example
As an example of using attributes, consider the following code, in which the
DefaultEvent attribute is applied on a class by using a positional string
parameter, ShowResult:
[DefaultEvent("ShowResult")]
public class Calculator: System.Windows.Forms.UserControl
{
...
}

Applying Multiple Attributes
You can apply more than one attribute to an element. You can enclose each
attribute in its own set of square brackets, although you can also enclose
multiple attributes, separated with commas, in the same set of square brackets.
In some circumstances, you must specify exactly which element an attribute is
associated with. For example, in the case of assembly attributes, place them
after any using clauses but before any code, and explicitly specify them as
attributes of the assembly.
The following example shows how to use the CLSCompliant assembly
attribute. This attribute indicates whether or not an assembly strictly conforms
to the Common Language Specification.
using System;
[assembly:CLSCompliant(true)]

class MyClass
{
...
}

6 Module 17 (Optional): Attributes


Common Predefined Attributes
! .NET Provides Many Predefined Attributes
# General attributes
# COM interoperability attributes
# Transaction handling attributes
# Visual designer component-building attributes

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The capabilities of predefined attributes in the .NET Framework encompass a
wide range of areas, from interoperability with COM to compatibility with
visual design tools.
This topic describes some of the common predefined attributes that are
provided by the .NET Framework. However, it is not intended to be
comprehensive. For more information about predefined attributes, refer to the
Microsoft Visual Studio .NET documentation.
General Attributes
The following list summarizes some of the general attributes that are provided
by the .NET Framework.
Attribute Applicable to Description

Conditional Method Tests to see whether a named
symbol is defined. If it is
defined, any calls to the method
are executed normally. If the
symbol is not defined, the call
is not generated.
DllImport Method Indicates that the method is
implemented in unmanaged
code, in the specified DLL. It
causes the DLL to be loaded at
run time and the named method
to execute.
SecurityPermissionAttributes Assembly,
Class, Struct,
Constructor,
Method
Allows security actions for
SecurityPermission to be
applied to code using
declarative security.

Topic Objective
To list some common
predefined attributes.
Lead-in
The .NET Framework
provides a large number of
predefined attributes.
Module 17 (Optional): Attributes 7


COM Interoperability Attributes
When using the attributes to provide interoperability with COM, the goal is to
ensure that using COM components from the managed .NET Framework
environment is as seamless as possible. The .NET Framework has many
attributes relating to COM interoperability. Some of these are listed in the
following table.
Attribute Applicable to Description

ComImport Class/Interface Indicates that a class or interface
definition was imported from a COM
type library.
ComRegisterFunction Method Specifies the method to be called when
a .NET Framework assembly is
registered for use from COM.
ComUnregisterFunction Method Specifies the method to be called when
a .NET assembly is unregistered for use
from COM.
DispId Method, field,
property
Indicates which dispatch ID is to be
used for the method, field, or property.
In parameter Indicates that the data should be
marshaled from the caller to the callee.
MarshalAs Field,
parameter,
return values
Specifies how data should be marshaled
between COM and the managed
environment.
ProgId Class Specifies which prog ID is to be used
for the class.
Out parameter Indicates that data should be marshaled
from the callee back to caller.
InterfaceType Interface Specifies whether a managed interface
is IDispatch, IUnknown, or Dual when
it is exposed to COM.

For more information about COM interoperability, see Module 15,
Interoperating Between Managed and Unmanaged Code, in Course 2349B,
Programming with the Microsoft .NET Framework (Microsoft Visual C# .NET),
or search for "Microsoft ComServices" in the .NET Framework SDK.
Transaction Handling Attributes
Components running in a COM+ environment use transaction management.
The attribute you use for this purpose is shown in the following table.
Attribute Applicable to Description

Transaction Class Specify the type of transaction that should be
available to this object.

Delivery Tip
Avoid getting into long
conversations about COM,
transactions, and
interoperability because this
is beyond the scope of this
module. This information is
presented to show that the
.NET Framework is
compatible with COM. Tell
students that COM and
interoperability is covered in
depth in Module 15,
Interoperating Between
Managed and Unmanaged
Code, in this course.
8 Module 17 (Optional): Attributes


Visual Designer Component-Building Attributes
Developers who build components for a visual designer use the attributes listed
in the following table.
Attribute Applicable to Description

Bindable Property Specifies whether a property is typically used
for binding.
DefaultProperty Class Specifies the default property for the
component.
DefaultValue Property Indicates that the property is the default value
for the component.
Localizable Property When code is generated for a component,
members that are marked with
Localizable(true) have their property values
saved in resource files. You can localize these
resource files without modifying the code.
DefaultEvent Class Specifies the default event for the component.
Category Property,
event
Specifies the category into which the visual
designer should place this property or event in
the property window.
Description Property,
event
Defines a brief piece of text to be displayed at
the bottom of the property window in the visual
designer when this property or event is selected.

Module 17 (Optional): Attributes 9


Using the Conditional Attribute
! Serves As a Debugging Tool
# Causes conditional compilation of method calls, depending on the
value of a programmer-defined symbol
# Does not cause conditional compilation of the method itself
! Restrictions on Methods
# Must have return type of void
# Must not be declared as override
# Must not be from an inherited interface
class MyClass
{
[Conditional ("DEBUGGING")]
public static void MyMethod( )
{
...
}
}
class MyClass
{
[Conditional ("DEBUGGING")]
public static void MyMethod( )
{
...
}
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
You can use the Conditional attribute as a debugging aid in your C# code. This
attribute causes conditional compilation of method calls, depending on the
value of a symbol that you define. It lets you invoke methods that, for example,
display the values of variables, while you test and debug code. After you have
debugged your program, you can undefine the symbol and recompile your
code without changing anything else. (Or you can simply remove the symbol
from the command line, and not change anything.)
Example
The following example shows how to use the Conditional attribute. In this
example, the MyMethod method in MyClass is tagged with the Conditional
attribute by the symbol DEBUGGING:
class MyClass
{
[Conditional ("DEBUGGING")]
public static void MyMethod( )
{
...
}
}

Topic Objective
To show how to use the
Conditional attribute.
Lead-in
The Conditional attribute is
frequently used for
debugging classes.
Delivery Tip
The Conditional attribute is
common and is used in the
labs. Make sure students
understand how to use it.
10 Module 17 (Optional): Attributes


The symbol DEBUGGING is defined as follows:
#define DEBUGGING

class AnotherClass
{
public static void Test( )
{
MyClass.MyMethod( );
}
}

As long as the symbol DEBUGGING remains defined when the method call is
compiled, the method call will operate normally. When DEBUGGING is
undefined, the compiler will omit calls to the method. Therefore, when you run
the program, it will be treated as though that line of code does not exist.
You can define the symbol in one of two ways. You can either add a #define
directive to the code as shown in the preceding example, or define the symbol
from the command line when you compile your program.
Restrictions on Methods
The methods to which you can apply a Conditional attribute are subject to a
number of restrictions. In particular, they must have a return type of void, they
must not be marked as override, and they must not be the implementation of a
method from an inherited interface.

The Conditional attribute does not cause conditional compilation of the
method itself. The attribute only determines the action that will occur when the
method is called. If you require conditional compilation of a method, then you
must use the #if and #endif directives in your code.

Note
Module 17 (Optional): Attributes 11


Using the DllImport Attribute
! With the DllImport Attribute, You Can:
# Invoke unmanaged code in DLLs from a C# environment
# Tag an external method to show that it resides in an
unmanaged DLL
[DllImport("MyDLL.dll", EntryPoint=MyFunction")]
public static extern int MyFunction(string param1);
public class MyClass( )
{
...
int result = MyFunction("Hello Unmanaged Code");
...
}
[DllImport("MyDLL.dll", EntryPoint=MyFunction")]
public static extern int MyFunction(string param1);
public class MyClass( )
{
...
int result = MyFunction("Hello Unmanaged Code");
...
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
You can use the DllImport attribute to invoke unmanaged code in your C#
programs. Unmanaged code is the term used for code that has been developed
outside the .NET environment (that is, standard C compiled into DLL files). By
using the DllImport attribute, you can invoke unmanaged code residing in
dynamic-link libraries (DLLs) from your managed C# environment.
Invoking Unmanaged Code
The DllImport attribute allows you to tag an extern method as residing in an
unmanaged DLL. When your code calls this method, the common language
runtime locates the DLL, loads it into the memory of your process, marshals
parameters as necessary, and transfers control to the address at the beginning of
the unmanaged code. This is unlike a normal program, which does not have
direct access to the memory that is allocated to it. The following code provides
an example of how to invoke unmanaged code:
[DllImport("MyDLL.dll", EntryPoint="MyFunction")]
public static extern int MyFunction(string param1);

public class MyClass( )
{
...
int result = MyFunction("Hello Unmanaged Code");
...
}

Topic Objective
To explain how to use the
DllImport attribute.
Lead-in
The DllImport attribute is
used to handle unmanaged
code.
12 Module 17 (Optional): Attributes


Using the Transaction Attribute
! To Manage Transactions in COM+
# Specify that your component be included when a
transaction commit is requested
# Use a Transaction attribute on the class that implements
the component
using EnterpriseServices;
...
[Transaction(TransactionOption.Required)]
public class MyTransactionalComponent : ServicedComponent
{
...
}
using EnterpriseServices;
...
[Transaction(TransactionOption.Required)]
public class MyTransactionalComponent : ServicedComponent
{
...
}

*****************************ILLEGAL FOR NON-TRAINER USE******************************
It is likely that, as a Microsoft Visual Basic or C++ developer working in a
Microsoft environment, you are familiar with technologies such as COM+. An
important feature of COM+ is that it allows you to develop components that can
participate in distributed transactions, which are transactions that can span
multiple databases, machines, and components.
Managing Transactions in COM+
Writing code to guarantee a correct transaction commit in a distributed
environment is difficult. However, if you use COM+, it takes care of managing
the transactional integrity of the system and coordinating events on the network.
In this case, you only need to specify that your component be included when an
application that uses your component requests a transaction commit. To make
this specification, you can use a Transaction attribute on the class that
implements the component, as follows:
using EnterpriseServices;
...
[Transaction(TransactionOption.Required)]
public class MyTransactionalComponent : ServicedComponent
{
...
}

The Transaction attribute is one of the predefined .NET Framework attributes
that the .NET Framework runtime interprets automatically.
Topic Objective
To show how to use the
Transaction attribute.
Lead-in
The Transaction attribute is
used to manage
transactions.
Module 17 (Optional): Attributes 13


" "" " Defining Custom Attributes
! Defining Custom Attribute Scope
! Defining an Attribute Class
! Processing a Custom Attribute
! Using Multiple Attributes

*****************************ILLEGAL FOR NON-TRAINER USE******************************
When you encounter a situation in which none of the predefined .NET
Framework attributes satisfy your requirements, you can create your own
attribute. Such a custom attribute will provide properties that allow you to store
and retrieve information from the attribute.
Like predefined attributes, custom attributes are objects that are associated with
one or more programmatic elements. They are stored with the metadata of their
associated elements, and they provide mechanisms for a program to retrieve
their values.
After completing this lesson, you will be able to:
! Define your own custom attributes.
! Use your own custom attributes.
Topic Objective
To introduce the topics
covered in this section.
Lead-in
You can define your own
custom attributes.
14 Module 17 (Optional): Attributes


Defining Custom Attribute Scope
! Use the AttributeUsage Tag to Define Scope
# Example
! Use the Bitwise or Operator (|) to Specify
Multiple Elements
# Example
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class MyAttribute: System.Attribute
{ ... }
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class MyAttribute: System.Attribute
{ ... }
[AttributeUsage(AttributeTargets.Method)]
public class MyAttribute: System.Attribute
{ ... }
[AttributeUsage(AttributeTargets.Method)]
public class MyAttribute: System.Attribute
{ ... }

*****************************ILLEGAL FOR NON-TRAINER USE******************************
As with some predefined attributes, you must explicitly specify the
programming element to which you want to apply a custom attribute. To do so,
you annotate your custom attribute with an AttributeUsage tag as shown in the
following example:
[AttributeUsage(target_elements)]
public class MyAttribute: System.Attribute
{ ... }

Defining Attribute Scope
The parameter to AttributeUsage contains values from the
System.AttributeTargets enumeration to specify how the custom attribute can
be used. The members of this enumeration are summarized in the following
table.
Member name Attribute can be applied to

All Any element
Assembly assembly
Class class
Constructor constructor
Delegate delegate
Enum enum
Event event
Field field
Interface interface
Method method
Module module
Topic Objective
To describe the scope of a
custom attribute.
Lead-in
You need to scope your
custom attribute to define
the elements that can use
your custom attribute.
Module 17 (Optional): Attributes 15


(continued)
Member name Attribute can be applied to

Parameter parameter
Property property
ReturnValue return value
Struct struct

Example of Using Custom Attributes
To specify that the MyAttribute custom attribute can be applied only to
methods, use the following code:
[AttributeUsage(AttributeTargets.Method)]
public class MyAttribute: System.Attribute
{
...
}

Specifying Multiple Elements
If the attribute can be applied to more than one element type, use the bitwise
or operator (|) to specify multiple target types. For example, if MyAttribute
can also be applied to constructors, the earlier code will be modified as follows:
[AttributeUsage(AttributeTargets.Method |
!AttributeTargets.Constructor)]
public class MyAttribute: System.Attribute
{
...
}

If a developer attempts to use MyAttribute in a context different than that
which is defined by AttributeUsage, the developers code will not compile.
16 Module 17 (Optional): Attributes


Defining an Attribute Class
! Deriving an Attribute Class
# All attribute classes must derive from System.Attribute,
directly or indirectly
# Suffix name of attribute class with Attribute
! Components of an Attribute Class
# Define a public constructor for the attribute class whose
arguments are the required positional parameters
# Use non-static public read-write properties for optional
named parameters

*****************************ILLEGAL FOR NON-TRAINER USE******************************
After you define the scope of a custom attribute, you need to specify the way
you want the custom attribute to behave. For this purpose, you must define an
attribute class. Such a class will define the name of the attribute, how it can be
created, and the information that it will store.
The .NET Framework SDK provides a base class, System.Attribute that you
must use to derive custom attribute classes and to access the values held in
custom attributes.
Deriving an Attribute Class
All custom attribute classes must derive from System.Attribute, either directly
or indirectly. The following code provides an example:
public class DeveloperInfoAttribute: System.Attribute
{
...
public DeveloperInfoAtribute(string developer)
public string Date
{
get { ... }
set { ... }
}
}

It is a good practice to append the name of a custom attribute class with the
suffix Attribute, as in DeveloperInfoAttribute. This makes it easier to
distinguish the attribute classes from the non-attribute classes.
Topic Objective
To describe what attribute
classes are and how they
are defined.
Lead-in
To define custom attributes,
you must first define an
attribute class.
Delivery Tip
Use the terminology
carefully. Talking about
attributes having attributes
could be confusing to some
students.
Key Points
1. Always append the suffix
Attribute to the end of
custom attribute classes.
2. Custom attribute classes
must descend from
System.Attribute.
Module 17 (Optional): Attributes 17


Components of an Attribute Class
All attribute classes must have a constructor. For example, the DeveloperInfo
attribute requires the name of the developer as a string parameter; it therefore
has a constructor that accepts a string parameter. A custom attribute must define
a single constructor that sets the mandatory information. The positional
parameter or parameters of the attribute pass this information to the constructor.
An attribute class can allow for optional values by declaring non-static public
read-write properties or fields. You can specify these optional values as named
parameters when using the attribute.
For example, the following DeveloperInfoAttribute provides the required
developer name, Bert, as a positional parameter and specifies the optional Date
parameter, 08-28-2001 as a named parameter:
[DeveloperInfoAttribute("Bert", Date="08-28-2001")]
public class MyClass
{
...
}

18 Module 17 (Optional): Attributes


Processing a Custom Attribute
! The Compilation Process
1. Searches for the Attribute Class
2. Checks the Scope of the Attribute
3. Checks for a Constructor in the Attribute
4. Creates an Instance of the Object
5. Checks for a Named Parameter
6. Sets Field or Property to Named Parameter Value
7. Saves Current State of Attribute Class

*****************************ILLEGAL FOR NON-TRAINER USE******************************
When the compiler encounters an attribute on a programming element, the
compiler uses the following process to determine how to apply the attribute:
1. Searches for the attribute class
2. Checks the scope of the attribute
3. Checks for a constructor in the attribute
4. Creates an instance of the object
5. Checks for a named parameter
6. Sets the field or property to a named parameter value
7. Saves the current state of the attribute class

To be completely accurate, the compiler actually verifies that it could create the
attribute, and then stores the information to do so in the metadata. The compiler
does not create attribute instances at compile time.
Topic Objective
To describe how the
compiler processes a
custom attribute.
Lead-in
In this topic, you will see the
process that occurs when
the compiler encounters an
attribute on a program
element.
Module 17 (Optional): Attributes 19


Example
To learn more about how the compiler handles attributes, consider the
following example:
[AttributeUsage(AttributeTargets.Class)]
public class DeveloperInfoAttribute: System.Attribute
{
...
}
.....
{
.....
}

[DeveloperInfo("Bert", Date="08-28-2001")]
public class MyClass
{
...
}

The Compilation Process
In the preceding example, when MyClass is compiled, the compiler will search
for an attribute class called DevloperInfoAttribute. If the class cannot be
located, the compiler will then search for DeveloperInfo.
After it finds DeveloperInfo, the compiler will check whether the attribute is
allowed on a class. Then it will check for a constructor that matches the
parameters specified in the attribute use. If it finds one, it creates an instance of
the object by calling the constructor with the specified values.
If there is a named parameter, the compiler matches the name of the parameter
with a field or property in the attribute class, and then sets the field or property
to the specified value. Then the current state of the attribute class is saved to the
metadata for the program element on which it is applied.

As is mentioned in the previous topic, it is a good practice to add the
suffix Attribute to the name of an attribute class. Strictly speaking, it is not
necessary to do so. Even if you omit the Attribute suffix as shown in the
example, your code will still compile correctly. However, without the Attribute
suffix there are potential issues concerning how the compiler searches for
classes. Always use the Attribute suffix.

Note
20 Module 17 (Optional): Attributes


Using Multiple Attributes
! An Element Can Have More Than One Attribute
# Define both attributes separately
! An Element Can Have More Than One Instance of The
Same Attribute
# Use AllowMultiple = true

*****************************ILLEGAL FOR NON-TRAINER USE******************************
You can apply more than one attribute to a programming element, and you can
use multiple instances of the same attribute in an application.
Using Multiple Attributes
You can apply more than one attribute to a programming element. For example,
the following code shows how you can tag the FinancialComponent class with
two attributes: DeveloperInfo and DefaultProperty:
[DeveloperInfo("Bert", Date="08-28-2001")]
[DefaultProperty("Balance")]
public class FinancialComponent:
{
...
public long Balance
{
...
}
}

Topic Objective
To describe how the same
attribute can be applied
multiple times to the same
programming element.
Lead-in
Depending upon the
circumstances, it might be
useful to use a custom
attribute multiple times on
the same element.
Module 17 (Optional): Attributes 21


Using the Same Attribute Multiple Times
The default behavior of a custom attribute does not permit multiple instances of
the attribute. However, under some circumstances it might make sense to allow
an attribute to be used on the same element more than once.
An example of this is the custom attribute DeveloperInfo. This attribute allows
you to record the name of the developer that wrote a class. If more than one
developer was involved in the development, you need to use the DeveloperInfo
attribute more than once. For an attribute to permit this, you must mark it as
AllowMultiple in the AttributeUsage attribute, as follows:
[AttributeUsage(AttributeTargets.Class, AllowMultiple=true)]
public class DeveloperInfoAttribute: System.Attribute
{
...
}

Delivery Tip
Stress that the default
behavior of a custom
attribute is that it does not
permit its multiple use.
22 Module 17 (Optional): Attributes


" "" " Retrieving Attribute Values
! Examining Class Metadata
! Querying for Attribute Information

*****************************ILLEGAL FOR NON-TRAINER USE******************************
After you have applied attributes to programming elements in your code, it is
useful to be able to determine the values of the attributes. In this section, you
will learn how to use reflection to examine the attribute metadata of a class and
query classes for attribute information.
After completing this lesson, you will be able to:
! Use reflection to examine the attribute metadata of a class.
! Query classes for attribute information.

Topic Objective
To introduce the topics
covered in this section.
Lead-in
Having learned to create a
custom attribute and apply it
to a class, you now need to
be able to query and
process attribute
information.
Module 17 (Optional): Attributes 23


Examining Class Metadata
! To Query Class Metadata Information:
# Use the MemberInfo class in System.Reflection
# Populate a MemberInfo array by using the method
System.Type.GetMembers
# Create a System.Type object by using the
typeof operator
! Example
System.Reflection.MemberInfo[ ] memberInfoArray;
memberInfoArray = typeof(MyClass).GetMembers( );
System.Reflection.MemberInfo[ ] memberInfoArray;
memberInfoArray = typeof(MyClass).GetMembers( );

*****************************ILLEGAL FOR NON-TRAINER USE******************************
The .NET Framework runtime supplies a mechanism called reflection that
allows you to query information held in metadata. Metadata is where attribute
information is stored.
Using the MemberInfo Class
The .NET Framework provides a namespace named System.Reflection, which
contains classes that you can use for examining metadata. One particular class
in this namespacethe MemberInfo classis very useful if you need to find
out about the attributes of a class.
To populate a MemberInfo array, you can use the GetMembers method of the
System.Type object. To create a System.Type object, you use the typeof
operator with a class or any other element, as shown in the following code:
System.Reflection.MemberInfo[ ] memberInfoArray;
memberInfoArray = typeof(MyClass).GetMembers( );
...

Once created, the memberInfoArray elements can be queried for metadata
information about the members of the class MyClass.

If you need more detailed information, for example, if you want to
discover the values of attributes that a method has, you can use a MethodInfo
object. In addition, there are other Info classes: ConstructorInfo, EventInfo,
FieldInfo, ParameterInfo, and PropertyInfo. Detailed information about how
to use these classes is beyond the scope of this course. For more information,
search for System.Reflection namespace in the .NET Framework SDK.


MemberInfo is actually the abstract base class of the other Info types.

Topic Objective
To describe how to access
the information inside a
class at run time.
Lead-in
Attribute information is
stored with the metadata of
a class. To gain access to it,
you first need to retrieve the
metadata for that class.
Tip
Note
24 Module 17 (Optional): Attributes


Querying for Attribute Information
! To Retrieve Custom Attribute Information:
# Use GetCustomAttributes to retrieve all attribute
information as an array
# Iterate through the array and examine the values of each
element in the array
# Use the IsDefined method to determine whether a
particular attribute has been defined for a class
System.Reflection.MemberInfo typeInfo;
typeInfo = typeof(MyClass);
object[ ] attrs = typeInfo.GetCustomAttributes(false);
System.Reflection.MemberInfo typeInfo;
typeInfo = typeof(MyClass);
object[ ] attrs = typeInfo.GetCustomAttributes(false);

*****************************ILLEGAL FOR NON-TRAINER USE******************************
After you create a MemberInfo variable typeInfo and initialize it with
typeof(MyClass), you can query it to get information about the attributes that
are applied to its associated class.
Retrieving Attribute Information
The MemberInfo object has a method called GetCustomAttributes. This
method retrieves the information about all attributes of a class and stores it as
an array, as shown in the following code:
System.Reflection.MemberInfo typeInfo;
typeInfo = typeof(MyClass);
object [ ] attrs = typeInfo.GetCustomAttributes(false);

You can then iterate through the array to find the values of the attributes that
you are interested in.
Topic Objective
To show how to use the
GetCustomAttributes
method.
Lead-in
Once you have the
MemberInfo for a class, you
can query to find out
attribute information for that
class.
For Your Information
GetCustomAttribute is an
overloaded method. The
parameter specifies whether
to search the inheritance
chain.
Module 17 (Optional): Attributes 25


Iterating Through Attributes
You can iterate through the array of attributes and examine the value of each
one in turn. In the following code, the only attribute of interest is
DeveloperInfoAttribute, and all the others are ignored. For each
DeveloperInfoAttribute found, the values of the Developer and Date
properties are displayed as follows:
...
object [ ] attrs = typeInfo.GetCustomAttributes(false);
foreach(Attribute atr in attrs) {
if (atr is DeveloperInfoAttribute) {
DeveloperInfoAttribute dia = (DeveloperInfoAttribute)atr;
Console.WriteLine("{0} {1}", dia.Developer, dia.Date);
}
}
...

GetCustomAttributes is an overloaded method. If you only want values
for that one attribute type, you can invoke this method by passing the type of
the custom attribute you are looking for through it, as shown in the following
code:
object [ ] attrs =
typeInfo.GetCustomAttributes(typeof(DeveloperInfoAttribute),
false);


Using the IsDefined Method
If there are no matching attributes for a class, GetCustomAttributes returns a
null object reference. However, to find out whether a particular attribute has
been defined for a class, you can use the IsDefined method of MemberInfo as
follows:
Type devInfoAttrType = typeof(DeveloperInfoAttribute);
if (typeInfo.IsDefined(devInfoAttrType, false)) {
Object [ ] attrs =
typeInfo.GetCustomAttributes(devInfoAttrType, false);
...
}

You can use the Microsoft Intermediate Language Disassembler to see
these attributes inside the assembly.

Tip
Note
26 Module 17 (Optional): Attributes


Demonstration: Custom Attributes

*****************************ILLEGAL FOR NON-TRAINER USE******************************
This demonstration shows how to declare a custom attribute and apply the
custom attribute to a class and to a method. The .NET Framework reflection
mechanism is then used to retrieve metadata information about the class and its
members including custom attributes. The code for this demonstration is
located in <install folder>\Democode\Mod17\CustomAttribute.
The demonstration declares a custom attribute MyAttribute and applies this
attribute both to a class named MyClass1 and to a public method of MyClass1
named MyMethod. The demonstration then uses reflection to iterate over and
print out metadata for MyClass1.
The output of this demonstration is similar to the following:
MyClass1 has 1 custom attributes
Custom Attribute MyAttribute's Name Property:
This is an example attribute associated with the class!
MyClass1


MyClass1 members:
member name: GetHashCode has 0 custom attributes:
member name: Equals has 0 custom attributes:
member name: ToString has 0 custom attributes:
member name: MyMethod has 1 custom attributes:
attribute has type: MyAttribute
Name Property: This is an example attribute associated!
with the method MyMethod in MyClass1
member name: GetType has 0 custom attributes:
member name: .ctor has 0 custom attributes:

Topic Objective
This demonstration shows
how to declare and apply
custom attributes and how
to use reflection to retrieve
custom attribute metadata.
Lead-in
This demonstration shows
how to declare and apply a
custom attribute and how to
use the .NET Framework
reflection mechanism to
retrieve this metadata
information.
Module 17 (Optional): Attributes 27


Lab 17: Defining and Using Attributes

*****************************ILLEGAL FOR NON-TRAINER USE******************************
Objectives
After completing this lab, you will be able to:
! Use the predefined Conditional attribute.
! Create a custom attribute.
! Add a custom attribute value to a class.
! Use reflection to query attribute values.

Prerequisites
Before working on this lab, you should be familiar with the following:
! Creating classes in C#
! Defining constructors and methods
! Using the typeof operator
! Using properties and indexers in C#

Estimated time to complete this lab: 45 minutes
Topic Objective
To introduce the lab.
Lead-in
In this lab, you will use the
predefined Conditional
attribute, and then create
and use a custom attribute.
Explain the lab objectives.
28 Module 17 (Optional): Attributes


Exercise 1
Using the Conditional Attribute
In this exercise, you will use the predefined Conditional attribute to
conditionally execute your code.
Conditional execution is a useful technique if you want to incorporate testing or
debugging code into a project but do not want to edit the project and remove the
debugging code after the system is complete and functioning correctly.
During this exercise, you will add a method called DumpToScreen to the
BankAccount class. This method will display the details of the account. You
will use the Conditional attribute to execute this method depending on the
value of a symbol called DEBUG_ACCOUNT.
! Apply the Conditional attribute
1. Open the Bank.sln project in the install folder\Labs\Lab17\Starter\Bank
folder.
2. In the BankAccount class, add a public void method called
DumpToScreen that takes no parameters.
The method must display the contents of the account: account number,
account holder, account type, and account balance. The following code
shows a possible example of the method:
public void DumpToScreen( )
{
Console.WriteLine("Debugging account {0}. Holder is {1}.
!Type is {2}. Balance is {3}",
this.accNo, this.holder, this.accType, this.accBal);
}

3. Make use of the methods dependence on the DEBUG_ACCOUNT
symbol.
Add the following Conditional attribute before the method as follows:
[Conditional("DEBUG_ACCOUNT")]

4. Add a using directive for the System.Diagnostics namespace.
5. Compile your code and correct any errors.

Module 17 (Optional): Attributes 29


! Test the Conditional attribute
1. Open the TestHarness.sln project in the install folder\Labs\Lab17\
Starter\TestHarness folder.
2. Add a reference to the Bank library.
a. In Solution Explorer, expand the TestHarness tree.
b. Right-click References, and then click Add Reference.
c. Click Browse, and then navigate to install folder\Labs\Lab17\
Starter\Bank\Bin\Debug.
d. Click Bank.dll, click Open, and then click OK.
3. Review the Main method of the CreateAccount class. Notice that it creates
a new bank account.
4. Add the following line of code to Main to call the DumpToScreen method
of myAccount:
myAccount.DumpToScreen( );

5. Save your work, compile the project, and correct any errors.
6. Run the test harness.
Notice that nothing happens. This is because the DumpToScreen method
has not been called.
7. Use the MSIL disassembler (ildasm), from the Visual Studio .NET
command prompt to examine install folder\Labs\Lab17\Starter\
Bank\Bin\Debug\Bank.dll.

To use Microsoft Visual Studio .NET tools within a command
prompt window, the command prompt window must have the proper
environment settings. The Visual Studio .NET Command Prompt window
provides such an environment. To run a Visual Studio .NET Command
Prompt window: on the start menu, point to All Programs, point to
Microsoft Visual Studio .NET, Visual Studio .NET Tools, and then click
Visual Studio .NET Command Prompt.

You will see that the DumpToScreen method is present in the
BankAccount class.
8. Double-click the DumpToScreen method to display the Microsoft
intermediate language (MSIL) code.
You will see the ConditionalAttribute at the beginning of the method. The
problem is in the test harness. Because of the ConditionalAttribute on
DumpToScreen, the runtime will effectively ignore calls made to that
method if the DEBUG_ACCOUNT symbol is not defined when the calling
program is compiled. The call is made, but because DEBUG_ACCOUNT
is not defined, the runtime finishes the call immediately.
Important
30 Module 17 (Optional): Attributes


9. Close the MSIL disassembler.
10. Return to the test harness. At the top of the CreateAccount.cs file, before the
first using directive, add the following code:
#define DEBUG_ACCOUNT

This defines the DEBUG_ACCOUNT symbol.
11. Save and compile the test harness, correcting any errors.
12. Run the test harness.
Notice that the DumpToScreen method displays the information from
myAccount.

Module 17 (Optional): Attributes 31


Exercise 2
Defining and Using a Custom Attribute
In this exercise, you will create a custom attribute called
DeveloperInfoAttribute. This attribute will allow the name of the developer
and, optionally, the creation date of a class to be stored in the metadata of that
class. This attribute will permit multiple use because more than one developer
might be involved in the coding of a class.
You will then write a method that retrieves and displays all of the
DevloperInfoAttribute values for a class.
! Define a custom attribute class
1. Using Visual Studio .NET, create a new Microsoft Visual C# project, using
the information shown in the following table.
Element Value

Project Type Visual C# Projects
Template Class Library
Name CustomAttribute
Location install folder\Labs\Lab17\Starter

2. Change the name and file name of class Class1 to DeveloperInfoAttribute.
Make sure that you also change the name of the constructor.
3. Specify that the DeveloperInfoAttribute class is derived from
System.Attribute.
This attribute will be applicable to classes, enums, and structs only. It will
also be allowed to occur more than once when it is used.
4. Add the following AttributeUsage attribute before the class definition:
[AttributeUsage(AttributeTargets.Class |
!AttributeTargets.Enum | AttributeTargets.Struct,
!AllowMultiple=true)]

5. Document your attribute with a meaningful summary (between the
<summary> tags). Use the exercise description to help you.
6. The DeveloperInfoAttribute attribute requires the name of the developer of
the class as a mandatory parameter and takes the date that the class was
written as an optional string parameter. Add private instance variables to
hold this information, as follows:
private string developerName;
private string dateCreated;

7. Modify the constructor so that it takes a single string parameter that is also
called developerName, and add a line of code to the constructor that assigns
this parameter to this.developerName.
8. Add a public string read-only property called Developer that can be used to
get the value of developerName. Do not write a set accessor.
32 Module 17 (Optional): Attributes


9. Add another public string property that is called Date. This property should
have a get accessor that reads dateCreated and a set accessor that writes
dateCreated.
10. Compile the class and correct any errors.
Because the class is in a class library, the compilation process will produce
a DLL (CustomAttribute.dll) rather than a stand-alone executable program.
The complete code for the DeveloperInfoAttribute class follows:
namespace CustomAttribute
{
using System;
/// <summary>
/// This class is a custom attribute that allows
/// the name of the developer of a class to be stored
/// with the metadata of that class.
/// </summary>
[AttributeUsage(AttributeTargets.Class |
!AttributeTargets.Enum | AttributeTargets.Struct,
!AllowMultiple=true)]
public class DeveloperInfoAttribute: System.Attribute

{
private string developerName;
private string dateCreated;

// Constructor. Developer name is the only
// mandatory parameter for this attribute.
public DeveloperInfoAttribute(string developerName)
{
this.developerName = developerName;
}
public string Developer
{
get
{
return developerName;
}
}

// Optional parameter
public string Date
{
get
{
return dateCreated;
}
set
{
dateCreated = value;
}
}
}
}

Module 17 (Optional): Attributes 33


! Add a custom attribute to a class
1. You will now use the DeveloperInfo attribute to record the name of the
developer of the Rational number class. Open the Rational.sln project in
the install folder\Labs\Lab17\Starter\Rational folder.
2. Perform the following steps to add a reference to the CustomAttribute
library that you created earlier:
a. In Solution Explorer, expand the Rational tree.
b. Right-click References, and then click Add Reference.
c. In the Add Reference dialog box, click Browse.
d. Navigate to the install folder\Labs\Lab17\Starter\
CustomAttribute\Bin\Debug folder, and click CustomAttribute.dll.
e. Click Open, and then click OK.
3. Add a CustomAttribute.DeveloperInfo attribute to the Rational class,
specifying your name as the developer and the current date as the optional
date parameter, as follows:
[CustomAttribute.DeveloperInfo("Your Name",
!Date="Today")]

4. Add a second developer to the Rational class.
5. Compile the Rational project and correct any errors.
6. Open a Visual Studio .NET Command Prompt window and navigate to the
install folder\Labs\Lab17\Starter\Rational\Bin\Debug folder.
This folder should contain your Rational.exe executable.
7. Run the MSIL disassembler and open Rational.exe.
8. Expand the Rational namespace in the tree view.
9. Expand the Rational class.
10. Near the top of the class, notice your custom attribute and the values that
you supplied.
11. Close the MSIL disassembler.

34 Module 17 (Optional): Attributes


! Use reflection to query attribute values
Using the MSIL disassembler is only one way to examine attribute values.
You can also use reflection in C# programs. Return to Visual Studio, and
edit the TestRational class in the Rational project.
1. In the Main method, create a variable called attrInfo of type
System.Reflection.MemberInfo, as shown in the following code:
public static void Main( )
{
System.Reflection.MemberInfo attrInfo;
...

2. You can use a MemberInfo object to hold information about the members
of a class. Assign the Rational type to the MemberInfo object by using the
typeof operator, as follows:
attrInfo = typeof(Rational);

3. The attributes of a class are held as part of the class information. You can
retrieve the attribute values by using the GetCustomAttributes method.
Create an object array called attrs, and use the GetCustomAttributes
method of attrInfo to find all of the custom attributes used by the Rational
class, as shown in the following code:
object[ ] attrs = attrInfo.GetCustomAttributes(false);

4. Now you need to extract the attribute information that is stored in the attrs
array and print it. Create a variable called developerAttr of type
CustomAttribute.DeveloperInfoAttribute, and assign it the first element
in the attrs array, casting as appropriate, as shown in the following code:
CustomAttribute.DeveloperInfoAttribute developerAttr;
developerAttr =
!(CustomAttribute.DeveloperInfoAttribute)attrs[0];

In production code, you would use reflection rather than a cast to
determine the type of the attribute.

5. Use the get accessor of the DeveloperInfoAttribute attribute to retrieve the
Developer and Date attributes and print them out as follows:
Console.WriteLine("Developer: {0}\tDate: {1}",
!developerAttr.Developer, developerAttr.Date);

6. Repeat steps 4 and 5 for element 1 of the attrs array.
You can use a loop if you want to be able to retrieve the values of more than
two attributes.
Note
Module 17 (Optional): Attributes 35


7. Compile the project and correct any errors.
The completed code for the Main method is shown in the following code:
namespace Rational
{
using System;

// Test harness
public class TestRational
{
public static void Main( )
{
System.Reflection.MemberInfo attrInfo;
attrInfo = typeof(Rational);
object[ ] attrs = attrInfo.GetCustomAttributes(false);
CustomAttribute.DeveloperInfoAttribute developerAttr;
developerAttr =
!(CustomAttribute.DeveloperInfoAttribute)attrs[0];
Console.WriteLine("Developer: {0}\tDate: {1}",
!developerAttr.Developer, developerAttr.Date);
developerAttr =
!(CustomAttribute.DeveloperInfoAttribute)attrs[1];
Console.WriteLine("Developer: {0}\tDate: {1}",
!developerAttr.Developer, developerAttr.Date);
}
}
}

Here is an alternative Main that uses a foreach loop:
public static void Main( )
{
System.Reflection.MemberInfo attrInfo;
attrInfo = typeof(Rational);
object[ ] attrs = attrInfo.GetCustomAttributes(false);

foreach (CustomAttribute.DeveloperInfoAttribute
! devAttr in attrs)
{
Console.WriteLine("Developer: {0}\tDate: {1}",
!devAttr.Developer, devAttr.Date);
}
}

8. When you run this program, it will display the names and dates that you
supplied as DeveloperInfoAttribute information to the Rational class.

36 Module 17 (Optional): Attributes


Review
! Overview of Attributes
! Defining Custom Attributes
! Retrieving Attribute Values

*****************************ILLEGAL FOR NON-TRAINER USE******************************
1. Can you tag individual objects by using attributes?
No. Attributes can be associated with classes or other types, or with
methods, parameters, return values, constructors, assemblies, delegates,
events, interfaces, properties, or fields, but not with individual objects.


2. Where are attribute values stored?
An attribute value is stored with the metadata of the programming
element that it was used with.


3. What mechanism is used to determine the value of an attribute at run time?
Reflection.


Topic Objective
To reinforce module
objectives by reviewing
key points.
Lead-in
The review questions cover
some of the key concepts
taught in the module.
Module 17 (Optional): Attributes 37


4. Define an attribute class called CodeTestAttributes that is applicable only
to classes. It should have no positional parameters and two named
parameters called Reviewed and HasTestSuite. These parameters should be
of type bool and should be implemented by using read/write properties.
using System;
[AttributeUsage(AttributeTargets.Class)]
public class CodeTestAttributes: System.Attribute
{
public bool Reviewed
{
get { return reviewed; }
set { reviewed = value; }
}
public bool HasTestSuite
{
get { return hasTestSuite; }
set { hasTestSuite = value; }
}
private bool reviewed, hasTestSuite;
}


5. Define a class called Widget, and use CodeTestAttributes from the
previous question to mark that Widget has been reviewed but has no test
suite.
[CodeTestAttributes(Reviewed=true, HasTestSuite=false)]
class Widget
{
...
}


6. Suppose that Widget from the previous question had a method called
LogBug. Could CodeTestAttributes be used to mark only this method?
No. CodeTestAttributes can only target whole classes.






THIS PAGE INTENTIONALLY LEFT BLANK

S-ar putea să vă placă și