2349 Programming The Microsoft .NET Framework With C# (Prerel
2349 Programming The Microsoft .NET Framework With C# (Prerel
Delivery Guide
Course Number: 2349A
This course is based on the prerelease Beta 2 version of Microsoft Visual Studio .NET. Content in the final release of the course may be different from the content included in this prerelease version. All labs in the course are to be completed with the Beta 2 version of Visual Studio .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, 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 Microsoft Corporation. All rights reserved. Microsoft, MS-DOS, Windows, Windows NT, ActiveX, IntelliMirror, JScript, MSDN, MSN, PowerPoint, SQL Server, Visual Basic, Visual C++, Visual C#, Visual FoxPro, Visual Studio, Win32, and Windows Media 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.
iii
Contents
Introduction
Course Materials .................................................................................................. 2 Prerequisites......................................................................................................... 3 Course Outline ..................................................................................................... 4 Microsoft Certified Professional Program ........................................................... 8 Facilities............................................................................................................. 10
iv
vii
Description
The goal of this 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 or software companies 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 proficient in the C++ or Java programming languages and have been exposed to the C# language. Students can meet these prerequisites 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 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 given programming scenarios. Use the .Net Framework class library to efficiently create and manage strings, arrays, collections, and enumerators. 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.
!"
!"
!"
!"
!"
!"
!"
!"
viii
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 via Web Services and Object Remoting.
!" !"
ix
Course Timing
The following schedule is an estimate of the course timing. Your timing may vary.
Day 1
Start 9:00 9:30 10:00 10:15 11:00 11:15 11:45 12:00 1:00 1:15 1:30 2:00 2:30 2:45 End 9:30 10:00 10:15 11:00 11:15 11:45 12:00 1:00 1:15 1:30 2:00 2:30 2:45 4:15 Module Introduction Module 1: Overview of the Microsoft .NET Framework Break Module 2: Introduction to a Managed Execution Environment Lab 2: Building a Simple .NET Application Module 3: Working with Components Lab 3.1: Creating a .NET Framework Component Lunch Module 3: Working with Components (continued) Lab 3.2: Creating a Simple Console-Based Client Module 3: Working with Components (continued) Lab 3.3: Calling a Component Through an ASP .NET Page Break Module 4: Deployment and Versioning
Day 2
Start 9:00 9:50 10:00 11:30 12:30 1:15 2:30 2:45 3:30 End 9:50 10:00 11:30 12:30 1:15 2:30 2:45 3:30 4:00 Module Lab 4: Packaging and Deployment Break Module 5: Common Type System Lunch Lab 5: Building Simple Types Module 6: Working with Types Break Lab 6: Creating Classes Module 7: Strings, Arrays, and Collections
Day 3
Start 9:00 10:30 10:45 11:45 12:45 2:00 2:15 3:25 End 10:30 10:45 11:45 12:45 2:00 2:15 3:25 4:00 Module Module 7: Strings, Arrays, and Collections (continued) Break Lab 7: Working with Strings, Enumerators, and Collections Lunch Module 8: Delegates and Events Break Lab 8: Creating a Simple Chat Server Module 9: Memory and Resource Management
Day 4
Start 9:00 10:30 10:45 11:45 12:45 1:30 2:15 2:30 3:30 End 10:30 10:45 11:45 12:45 1:30 2:15 2:30 3:30 4:15 Module Module 9: Memory and Resource Management (continued) Break Lab 9: Memory and Resource Management Lunch Module 10: Data Streams and Files Lab 10: Files Break Module 11: Internet Access Lab 11: Creating a DateTime Client/Server Application
Day 5
Start 9:00 9:30 10:15 10:30 11:30 12:30 1:20 2:20 2:35 End 9:30 10:15 10:30 11:30 12:30 1:20 2:20 2:35 3:25 Module Module 12: Serialization Lab 12: Serialization Break Module 13: Remoting and Web Services Lunch Lab 13.1: Building an Order-Processing Application by Using Remoted Servers Module 13: Remoting and Web Services (continued) Break Lab 13.2: Using a Web Service
xi
Autorun.exe. When the CD is inserted into the CD-ROM drive, or when you double-click the autorun.exe file, this file opens the CD and allows you to browse the Student Materials or Trainer Materials CD, or install Internet Explorer. Default.htm. This file opens the Trainer Materials Web page. Readme.txt. This file contains a description of the compact disc contents and setup instructions in ASCII format (non-Microsoft Word document). 2349A_MS.doc. This file is the Classroom Setup Guide. It contains a description of classroom requirements, classroom configuration, and classroom setup instructions. 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 courserelated Web sites. Errorlog. This folder contains a template that is used to record any errors and corrections that you find in the course. Fonts. This folder contains fonts that are required to view the Microsoft PowerPoint presentation and Web-based materials. Mplayer. This folder contains files that are required to install Microsoft Windows Media Player. Powerpnt. This folder contains the PowerPoint slides that are used in this course. Pptview. This folder contains the PowerPoint Viewer, which is used to display the PowerPoint slides. 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
Autorun.exe. When the CD is inserted into the CD-ROM drive, or when you double-click the autorun.exe file, this file opens the CD and allows you to browse the Student Materials CD or install Internet Explorer. Default.htm. This file opens the Student Materials Web page. It provides you with resources pertaining to this course, including additional reading, review and lab answers, lab files, multimedia presentations, and courserelated Web sites. Readme.txt. This file contains a description of the compact disc contents and setup instructions in ASCII format (non-Microsoft Word document). 2349A_MS.doc. This file is the Classroom Setup Guide. It contains a description of classroom requirements, classroom setup instructions, and the classroom configuration. AddRead. This folder contains additional reading pertaining to this course. If there are no additional reading files, this folder does not appear. Appendix. This folder contains appendix files for this course. If there are no appendix files, this folder does not appear. Democode. This folder contains demonstration code. If there is no demonstration code, the Democode folder does not appear. Fonts. This folder contains fonts that are required to view the Microsoft 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. If this course does not include any multimedia presentations, this folder does not appear. Menu. This folder contains elements for autorun.exe. Mplayer. This folder contains files that are required to install Microsoft Windows Media Player. Practices. This folder contains files that are used in the hands-on practices. If there are no practices, the Practices folder does not appear. Sampapps. This folder contains the sample applications associated with this course. If there are no associated sample applications, the Sampapps folder does not appear. Sampcode. This folder contains sample code that is accessible through the Web pages on the Student Materials CD. If there is no sample code, the Sampcode folder does not appear. Sampsite. This folder contains files that create the sample site associated with this course. If there is no sample site, the Sampsite folder does not appear.
!"
!"
!"
!"
!"
!"
!"
!"
!"
!" !"
!"
!"
!"
!"
xiii
Setup. This folder contains additional files that may be required for lab setup. If no additional files are required, the Setup folder does not appear. 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. If no Word documents are included, this folder does not appear.
!"
!"
xiv
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. 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. 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. 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. Indicate the names of keys, key sequences, and key combinations for example, ALT+SPACEBAR. 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.
bold
italic
Title Capitals
[]
{}
!
... . . .
Introduction
Contents Introduction Course Materials Prerequisites Course Outline Microsoft Certified Professional Program Facilities 1 2 3 4 8 10
This course is based on the prerelease Beta 2 version of Microsoft Visual Studio .NET. Content in the final release of the course may be different from the content included in this prerelease version. All labs in the course are to be completed with the Beta 2 version of Visual Studio .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, 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 Microsoft Corporation. All rights reserved. Microsoft, MS-DOS, Windows, Windows NT, ActiveX, IntelliMirror, JScript, MSDN, MSN, PowerPoint, SQL Server, Visual Basic, Visual C++, Visual C#, Visual FoxPro, Visual Studio, Win32, and Windows Media 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
Presentation: 30 Minutes The Introduction module provides students with an overview of the course content, materials, and logistics for course 2349A, Programming the Microsoft .NET Framework with C# (Prerelease).
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 2349A: Programming the Microsoft .NET Framework with C# (Prerelease) 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.
!"
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.
!"
Setup Provide the students with any necessary setup information for the course. Microsoft Official Curriculum Present an overview of the Microsoft Official Curriculum program and refer students to the Microsoft Official Curriculum Web page at http://www.microsoft.com/trainingandservices/
!"
!"
Microsoft Certified Professional Program Inform students about the Microsoft Certified Professional (MCP) program and the various certification options.
!"
Introduction
Introduction
Topic Objective
To introduce yourself, establish credibility, meet students, and set student expectations for the course.
! ! ! ! ! ! !
Name Company Affiliation Title/Function Job Responsibility Programming Experience .NET Framework Experience Expectations for the Course
Lead-in
Good morning. Welcome to Course 2349A: Programming the Microsoft .NET Framework with C# (Prerelease). 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.
Introduction
Course Materials
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...
Name Card Student Workbook Student Materials Compact Disc Course Evaluation
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.
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. Note 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.
!"
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.
!"
Course evaluation. To provide feedback on the instructor, course, and software product, send e-mail to mstrain@microsoft.com. Be sure to type Course 2349A in the subject line. Your comments will help us improve future courses. To provide additional comments or inquire about the Microsoft Certified Professional program, send e-mail to mcp@msprograms.com.
Introduction
Prerequisites
Topic Objective
To present and describe the prerequisites for this course.
! !
Lead-in
The following prerequisite knowledge is needed for this course.
Proficiency in the C++ or Java Programming Languages Some Exposure to the C# Language
Proficiency in the C++ or Java programming languages Some exposure to the C# language Students can meet the prerequisites by taking Course 2124A, Introduction to C# Programming for the Microsoft .NET Platform.
Introduction
Course Outline
Topic Objective
To provide an overview of each module and what students will learn.
! !
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
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.
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 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 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.
Introduction
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 given programming scenarios.
Introduction
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 Web Services
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
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 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 Web Services and Object Remoting.
Introduction
Lead-in
The Microsoft Certified Professional Program includes these certifications
http://www.microsoft.com/trainingandservices/
Microsoft Certified Systems Engineer (MCSE) Microsoft Certified Database Administrator (MCDBA) Microsoft Certified Solution Developer (MCSD) Microsoft Certified Professional + Site Building (MCP + Site Building) Microsoft Certified Professional (MCP) Microsoft Certified Trainer (MCT)
For More Information See the Certification section of the Web page provided on the compact disc or the Microsoft Training and Certification Web site at http://www.microsoft.com/trainingandservices/ You can also send e-mail to mcp@msprograms.com if you have specific certification questions.
Introduction
10
Introduction
Facilities
Topic Objective
To inform students of class logistics and rules for the training site.
Class Hours
Lead-in
Before we start, lets go over the class logistics.
Building Hours
Phones
Parking
Messages
Rest Rooms
Smoking
Meals
Recycling
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 course is based on the prerelease Beta 2 version of Microsoft Visual Studio .NET. Content in the final release of the course may be different from the content included in this prerelease version. All labs in the course are to be completed with the Beta 2 version of Visual Studio .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, 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 Microsoft Corporation. All rights reserved. Microsoft, MS-DOS, Windows, Windows NT, ActiveX, IntelliMirror, JScript, MSDN, MSN, PowerPoint, SQL Server, Visual Basic, Visual C++, Visual C#, Visual FoxPro, Visual Studio, Win32, and Windows Media 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.
iii
Describe the .NET Framework and its components. Explain the relationship between the .NET Framework Class Library and namespaces.
Required Materials
To teach this module, you need the following materials: Microsoft PowerPoint file 2349A_01.ppt
Preparation Tasks
To prepare for this module, you should: Read all of the materials for this module.
iv
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 C# to teach the .NET Framework. Only minimal coverage of Web Services, user interfaces, ADO.NET, and ASP.NET is provided. Other courses will cover these technologies in more detail.
!"
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 2349A, Programming the Microsoft .NET Framework with C# (Prerelease). 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).
!"
What Is a Web Service? This slide is the key slide for explaining 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 Developers Kit (SDK) and Microsoft Visual Studio .NET provide tools to simplify the creation of Web Services. Note that Web Services are appropriate for internal applications as well as external applications. An enterprise is likely to run multiple platforms; 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 Web Services.
Web Forms and 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 2349A, Programming the Microsoft .NET Framework with C# (Prerelease), only in terms of versioning. Most of the System.Reflection namespace is not covered. Also explain that the ADO.NET namespaces and security namespaces are not taught in this course.
Overview
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.
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.
Lead-in
In this section, you will learn about the Microsoft .NET Framework.
The .NET Framework Common Language Runtime The .NET Framework Class Library ADO.NET: Data and XML What is a Web Service? Web Forms and Services
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 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 a Web Service? Web Forms and Services
Lead-in
The .NET Framework is an architecture consisting of a runtime, the class library, and language support.
User Interface
ADO.NET: Data and XML .NET Framework Class Library Common Language Runtime
Message Queuing COM+ (Transactions, Partitions, Object Pooling) IIS WMI
Win32
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 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.
ADO.NET
ADO.NET is the next generation of 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).
Web Services
The .NET Framework provides tools and classes for building, testing, and distributing 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
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.
.NET Framework Class Library Support Thread Support Type Checker Security Engine MSIL to Native Compilers Code Manager Class Loader COM Marshaler Exception Manager Debugger Garbage Collection
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.
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 Class loader Microsoft Intermediate Language (MSIL) to native compiler Code manager Garbage collection Security engine Debugger Type checker Exception manager Thread support COM marshaler .NET Framework class library support Description Manages metadata, and the loading and layout of classes. Converts MSIL to native code on a just-in-time basis.
Manages code execution. Provides automatic lifetime management of all of objects in the .NET Framework, garbage collection is a multiprocessor, scalable garbage collector. Provides evidence-based security, based on user identity and the origin of the code. Enables the developer to debug an application and trace the execution of code. Does not allow unsafe casts or uninitialized variables. MSIL can be verified to guarantee type safety. Provides structured exception handling, which is integrated with Windows Structured Exception Handling (SEH). Error reporting has been improved. Provides classes and interfaces that enable multithreaded programming. Provides marshaling to and from COM. Integrates code with the runtime that supports the .NET Framework class library.
Enables cross-language inheritance and debugging Integrates well with tools Enhances developer productivity by reducing the number of APIs to learn
Lead-in
In this topic, you will learn how .NET Framework class library exposes features of the runtime and provides other high-level services.
! !
Makes it easy to add or modify framework features Allows creation of secure applications
Is Secure
$
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.
Is Extensible
You can extend the library by creating your own classes and compiling them into libraries. If designed properly, your class libraries will also be objectoriented 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.
Lead-in
The .NET Framework provides a new set of ADO.NET classes to handle data.
Serialization
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 Web Services, including such features as moving back and forth from objects to an XML representation.
Lead-in
Web Services are the fundamental building blocks of the .NET platform.
!
Web Service
A programmable application component accessible via standard Web protocols
Universal Description, Discovery, and Integration
Provide a Directory of Services on the Internet Web Services are defined in terms of the formats and ordering of messages Web Services consumers can send and receive messages using XML Built using open Internet protocols
UDDI
WSDL
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. Web Services are units of application logic that provide data and services to other applications. Applications access 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 Web Service is implemented.
SOAP
SOAP defines how messages are formatted, sent, and received when working with Web Services. SOAP is also an industry standard that is built on XML and HTTP. Any platform that supports the SOAP standard can support Web Services.
10
11
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.
Services Description Discovery Protocols Caching Configuration
ASP.NET
System.Web
UI HtmlControls WebControls
Security SessionState
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 Web Services provide the building blocks for constructing distributed Web-based applications. 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 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 Web Services and Web UIs.
System.Web.Services
The System.Web.Services namespace has classes that handle 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.
12
# Overview of Namespaces
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.
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:
!" !"
13
Namespaces
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.
Collections Configuration Diagnostics Globalization IO Net Reflection Resources
System
Security ServiceProcess Text Threading Runtime .InteropServices .Remoting .Serialization
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.
14
Module 10
$
Module 3
$ $
Module 11
$ $
Lead-in
This course covers many of the System namespaces. Some namespaces not covered are the System.Data and System.Security namespaces.
Module 4
$
Module 12
$
Module 7
$ $
Module 13
$ $
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.
15
Review
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.
1. List the components of the .NET Framework. The common language runtime, .NET Framework class library, data and 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 a Web Service? Web Services are programmable Web components 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.
This course is based on the prerelease Beta 2 version of Microsoft Visual Studio .NET. Content in the final release of the course may be different from the content included in this prerelease version. All labs in the course are to be completed with the Beta 2 version of Visual Studio .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, 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 Microsoft Corporation. All rights reserved. Microsoft, MS-DOS, Windows, Windows NT, ActiveX, IntelliMirror, JScript, MSDN, MSN, PowerPoint, SQL Server, Visual Basic, Visual C++, Visual C#, Visual FoxPro, Visual Studio, Win32, and Windows Media 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.
iii
Create simple console applications in C#. Explain how code is compiled and executed in a managed execution environment. Explain the concept of garbage collection.
!"
Required Materials
To teach this module, you need the following materials:
!" !"
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.
iv
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.
Running the resulting executable file will generate the following output:
Hello World using C#!
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 Developers 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.
Overview
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.
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 Developers 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.
!"
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.
Demonstration: Hello World Using a Namespace Defining a Namespace and a Class Entry Points, Scope, and Declarations Console Input and Output Case Sensitivity
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. 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 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.
Lead-in
In this demonstration, you will learn how to build a simple application in C#.
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.
Using a 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#:
using System.IO; using System.IO; ... ... FileStream aFileStream; FileStream aFileStream;
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 obviates 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.
Lead-in
C# supports the creation of custom namespaces and classes within those namespaces.
namespace CompCS { namespace CompCS { public class StringComponent { public class StringComponent { ... ... } } } }
C# supports the creation of custom namespaces and classes within those namespaces. Tip 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.
Lead-in
Every executable program must contain an external entry point, where the application begins its execution.
In C#, Objects Must Be Declared Before They Can Be Used and Are Instantiated Using the New Keyword
Lib.Comp myComp = new Lib.Comp(); Lib.Comp myComp = new Lib.Comp();
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.
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.
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. 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#!); } }
Case Sensitivity
Topic Objective
To describe case sensitivity issues in programming languages.
!
Lead-in
C++ and C# are casesensitive, but Visual Basic is not case-sensitive.
Components should be fully usable from both casesensitive and case-insensitive languages Case should not be used to distinguish between identifiers within a single name scope
C++ and C# are case-sensitive, but Microsoft Visual Basic is not casesensitive. 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 casesensitive languages. This constraint does not apply to your definitions of private classes, private methods on public classes, or local variables.
Note 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 basic 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.
10
Compiler Options The Process of Managed Execution Metadata Microsoft Intermediate Language Assemblies Common Language Runtime Tools Demonstration: Using the MSIL Dissembler Just-In-Time Compilation Application Domains Garbage Collection
Lead-in
Most aspects of programming in the .NET Framework are the same for all compatible languages; each supported language compiler produces selfdescribing, managed Microsoft intermediate language (MSIL) code.
Most aspects of programming in the .NET Framework are the same for all compatible languages; each supported language compiler produces selfdescribing, managed Microsoft intermediate language (MSIL) code. All managed code runs using the common language runtime, which provides crosslanguage integration, automatic memory management, cross-language exception handling, enhanced security, and a consistent and simplified programming model. 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.
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.
11
Compiler Options
Topic Objective
To introduce compiler options in C#.
!
Lead-in
The .NET Framework includes a command line compiler for C#.
The .NET Framework includes a command line compiler for C#. The filename 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 HelloCS.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, HelloCS.exe.
12
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:HelloCS.exe /t:exe HelloCS.cs
The /t switch is equivalent to the /target switch. For more information about compiler options, see the .NET Framework SDK documentation.
13
Compiler Compiler
Class Loader Class Loader JIT Compiler JIT Compiler with optional with optional verification verification Trusted, pre-JITed code only
Managed Native Code
Lead-in
In the .NET Framework, the common language runtime provides the infrastructure for a managed execution environment.
Runtime Engine
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.
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.
!"
14
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 common language runtimes fundamental unit of sharing, deployment, security, and versioning. 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 justin-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.
15
Metadata
Topic Objective
To explain how metadata is used in the common language runtime.
! ! !
Declarative Information Emitted at Compile Time Included with All .NET Framework Files and Assemblies Metadata Allows the Runtime to:
$ $ $ $
Lead-in
Every compiler that targets the common language runtime is required to emit full metadata into every managed module.
Load and locate code Enforce code security Generate native code at runtime Provide reflection
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.
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.
16
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 2349A, Programming the Microsoft .NET Framework with C# (Prerelease).
17
Lead-in
Microsoft intermediate language, sometimes called managed code, is the set of instructions that the compiler produces as it compiles source code.
Produced by Each Supported Language Compiler Converted to Native Language by the Common Language Runtime's JIT Compilers
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.
18
Assemblies
Topic Objective
This topic introduces the concept of an assembly and the role of the assembly manifest.
Managed Module Managed Module (MSIL and Metadata) (MSIL and Metadata) Managed Module Managed Module (MSIL and Metadata) (MSIL and Metadata)
Assembly Assembly
Lead-in
The common language runtime uses an assembly as the functional unit of sharing and reuse.
.html .gif Multiple Managed Modules and Resource Files Are Compiled to Produce an Assembly
Manifest Manifest
Resource Files
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.
19
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 singlefile 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.
20
Lead-in
The common language runtime provides two tools that you can use together to test and debug MSIL code.
MSIL Assembler (ilasm.exe) produces a final executable binary MSIL Disassembler (ildasm.exe) inspects metadata and code of a managed binary
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.
21
Lead-in
This demonstration shows how to use the MSIL Disassembler to view an assemblys metadata.
This demonstration shows how to use the MSIL Disassembler to view an assemblys metadata.
22
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:
23
Just-In-Time Compilation
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.
MSIL converted to native code as needed Resulting native code stored for subsequent calls JIT compiler supplies the CPU-specific conversion
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.
24
Application Domains
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.
!
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
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. 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.
25
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.
!"
Note 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. Crossdomain calls use the same remote call infrastructure as calls between two processes or between two computers.
26
Lead-in
In this demonstration, you will see how a single-file private assembly is loaded and executed.
Delivery Tip
To launch 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.
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 entrypoint (the main method in the case of a C# program), and transfers control to this entry point.
!"
!"
!"
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.
27
Garbage Collection
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.
!
Garbage Collection Provides Automatic Object Memory Management in the .NET Framework You No Longer Need to Track and Free Object Memory
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 2349A, Programming the Microsoft .NET Framework with C# (Prerelease).
28
Lead-in
In this lab, you will learn how to write, compile, and run a simple application in C# and Visual Basic, and how to use the MSIL Disassembler to examine an assembly.
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.
29
When you press ENTER, the program outputs the text Hello and whatever text you typed as input.
30
2. Open HelloLabCS.exe and double-click the Manifest icon. 3. Note the following: a. The externally referenced library named mscorlib b. The assembly name HelloLabCS c. Version information (for the HelloLabCS assembly and mscorlib) 4. Close the Manifest window, double-click the MainApp icon, double-click the Main icon, and view the MSIL and source code.
31
Review
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.
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.
32
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 course is based on the prerelease Beta 2 version of Microsoft Visual Studio .NET. Content in the final release of the course may be different from the content included in this prerelease version. All labs in the course are to be completed with the Beta 2 version of Visual Studio .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, 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 Microsoft Corporation. All rights reserved. Microsoft, MS-DOS, Windows, Windows NT, ActiveX, IntelliMirror, JScript, MSDN, MSN, PowerPoint, SQL Server, Visual Basic, Visual C++, Visual C#, Visual FoxPro, Visual Studio, Win32, and Windows Media 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.
iii
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.
!"
!"
Required Materials
To teach this module, you need the following materials: Microsoft PowerPoint file 2349A_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.
iv
Demonstration
This section provides demonstration procedures that will not fit in the margin notes or are not appropriate for the student notes.
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 listbox should be filled with four C# strings and four Visual Basic strings.
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
vii
viii
Module Strategy
Use the following strategy to present this module:
!"
An Introduction to Key .NET Framework Technologies Briefly introduce Windows Forms, Web Forms, and Web Services. Explain that you will show an example of a Windows Form in this module. Tell students that they will learn about Web Services in Module 13, Remoting and Web Services, in Course 2349A, Programming the Microsoft .NET Framework with C# (Prerelease).
!"
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 standalone client applications with that of a 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.
Overview
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.
! ! !
An Introduction to Key .NET Framework Development Technologies Creating a Simple .NET Framework Component Creating a Simple Console Client Creating an ASP.NET Client
In this module, you will learn how to create a small client/server application in C#. The steps 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 2349A, Programming the Microsoft .NET Framework with C# (Prerelease), the content of this module is based on the Introduction to Developing with the .NET Framework tutorial in the .NET Framework Software Developers 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.
!"
!"
Lead-in
To develop applications in the .NET Framework, you should be familiar with Windows Forms, ASP.NET Web Forms, and Web Services.
To develop applications in the .NET Framework, you should be familiar with the following technologies: Windows Forms, ASP.NET Web Forms, and 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.
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.
BETA MATERIALS FOR MICROSOFT CERTIFIED TRAINER PREPARATION PURPOSES ONLY
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.
Lead-in
In this section, you will learn how to use C# to create a simple component.
Using Namespaces and Declaring the Class Creating the Class Implementation Implementing Structured Exception Handling Creating a Property Compiling the Component
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. Delivery Tip
If you prefer, you can use Notepad to demonstrate the code in this section, instead of discussing the content of each slide.
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#.
Lead-in
To create a new namespace that encapsulates the classes that you will be creating, you use the namespace statement.
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 {...}
Lead-in
The component provides a wrapper for an array of strings.
!
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.
public string GetString(int index) {...} public string GetString(int index) {...}
!
Lead-in
The component uses the GetString method to return the strings in the stringSet array.
if((index < 0) || (index >= stringSet.Length)) { if((index < 0) || (index >= stringSet.Length)) { throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException(); } } return stringSet[index]; return stringSet[index]; ! Exceptions May Be Caught by the Caller in try, catch, finally block
!
Programs must be able to uniformly handle errors and exceptions that occur during execution. The common language runtime helps you design errortolerant 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 errorhandling 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.
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 block. 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 } catch(Exception e) // place code that } finally { // place code that } throw exceptions { handles exceptions
In general, it is good programming practice to catch a specific type of exception rather than using the general catch statement above 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.
Creating a Property
Topic Objective
To explain how to create a property.
!
Create a Read-Only Count Property to Get the Number of String Elements in the stringSet Array
public int Count { public int Count { get { return stringSet.Length; } get { return stringSet.Length; } } }
Lead-in
To provide the number of string elements in the stringSet array in C#, you create a read-only property called Count.
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; } }
10
Lead-in
To compile the source code, first you must save the source file.
Otherwise, an executable with a .dll file extension is created instead of a DLL library
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:
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.
11
Lead-in
In this lab, you will learn how to create a simple .NET Framework component in C#.
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.
12
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.
13
Lead-in
In this section, you will learn how to write a simple console application that calls a .NET Framework runtime-compatible component.
Using the Libraries Instantiating the Component Calling the Component Building the Client
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.
14
Lead-in
By using the namespaces in the program, you can reference types in the library without fully qualifying the type name.
If Multiple Namespaces Contain the Same Type Name, Create a Namespace Alias to Remove Ambiguity
using CSStringComp = CompCS.StringComponent; using CSStringComp = CompCS.StringComponent; using VBStringComp = CompVB.StringComponent; using VBStringComp = CompVB.StringComponent;
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 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;
15
Declare a Local Variable of Type StringComponent Create a New Instance of the StringComponent Class
Lead-in
To instantiate the StringComponent class, you declare a local variable of type StringComponent and create a new instance of StringComponent.
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 CompCS; class MainApp { public static void Main() { CompCS.StringComponent myCSStringComp = new CompCS.StringComponent(); ... } }
16
Iterate over All the Members of StringComponent and Output the Strings to the Console
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.
for (int index = 0; for (int index = 0; index < myCSStringComp.Count; index++) { index < myCSStringComp.Count; index++) { Console.WriteLine Console.WriteLine (myCSStringComp.GetString(index)); (myCSStringComp.GetString(index)); } }
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 CompCS; class MainApp { public static void Main() { StringComponent myCSStringComp = new StringComponent(); // 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)); } } }
17
Lead-in
To build the client, you can compile the source code from a command prompt window.
Use the /reference Switch to Reference the Assemblies That Contain the StringComponent Class
csc /reference:CompCS.dll,CompVB.dll! csc /reference:CompCS.dll,CompVB.dll! /out:ClientCS.exe ClientCS.cs /out:ClientCS.exe ClientCS.cs
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
18
Lead-in
In this lab, you will learn how to create a simple console-based client application that calls a component.
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.
19
!" write the code to call the C# and Visual Basic components To
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.
20
21
Lead-in
In this demonstration, you will see how to create a Windows Forms client.
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 listbox on a form the string output that was displayed in the system console in previous examples.
using using using using using using System; System.Drawing; System.Collections; System.ComponentModel; System.Windows.Forms; System.Data;
using CSStringComp = CompCS.StringComponent; using VBStringComp = CompVB.StringComponent; namespace WinForm_Client { /// <summary> /// Summary description for Form1. /// </summary> 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;
22
23
24
25
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.
Writing the HTML for the ASP.NET Application Coding the Page_Load Event Handler Generating the HTML Response
In this module, you have learned how to create two standalone 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 standalone 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.
26
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.
The .NET Framework provides the capability to execute code that was used in the preceding standalone 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 Hypertext Transfer Protocol (HTTP) request; and the page itself is compiled dynamically, unlike the standalone 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 2349A_mod3.htm file from the Media folder.
Tell students that they can view the animation again later for themselves by opening the 2349A_mod3.htm file from the Media folder.
27
<%@ Page Language="C#" Description="ASP.NET Client" %> <%@ Page Language="C#" Description="ASP.NET Client" %>
!
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.
An ASP.NET file is a text file that contains markup syntax for coding serverside 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.
The following table describes the two @ Page directive attributes that are used in the sample client ASP.NET page.
Attribute Language Description Description Language that is used when compiling all <% %> and <%= %> blocks within a page. Can be Visual Basic, C#, or Microsoft JScript .NET. Provides a text description of the page. Supports any string description.
28
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.
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.
29
Lead-in
The Page_Load event contains most of the code for the sample client program.
The Page_Load event contains most of the code for the sample client program.
30
Notice that the client code that calls the string component is very similar to the code for the standalone 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. Note 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.
31
Lead-in
The following example shows how to create the HTML response by using the Message object.
<body> <body> <span id="Message" runat=server/> <span id="Message" runat=server/> </body> </body>
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.
32
<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>
33
Lead-in
In this demonstration, you will see how to test the ASP.NET page.
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.
34
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.
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.
35
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.
BETA MATERIALS FOR MICROSOFT CERTIFIED TRAINER PREPARATION PURPOSES ONLY
36
37
38
Review
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.
! ! !
An Introduction to Key .NET Framework Development Technologies Creating a Simple .NET Framework Component Creating a Simple Console Client Creating an ASP.NET Client
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.
This course is based on the prerelease Beta 2 version of Microsoft Visual Studio .NET. Content in the final release of the course may be different from the content included in this prerelease version. All labs in the course are to be completed with the Beta 2 version of Visual Studio .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, 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 Microsoft Corporation. All rights reserved. Microsoft, MS-DOS, Windows, Windows NT, ActiveX, IntelliMirror, JScript, MSDN, MSN, PowerPoint, SQL Server, Visual Basic, Visual C++, Visual C#, Visual FoxPro, Visual Studio, Win32, and Windows Media 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.
iii
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.
Required Materials
To teach this module, you need the following materials: Microsoft PowerPoint file 2349A_04.ppt
Preparation Tasks
To prepare for this module, you should:
!" !"
Read all of the materials for this module. Complete the lab.
iv
Module Strategy
Use the following strategy to present this module:
!"
Introduction to Application Deployment Introduce common concepts, such as the Microsoft .NET Frameworks 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 standalone 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 2349A, Programming the Microsoft .NET Framework with C# (Prerelease).
!"
Related Topics and Tools 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. You should simply inform the students of these topics and encourage them to look for more information in the .NET Framework Software Developers 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.
Overview
Topic Objective
To provide an overview of the module topics and objectives.
! ! !
Introduction to Application Deployment Application Deployment Scenarios Related Topics and Tools
Lead-in
In this module, you will learn about packaging and deployment of Microsoft .NET Framework applications and assemblies.
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 2349A, Programming the Microsoft .NET Framework with C# (Prerelease). Because this course is an introduction to programming in the .NET Framework, you should spend some time reading the .NET Framework Software Developers 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.
Lead-in
You can deploy a .NET Framework application in several ways.
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 against the same .NET Framework runtime. This section introduces common concepts, such as the .NET Frameworks 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.
Common Concepts
Topic Objective
To review the organization of the .NET Framework class library and assemblies, and the role of the assembly manifest.
!
Organized in a hierarchy of namespaces Stored in PE files, such as DLLs and EXEs Fully described by metadata 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
Lead-in
The .NET Framework provides a common class library that is organized into a hierarchical tree of namespaces.
Assemblies:
$ $ $ $
As you learned in Module 2, Introduction to a Managed Execution Environment, in Course 2349A, Programming the Microsoft .NET Framework with C# (Prerelease), in addition to the common language runtime, the .NET Framework provides 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. For Your Information
This topic reviews information that was covered in Module 2. Do not spend much time on this topic. Simply present the material as the starting point for a discussion of deployment and versioning.
Simple Applications
Topic Objective
To describe how easily a application can be executed in the .NET Framework.
! !
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
$
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.
! !
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. Simply deleting the executable file, if it was copied locally, is sufficient to uninstall the application and leave zero footprint on the computer.
Componentized Applications
Topic Objective
To describe how assemblies are handled in componentized applications.
!
Lead-in
Componentized applications are only slightly more complex than applications.
!
Same as a simple application Deployed into a common subdirectory Require a strong name and version information Deployed into the global assembly cache
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 uninstall 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.
Configuration
$
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.
!
Maintained in plain-text files 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
Deployment
$
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.
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
Lead-in
This section shows how to deploy a variety of applications, from simple applications to applications that make use of assembly versioning.
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.
A Simple Application
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#.
!
Use the Microsoft Intermediate Language Disassembler (Ildasm.exe) to Examine the Assembly Manifest
$ $ $
Version information Imported types Exported types Running the executable file directly from a file server, or Installing it locally by copying the file Uninstall the application by deleting the file
In Module 2, Introduction to a Managed Execution Environment, in Course 2349A, Programming the Microsoft .NET Framework with C# (Prerelease), you looked at the simplest .NET Framework application, the traditional Hello World application written in C#. The C# source code for that program, HelloCS, 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.
The simple Hello World application highlights a particularly important concept behind programming for the .NET Framework. The application is clearly selfdescribing: 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. To see additional information about the application, double-click the Manifest icon. The following window appears:
10
The preceding illustration shows the manifest, which contains information about the assembly, including the version (not yet set), and external libraries, and types within those libraries, that 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 impact on the client workstation. There is also nothing to clean up because there is nothing to uninstall on the client workstation. As you would expect, HelloCS.exe can also be copied to a local volume. In this scenario, you can uninstall the program by simply deleting the file, and as before, nothing would remain on the workstation. Whether you run HelloCS.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 HelloCS.exe to stop functioning.
11
A Componentized Application
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.
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.
12
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; } } } }
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]); } // ... } }
The MSIL disassembler displays the Stringer.dll and shows all of the members, as in the following illustration.
14
Like HelloCS.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 uninstall the application, you need only delete the two files.
15
Lead-in
The .NET Framework provides a configuration mechanism that allows administrators to specify a directory from which to load private assemblies.
<configuration> <configuration> <runtime> <runtime> <assemblyBinding <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> xmlns="urn:schemas-microsoft-com:asm.v1"> <probing privatePath=MyStringer"/> <probing privatePath=MyStringer"/> </assemblyBinding> </assemblyBinding> </runtime> </runtime> </configuration> </configuration>
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.
16
The Client.exe configuration file is called Client.exe.config. It specifies a privatePath tag, as shown in the following example:
<configuration> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <probing privatePath="MyStringer"/> </assemblyBinding> </runtime> </configuration>
Important 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 tag to determine where to look for componentsin addition to looking in the application directory. Note 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 uninstalls 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.
17
A Strong-Named Assembly
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.
#if STRONG #if STRONG [assembly: System.Reflection.AssemblyVersion("1.0.0.0")] [assembly: System.Reflection.AssemblyVersion("1.0.0.0")] [assembly: System.Reflection.AssemblyKeyFile("orgKey.snk")] [assembly: System.Reflection.AssemblyKeyFile("orgKey.snk")] #endif #endif
$
Compile:
csc /define:STRONG /target:library /out:AReverser.dll! csc /define:STRONG /target:library /out:AReverser.dll! AReverser.cs AReverser.cs
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 Frameworks solution to these problems.
18
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. 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. Note The code details of AReverser are not relevant to deployment issues and therefore are not shown. This modules lab contains the complete code details.
After compiling the new AReverser assembly, you can examine the metadata by using the MSIL disassembler. The MSIL disassembler indicates that the assembly lacks an originator and 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
19
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
Lead-in
While components can easily be shared by related applications simply 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.
gacutil /l gacutil /l
!
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 simply 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 and 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.
Note 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 entering:
gacutil /l
21
You can also examine the system assembly cache by navigating to the \WindowsDirectory\Assembly directory and by using the cache shell extension.
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
A Versioned Assembly
Topic Objective
To discuss the physical representation and logical mapping of the compatibility version number.
!
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
$
Lead-in
The final packaging and deployment example in this module involves updating the shared assembly to a new version.
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>
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
BETA MATERIALS FOR MICROSOFT CERTIFIED TRAINER PREPARATION PURPOSES ONLY
23
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
Specify version 2.0.0.0 for one and 2.0.1.0 for the other For AReverser_v2.0.0.0\AReverser.cs:
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.
#if STRONG #if STRONG [assembly: System.Reflection.AssemblyVersion("2.0.0.0")] [assembly: System.Reflection.AssemblyVersion("2.0.0.0")] [assembly: System.Reflection.AssemblyKeyFile("orgVerKey.snk")] [assembly: System.Reflection.AssemblyKeyFile("orgVerKey.snk")] #endif #endif
!
Build
csc /define:STRONG /target:library! csc /define:STRONG /target:library! /out:AReverser_v2.0.0.0\AReverser.dll! /out:AReverser_v2.0.0.0\AReverser.dll! AReverser_v2.0.0.0\AReverser.cs AReverser_v2.0.0.0\AReverser.cs
!
.assembly AReverser .assembly AReverser { ... { ... .publickey = (00 24 ... 82 B1 F2 A0 ) .publickey = (00 24 ... 82 B1 F2 A0 ) .hash algorithm 0x00008004 .hash algorithm 0x00008004 .ver 2:0:1:0 .ver 2:0:1:0
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
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
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 from 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
Binding Policy
Topic Objective
To discuss binding policy.
!
Policy Resolution
$
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.
!
Allows an assembly reference, specified at compile time, to be modified after the application has been deployed without recompiling Application-policy resolution Publisher-policy resolution Administrator-policy resolution Note: XML is case-sensitive
! !
Version Numbers of Assemblies That Are Not Strong-Named Are Not Checked Configuration File Tag Examples $ privatePath $ bindingRedirect
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 tag 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. Note The version numbers of assemblies that are not strong-named are not checked. Publisher-policy resolution allows shared-component vendors to make compatibility statements among different revisions of their software. These perassembly configuration files are distributed as strong-named assemblies and are installed into the global assembly cache (GAC) 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.
27
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 XMLbased 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
Lead-in
After you have configured both versions of the AReverser assembly to use strong names, you can deploy those versions.
!
Compile the VerClient Executable and Specify Version 2.0.0.0 of the AReverser Component
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
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.
This option allows an administrator to reconfigure an application without having to have it recompiled.
30
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>
Note 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. Important The Sn.exe switches to obtain the publicKeyToken do not work correctly in some of the earlier .NET Framework versions, such as build 9188. Key Points
Reinforce the fact that the configuration files XML tags are case-sensitive.
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.0compatible 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"/>
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.
31
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
32
Lead-in
This section briefly introduces additional topics that are related to deployment and versioning.
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.
33
Related Topics
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.
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 simply inform the students of these topics and encourage them to look for more information in the provided references.
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.
34
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.
35
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)
Lead-in
The .NET Framework SDK includes several useful tools for examining assemblies and working with the global assembly cache.
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.
The preceding command removes only the hello assembly that matches the fully specified version number, culture, and public key.
36
The following command lists the contents of the global assembly cache:
gacutil /l
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. Tip Place a shortcut to Ildasm.exe in your SendTo folder.
37
The Assembly Binding Log Viewer displays details for failed 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.
38
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.
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.
39
2. Run the Assembly Binding Log Viewer by executing Fuslogvw.exe. Then select the Client.exe application, and view the log entry.
40
3. 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
41
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.
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.
42
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.
43
Review
Topic Objective
To reinforce module objectives by reviewing key points.
! ! !
Introduction to Application Deployment Application Deployment Scenarios Related Topics and Tools
Lead-in
The review questions cover some of the key concepts taught in the module.
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.
44
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 course is based on the prerelease Beta 2 version of Microsoft Visual Studio .NET. Content in the final release of the course may be different from the content included in this prerelease version. All labs in the course are to be completed with the Beta 2 version of Visual Studio .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, 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 Microsoft Corporation. All rights reserved. Microsoft, MS-DOS, Windows, Windows NT, ActiveX, IntelliMirror, JScript, MSDN, MSN, PowerPoint, SQL Server, Visual Basic, Visual C++, Visual C#, Visual FoxPro, Visual Studio, Win32, and Windows Media 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.
iii
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.
!"
Required Materials
To teach this module, you need the following materials: Microsoft PowerPoint file 2349A_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.
Overview
Topic Objective
To provide an overview of the module topics and objectives.
! ! !
An Introduction to the Common Type System Elements of the Common Type System Object-Oriented Characteristics
Lead-in
In this module, you will learn about the Common Type System and object-oriented programming.
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.
!"
Lead-in
In this section, you will learn about the architecture of the Common Type System.
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.
Lead-in
A type defines characteristics for a set of values.
Primitive Types Built-in Types Interface Types Contract specifying method and property signature
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 Public Equals Public GetHashCode Description Determines whether the specified object is the same instance as the current object Serves as a hash function for a particular type; suitable for use in hashing algorithms and data structures, such as a hash table Gets the type of the object Returns a string that represents the current object Allows an object to attempt to free resources and perform other cleanup operations before the object is reclaimed by garbage collection Creates a shallow copy of the current object
Protected MemberwiseClone
For more information about the methods of System.Object, see Module 6, Working with Types, in Course 2349A, Programming the Microsoft .NET Framework with C# (Prerelease).
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.
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 2349A, Programming the Microsoft .NET Framework with C# (Prerelease).
Allocated on stack Assigned as copies Default behavior is pass by value Allocated on heap using the new keyword Assigned as references Passed by reference
Lead-in
With the exception of interfaces, all types in the Common Type System are divided into two categories: value types and reference types.
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.
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; } }
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.
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"); } }
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.
Lead-in
In this section, you will learn about primitive types, objects, properties, and other elements of the Common Type System.
In this section, you will learn about primitive types, objects, properties, and other elements of the Common Type System.
Primitive Types
Topic Objective
To explain how primitive types are used in the common language runtime.
! ! ! !
Simple Small Types Common in Most Languages Naming Differences C# Support Conversions
Lead-in
Primitive types in the .NET Framework common language runtime are simple value types, commonly found in most programming languages.
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 bool char float32 float64 int8 int16 int32 int64 unsigned int8 unsigned int16 unsigned int32 unsigned int64 Name in C# bool char float double sbyte short int long byte ushort uint ulong Name in class library System.Boolean System.Char System.Single System.Double System.SByte System.Int16 System.Int32 System.Int64 System.Byte System.UInt16 System.UInt32 System.UInt64 Description True/false value Unicode 16-bit char IEEE 32-bit float IEEE 64-bit float Signed 8-bit integer Signed 16-bit integer Signed 32-bit integer Signed 64-bit integer Unsigned 8-bit integer Unsigned 16-bit integer Unsigned 32-bit integer Unsigned 64-bit integer
10
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.
11
Objects
Topic Objective
To explain the role of object types in the Common Type System.
! ! ! !
Every Class Inherits from System.Object Objects Specify Data and Behavior Fields Define the Data Methods Define the Behavior
Lead-in
Everything in the Common Type System is an object and inherits from System.Object.
class Accumulator class Accumulator { { public float Result; public float Result; public void Add(float amount) public void Add(float amount) { { Result += amount; Result += amount; } } } }
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; }
12
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; } }
13
Constructors
Topic Objective
To explain how to use constructors to initialize classes.
! !
Constructors Are Used to Initialize Classes Constructors Can Have Zero or More Parameters
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.
class Rectangle class Rectangle { { private int x1,y1,x2,y2; private int x1,y1,x2,y2; public Rectangle(int x1, int y1, int x2,int y2) public Rectangle(int x1, int y1, int x2,int y2) { { this.x1 = x1; this.x1 = x1; this.x2 = x2; this.x2 = x2; this.y1 = y1; this.y1 = y1; this.y2 = y2; this.y2 = y2; } } } }
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.
14
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.
15
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); } }
16
Properties
Topic Objective
To describe how to use properties.
! !
Properties Are Similar to Fields Properties Use get and set Accessor Methods for Managing Data Values
public float Start public float Start { { get get { { return start; return start; } } set set { { if (start >= 0) start = value; if (start >= 0) start = value; } } } }
Lead-in
Properties are values that can be stored or retrieved on a class.
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 from 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.
17
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; } } }
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.
18
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.
19
Custom Types
Topic Objective
To describe how to build data types that use the C# struct keyword.
! ! !
Inherit from System.ValueType Are Defined with the struct Keyword in C# Can Have Methods, Properties, and Fields
Lead-in
You can build your own data type by using the C# struct keyword.
struct Employee struct Employee { { public string Name; public string Name; public ushort Age; public ushort Age; public DateTime HireDate; public DateTime HireDate; public float Tenure() public float Tenure() { { TimeSpan ts = DateTime.Now HireDate; TimeSpan ts = DateTime.Now HireDate; return ts.Days/(float)365; return ts.Days/(float)365; } } } }
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; }
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.
BETA MATERIALS FOR MICROSOFT CERTIFIED TRAINER PREPARATION PURPOSES ONLY
20
Enumerations
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.
!
Bit Flags
enum SeatPreference : ushort enum SeatPreference : ushort { { Window, //Assigned value 0 Window, //Assigned value 0 Center, //Assigned value 1 Center, //Assigned value 1 Aisle //Assigned value 2 Aisle //Assigned value 2 } }
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.
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.
21
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());
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);
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 (|).
22
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());
When using the Flags attribute, you must specify the values for the literals manually.
23
Interfaces
Topic Objective
To describe the uses of 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
Lead-in
An interface is a contractual description of a set of related methods and properties.
! !
interface ICDPlayer interface ICDPlayer { { void Play(short playTrackNum); void Play(short playTrackNum); void Pause(); void Pause(); void Skip(short numTracks); void Skip(short numTracks); short CurrentTrack short CurrentTrack { { get; get; set; set; } } } }
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. The following example shows how to implement the ICDPlayer interface in a class called Device.
24
25
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 2349A, Programming the Microsoft .NET Framework with C# (Prerelease).
26
# Object-Oriented Characteristics
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.
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.
27
Abstraction
Topic Objective
To describe the use and advantages of abstraction in object-oriented programming.
! !
Abstraction Works from the Specific to the General Grouping Elements Makes It Easier to Work with Complex Data Types Abstraction Is Supported Through Classes
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.
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.
28
Encapsulation
Topic Objective
To explain the process of encapsulation in objectoriented 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.
! !
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 public protected internal private Description Makes a member available to all other classes in all assemblies Makes a member available only to classes that inherit from this class Makes a member available only to other classes in the same assembly Makes a member available only to the same class
29
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 enum class interface struct Default member accessibility public private public 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 } }
30
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 } }
31
Inheritance
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.
!
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
! !
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 2349A, Programming the Microsoft .NET Framework with C# (Prerelease). 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. 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.
BETA MATERIALS FOR MICROSOFT CERTIFIED TRAINER PREPARATION PURPOSES ONLY
32
33
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(); } }
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.
34
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); } }
35
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.
36
Polymorphism
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.
!
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
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!"); } }
37
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.
38
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(); } }
39
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() {/*..*/ } }
40
Lead-in
In this lab, you will create simple types and enumerations, and work with inheritance.
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.
!" !"
41
42
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.
43
44
45
Review
Topic Objective
To reinforce module objectives by reviewing key points.
! ! !
An Introduction to the Common Type System Elements of the Common Type System Object-Oriented Characteristics
Lead-in
The review questions cover some of the key concepts taught in the module.
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 }
46
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 course is based on the prerelease Beta 2 version of Microsoft Visual Studio .NET. Content in the final release of the course may be different from the content included in this prerelease version. All labs in the course are to be completed with the Beta 2 version of Visual Studio .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, 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 Microsoft Corporation. All rights reserved. Microsoft, MS-DOS, Windows, Windows NT, ActiveX, IntelliMirror, JScript, MSDN, MSN, PowerPoint, SQL Server, Visual Basic, Visual C++, Visual C#, Visual FoxPro, Visual Studio, Win32, and Windows Media 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.
iii
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.
!"
Required Materials
To teach this module, you need the following materials: Microsoft PowerPoint file 2349A_06.ppt
Preparation Tasks
To prepare for this module, you should:
!" !"
Read all of the materials for this module. Complete the lab.
iv
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 2349A, Programming the Microsoft .NET Framework with C# (Prerelease).
!"
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. However, note that detailed coverage of these topics is beyond the scope of this course. Recommend that students read more about these topics in the .NET Framework Software Developers Kit (SDK) documentation.
Overview
Topic Objective
To provide an overview of the module topics and objectives.
! ! ! ! !
System.Object Class Functionality Specialized Constructors Type Operations Interfaces Managing External Types
Lead-in
In this module, you will learn how to apply your knowledge of the Common Type System to various programming scenarios.
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 .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.
!"
Lead-in
In this section, you will learn about the common methods that you need to override on the System.Object class.
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 2349A, Programming the Microsoft .NET Framework with C# (Prerelease). Also, this section does not cover the MemberwiseClone method.
Hash Codes
Topic Objective
To explain how to use hash codes to perform quick lookups in tables and other types of collections.
! ! !
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
Lead-in
A hash function is used to quickly generate a number, or hash code, that corresponds to the value of an object.
!
struct Student { struct Student { string name; string name; int ID; //Unique for each instance int ID; //Unique for each instance public override int GetHashCode() { public override int GetHashCode() { return ID; return ID; } } } }
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. 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; } }
Identity
Topic Objective
To explain how identity is determined in the .NET Framework common language runtime.
!
Compare to Determine If Two References Are Actually the Same Object Use the Object.ReferenceEquals Method to Test Identity
Lead-in
There are two kinds of comparison for objects: identity and equality. This topic covers object identity.
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.
Equality
Topic Objective
To explain how to override the Equals method and to introduce guidelines for implementing code to provide equality comparison for types.
! ! ! !
Comparing Two Objects to Determine If They Are Equal Override the Equals Method Supply == and != Operators Guidelines
$ $
Lead-in
Objects can also be compared for equality.
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
$ $
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. The following example shows how to override the Equals method and the == and != operators to test user-defined Rectangle objects for equality.
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
String Representation
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.
struct President struct President { { public string FirstName; public string FirstName; public string LastName; public string LastName; public override string ToString() public override string ToString() { { return FirstName + + LastName; return FirstName + + LastName; } } } }
!
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); } }
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); } }
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
# Specialized 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.
This section covers more advanced types of constructors. It explains how static constructors work, when to use them, and when to use private constructors.
13
Static Constructors
Topic Objective
To explain how static constructors work.
!
Lead-in
Static constructors are used to initialize static fields.
class DeviceConnection class DeviceConnection { public static uint ConnectionCount; { public static uint ConnectionCount; public void OpenConnection(string connectionName) public void OpenConnection(string connectionName) { ConnectionCount++; { ConnectionCount++; //Other work to open device } //Other work to open device } static DeviceConnection() static DeviceConnection() { //Initialize static members { //Initialize static members ConnectionCount = 0; } ConnectionCount = 0; } } }
!
.cctor in Disassembly
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. 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; } }
14
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. 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 zero. 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.
15
Private Constructors
Topic Objective
To explain when to use 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
Lead-in
A private constructor can never be called. Therefore any class with a private constructor cannot be instantiated.
class Trig class Trig { { public static double Sin public static double Sin { //Calculate and return { //Calculate and return public static double Cos public static double Cos { //Calculate and return { //Calculate and return public static double Tan public static double Tan { //Calculate and return { //Calculate and return private Trig(){} private Trig(){} } }
(double x) (double x) sin(x) } sin(x) } (double x) (double x) cos(x) } cos(x) } (double x) (double x) tan(x) } tan(x) }
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(){} }
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. One possible use for classes with all static members is maintaining global algorithms, as shown in the previous example. Another use would be for using a class as a singleton class. A singleton class only has one set of values and methods available while a program is running.
16
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. Note 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.
17
# Type Operations
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.
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.
18
Conversions
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.
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.
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.
19
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 sbyte byte short ushort int uint long char float ulong To short, int, long, float, double, or decimal short , ushort, int, uint, long, ulong, float, double, or decimal int, long, float, double, or decimal int, uint, long, ulong, float, double, or decimal long, float, double, or decimal long, ulong, float, double, or decimal float, double, or decimal ushort, int, uint, long, ulong, float, double, or decimal double 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.
20
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 } }
21
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.
!"
22
Casting
Topic Objective
To explain how casting is used in the .NET Framework.
! ! !
Casting Up from Derived Class to Base Class Casting Down from Base Class to Derived Class Type Operators
$ $ $
Lead-in
Casting is used for explicit conversions. However, casting is also used for changing the type that is used to reference an object.
is as typeof
Casting Interfaces
Casting is used for explicit conversions. However, casting is also used for changing the type that is used to reference an object.
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.
23
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 2, Creating an Assembly, in Course 2350A, Securing and Deploying Microsoft .NET Assemblies (Prerelease).
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();
24
Boxing
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.
int x = 5; //Value type int x = 5; //Value type Object o = x; //Boxed Object o = x; //Boxed Console.WriteLine("The answer is : {0} ", x);//Boxed Console.WriteLine("The answer is : {0} ", x);//Boxed
!
Unboxing Occurs to Retrieve Value Type from Object Boxing Can Be Expensive If Inside Loops
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.
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.
25
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.
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 }
26
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 }
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.
27
# 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.
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.
28
Inheritance Considerations
Topic Objective
To explain the use of inheritance and the use of interfaces.
!
Multiple Inheritance
{void DoSomething1();} {void DoSomething1();} {void DoSomething2();} {void DoSomething2();} : IFoo, IBar {...} : IFoo, IBar {...}
Lead-in
Interfaces are used only through inheritance.
interface IFoo interface IFoo interface IBar interface IBar class MyObject class MyObject
!
interface INewInterface : IFoo, IBar interface INewInterface : IFoo, IBar {void DoSomething3();} {void DoSomething3();}
!
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.
29
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"); } }
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.
30
31
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(); } }
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
When interface member names that are combined with class names are confusing When inheriting from multiple interfaces with same members
Lead-in
When you implement an interface in a class, the interface methods are exposed through the interface type, and the class type.
interface IFoo {void DoSomething();} interface IFoo {void DoSomething();} interface IBar {void DoSomething();} interface IBar {void DoSomething();} class MyObject : IFoo, IBar { class MyObject : IFoo, IBar { void IFoo.DoSomething() void IFoo.DoSomething() {Console.WriteLine("IFoo DoSomething called");} {Console.WriteLine("IFoo DoSomething called");} void IBar.DoSomething() void IBar.DoSomething() {Console.WriteLine("IBar DoSomething called");} {Console.WriteLine("IBar DoSomething called");} } }
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(); } }
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(); } }
34
Lead-in
The .NET Framework common language runtime is not an isolated system. Most programs will require some level of interaction with outside applications.
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. This course does not cover all aspects of Platform Invocation Services or COM Integration Services. For more information, see Interoperating with Unmanaged Code in the .NET Framework SDK documentation.
35
Also Known as PInvoke Use dllimport Attribute to Import an API Function from an External DLL
Lead-in
Platform Invocation Services, also known as PInvoke, allow managed code to interact with unmanaged code in existing DLLs.
[DllImport("user32.dll", CharSet=CharSet.Ansi)] [DllImport("user32.dll", CharSet=CharSet.Ansi)] public static extern int MessageBox(int h, string m, public static extern int MessageBox(int h, string m, string c, int type); string c, int type); public static void Main() public static void Main() { string pText = "Hello World!"; { string pText = "Hello World!"; string pCaption = "PInvoke Test"; string pCaption = "PInvoke Test"; MessageBox(0, pText, pCaption, 0); } MessageBox(0, pText, pCaption, 0); }
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 the Platform Invocation Services User Guide in the .NET Framework SDK documentation.
36
COM Interoperability
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.
Must create COM Callable Wrapper Create CCWs by using Tlbexp.exe Register by using Regasm.exe Must create Runtime Callable Wrapper Create RCWs by using Tlbimp.exe
COM interoperability allows managed code to create and call COM objects. It also allows COM objects to create and call .NET Framework objects.
37
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.
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.
!" !"
38
39
3. Assign the Building and Room values to the new type and return an instance of the new type.
!" test To
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.
40
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.
BETA MATERIALS FOR MICROSOFT CERTIFIED TRAINER PREPARATION PURPOSES ONLY
41
42
Review
Topic Objective
To reinforce module objectives by reviewing key points.
! ! ! ! !
System.Object Class Functionality Specialized Constructors Type Operations Interfaces Managing External Types
Lead-in
The review questions cover some of the key concepts taught in the module.
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.
43
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().
This course is based on the prerelease Beta 2 version of Microsoft Visual Studio .NET. Content in the final release of the course may be different from the content included in this prerelease version. All labs in the course are to be completed with the Beta 2 version of Visual Studio .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, 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 Microsoft Corporation. All rights reserved. Microsoft, MS-DOS, Windows, Windows NT, ActiveX, IntelliMirror, JScript, MSDN, MSN, PowerPoint, SQL Server, Visual Basic, Visual C++, Visual C#, Visual FoxPro, Visual Studio, Win32, and Windows Media 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.
iii
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.
Required Materials
To teach this module, you need the following materials: Microsoft PowerPoint file 2349A_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.
iv
Demonstrations
This section provides demonstration procedures that will not fit in the margin notes or are not appropriate for 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\Demo07\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\Demo07\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 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.
Overview
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.
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.
# Strings
Topic Objective
To introduce the topics in the section.
! ! ! ! ! ! ! ! ! !
Parse Format Format Examples Changing Case Compare Trim and Pad Split and Join StringBuilder C# Specifics Regular Expressions
Lead-in
In this section, you will learn how to work with strings in the .NET Framework.
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.
Parse
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.
string MyString = "12345"; string MyString = "12345"; int MyInt = int.Parse(MyString); int MyInt = int.Parse(MyString); MyInt++; MyInt++; Console.WriteLine(MyInt); Console.WriteLine(MyInt); // The output to the console is "12346". // The output to the console is "12346".
!
string MyString = "123,456"; string MyString = "123,456"; int MyInt = int.Parse(MyString, int MyInt = int.Parse(MyString, System.Globalization.NumberStyles.AllowThousands); System.Globalization.NumberStyles.AllowThousands); Console.WriteLine(MyInt); Console.WriteLine(MyInt); // The output to the console is "123456". // The output to the console is "123456".
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".
Format
Topic Objective
To explain how to use format strings, or specifiers, to format the appearance of your application.
!
Format Strings Are Used in Methods That Create String Representations of a .NET Framework Data Type
$
Lead-in
The .NET Framework provides several format strings that you can use to format the appearance of strings that derive from other objects.
To display $100.00 to the console on computers on which U.S. English is the current culture
int MyInt = 100; int MyInt = 100; string MyString = MyInt.ToString("C"); string MyString = MyInt.ToString("C"); Console.WriteLine(MyString); Console.WriteLine(MyString);
$
Alternatively
int MyInt = 100; int MyInt = 100; Console.WriteLine("{0:C}", MyInt); Console.WriteLine("{0:C}", MyInt);
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.
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.
Format Examples
Topic Objective
To provide examples of format strings that return common numeric string types.
!
Currency Format C or c
$
Lead-in
Lets look at some examples of format strings that return the value of MyString in the currency and date time formats.
int MyInt = 12345; int MyInt = 12345; string MyString = MyInt.ToString( "c" ); string MyString = MyInt.ToString( "c" ); // In the U.S. English culture: "$12,345.00" // In the U.S. English culture: "$12,345.00"
!
DateTime MyDate = new DateTime(2000, 1, 1, 0, 0, 0); DateTime MyDate = new DateTime(2000, 1, 1, 0, 0, 0); string MyString = MyDate.ToString( "d" ); string MyString = MyDate.ToString( "d" ); // In the U.S. English culture: "1/1/2000" // In the U.S. English culture: "1/1/2000"
The standard numeric, picture numeric, date and time, and enumeration format strings are described in detail in the .NET Framework Software Developers Kit (SDK). This topic provides examples of these format strings. Standard numeric format strings are used to return common numeric string types. They take the form Xn, in which X is the format character specifier, and n is the precision specifier. The following examples show the use of the format string that returns the value of MyString 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". MyString = MyInt.ToString( "c3" ); // In the U.S. English culture, MyString has the value: // "$12,345.000".
The following table describes format strings that are used with standard date and time types.
Date and format character d D Description Short date pattern Long date pattern Default return format (without precision specifier) M/d/yyyy dddd, MMMM dd, yyyy
The following example shows the use of the format string that returns the value of MyString in the short date pattern format:
DateTime MyDate = new DateTime(2000, 1, 1, 0, 0, 0); string MyString = MyDate.ToString( "d" ); // In the U.S. English culture, MyString has the value: // "1/1/2000".
Changing Case
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.
!
You should convert the case of strings that are entered by users before comparing them String.ToUpper converts to upper case
string MyString = "hello world!"; string MyString = "hello world!"; // outputs: HELLO WORLD! // outputs: HELLO WORLD! Console.WriteLine(MyString.ToUpper()); Console.WriteLine(MyString.ToUpper());
$
string MyString = "HELLO WORLD!"; string MyString = "HELLO WORLD!"; // outputs: hello world! // outputs: hello world! Console.WriteLine(MyString.ToLower()); Console.WriteLine(MyString.ToLower());
When you write an application that accepts input from a user, you can never be sure what case the user will use to enter the data. Because the methods that compare strings and characters are case-sensitive, you must convert the case of strings that are entered by users before comparing them to constant values. You can easily change the case of a string by using the two methods that are described in the following table.
Method Name String.ToUpper String.ToLower Use Converts all characters in a string to uppercase. 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());
10
Compare
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 valuecomparison methods to compare the values of 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!"; string MyString = "Hello World!"; Console.WriteLine( Console.WriteLine( String.Compare(MyString,"Hello World!")); String.Compare(MyString,"Hello World!")); // outputs: 0 // outputs: 0
The .NET Framework provides several methods to compare the values of strings. The following table describes some of the value-comparison methods.
Method Name String.Compare String.StartsWith String.IndexOf Use Compares the values of two strings. Returns an integer value. Determines if a string begins with the string passed. Returns a Boolean value. Returns the index position of a character, starting from the beginning of a string.
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. The following table shows the three integer values that are returned by the Compare(string strA, string strB) method.
Value Type A negative integer 0 A positive integer Condition strA is less than strB strA equals strB strA is greater than strB
11
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!"));
12
Lead-in
You can remove or extend the spaces around strings by using methods of the System.String class.
string MyString = " Big "; string MyString = " Big "; Console.WriteLine("Hello{0}World!", MyString ); Console.WriteLine("Hello{0}World!", MyString ); string TrimString = MyString.Trim(); string TrimString = MyString.Trim(); Console.WriteLine("Hello{0}World!", TrimString ); Console.WriteLine("Hello{0}World!", TrimString ); // outputs the following lines to the console: // outputs the following lines to the console: //Hello Big World! //Hello Big World! //HelloBigWorld! //HelloBigWorld!
!
string MyString = "Hello World!"; string MyString = "Hello World!"; Console.WriteLine(MyString.PadLeft(20, '-')); Console.WriteLine(MyString.PadLeft(20, '-')); // outputs the following line to the console: // outputs the following line to the console: //--------Hello World! to the console. //--------Hello World! to the console.
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 String.Trim String.Remove Use Removes white spaces from the beginning and end of a string. 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 );
For a complete list of trim methods in the System.String class, see Trimming and Removing Characters in the .NET Framework SDK documentation.
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 String.PadLeft String.PadRight Use Right aligns and pads a string, so its rightmost character is a specified distance from the beginning of the string. 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, '-'));
14
Lead-in
The System.String class provides the Split method to break up strings and the Join method to concatenate strings.
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
string Line = "Hello World"; string Line = "Hello World"; string[] Words = Line.Split(null); string[] Words = Line.Split(null); // Words[0] = "Hello" and Words[1] = "World" // Words[0] = "Hello" and Words[1] = "World"
!
The System.String class provides the Split method to break up strings and the Join method to concatenate strings.
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"
15
StringBuilder
Topic Objective
To explain how to use the StringBuilder method to modify a string without creating a new object.
! !
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
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.
// MyStringBuilder can hold a maximum of 25 characters // MyStringBuilder can hold a maximum of 25 characters StringBuilder MyStringBuilder = StringBuilder MyStringBuilder = new StringBuilder("Hello World!", 25); new StringBuilder("Hello World!", 25);
!
Methods Include:
$
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.
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);
16
StringBuilder Methods
The following table describes the methods that you can use to modify the contents of the StringBuilder object.
Method Name StringBuilder.Append StringBuilder.AppendFormat StringBuilder.Insert StringBuilder.Remove StringBuilder.Replace Use Appends information to the end of the current StringBuilder object. Replaces zero or more format specifications with the appropriately formatted value of an object. Inserts a string or object into the specified index of the current StringBuilder object. Removes a specified number of indexes from the current StringBuilder object. Replaces a specified index or character with the passed character.
17
C# Specifics
Topic Objective
To explain what the string type is in the .NET Framework and to describe the functions of the +, [], and != operators, and @quoting.
!
Alias for System.String Equality operators (== and !=) compare the values of string objects, not references The + operator concatenates strings
Lead-in
The string type represents a string of Unicode characters; string is an alias for System.String in the .NET Framework.
string a = "\u0068ello "; string a = "\u0068ello "; string b = "world"; string b = "world"; Console.WriteLine( a + b == "hello world" );//True Console.WriteLine( a + b == "hello world" );//True $ The [] operator accesses individual characters of a string char x = "test"[2]; // x = 's'; char x = "test"[2]; // x = 's';
$
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 );
String literals are of type string and can be written in two forms: quoted and @quoted. Quoted string literals are enclosed in double 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
18
@-quoted string literals start with @ and are enclosed in double 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
19
Regular Expressions
Topic Objective
To briefly describe how regular expressions can be used in the .NET Framework.
! !
Regular Expressions Powerful Text Processing Pattern-Matching Notation Allows You to:
$ $ $
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.
Find specific character patterns Extract, edit, replace, or delete text substrings 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.
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.
20
Terminology 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.
!
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
$ $
Lead-in
In this module, the term collection is used in its broader sense: to describe a group of items.
System.Array System.Collections
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.
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.
21
Lead-in
The System.Array class is the base class of all array types and contains methods for creating, manipulating, searching, and sorting arrays.
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.
22
System.Array
Topic Objective
To explain how array types are used and defined.
! !
System.Array Is the Base Class of All Array Types Arrays Implement the Following Interfaces
$
Lead-in
The System.Array class is the base class of all array types and contains methods for creating, manipulating, searching, and sorting arrays.
ICloneable, IList, ICollection, and IEnumerable Creating, manipulating, searching, and sorting
Null, Empty String, and Empty (0 item) Arrays Should Be Treated the Same
$
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.
23
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 BinarySearch CreateInstance Sort Use Overloaded. Searches a one-dimensional sorted Array for a value, using a binary search algorithm. Overloaded. Initializes a new instance of the Array class. Overloaded. Sorts the elements in one-dimensional Array objects. Use Gets a value that indicates whether the Array has a fixed size. Gets a value that indicates whether the Array is read-only. Gets the total number of elements in all of the dimensions of the Array. Gets the rank (number of dimensions) of the Array.
Note The IsFixedSize and IsReadOnly properties are always false unless they are overridden by a derived class.
24
The following table describes some of the public instance methods that are available through the System.Array class.
Instance Method Clone GetEnumerator GetLength GetLowerBound GetUpperBound GetValue SetValue Use Creates a shallow copy of the Array. Returns an IEnumerator for the Array. Gets the number of elements in the specified dimension of the Array. Gets the lower bound of the specified dimension in the Array. Gets the upper bound of the specified dimension in the Array. Overloaded. Gets the values of the Array elements at the specified indexes. 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.Length 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.
25
C# Specifics
Topic Objective
To explain features that are unique to C# arrays.
! !
C# Array Indexes Start at Zero Declaring an Array Size Is Not Part of Its Type Creating an Array
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.
int[] numbers; // declare numbers as int[] numbers; // declare numbers as // an int array of any size // an int array of any size
!
int[] numbers = new int[5]; // declare and create int[] numbers = new int[5]; // declare and create
!
int[] numbers = new int[5] {1, 2, 3, 4, 5}; int[] numbers = new int[5] {1, 2, 3, 4, 5};
!
int[] numbers = {1, 2, 3, 4, 5}; int[] numbers = {1, 2, 3, 4, 5}; int LengthOfNumbers = numbers.Length; int LengthOfNumbers = numbers.Length;
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 square 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 = new int[20]; // numbers is a 10-element array // now it's a 20-element array
26
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]; }
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); } } }
Initializing Arrays
C# provides simple and straightforward ways to initialize arrays when they are declared, by enclosing the initial values in curly braces ( {} ). Important If an array is not initialized when it is declared, array members are automatically initialized to the default initial value for the array type.
28
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"} };
29
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} };
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[] {1, new int[] {3, }; numbers[1][1] = = new int[][] 2}, 4} 5;
The System.Array class provides many other useful methods and properties, such as methods for sorting, searching, and copying arrays.
30
Iterating Over
Topic Objective
To explain the role of the IEnumerable and IEnumerator interfaces in collections.
!
System.Array and System.Collections Classes Implement IEnumerable Interface and its GetEnumerator Method Enumerator Classes Implement IEnumerator
$
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.
int[] numbers = new int[5] {1, 2, 3, 4, 5}; int[] numbers = new int[5] {1, 2, 3, 4, 5}; IEnumerator e = numbers.GetEnumerator(); IEnumerator e = numbers.GetEnumerator(); while (e.MoveNext()) { while (e.MoveNext()) { Console.WriteLine("Number: {0}", (int)e.Current); Console.WriteLine("Number: {0}", (int)e.Current); } } // alternatively // alternatively foreach (int i in numbers) foreach (int i in numbers) { { Console.WriteLine("Number: {0}", i); Console.WriteLine("Number: {0}", i); } }
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. Note 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}
31
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 following public instance property: Current 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 MoveNext Reset Use Advances the enumerator to the next element of the collection. 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. Once 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
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); }
33
Comparing
Topic Objective
To explain how the IComparer and IComparable interfaces are used to sort and order a collections items.
!
! IComparers Compare Method Compares Two Objects of Any Type int Compare( object x, object y ); int Compare( object x, object y );
$
Lead-in
To sort a collection, you must be able to compare and order the items of the collection.
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
int CompareTo( object anObject ); int CompareTo( object anObject ); ! CompareTo Returns
Meaning Instance is less than object Instance is equal to object Instance is greater than object
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.
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.
34
The following table describes the possible meanings of the return value.
Value Less than zero Zero Greater than zero Meaning This instance is less than anObject. This instance is equal to anObject. 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.
35
Sorting
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.
public int CompareTo(Object anObject) { public int CompareTo(Object anObject) { if ( anObject == null) return 1; if ( anObject == null) return 1; if ( !(anObject is <classname>) ) { if ( !(anObject is <classname>) ) { throw new ArgumentException(); } throw new ArgumentException(); } // Do comparison and return a // Do comparison and return a // negative integer if instance < anObject // negative integer if instance < anObject // 0 if instance == anObject // 0 if instance == anObject // positive integer if instance > anObject // positive integer if instance > anObject } }
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 once they are constructed their sort, ordering cannot be changed.
36
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.
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. For Your Information
You should carefully cover the CompareTo method because it is used in the lab.
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(); }
37
// 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 next page
BETA MATERIALS FOR MICROSOFT CERTIFIED TRAINER PREPARATION PURPOSES ONLY
38
39
Lead-in
The System.Collections namespace contains interfaces and classes that define various collections of objects, such as lists, queues, arrays, hashtables, and dictionaries.
Examples of System.Collections Classes Lists Dictionaries SortedList Collection Usage Guidelines Type Safety and Performance
The System.Collections namespace contains interfaces and classes that define various collections of objects, such as lists, queues, arrays, hashtables, and dictionaries.
40
ArrayList
$
Implements IList by using a dynamically-sized array Provides abstract base class for strongly-typed collection of associated keys and values Represents a collection of keys and values that are organized around the keys hash code Represents the collection of keys and values, sorted by keys and accessible by key and index
DictionaryBase
$
Lead-in
Lets look briefly at some of the collection classes in the System.Collections namespace.
!
Hashtable
$
SortedList
$
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.
The following table shows some of the collection classes in the System.Collections namespace.
Class ArrayList BitArray Description Implements the IList interface by using an array whose size is dynamically increased as required. 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). Provides the abstract base class (MustInherit in Microsoft Visual Basic) for a strongly-typed collection. Provides the abstract base class (MustInherit in Visual Basic) for a strongly-typed collection of associated keys and values. Represents a collection of associated keys and values that are organized around the hash code of the key. Represents a first-in, first-out collection of objects. Provides the abstract base class (MustInherit in Visual Basic) for a strongly-typed read-only collection. Represents a collection of associated keys and values that are sorted by the keys and are accessible by key and by index. Represents a simple last-in-first-out collection of type Object.
CollectionBase DictionaryBase
Stack
41
Lists
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.
!
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 Add, Clear, Contains, Insert, IndexOf, Remove, and RemoveAt
Methods Include:
$
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 IsFixedSize IsReadOnly Item Use When implemented by a class, gets a value indicating whether the IList has a fixed size. When implemented by a class, gets a value indicating whether the IList is read-only. 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.
42
The following table describes the public instance methods that the IList interface defines.
Method Add Clear Contains IndexOf Insert Remove RemoveAt Use When implemented by a class, adds an item to the IList. When implemented by a class, removes all items from the IList. When implemented by a class, determines whether the IList contains a specific value. When implemented by a class, determines the index of a specific item in the IList. When implemented by a class, inserts an item to the IList at the specified position. When implemented by a class, removes the first occurrence of a specific object from the IList. When implemented by a class, removes the IList item at the specified index.
43
Demonstration: ArrayList
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.
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 next page
BETA MATERIALS FOR MICROSOFT CERTIFIED TRAINER PREPARATION PURPOSES ONLY
44
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.
45
Dictionaries
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.
!
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 Add, Clear, Contains, GetEnumerator, and Remove
Methods Include:
$
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.
!"
!"
46
The following tables describe some of the public instance properties that the IDictionary interface implements.
Property IsFixedSize IsReadOnly Item Use When implemented by a class, gets a value indicating whether the IDictionary has a fixed size. When implemented by a class, gets a value indicating whether the IDictionary is read-only. 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 Values When implemented by a class, gets an ICollection containing the keys of the IDictionary. When implemented by a class, gets an ICollection containing the values in the IDictionary.
The following tables describe some of the public instance methods that the IDictionary interface implements.
Method Add Contains GetEnumerator Remove Use When implemented by a class, adds an entry with the provided key and value to the IDictionary. When implemented by a class, determines whether the IDictionary contains an entry with the specified key. When implemented by a class, returns an IDictionaryEnumerator for the IDictionary. 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.
47
Demonstration: Hashtable
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.
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) {...}
48
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 next page
49
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 -1254 6839 3948 1930 0123 0569 Name ---Brian Seth Rajesh Lakshan Jay Brad
50
SortedList
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.
!
One array for the keys and another array for the associated values
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.
SortedList mySL = new SortedList(); SortedList mySL = new SortedList(); // Add an entry with a key = "First" and a value = 1 // Add an entry with a key = "First" and a value = 1 mySL.Add("First", 1); mySL.Add("First", 1); // Increment the value of the entry whose key = "First" // Increment the value of the entry whose key = "First" mySL["First"] = (Int32)mySL["First"] + 1; mySL["First"] = (Int32)mySL["First"] + 1;
! !
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
for ( int i = 0; i < myList.Count; i++ ) { for ( int i = 0; i < myList.Count; i++ ) { Console.WriteLine( "\t{0}:\t{1}", Console.WriteLine( "\t{0}:\t{1}", myList.GetKey(i), myList.GetByIndex(i) ); } myList.GetKey(i), myList.GetByIndex(i) ); }
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. For Your Information
You should carefully cover the SortedList class, the Add method, and the [] Operator because they are used in the lab.
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.
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:
Count: 3 Capacity: 16 Keys and Values: -KEY-VALUEFirst: 1 Second: 2 Third: 3 -KEYFirst: Second: Third: -VALUE1 2 4
52
Lead-in
Arrays and collections are used similarly, but they perform differently.
!
$
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
Indexed Properties in Collections Should Only Be Used As the Default Member of a Collection Class or Interface Use Collections to Avoid the Inefficiencies in the Following Code
for (int i = 0; i < obj.Foo.Count; i++) for (int i = 0; i < obj.Foo.Count; i++) DoSomething(obj.Foo[i]); DoSomething(obj.Foo[i]);
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.
53
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-firstout or 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 keymultiplevalues 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
Type errors found at runtime, rather than at compile time Runtime overhead of casting Runtime overhead of boxing/unboxing operations for value types System.Collections.Specialized namespace contains stringspecific collections Create your own type-specific collection class by inheriting from a System.Collections class and adding type-specific members Encapsulate value types in a class Manipulate value types through interfaces
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.
!
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.
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.
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 NameValueCollection Description 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. Represents a collection of strings. Implements a hash table with the key strongly-typed to be a string, rather than an object. Supports a simple iteration over a StringCollections.
56
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()); } }
57
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.
58
59
60
61
62
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
63
Review
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.
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.
64
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.
This course is based on the prerelease Beta 2 version of Microsoft Visual Studio .NET. Content in the final release of the course may be different from the content included in this prerelease version. All labs in the course are to be completed with the Beta 2 version of Visual Studio .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, 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 Microsoft Corporation. All rights reserved. Microsoft, MS-DOS, Windows, Windows NT, ActiveX, IntelliMirror, JScript, MSDN, MSN, PowerPoint, SQL Server, Visual Basic, Visual C++, Visual C#, Visual FoxPro, Visual Studio, Win32, and Windows Media 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.
iii
Use the delegate class to create type-safe callback functions and eventhandling 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.
!"
!"
Required Materials
To teach this module, you need the following materials: Microsoft PowerPoint file 2349A_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.
iv
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 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.
Overview
Topic Objective
To provide an overview of the module topics and objectives.
! ! ! !
Delegates Multicast Delegates Events When to Use Delegates, Events, and Interfaces
Lead-in
Delegates are the objectoriented equivalents of function pointers.
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 eventhandling 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.
!"
!"
# Delegates
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.
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.
Delegate Scenario
Topic Objective
To illustrate one use of delegates through the scenario of a switch and a light bulb.
1 - Change in switch position invokes switchs OnFlip method
Switch Object
Lead-in
This scenario of a switch that controls a light illustrates one use of delegates.
OnFlip method
2 - OnFlip Method invokes delegate
Light Object
OnFlipCallback method
4 - OnFlipCallback method changes lights state
To run the build slide, click through the lower left button on the slide.
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.
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.
Declaring a Delegate
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.
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 // declares a delegate for a method that takes a single // argument of type string and has a void return type // argument of type string and has a void return type delegate void MyDelegate1(string s); delegate void MyDelegate1(string s);
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. For Your Information
The details of how the common language runtime creates delegates are presented later in this module.
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);
Instantiating a Delegate
Topic Objective
To explain how to instantiate delegates.
! !
A Delegate Object Is Created with the new Operator Delegate Objects Are Immutable
Lead-in
Once you have declared a delegate type, you can create a delegate object and associate it with a particular method.
// instantiating a delegate to a static method Hello // instantiating a delegate to a static method Hello // in the class MyClass // in the class MyClass MyDelegate1 a = new MyDelegate1(MyClass.Hello); MyDelegate1 a = new MyDelegate1(MyClass.Hello); // instantiating a delegate to an instance method // instantiating a delegate to an instance method // AMethod in object p // AMethod in object p MyClass p = new MyClass(); MyClass p = new MyClass(); MyDelegate1 b = new MyDelegate1(p.AMethod); MyDelegate1 b = new MyDelegate1(p.AMethod);
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.
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.
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);
Calling a Delegate
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.
The name of the delegate object Followed by the parenthesized arguments to be passed to the delegate
// // // // // //
given the previous delegate declaration and given the previous delegate declaration and instantiation, the following invokes MyClass' instantiation, the following invokes MyClass' static method Hello with the parameter "World" static method Hello with the parameter "World"
a("World"); a("World");
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. For Your Information
Refer to the previous slide on delegate declaration and instantiations.
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, given 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");
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.
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.
10
11
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
# Multicast Delegates
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.
Multicast Delegate Scenario Single vs. Multicast Delegates Creating and Invoking Multicast Delegates C# Language-Specific Syntax Delegate Details
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.
13
Switch Object
Lead-in
The following scenario of a switch that controls two lights illustrates the use of multicast delegates.
OnFlip method
2 - OnFlip method invokes multicast delegate1 OnFlipCallback method OnFlipCallback method 7 - OnFlipCallback method changes light2s state 6 - delegate2 invokes light2s OnFlipCallback
3 - delegate1 invokes light1s OnFlipCallback Invocation list Multicast delegate1 object Multicast delegate1 object
5 - delegate2 is invoked
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. 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.
BETA MATERIALS FOR MICROSOFT CERTIFIED TRAINER PREPARATION PURPOSES ONLY
14
All Delegates Have an Invocation List of Methods That Are Executed When Their Invoke Method is Called Single-Cast Delegates: Derived Directly From System.Delegate
$
Lead-in
When delegate declarations are compiled, the compiler generates a new class, which derives from two delegate classes provided by the .NET Framework.
!
Invocation list contains only one method 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
When delegate declarations are compiled, the compiler, in effect, generates a new class. The new delegate class derives from one of the two delegate classes provided by the .NET Framework: System.Delegate and System.MulticastDelegate. 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 Delegate class contains an invocation list with one method, and a delegate that derives from the MulticastDelegate class may contain an invocation list with multiple methods. 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 objectwith 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.
15
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.
16
// assign to c the composition of delegates a and b // assign to c the composition of delegates a and b c = (MyDelegate2)Delegate.Combine(a, b); c = (MyDelegate2)Delegate.Combine(a, b); // assign to d the result of removing a from c // assign to d the result of removing a from c d = (MyDelegate2)Delegate.Remove(c, a); d = (MyDelegate2)Delegate.Remove(c, a); // Iterate through c's invocation list // Iterate through c's invocation list // and invoke all delegates except a // and invoke all delegates except a Delegate[] DelegateList = c.GetInvocationList(); Delegate[] DelegateList = c.GetInvocationList(); for (int i = 0; i < DelegateList.Length; i++) { for (int i = 0; i < DelegateList.Length; i++) { if (DelegateList[i].Target != aFoo1) { if (DelegateList[i].Target != aFoo1) { ((MyDelegate2) DelegateList[i])(); ((MyDelegate2) DelegateList[i])(); } } } }
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.
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.
17
// 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
18
C# Language-Specific Syntax
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.
C# Delegates That Return Void Are Multicast Delegates In C#, Use the + and - Operators to Add and Remove Invocation List Entries
$
MyDelegate a, b, c, d; MyDelegate a, b, c, d; a = new MyDelegate(Foo); a = new MyDelegate(Foo); b = new MyDelegate(Bar); b = new MyDelegate(Bar); c = a + b; // Compose two delegates to make another c = a + b; // Compose two delegates to make another d = c - a; // Remove a from the composed delegate d = c - a; // Remove a from the composed delegate a += b; // Add delegate b to a's invocation list a += b; // Add delegate b to a's invocation list a -= b; // Remove delegate b from a's list a -= b; // Remove delegate b from a's list
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.
19
Lead-in
You can add and remove methods from the invocation list of multicast delegates by using the + and operators.
In this demonstration, you will learn how to 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.
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 following page
20
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"); } } }
d: invoking:
and invoking:
21
Delegate Details
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.
// delegate void MyDelegate3(string val); // delegate void MyDelegate3(string val); class MyDelegate3 : System.MulticastDelegate { class MyDelegate3 : System.MulticastDelegate { public MyDelegate3(object obj, methodref mref) public MyDelegate3(object obj, methodref mref) : base (obj, mref) { //... : base (obj, mref) { //... } } public void virtual Invoke(string val) { //... public void virtual Invoke(string val) { //... } } }; };
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.
22
# Events
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.
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 event handling.
23
Declaring an Event
Topic Objective
To explain how to declare events.
! !
Declare the Delegate Type for the Event Declare the Event
$
Lead-in
To declare an event inside a class, you first must declare a delegate type for the event.
// MouseClicked delegate declared // MouseClicked delegate declared public delegate void MouseClickedEventHandler(); public delegate void MouseClickedEventHandler(); public class Mouse public class Mouse { { // MouseClicked event declared // MouseClicked event declared public static event MouseClickedEventHandler public static event MouseClickedEventHandler MouseClickedHandler; MouseClickedHandler; //... //... } }
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 MouseClickedHandler:
public class Mouse { public static event MouseClickedEventHandler MouseClickedHandler; //... }
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 MouseClickedHandler, which refers to delegates of type MouseClickedEventHandler, is created.
24
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_MouseClickedHandler and remove_MouseClickedHandler. 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.
25
Connecting to an Event
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.
// Clients method to handle the MouseClick event // Clients method to handle the MouseClick event private void MouseClicked() { //... private void MouseClicked() { //... } } //... //... // Client code to connect to MouseClicked event // Client code to connect to MouseClicked event Mouse.MouseClickedHandler += new Mouse.MouseClickedHandler += new MouseClickedEventHandler(MouseClicked); MouseClickedEventHandler(MouseClicked); // Client code to break connection to MouseClick event // Client code to break connection to MouseClick event Mouse.MouseClickedHandler -= new Mouse.MouseClickedHandler -= new MouseClickedEventHandler(MouseClicked); MouseClickedEventHandler(MouseClicked);
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 { // aClient's method to handle the MouseClick event private void MouseClicked() { //... } //... // code to connect to Mouse class' MouseClicked event Mouse.MouseClickedHandler += new MouseClickedEventHandler(MouseClicked); // code to break the connection to MouseClicked event Mouse.MouseClickedHandler -= new MouseClickedEventHandler(MouseClicked); //... }
26
Raising an Event
Topic Objective
To show when and how to raise an event.
!
Lead-in
Once a class has declared an event, it can treat that event like a field of the indicated delegate type.
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 (MouseClickedHandler != null) MouseClickedHandler();
27
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
Lead-in
You should follow .NET Framework guidelines if you intend to use your component in the .NET Framework.
public class SwitchFlippedEventArgs : EventArgs { //... public class SwitchFlippedEventArgs : EventArgs { //... } } public delegate void SwitchFlippedEventHandler( public delegate void SwitchFlippedEventHandler( object sender, SwitchFlippedEventArgs e); object sender, SwitchFlippedEventArgs e); public event SwitchFlippedEventHandler SwitchFlippedHandler; public event SwitchFlippedEventHandler SwitchFlippedHandler;
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: 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
!"
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.
BETA MATERIALS FOR MICROSOFT CERTIFIED TRAINER PREPARATION PURPOSES ONLY
28
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.
29
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 SwitchFlippedHandler; // 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 (SwitchFlippedHandler != null) { // call the delegate if non-null SwitchFlippedHandler(this, e); } } //... } {
30
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 methods Client signs up for the callback function through methods More than one object will care The callback function entails complex behavior, such as multiple methods
Lead-in
Delegates, interfaces, and events all provide callback functionality, but each has specific usage characteristics that make it better suited to particular situations.
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 a separate add method.
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 care.
!"
Use an interface if: The callback function entails complex behavior, as when more than one callback method is invoked.
31
Lead-in
In this lab, you will create use delegates and events with a simple chat server application.
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-andsubscribe design.
32
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.
33
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.
34
35
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.
36
!" extend the programs main entry point by performing the following To
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.
37
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 To
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.
38
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.
39
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 To
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.
40
Review
Topic Objective
To reinforce module objectives by reviewing key points.
! ! ! !
Delegates Multicast Delegates Events When to Use Delegates, Events, and Interfaces
Lead-in
The review questions cover some of the key concepts taught in this module.
1. Write the code to declare a delegate that is named ProcessOrderCallback for the following method:
static public bool ProcessOrder(int Quantity, 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) { //... };
41
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. Given 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 course is based on the prerelease Beta 2 version of Microsoft Visual Studio .NET. Content in the final release of the course may be different from the content included in this prerelease version. All labs in the course are to be completed with the Beta 2 version of Visual Studio .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, 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 Microsoft Corporation. All rights reserved. Microsoft, MS-DOS, Windows, Windows NT, ActiveX, IntelliMirror, JScript, MSDN, MSN, PowerPoint, SQL Server, Visual Basic, Visual C++, Visual C#, Visual FoxPro, Visual Studio, Win32, and Windows Media 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.
iii
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.
!"
Required Materials
To teach this module, you need the following materials: Microsoft PowerPoint file 2349A_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.
iv
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\Demo09\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 Resurrection methods.
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.
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 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.
Overview
Topic Objective
To provide an overview of the module topics and objectives.
! ! ! ! !
Memory Management Basics Non-Memory Resource Management Implicit Resource Management Explicit Resource Management Optimizing Garbage Collection
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.
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.
!"
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.
Developer Backgrounds Manual vs. Automatic Memory Management Memory Management of .NET Framework Types Simple Garbage Collection
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.
Developer Backgrounds
Topic Objective
To discuss various developer backgrounds with regard to memory management.
!
COM
$
Lead-in
Your experience with memory management will vary depending upon your development background.
!
Manually implement reference counting and handle circular references Manually use the new operator and delete operator Accustomed to automatic memory management
C++
$
Visual Basic
$
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.
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.
Lead-in
Manual memory management requires that you manage the allocation and deallocation of blocks of memory.
!
Programmer manages memory Failure to release memory Invalid references to freed memory Eases programming task Eliminates a potential source of bugs
Common Problems
$ $
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.
Lead-in
In .the NET Framework, all values have a type, which may be a value type or a reference type.
!
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.
Allocation Memory for an instance of a value type is created when the activation record for its scope is pushed onto 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.
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.
!
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
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.
10
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.
This animation illustrates the .NET Framework common language runtimes garbage collection process. Note 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.
!"
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.
11
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
Lead-in
Managed objects sometimes encapsulate control over resources that are not managed by the runtime.
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.
13
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 nonmemory resources.
Finalization Garbage Collection with Finalization Finalization Guidelines Controlling Garbage Collection
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 nonmemory resources. In this section, you will learn how to take advantage of this feature.
14
Finalization
Topic Objective
To define and describe finalization in garbage collection.
! ! !
Finalize Code Called by Garbage Collection In C#, the Finalize Code Is Provided by a Destructor Use C# Destructor to Implicitly Close a FileStream
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.
class Foo { class Foo { private System.IO.FileStream fs; private System.IO.FileStream fs; //... //... public Foo() { public Foo() { fs = new System.IO.FileStream( fs = new System.IO.FileStream( bar, FileMode.CreateNew); bar, FileMode.CreateNew); } } ~Foo() { fs.Close(); } ~Foo() { fs.Close(); } } }
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.
15
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.
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 on explicit resource management, see Explicit Resource Management in this module.
16
Finalization queue
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.
! !
References added to freachable queue Objects are now reachable and not garbage Unreachable objects' memory is reclaimed
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.
BETA MATERIALS FOR MICROSOFT CERTIFIED TRAINER PREPARATION PURPOSES ONLY
17
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.
Executes freachable objects' Finalize methods References removed from freachable queue Unless resurrected, objects are now garbage May be reclaimed next time garbage collection occurs
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.
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.
18
19
Lead-in
This animation illustrates the .NET Framework common language runtimes 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.
This animation illustrates the .NET Framework common language runtimes garbage collection process, including finalization. Note 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.
!"
!"
!"
20
Finalization Guidelines
Topic Objective
To alert students to issues that are associated with finalization.
!
Performance costs Complexity Delay of memory resource release Avoid calling other objects Avoid making assumptions about thread ID Avoid making references to other objects
Lead-in
This topic provides guidelines for handling finalization.
!
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.
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 onto. 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.
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
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
Lead-in
The System.GC class of the .NET Framework contains methods that can be used to control the garbage collection process.
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 on 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();
Once 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);
23
Demonstration: Finalization
Topic Objective
To demonstrate how garbage collection handles finalization and resurrection.
Lead-in
This demonstration shows how garbage collection handles finalization and resurrection.
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 Resurrection methods.
// 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.
24
// 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 next page
BETA MATERIALS FOR MICROSOFT CERTIFIED TRAINER PREPARATION PURPOSES ONLY
25
// 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
Lead-in
Your classes should provide an explicit and an implicit way to free resources.
The IDisposable Interface and the Dispose Method Temporary Resource Usage Design Pattern
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.
27
Inherit from the IDisposable Interface Implement the Dispose Method Follow the .NET Framework SDKs Design Pattern
Lead-in
Classes that contain nonmemory resources should provide explicit resource management by inheriting from the IDisposable interface and implementing its Dispose method.
class ResourceWrapper : IDisposable class ResourceWrapper : IDisposable { { // see code example for details // see code example for details
} }
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. 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.
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 Developers 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:
28
29
30
!"
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 onto 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.
!"
!"
31
Lead-in
This demonstration shows how to perform explicit resource management by using the IDisposable interface.
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 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); }
32
Lead-in
In a temporary resource use scenario, you allocate, use, and dispose of a resource in a short period of time.
void DoSomething() { void DoSomething() { Resource r = new Resource(...); Resource r = new Resource(...); try { r.Foo(); } try { r.Foo(); } finally { finally { if (r != null) ((IDisposable)r).Dispose(); if (r != null) ((IDisposable)r).Dispose(); } } } }
!
Using Statement
using (Resource r1 = new Resource()) { using (Resource r1 = new Resource()) { r1.Foo(); r1.Foo(); } }
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(); } }
33
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(); }
34
35
The method ReadKey.Dispose() is trivially inlined by the just-in-time (JIT) compiler. The code will be similar to the following optimal expansion:
class Test { RWLock aLock = new RWLock(); void f() { aLock.ReadLock(); try { /* do stuff */ } finally { aLock.ReadUnlock(); } } }
Note The use of this design pattern does not preclude use of the class in a natural way by languages that do not support the using pattern. Users of such languages can ignore the returned key and call ReadUnlock directly.
BETA MATERIALS FOR MICROSOFT CERTIFIED TRAINER PREPARATION PURPOSES ONLY
36
Lead-in
In the .NET Framework, garbage collection has several advanced features that you can use to improve the performance of your software.
The .NET Framework common language runtimes 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.
37
Weak References
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.
Object obj = new Object(); // create strong reference Object obj = new Object(); // create strong reference WeakReference wr = new WeakReference(obj); WeakReference wr = new WeakReference(obj); obj = null; // remove strong reference obj = null; // remove strong reference // ... // ... obj = (Object) wr.Target; obj = (Object) wr.Target; if (obj != null) {//garbage collection hasnt occurred if (obj != null) {//garbage collection hasnt occurred // ... // ... } } else {// object was collected, reference is null else {// object was collected, reference is null //... //... } }
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.
38
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 Short weak reference Long weak reference Definition Does not track resurrection Tracks resurrection
39
Lead-in
This demonstration shows how garbage collection handles weak references.
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 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 singlestep 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.
40
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)); } //Code continued next page
BETA MATERIALS FOR MICROSOFT CERTIFIED TRAINER PREPARATION PURPOSES ONLY
42
43
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.
To Return the Maximum Number of Generations That the System Currently Supports:
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. 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);
44
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;
45
Demonstration: Generations
Topic Objective
To demonstrate how garbage collection handles generations.
Lead-in
This demonstration shows how garbage collection handles generations.
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.
// 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(); Collect(); obj.DisplayGeneration(); //Code continued next page
// Displays 2
// Displays 2
(max generation)
46
// 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); }
47
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.
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 the Start menu, click Run, and type perfmon.exe in the text box. 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.
48
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 client 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.
49
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.
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.
50
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.
51
52
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. Tip 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 contains a SimpleTextBuffer object named aSimpleTextBuffer. Note 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 Dispose should not throw an exception. 4. For both classes, add an IDisposable.Dispose method that calls Dispose 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 IDisposable.Dispose method. Tip To call IDisposable.Dispose, you must cast this to IDisposable. 6. For the SentenceFormatter and SimpleTextBuffer methods, which require resources that are freed by their classes Dispose method, add an initial check that throws an ObjectDisposedException if those resources have been released. Tip When the SentenceFormatters Dispose method is called it frees its sentenceBuffer and aSimpleTextBuffer resources. The SentenceFormatter;s 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.
53
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?
Note With explicit resource management, the output is identical to the output in Exercise 1. All the characters you entered are output and counted.
54
2. After the code in the preceding step is a statement 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
Note 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.
55
Review
Topic Objective
To reinforce module objectives by reviewing key points.
! ! ! ! !
Memory Management Basics Non-Memory Resource Management Implicit Resource Management Explicit Resource Management Optimizing Garbage Collection
Lead-in
The review questions cover some of the key concepts taught in the module.
1. Describe two common problems with manual memory management that the .NET Frameworks automatic memory management addresses. Failure to release memory (Memory leaks) Accessing in valid 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.
56
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 course is based on the prerelease Beta 2 version of Microsoft Visual Studio .NET. Content in the final release of the course may be different from the content included in this prerelease version. All labs in the course are to be completed with the Beta 2 version of Visual Studio .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, 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 Microsoft Corporation. All rights reserved. Microsoft, MS-DOS, Windows, Windows NT, ActiveX, IntelliMirror, JScript, MSDN, MSN, PowerPoint, SQL Server, Visual Basic, Visual C++, Visual C#, Visual FoxPro, Visual Studio, Win32, and Windows Media 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.
iii
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 Frameworks isolated storage mechanism.
!"
!"
!"
!"
!"
!"
Required Materials
To teach this module, you need the following materials: Microsoft PowerPoint file 2349A_10.ppt
Preparation Tasks
To prepare for this module, you should:
!" !"
Read all of the materials for this module. Complete the lab.
iv
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 2349A, Programming the Microsoft .NET Framework with C# (Prerelease).
!"
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 1/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.
Overview
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.
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, as asynchronous operations are beyond the scope of this course. After completing this module, you will be able to: 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.
!"
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 Frameworks isolated storage mechanism.
!"
!"
!"
!"
!"
!"
Streams
Topic Objective
To introduce the functions of the Stream class and its subclasses.
!
Stream classes inherit from System.IO.Stream CanRead, CanWrite, and CanSeek properties Flush method outputs and clears internal buffers Close method performs an implicit Flush for buffered streams NetworkStream, BufferedStream, MemoryStream, FileStream
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.
!
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.
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.
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:
# #
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.
! !
StreamReader and StreamWriter, which read and write to a stream StringReader and StringWriter, which read and write to a string and StringBuilder respectively
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 BinaryReader and BinaryWriter Description These classes read and write primitive types as binary values in a specific encoding to and from a stream. The implementations of these classes are designed for character input and output. These classes are derived from the TextReader and TextWriter classes and read and write their characters to a stream. 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.
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(); } } }
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(); } }
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.
FileStream Class File and FileInfo Class Reading Text Example Writing Text Example Directory and DirectoryInfo Class FileSystemWatcher Isolated Storage
Lead-in
The .NET Frameworks System.IO namespace provides a number of useful classes for manipulating files and directories.
The .NET Frameworks System.IO namespace provides a number of useful classes for manipulating files and directories. Important 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. Caution 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.
FileStream Class
Topic Objective
To define the FileStream class and the types that are used as parameters in some FileStream constructors.
! !
The FileStream Class Is Used for Reading from and Writing to Files FileStream Constructor Parameter Classes
# # #
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.
FileMode Open, Append, Create FileAccess Read, ReadWrite, Write FileShare None, Read, ReadWrite, Write
FileStream f = new FileStream(name, FileMode.Open, FileStream f = new FileStream(name, FileMode.Open, FileAccess.Read, FileShare.Read); FileAccess.Read, FileShare.Read);
!
Specified by byte offset Offset is relative to seek reference point: Begin, Current, End
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 Open Append Create Description This constant is used to open an existing file. This constant is used to append to a file. 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.
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);
10
Create, copy, delete, move, and open files Create, copy, delete, move, and open files Can eliminate some security checks when reusing an object. Assign to aStream a newly created file named foo.txt in the current directory
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.
Example:
#
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");
11
Lead-in
In the following example, you read an entire file and are notified when the end of the file is detected.
//... //... StreamReader sr = File.OpenText(FILE_NAME); StreamReader sr = File.OpenText(FILE_NAME); String input; String input; while ((input=sr.ReadLine())!=null) { while ((input=sr.ReadLine())!=null) { Console.WriteLine(input); Console.WriteLine(input); } } Console.WriteLine ( Console.WriteLine ( "The end of the stream has been reached."); "The end of the stream has been reached."); sr.Close(); sr.Close(); //... //...
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.
12
Create a File Write a String, an Integer, and a Floating Point Number Close the File
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.
//... //... StreamWriter sw = File.CreateText("MyFile.txt"); StreamWriter sw = File.CreateText("MyFile.txt"); sw.WriteLine ("This is my file"); sw.WriteLine ("This is my file"); sw.WriteLine ( sw.WriteLine ( "I can write ints {0} or floats {1}", 1, 4.2); "I can write ints {0} or floats {1}", 1, 4.2); sw.Close(); sw.Close(); //... //...
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(); } }
13
Create, move, and enumerate through directories and subdirectories Create, move, and enumerate through directories and subdirectories Can eliminate some security checks when reusing an object Enumerating through the current directory
Lead-in
The Directory and DirectoryInfo classes expose routines for creating, moving, and enumerating through directories and subdirectories.
#
Example:
#
DirectoryInfo dir = new DirectoryInfo("."); DirectoryInfo dir = new DirectoryInfo("."); foreach (FileInfo f in dir.GetFiles("*.cs")) { foreach (FileInfo f in dir.GetFiles("*.cs")) { String name = f.FullName; } String name = f.FullName; }
!
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); } } }
14
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 664 403 7/20/2000 10:42 AM 7/27/2000 3:11 PM 8/8/2000 10:25 AM C:\Bin\paramatt.cs C:\Bin\tst.cs 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:\\"
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.
15
FileSystemWatcher
Topic Objective
To explain how the FileSystemWatcher component can be used to monitor and react to changes in a file system.
! !
FileSystemWatcher watcher = new FileSystemWatcher(); FileSystemWatcher watcher = new FileSystemWatcher(); ! Configure watcher.Path = args[0]; watcher.Path = args[0]; watcher.Filter = "*.txt"; watcher.Filter = "*.txt"; watcher.NotifyFilter = NotifyFilters.FileName; watcher.NotifyFilter = NotifyFilters.FileName; watcher.Renamed += new watcher.Renamed += new RenamedEventHandler(OnRenamed); RenamedEventHandler(OnRenamed);
!
Lead-in
You use the FileSystemWatcher component to monitor a file system and react when changes to it occur.
Begin Watching
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 given 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". Note 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 Developers 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 given.
16
17
18
Isolated Storage
Topic Objective
To introduce isolated storage and its potential uses.
!
Isolated Storage Provides Standardized Ways of Associating Applications with Saved Data Semi-Trusted Web Applications Require:
# #
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.
Isolation of their data from other applications' data Safe access to a computers file system
public sealed class IsolatedStorageFile : IsolatedStorage, IDisposable public sealed class IsolatedStorageFile : IsolatedStorage, IDisposable
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 applications files to minimize storage conflicts.
19
20
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.
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.
21
5. View the WordCount.cs file, and locate the last line in Applications Main method, which is:
return 0;
6. Right-click on this line, and select Run To Cursor. In the console window, you should see the following text:
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.
22
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 To
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.
23
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
24
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.
25
Review
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.
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.
26
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 course is based on the prerelease Beta 2 version of Microsoft Visual Studio .NET. Content in the final release of the course may be different from the content included in this prerelease version. All labs in the course are to be completed with the Beta 2 version of Visual Studio .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, 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 Microsoft Corporation. All rights reserved. Microsoft, MS-DOS, Windows, Windows NT, ActiveX, IntelliMirror, JScript, MSDN, MSN, PowerPoint, SQL Server, Visual Basic, Visual C++, Visual C#, Visual FoxPro, Visual Studio, Win32, and Windows Media 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.
iii
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.
!"
Required Materials
To teach this module, you need the following materials: Microsoft PowerPoint file 2349A_11.ppt
Preparation Tasks
To prepare for this module, you should:
!" !"
Read all of the materials for this module. Complete the lab.
iv
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 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 Frameworks 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.
Overview
Topic Objective
To provide an overview of the module topics and objectives.
! ! ! ! ! !
Internet Application Scenarios The WebRequest and WebResponse Model Application Protocols Handling Errors Security Best Practices
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.
The Microsoft .NET Frameworks 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 Microsofts WinInet API. These classes provide varying levels of detail, from a generic request/response model to fine-grained 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 Web Services that are based on the standard Simple Object Access Protocol (SOAP). For more information about the remoting and Web Services approaches to inter-application communication, see Module 12, Serialization, in Course 2349A, Programming the Microsoft .NET Framework with C# (Prerelease). 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.
!"
Lead-in
Several types of applications use the System.Net classes to send or receive data over the Internet.
!
Obtain data from back-end sources for a browser request Send and receive data by acting as servers and clients A robust implementation of HTTP 1.1, including: Pipelining, chunking, authentication, pre-authentication, encryption, proxy support, server certificate validation, and connection management
Peer-to-Peer Applications
#
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, Keepalive, 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.
!"
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.
Uniform Resource Identifier NetworkStream Class Creating a WebRequest Invoking a WebRequest Sending Data Receiving Data Using the WebRequest and WebResponse Model
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. 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.
Note A 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.
URI Contains:
# # # #
Lead-in
The .NET Framework uses the URI to identify the desired communication protocol and Internet resource.
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 Scheme identifier http Server identifier www.contoso.com Path identifier /whatsnew.aspx Query String ?date=today
Example: http://www.contoso.com/whatsnew.aspx?date=today
# # # #
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.
NetworkStream Class
Topic Objective
To explain the function of Network streams in the .NET Framework.
!
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
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.
!
// reading ASCII stream to string // reading ASCII stream to string Byte[] read = new Byte[32]; Byte[] read = new Byte[32]; int bytes = anASCIIStream1.Read(read, 0, read.Length); int bytes = anASCIIStream1.Read(read, 0, read.Length); string stringData = Encoding.ASCII.GetString(read); string stringData = Encoding.ASCII.GetString(read); // writing string to ASCII stream // writing string to ASCII stream Byte[] asciiBytes = Encoding.ASCII.GetBytes(stringData); Byte[] asciiBytes = Encoding.ASCII.GetBytes(stringData); anASCIIStream2.Write(asciiBytes, asciiBytes.Length, 0); anASCIIStream2.Write(asciiBytes, asciiBytes.Length, 0);
!
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.
Creating a WebRequest
Topic Objective
To describe how client applications use the WebRequest.
!
Created by calling WebRequest.Create method Set any property values that are required Cast to access protocol-specific features
Lead-in
Client applications request data from servers by using the WebRequest class and its descendents.
HttpWebRequest httpReq = (HttpWebRequest) HttpWebRequest httpReq = (HttpWebRequest) WebRequest.Create("http://www.contoso.com/"); WebRequest.Create("http://www.contoso.com/"); // Turn off connection keep-alives. // Turn off connection keep-alives. httpReq.KeepAlive = false; httpReq.KeepAlive = false;
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/");
10
Invoking a WebRequest
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.
HttpWebResponse httpResp = HttpWebResponse httpResp = (HttpWebResponse)httpReq.GetResponse(); (HttpWebResponse)httpReq.GetResponse(); //Get the HTTP content length returned by the server. //Get the HTTP content length returned by the server. String contentLength = String contentLength = httpResp.ContentLength.ToString(); httpResp.ContentLength.ToString();
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();
11
Sending Data
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.GetRequestS tream method.
// ... // ... try try { { byte[] sendData = byte[] sendData = Encoding.ASCII.GetBytes("some data"); Encoding.ASCII.GetBytes("some data"); int sendLength = sendData.Length; int sendLength = sendData.Length; HttpWebRequest httpReq = HttpWebRequest httpReq = (HttpWebRequest) WebRequest.Create( (HttpWebRequest) WebRequest.Create( "http://www.contoso.com/"); "http://www.contoso.com/"); httpReq.Method = "POST"; httpReq.Method = "POST"; httpReq.ContentLength = sendLength; httpReq.ContentLength = sendLength; Stream sendStream = httpReq.GetRequestStream(); Stream sendStream = httpReq.GetRequestStream(); sendStream.Write(sendData,0,sendLength); sendStream.Write(sendData,0,sendLength); sendStream.Close(); sendStream.Close(); } } catch(Exception e) {//...} //... catch(Exception e) {//...} //...
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.
12
Receiving Data
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.
// Get the response stream. // Get the response stream. Stream respstrm = resp.GetResponseStream(); Stream respstrm = resp.GetResponseStream(); // Create a buffer to hold the response data. // Create a buffer to hold the response data. int BufferSize = 512; int BufferSize = 512; Byte[] Buffer = new Byte[BufferSize]; Byte[] Buffer = new Byte[BufferSize]; // Read the stream to access the data. // Read the stream to access the data. int bytesRead = respstrm.Read(Buffer, 0, BufferSize); int bytesRead = respstrm.Read(Buffer, 0, BufferSize); while (bytesRead > 0) { while (bytesRead > 0) { Console.Write( Console.Write( Encoding.ASCII.GetString(Buffer, 0, bytesRead)); Encoding.ASCII.GetString(Buffer, 0, bytesRead)); bytesRead = respstrm.Read(Buffer, 0, BufferSize); bytesRead = respstrm.Read(Buffer, 0, BufferSize); } } respstrm.Close(); respstrm.Close();
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();
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.
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 ASCIIencoded 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 //... sr.Close();
14
Lead-in
You will now see an example of the use of the WebRequest and WebResponse model as discussed in this section.
The following code example fully demonstrates the use of the WebRequest and WebResponse model as discussed in this section:
using using using using System; System.Net; System.IO; 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 next page
15
16
$ Application 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.
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, as well as information about using the Windows Sockets interface to implement custom protocols.
17
HTTP
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.
! !
Support Most HTTP 1.1 features HTTP Redirects Automatically if AllowAutoRedirect Property Is true (the Default) Use ServicePoint, ServicePointManager, and ConnectGroupName Classes to Manage Connections
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.
18
19
Lead-in
The Dns class is a static class that retrieves information about a specific host from the Internet Domain Name System.
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 dottedquad 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");
20
Lead-in
Applications can use TCP and UDP services with the TcpClient, TcpListener, and UdpClient classes.
TcpClient tcpc = new TcpClient(serverURI, 13); TcpClient tcpc = new TcpClient(serverURI, 13); Stream s = tcpc.GetStream(); Stream s = tcpc.GetStream(); Byte[] read = new Byte[32]; Byte[] read = new Byte[32]; int bytes = s.Read(read, 0, read.Length); int bytes = s.Read(read, 0, read.Length); String strInData = Encoding.ASCII.GetString(read); String strInData = Encoding.ASCII.GetString(read);
!
TcpListener tcpl = new TcpListener(13); TcpListener tcpl = new TcpListener(13); tcpl.Start(); tcpl.Start(); while (!done) { while (!done) { // Accept will block until someone connects // Accept will block until someone connects Socket s = tcpl.AcceptSocket(); Socket s = tcpl.AcceptSocket(); //Code to handle request goes here //Code to handle request goes here } } tcpl.Stop(); tcpl.Stop();
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.
21
The following code demonstrates how to set up a TcpListener object to monitor TCP port 13:
TcpListener tcpl = new TcpListener(13); // listen on port 13 tcpl.Start(); while (!done) { // Accept will block until someone connects Socket s = tcpl.AcceptSocket(); //Code to handle request goes here } tcpl.Stop();
22
Sockets
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.
! ! !
System.Net Classes Are Built on System.Net.Sockets System.Net.Sockets Are Based on the WinSock32 API See Example in Student Notes
Lead-in
The System.Net.Sockets namespace contains an implementation of the Windows Sockets interface.
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.
23
The following example shows how the Socket class can be used to send data to an HTTP server and receive the response:
using using using using using System; System.Net; System.Net.Sockets; System.IO; 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 = ""; // IPAddress 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 next page
24
25
Handling Errors
Topic Objective
To explain how the Status property is used to determine if an error has occurred.
!
Has Status property, value from WebExceptionStatus If WebExceptionStatus.ProtocolError, then WebResponse contains protocol error information
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.
try try { { // ... Create a request, get response and process stream. } // ... Create a request, get response and process stream. } catch (WebException webExcp) catch (WebException webExcp) { { Console.WriteLine("A WebException has been caught"); Console.WriteLine("A WebException has been caught"); Console.WriteLine(webExcp.ToString()); Console.WriteLine(webExcp.ToString()); WebExceptionStatus status = webExcp.Status; WebExceptionStatus status = webExcp.Status; if (status == WebExceptionStatus.ProtocolError) { if (status == WebExceptionStatus.ProtocolError) { Console.Write( Console.Write( "The server returned protocol error "); "The server returned protocol error "); Console.WriteLine(webExcp.Response.ToString()); Console.WriteLine(webExcp.Response.ToString()); } } } } catch (Exception e) catch (Exception e) {// Code to catch other exceptions goes here.} {// Code to catch other exceptions goes here.}
The WebRequest and WebResponse classes throw system exceptions, such as InvalidArgumentException, and Web-specific exceptions, which are instances of WebException and 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 ConnectFailure ConnectionClosed KeepAliveFailure NameResolutionFailure ProtocolError ReceiveFailure RequestCanceled SecureChannelFailure SendFailure Description The remote service could not be contacted at the transport level. The connection was closed prematurely. The server closed a connection made with the Keep-alive header set. The name service could not resolve the host name. The response received from the server was complete but indicated an error at the protocol level. A complete response was not received from the remote server. The request was canceled. An error occurred in a secure channel link. A complete request could not be sent to the remote server.
26
Module 11: Internet Access (continued) Value ServerProtocolViolation Success Timeout TrustFailure Description The server response was not a valid HTTP response. No error was encountered. No response was received within the time-out set for the request. 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 using using using System; System.Net; System.IO; 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."); // 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 next page
27
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
$ Security
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.
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.
29
Web Proxy
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.
WebProxy proxyObject = new WebProxy( WebProxy proxyObject = new WebProxy( "http://webproxy:80/"); "http://webproxy:80/"); GlobalProxySelection.Select = proxyObject; GlobalProxySelection.Select = proxyObject;
!
WebRequest req = WebRequest.Create( WebRequest req = WebRequest.Create( "http://www.contoso.com/"); "http://www.contoso.com/"); req.Proxy = new WebProxy( req.Proxy = new WebProxy( "http://alternateproxy:80/"); "http://alternateproxy:80/");
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
Lead-in
The WebRequest and WebResponse classes use SSL automatically. String MyURI = "https://www.contoso.com/"; String MyURI = "https://www.contoso.com/"; WebRequest wReq = WebRequest.Create(MyURI); WebRequest wReq = WebRequest.Create(MyURI);
The WebRequest and WebResponse classes use SSL automatically. The WebRequest object decides to use SSL on the basis of the URI that it is given. 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);
31
Internet Authentication
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.
Basic, digest, negotiate, NTLM, and Kerberos authentication Users can also create their own authentication NetworkCredential for a single Internet resource CredentialCache for multiple Internet resources
! !
Authentication Managed by the AuthenticationManager Some Schemes Allow Pre-Authentication to Save Time
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
Basic 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 MyURI = "http://www.contoso.com/"; WebRequest wReq = WebRequest.Create(MyURI); wReq.Credentials = new NetworkCredential( "username", "password");
33
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. Note NTLM authentication does not work through a proxy server.
34
Permissions
Topic Objective
To explain which permission classes best suit which application types.
!
WebPermissions
#
Lead-in
The WebPermissions and SocketPermissions classes provide Internet security for applications that use System.Net.
Controls an application's right to request data from a URI or to serve a URI to the Internet Controls an application's right to accept data on a local port or to contact applications WebRequest and its descendents use WebPermissions Socket-level access uses SocketPermissions Accept application can answer an incoming connection Connect application can initiate a connection
SocketPermissions
#
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 socketlevel 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.
35
Best Practices
Topic Objective
To introduce best practices that will help students use the System.Net classes more effectively.
!
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
#
Lead-in
The following recommendations will help you use the classes that are contained in System.Net more effectively.
! !
When Possible, Use TcpClient or UdpClient, Instead of Writing Directly to a Socket Use the CredentialCache Class If Credentials Are Required
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 impact 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.
36
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.TcpLi stener class to accept requests from and provide date and time information to clients.
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.
37
4. Rename the wizard generated Class1 to Server. 5. Modify the Main method to: a. Instantiate a TcpListener object to listen on port 13. 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. Write out to the console a message that contains the string that was just sent. 6. Build the server application.
38
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 output a message that states that the named server could not be found to the console. The message should include the exceptions data. Then exit the application. e. In a try/catch block do the following steps: i. Connect to the named server using port 13. ii. Get the stream. iii. Read all of the bytes in the stream into the byte array that you created in step 4b, and store the number of bytes that are read in an integer. iv. Convert the ASCII-encoded byte array into a string. v. Write out to the console a message that states the number of bytes received and the current date and time string retrieved from the server. vi. Close the TcpClient object. vii. If an Exception exception is thrown, print out an error message to the console and exit.
BETA MATERIALS FOR MICROSOFT CERTIFIED TRAINER PREPARATION PURPOSES ONLY
39
f. Write out to the console the following message: Press ENTER to exit. g. Do a read from the console to wait for the ENTER key and exit the application. 7. Build the client application.
5. Run the client application as directed in step 2 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 console window:
datetimeclient foo
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 2 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
8. Note that the client application outputs the number of bytes received and the current date and time string retrieved from the server.
40
Review
Topic Objective
To reinforce module objectives by reviewing key points.
! ! ! ! ! !
Internet Application Scenarios The WebRequest and WebResponse Model Application Protocols Handling Errors Security Best Practices
Lead-in
The review questions cover some of the key concepts taught in the module.
1. Name the four parts of the following URI: http://www.microsoft.com/default.htm?foo=bar Scheme identifier Server identifier Path identifier Query String http www.Microsoft.com /default.htm ?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();
41
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
Contents Overview Serialization Scenarios Serialization Attributes Object Graph Serialization Process Serialization Example Deserialization Example Custom Serialization Custom Serialization Example Security Issues Lab 12: Serialization Review 1 2 4 5 7 9 10 12 14 17 18 27
This course is based on the prerelease Beta 2 version of Microsoft Visual Studio .NET. Content in the final release of the course may be different from the content included in this prerelease version. All labs in the course are to be completed with the Beta 2 version of Visual Studio .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, 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 Microsoft Corporation. All rights reserved. Microsoft, MS-DOS, Windows, Windows NT, ActiveX, IntelliMirror, JScript, MSDN, MSN, PowerPoint, SQL Server, Visual Basic, Visual C++, Visual C#, Visual FoxPro, Visual Studio, Win32, and Windows Media 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.
iii
Required Materials
To teach this module, you need the following materials: Microsoft PowerPoint file 2349A_12.ppt
Preparation Tasks
To prepare for this module, you should:
!" !"
Read all of the materials for this module. Complete the lab.
iv
Module Strategy
Use the following strategy to present this module:
!"
Serialization Scenarios Discuss briefly how serialization is used in scenarios such as persisting inmemory 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.
Overview
Topic Objective
To provide an overview of the module topics and objectives.
! ! ! ! ! ! ! ! !
Serialization Scenarios Serialization Attributes Object Graph Serialization Process Serialization Example Deserialization Example Custom Serialization Custom Serialization Example Security Issues
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.
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.
Serialization Scenarios
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.
Persistence
#
Store and retrieve a graph of objects to and from a file Pass by value arguments that are transmitted between processes
Remoting
#
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.
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 2349A, Programming the Microsoft .NET Framework with C# (Prerelease).
Serialization Attributes
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.
[Serializable] public class MyClass { [Serializable] public class MyClass { [NonSerialized] int _cashSize; [NonSerialized] int _cashSize; //... //... } }
!
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.
Object Graph
Topic Objective
To define an object graph and explain its function.
3 4 Cat
Lead-in
An object graph is a set of objects that share a set of references to each other.
Horse
Duck
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.
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.
Serialization Process
Topic Objective
To introduce the classes that are used in the serialization process.
!
ObjectIDGenerator generates IDs for objects ObjectManager tracks objects as they are being deserialized FileStream, MemoryStream, NetStream Writes or reads data in a specified format to the output or input streams Runtime provides BinaryFormatter and SoapFormatter
Lead-in
The process of serializing an object graph involves identifying the individual objects in the graph and the relationships between them.
Formatter Class
#
The process of serializing an object graph involves identifying the individual objects in the graph and the relationships between them.
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.
Both the ObjectIDGenerator and ObjectManager classes are pluggable, so you can build your own alternatives. Note 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.
BETA MATERIALS FOR MICROSOFT CERTIFIED TRAINER PREPARATION PURPOSES ONLY
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.
Serialization Example
Topic Objective
To provide an example of serialization of an object graph. class SerializeExample{ class SerializeExample{ public static void Main(String[] args) { public static void Main(String[] args) { // create the object graph // create the object graph ArrayList l = new ArrayList(); ArrayList l = new ArrayList(); for (int x=0; x< 100; x++) { for (int x=0; x< 100; x++) { l.Add (x); l.Add (x); } } // create the filestream // create the filestream FileStream s = File.Create("foo.bin"); FileStream s = File.Create("foo.bin"); // create the BinaryFormatter // create the BinaryFormatter BinaryFormatter b = new BinaryFormatter(); BinaryFormatter b = new BinaryFormatter(); // serialize the graph to the stream // serialize the graph to the stream b.Serialize(s, l); b.Serialize(s, l); s.Close(); s.Close(); } }
Lead-in
This code sample shows how to perform default serialization of a graph of objects, whose root is an ArrayList.
} }
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 using using using using System; System.IO; System.Collections; System.Runtime.Serialization; 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(); } }
10
Deserialization Example
Topic Objective
To show how to create a clone of the graph by deserializing it. class DeSerialize class DeSerialize { { public static void Main(String[] args) public static void Main(String[] args) { { // open the filestream // open the filestream FileStream s = File.OpenRead("foo.bin"); FileStream s = File.OpenRead("foo.bin"); // create the formatter // create the formatter BinaryFormatter b = new BinaryFormatter(); BinaryFormatter b = new BinaryFormatter(); // deserialize // deserialize ArrayList p = (ArrayList) b.Deserialize(s); ArrayList p = (ArrayList) b.Deserialize(s); s.Close(); s.Close(); // print out the new object graph // print out the new object graph // see module text for PrintValues code // see module text for PrintValues code PrintValues(p); PrintValues(p);
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.
} }
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 using using using using System; System.IO; System.Collections; System.Runtime.Serialization; 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 next page
11
12
Custom Serialization
Topic Objective
To explain what is required to implement ISerializable for performing custom serialization.
!
When some of the data is not valid after deserialization When some of the data must be obtained by calculation 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
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.
ISerializable Requires:
#
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.
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
Lead-in
This example shows how to provide custom serialization for a class named ISerializableExample.
The following example shows how to provide custom serialization for a class:
using using using using using System; System.IO; System.Runtime.Serialization; System.Runtime.Serialization.Formatters; System.Runtime.Serialization.Formatters.Soap;
public class Sample { public static void Main() { ToFile(); FromFile(); } //Code continued next page
15
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 next page
BETA MATERIALS FOR MICROSOFT CERTIFIED TRAINER PREPARATION PURPOSES ONLY
16
Outputs:
ToFile i: 1 j: 20 k: 50 FromFile i: 1 j: 20 k: 50
17
Security Issues
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.
If private data is sensitive, consider encrypting the stream before transmitting or saving to a disk
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.
18
Lead-in
In this lab, you will write a client/server application that uses serialization.
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.
19
20
21
Note 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.
22
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
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
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.
27
Review
Topic Objective
To reinforce module objectives by reviewing key points.
! ! ! ! ! ! ! ! !
Serialization Scenarios Serialization Attributes Object Graph Serialization Process Serialization Example Deserialization Example Custom Serialization Custom Serialization Example Security Issues
Lead-in
The review questions cover some of the key concepts taught in the module.
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
This course is based on the prerelease Beta 2 version of Microsoft Visual Studio .NET. Content in the final release of the course may be different from the content included in this prerelease version. All labs in the course are to be completed with the Beta 2 version of Visual Studio .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, 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 Microsoft Corporation. All rights reserved. Microsoft, MS-DOS, Windows, Windows NT, ActiveX, IntelliMirror, JScript, MSDN, MSN, PowerPoint, SQL Server, Visual Basic, Visual C++, Visual C#, Visual FoxPro, Visual Studio, Win32, and Windows Media 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.
iii
Write and configure distributed applications that use .NET Remoting. Create a Web Service by using Microsoft Visual Studio .NET and ASP.NET. Consume a Web Service by using the Web Services Description Language tool (Wsdl.exe).
!"
Required Materials
To teach this module, you need the following materials: Microsoft PowerPoint file 2349A_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.
iv
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.
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 remoting framework supports server-side and client-side activation of remote objects and describe the differences between server-side and clientside 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.
!"
Web Services Explain how to use Visual Studio .NET to implement an ASP.NET Web Service and how to access the Web Service from a Web browser and a client application. Use the Using Visual Studio .NET to Create a Web Service demonstration to illustrate the concepts that are covered in this section. Introduce the Web Service discovery process and the tools that are available for discovery.
Overview
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.
Microsoft .NET Remoting provides a framework that allows objects to interact across remoting boundaries, for example, across application domains (AppDomain). Web Services is a term for communication that uses industry-standard HTTP and Simple Object Access Protocol (SOAP) protocols. You can implement 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 a Web Service by using Microsoft Visual Studio .NET and ASP.NET. Consume a Web Service by using the Web Services Description Language tool (Wsdl.exe).
!"
# Remoting
Topic Objective
To provide an overview of the topics covered in this section.
! ! ! ! ! ! ! !
Remoting Overview Channels and Formatters Activation and Proxies Lease-Based Lifetime Object Marshaling Server-Side Client-Side Client Compilation Techniques
Lead-in
This section shows how .NET Remoting supports communication in various scenarios.
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 against different runtime environments, the process of calling methods on remote objects is almost identical to the process of calling local methods.
Remoting Overview
Topic Objective
To provide an overview of .NET Remoting.
Lead-in
The .NET Framework provides several services that are used in remoting.
Client AppDomain
Client Object Server Proxy Formatter
Server AppDomain
Formatter Server Object
Channel
Channel
Remoting Boundary
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
!"
!" !"
Note Because the .NET context mechanism is beyond the scope of this course, this module does not cover remoting calls between contexts.
Channels Transport Messages To and From Remote Objects Client Selects a Channel That Is Registered on the Server
$
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.
$
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 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
using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using System.Runtime.Remoting.Channels.Tcp; TcpChannel chan = new TcpChannel(8085); TcpChannel chan = new TcpChannel(8085); ChannelServices.RegisterChannel(chan); ChannelServices.RegisterChannel(chan);
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 framework 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 illegal 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.
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.
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 URI by using the HTTP protocol. You can create industry-standard 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 URI 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 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); //...
By calling new, Activator.CreateInstance, or Activator.GetObject Proxy represents remote object in clients AppDomain Proxy forwards clients calls, and returns results and exceptions Single call object handles only one request (stateless) Singleton object services multiple clients and requests (stateful) State maintained between method calls for specific client instance
Lead-in
The remoting framework supports server-side and client-side activation of remote objects.
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.
Activator.CreateInstance Used to create an object instance Activator.GetObject Normally used to connect to an object that is already running at a specified URI
!"
Server-Side Activation
Server-side activation supports single call and singleton modes of activation.
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.
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.
Lease-Based Lifetime
Topic Objective
To explain how using a leasing mechanism can extend the lifetime of clientactivated remote objects.
!
A Leasing Mechanism Controls the Lifetime of a ClientActivated Remote Object An Objects Lease Time Can Be Extended When an Objects Lease Time Reaches Zero
$ $ $
Lead-in
You can control the lifetime of client-activated remote objects by using a leasing mechanism.
! !
The object is disconnected from remoting infrastructure The object may be garbage-collected A lease provides an alternative to reference counting
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 Developers 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.
10
Object Marshaling
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.
!
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
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: The following scenarios are examples where objects are passed across a remoting boundary because of calls to remote objects:
!"
!"
11
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.
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 OS handle, application domain-bound. In this case, the OS handle would not be useful or appropriate in another application domain, process, or computer.
12
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. 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 { //. . . }
13
Server-Side
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.
Or a configuration file
RemotingConfiguration.Configure("MyHello.exe.config"); RemotingConfiguration.Configure("MyHello.exe.config");
All remote objects must be registered with the remoting framework before clients can access them. Object registration is normally 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. Note 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 URI 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.
14
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(RemotingSamples.HelloServer), "SayHello", WellKnownObjectMode.SingleCall);
In the preceding example, RemotingSamples.HelloServer is the name of the class, and SayHello is the object URI.
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 URI, 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. Note 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.
15
Client-Side
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. Activate Remote Object by Using:
$
Activator.GetObject
HelloServer obj = (HelloServer)Activator.GetObject( HelloServer obj = (HelloServer)Activator.GetObject( typeof(RemotingSamples.HelloServer), typeof(RemotingSamples.HelloServer), "tcp://localhost:8085/SayHello"); "tcp://localhost:8085/SayHello");
$
RemotingConfiguration.Configure("MyHello.exe.config"); RemotingConfiguration.Configure("MyHello.exe.config"); HelloServer obj = new HelloServer(); HelloServer obj = new HelloServer();
On the client side, a client registers the channel and activates the remote object.
Important You do not specify a port number when you register the client channel.
16
The only difference between GetObject and new is that GetObject allows you to specify a URI as a parameter, while new obtains the URI 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.
Once the configuration file has been loaded, the client can activate the object as follows:
HelloServer obj = new HelloServer();
17
Lead-in
When compiling the client code, the compiler requires type information about the HelloServer class.
!
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
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, simply point the tool at a remote URI and generate the required metadata.
Note The Web Service Utility extracts only metadata and does not generate the source for the remote object.
18
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.
Lead-in
Programmatic registration is a simple process, but it is not practical for use in reallife situations where large numbers of remote objects must be managed on a corporate network.
RemotingConfiguration.Configure(foo.exe.config); RemotingConfiguration.Configure(foo.exe.config);
!
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 } }
19
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 Configurecall on RemotingConfiguration only reads the relevant sections in the configuration file that apply to remoting, while the rest of the information is ignored. An application configuration file is an XML document that contains sections for various feature areas. The general format is a follows: Note Configuration files are case-sensitive.
20
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.
21
Demonstration: Remoting
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.
This demonstration shows how a client application can use .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 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.
Server-Side
The following sample code is the server source code, which is named Server.cs:
22
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 ); */ //Code continued next page
23
System.Console.WriteLine("Press Enter key to exit"); System.Console.ReadLine(); return 0; } 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.
24
Client-Side
The following sample code is the client source code, which is named Client.cs:
using using using using using using System; System.Runtime.Remoting; System.Runtime.Remoting.Channels; System.Runtime.Remoting.Channels.Tcp; System.Runtime.Remoting.Channels.Http; 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 next page
25
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 URI. For the clients TCP connection, the URI is simply tcp://localhost:8085/SayHello For the clients HTTP connection, the URI is http://localhost:8086/SayHello Important The URI 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.
26
To execute the application, start the server application from a console window, and then start the client application from another console window. Note The counter values on the two client calls should have the same value because the server objects activation mode is SingleCall.
Rebuild and execute as in the preceding example. Note the counter values on both calls. They should increase after each call.
27
Lead-in
In this lab, you will create a Web Service hosted in a .NET executable file, create a TCP server, and create a client that uses .NET Remoting to access a Web Service and a TCP server.
Objectives
After completing this lab, you will be able to:
!" !" !"
Create a Web Service hosted in a .NET executable file. Create a TCP server. Create a client that uses .NET Remoting to access both a 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.
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.
28
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 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 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.
29
30
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 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 console window, build Authenticate.exe.
31
32
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 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 console window, build Fulfillment.exe.
33
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 console window, build Testclient.exe. Important Do not forget to reference Authenticate.exe, and Fulfillment.exe in the command line.
34
10. Test the order system. a. Ensure that no server application is running and listening on ports 8085 and 8086. 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
35
# Web Services
Topic Objective
To provide an overview of the topics covered in this section.
! ! ! !
ASP.NET Web Services Overview ASP.NET Features Consuming a Web Service Web Service Discovery
Lead-in
In this section, you will learn how to use Visual Studio .NET to implement an ASP.NET Web Service and how to access the Web Service from a Web browser and a client application.
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 Web Services by simply 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 Web Service and how to access this Web Service from both a Web browser and a client application.
36
ASP.NET Files with .asmx Extensions Are Web Services Example of HelloWorld .asmx File
Lead-in
ASP.NET provides support for Web Services with the .asmx file, which is a text file that is similar to an .aspx file.
<%@ WebService Language="C#" Class="HelloWorld" %> <%@ WebService Language="C#" Class="HelloWorld" %> using System; using System; using System.Web.Services; using System.Web.Services; public class HelloWorld : WebService { public class HelloWorld : WebService { [WebMethod] public String SayHelloWorld() { [WebMethod] public String SayHelloWorld() { return "Hello World"; return "Hello World"; } } } }
!
http://Foo/Bar/HelloWorld.asmx http://Foo/Bar/HelloWorld.asmx
$
http://Foo/Bar/HelloWorld.asmx?wsdl http://Foo/Bar/HelloWorld.asmx?wsdl
This section introduces some of the basic features of ASP.NET that enable developers to host and use Web Services. The Internet is quickly evolving from its present stage where Web sites simply 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 Web Services.
37
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 enter 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.
The following example shows the code that is contained in the HelloWorld.asmx file. It is a simple example of a ASP.NET-hosted Web Service.
<%@ WebService Language="C#" Class="HelloWorld" %> using System; using System.Web.Services; public class HelloWorld : WebService { [WebMethod] public String SayHelloWorld() { return "Hello World"; } }
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 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.
To create a new virtual directory, right-click an existing directory, and click New. To promote an existing directory, right-click a virtual directory, and click Properties and 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 enter http://Foo/Bar/HelloWorld.asmx in the Address bar, the resulting page shows the public methods for this Web Service and the protocols, such as SOAP or HTTP GET, that you can use to invoke these methods. The public methods for the Web Service are marked with the [WebMethod] attribute. The Web Services Description Language XML file for this service is produced when you enter 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.
38
ASP.NET Features
Topic Objective
To explain how to create a 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 a Web Service by simply exposing a .NET class that inherits from the WebService class.
$ Class derives from WebService, method attribute [WebMethod] namespace MyNameSpace { namespace MyNameSpace { public class HelloWorld : WebService { public class HelloWorld : WebService { [WebMethod] public String SayHelloWorld() { [WebMethod] public String SayHelloWorld() { return "Hello World"; } return "Hello World"; } } } } } $ Class source file is compiled into a library DLL and placed in \Bin
The .asmx file contains a single line that names the class
In ASP.NET, you can create a 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 enter the following command:
>csc /out:bin\helloworld.dll /t:library helloworld.cs
39
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 Web Service methods or when the WebService class is also used in local applications. Note A local application can use any public method, but only [WebMethod] methods are remotely accessible through SOAP.
Data sets Data sets are a powerful new XML-based technique to represent disconnected data. Data sets can be returned from a Web Service method. Because Data sets can store complex information and relationships in an intelligent structure, they enable you to take full advantage of 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 URI that returns text as if it were a Web Service.
Further discussion of these and other advanced ASP.NET features is beyond the scope of this course.
40
Lead-in
In this demonstration, you will learn how to create a service called MathService that has a single method named SalesTax.
In this section, you have learned how to create a Web Service by using ASP.NET. Visual Studio .NET provides an even easier way to build ASP.NEThosted 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.
!" create a service called MathService that has a single method named To
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.
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 Web Service. c. Set the Name field to MathService. d. Set the Location field to http://<machinename>. 2. On the View menu, click Code. Because the Web Service in this demonstration is simple, you can work on the code directly. For more complex Web Services, you can use the Service1.asmx.cs Design palette that is displayed when you create a new project of type 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 commentedout WEB SERVICE EXAMPLE.
41
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 to create the Web Service. 6. Use Internet Explorer to access the Web Service at the following URL:
http://localhost/MathService/Service1.asmx
Tip To use Internet Explorer to view a Web Service from within the Visual Studio environment, right-click the .asmx file in the Solution Explorer window, and click View in Browser. 7. In the Internet Explorer page that is returned in step 6, click on the link for the Service1 operation labeled SalesTax. 8. In the Internet Explorer page that is returned in step 7, enter some parameters, and 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 Web Services description is obtained from the following URL:
http://localhost/MathService/Service1.asmx?wsdl
42
Lead-in
In addition to providing technology that enables you to create Web Services, the .NET Framework provides a sophisticated set of tools and code to consume Web Services.
In addition to providing technology that enables you to create Web Services, the .NET Framework provides a sophisticated set of tools and code to consume Web Services. Consuming a Web Service means accessing it as a client. Because Web Services are based on open protocols, such as SOAP and HTTP, this client technology can also be used to consume Web Services that are not built by using the .NET Framework.
43
The following process shows how to create a simple application that uses the Web Service that is created in the Using Visual Studio to Create a Web Service demonstration in this module. 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 a Web Service. If you feel that students may benefit from a full demonstration, you can demonstrate the steps here.
Note Typically a client uses a 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. 3. To generate a proxy for the Service1 class in a file named Service1.cs, enter the following command:
wsdl http://localhost/MathService/service1.asmx?wsdl
44
[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])); } //...
45
4. Write the client code that calls the remote object. This code is contained in Salestaxclient.cs, as follows:
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; } }
5. 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. 6. To build Salestaxclient.exe, enter the following command:
csc salestaxclient.cs service1.cs
7. 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
46
Discovery The Process of Locating and Interrogating Web Service Descriptions Discovery Services Are Evolving and Changing Rapidly Disco Microsofts Web Services Discovery Tool
$
! !
Lead-in
Web Service discovery is the process of dynamically locating and interrogating Web Service descriptions, which is a preliminary step for accessing a Web Service.
Disco file is an XML document that links to descriptions of Web Services Disco file is currently created and used by Visual Studio 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
$ $
Web Service discovery is the process of dynamically locating and interrogating Web Service descriptions, which is a preliminary step for accessing a Web Service. The discovery process allows a Web Service client that may not know the Web Services URI to locate a 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.
47
Lead-in
In this lab, you will access a Web Service by creating a proxy using the Web Services Description Language tool.
Objectives
After completing this lab, you will be able to: Access a 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.
Scenario
This lab is similar to the scenario used in Lab 13.1, Building an OrderProcessing 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 a Web Service to calculate the items cost including sales tax. This scenario simulates the case where you do not have access to the Web Services source code or assembly.
48
You will build a MathService 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 Web Service. Then you will build a client called Testclient2.exe that uses this proxy to access the Web Services Service1.SalesTax method to calculate the cost of the ordered item.
49
!" create the MathService Web Service using Visual Studio .NET To
Follow the steps presented in Demonstration: Using Visual Studio .NET to Create a Web Service in this module.
50
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 as follows: a. Reference System.Runtime.Remoting.dll, System.Web.Services.dll, 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. 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.
51
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
52
Review
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.
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.
53
5. What file extension is typically used by Microsofts ASP.NET-hosted Web Services? ASP.NET provides support for 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 a Web Service that is not implemented by using the .NET Framework or in which the 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 a Web Service and create a proxy class. The client can use the proxy class to invoke the methods of the Web Service.