(en) Programming Microsoft Visual C# 2005 - The Language (MS Press , 2006)
(en) Programming Microsoft Visual C# 2005 - The Language (MS Press , 2006)
html
Programming Microsoft
Visual C# 2005: The
Language
byDonis Marshall
Microsoft Press 2006 (704 pages)
ISBN:0735621810
Table of Contents
Programming Microsoft Visual C# 2005 —The Language
Introductio n
Part I - Core Languag e
Chapter 1 - Introduction to Visual C# Programmin g
Chapter 2 - Type s
Chapter 3 - Inheritanc e
Part II - Core Skill s
Chapter 4 - Introduction to Visual Studio 200 5
Chapter 5 - Arrays and Collection s
Chapter 6 - Generic s
Chapter 7 - Iterator s
Part III - More C# Languag e
Chapter 8 - Delegates and Event s
Chapter 9 - Exception Handlin g
Part IV - Debuggin g
Chapter 10 - Metadata and Reflectio n
Chapter 11 - MSIL Programmin g
Chapter 12 - Debugging with Visual Studio 200 5
Chapter 13 - Advanced Debuggin g
Part V - Advanced Concept s
Chapter 14 - Memory Managemen t
Chapter 15 - Unsafe Cod e
Appendix A - Operator Overloadin g
Index
List of Figures
List of Tables
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Back Cover
Get the essential, straightforward information you need to master the core capabilities of Visual C# 2005. Both
new and experienced developers get expert guidance, hands-on programming instruction, and practical
examples to help advance their proficiency in developing applications for Microsoft Windows and the Web.
Know when to catch exceptions —and handle them locally or propagate them
Use the Microsoft Visual Studio Debugger and explore advanced debugging techniques and tools
Donis Marshall is a trainer, a consultant and an author with 20 years of development experience and an
in-depth background on Microsoft .NET technologies. He is the author of several books, including .NET Security
Programming. Donis teaches classes on .NET programming, debugging, security, and design and architecture.
In addition, he is the president of DebugNow.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
All rights reserved. No part of the contents of this book may be reproduced or transmitted in any form or by
any means without the written permission of the publisher.
1 2 3 4 5 6 7 8 9 QWT 0 9 8 7 6 5
A CIP catalogue record for this book is available from the British Library.
Microsoft Press books are available through booksellers and distributors worldwide. For further information
about international editions, contact your local Microsoft Corporation office or contact Microsoft Press
International directly at fax (425) 936-7329. Visit our Web site at www.microsoft.com/mspress. Send
comments to [email protected].
Microsoft, IntelliSense, Microsoft Press, MSDN, Visual Basic, Visual C#, Visual Studio, the Visual Studio
logo, Win32, Windows, Windows CE, the Windows logo, Windows NT, and WinFX are either registered
trademarks or trademarks of Microsoft Corporation in the United States and/or other countries. Other product
and company names mentioned herein may be the trademarks of their respective owners.
The example companies, organizations, products, domain names, e-mail addresses, logos, people, places,
and events depicted herein are fictitious. No association with any real company, organization, product,
domain name, e-mail address, logo, person, place, or event is intended or should be inferred.
This book expresses the author's views and opinions. The information contained in this book is provided
without any express, statutory, or implied warranties. Neither the authors, Microsoft Corporation, nor its
resellers, or distributors will be held liable for any damages caused or alleged to be caused either directly or
indirectly by this book.
This book is dedicated to my father, Herbert Marshall. He was a nuclear engineer, an artist, a confidant, a
doting husband to my mother, and most of all a compassionate person. He was a giant amongst men and
touched innumerable lives. His three sons, including myself, miss him every day.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Acknowledgments
Programming Microsoft Visual C# 2005: The Language was completed with the collaboration of several
people. I am credited on the cover, but the contribution of others is no less important. I want to especially
acknowledge the contributions of Valerie Woolley, Ben Ryan, and Jim Rogers. If possible, they would also
receive cover credit. Valerie was the project manager and provided support, encouragement, patience, and
the occasional nudge when necessary. Ben, who was the acquisitions editor, had unyielding confidence in
my ability, which is greatly appreciated. Jim Rogers was the technical editor and diligently reviewed
hundreds of pages of manuscript and code. He helped assure the superb quality of the book.
I also want to thank John Bruno, who is an exceptional engineer, for reviewing and commenting on each
chapter, which was very helpful.
Writing a book is an all-consuming project. Unfortunately, much of this burden is carried by friends and loved
ones. I appreciate the patience and support of my mother, Lynn, and of my friends Herb, Jr., Chuck, and
Patty, along with a long list of other friends who were treated shabbily during this project. Thanks for your
understanding.
Finally, I want to acknowledge my children: Jason, Kristen, and Adam. They make every day special. They
are my motivation. A special acknowledgment is reserved for Jason. During most of this project, Jason was
stationed in Iraq as a Marine. He recently returned home safely. We are proud of you.
Donis Marshall
Donis Marshall is currently one of the few trainers endorsed by Microsoft Global Learning Services to
conduct Microsoft technology classes for Microsoft employees. In this capacity, Mr. Marshall travels
internationally, delivering dozens of classes to Microsoft developers and engineers in the United States,
Europe, and Asia. His repertoire includes classes on Advanced .NET UMD Debugging, Advanced .NET
Debugging workshops, .NET Design and Architecture, Visual Basic .NET Programming, .NET Interoperability
and Security, .NET Web Services, and ASP.NET. He also teaches .NET classes at Autodesk, NCCI, and
NASA.
Donis Marshall is a nationally recognized teacher of computer technology to developers and scientists. As
founder and lead instructor for The Training Alliance, he taught advanced technical classes for many Fortune
500 clients. He also managed a staff of technical instructors as Director of Advanced Technical Learning
Services at Productivity Point International, a national franchiser of training services.
Mr. Marshall is President of DebugNow (www.debugnow). DebugNow offers an assortment of innovative tools
for support engineers and developers to debug and monitor Win32 and .NET applications.
As a contractor, Donis Marshall has written thousands of lines of code for various entities.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Introduction
Microsoft Visual C# 2005 includes several enhancements to earlier versions of the language. If you want to
learn the new features of the language, Programming Microsoft Visual C# 2005: The Language offers detailed
explanations of each improvement. One of the most awaited additions to the .NET environment is generics.
Other additions include anonymous methods, static classes, and new classes that affect garbage collection.
This book introduces these new features, provides context, and displays sample code.
Enhancements are not limited to the language. The Microsoft Visual Studio IDE has also been enhanced in
Visual Studio 2005. Microsoft continues to expand upon the impressive assortment of rapid application
development (RAD) tools that are available. The Exception Assistant, code snippets, and visualizers are
some of the more notable advancements, where the emphasis is on providing helpful information that
markedly increases developer productivity. This book details these and other improvements to previous
versions of Visual Studio.
A larger portion of the lifetime of an application is spent in maintenance and debugging. Efficient and effective
debugging tools and techniques can facilitate a more robust application, which reduces the need to debug.
You can also resolve problems more quickly when they inevitably occur. A managed application has a
managed veneer and an unmanaged underpinning. Debugging requires an understanding of both realms
where a managed application exists: managed and unmanaged. From a debugging context, Chapter 12
examines both realms of a managed application and helps developers understand how to effectively debug a
managed application.
This book targets both professional and casual developers. Practical, in-depth explanations are offered for
even the most ardent developers. Sample code is provided as a complement to the content. For casual
developers, code is often the clearest explanation of in-depth concepts. Actually, even for professional
developers, sample code is often invaluable. For this reason, this book contains reams of code examples.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The first part, "Core Language," introduces the basic concepts of the language. Chapter 1 contains a general
overview of the language. Chapter 2 introduces types, which include classes and value types. Chapter 3
explains inheritance in C# and the related keywords, such as virtual, override, sealed, and abstract.
The second part, "Core Skills," covers the core skills required to create a C# application. Chapter 4 reviews
Visual Studio 2005, which is the central tool in developing a managed application. Chapter 5 explains arrays
and collections. It is hard to imagine a competent C# application that does not employ arrays or collections.
Chapter 6 introduces generics, which is a new feature of .NET Framework 2.0. Chapter 7 pertains to iterators
and the capability to enumerate collection-related classes.
The third part, "More C# Language," focuses on additional language features. Chapter 8 details managed
function pointers, which are represented by delegates and events in managed code. Chapter 9 explains
structured exception handling in the run time and within the C# language.
The fourth part, "Debugging," is an all-inclusive explanation of debugging managed code. The first two
chapters in this section provide an internal view of an assembly, which is critical for anyone debugging a
managed application: Chapter 10 introduces metadata and reflection; Chapter 11 is an overview of Microsoft
intermediate language (MSIL) programming. Chapter 12 discusses debugging with Visual Studio, which is
the preferred debugging environment for most developers. Finally, Chapter 13 discusses advanced debugging
using the MDbg, Windbg, and SOS debugger extensions.
The final part is "Advanced Concepts." Chapter 14 covers managed memory and garbage collection in the
managed environment. Chapter 15 explains unsafe code and direct pointer manipulation. This chapter also
discusses calling functions that are in unmanaged (native) libraries.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
System Requirements
You'll need the following hardware and software to build and run the code samples for this book:
Microsoft Windows XP with Service Pack 2, Microsoft Windows Server 2003 with Service Pack 1,
or Microsoft Windows 2000 with Service Pack 4
Microsoft Visual Studio 2005 Standard Edition or Microsoft Visual Studio 2005 Professional
Edition
600 MHz Pentium or compatible processor (1 GHz Pentium recommended)
192 MB RAM (256 MB or more recommended)
Video (800 × 600 or higher resolution) monitor with at least 256 colors (1024 × 768 High Color
16-bit recommended)
2 GB available space on installation drive; 1 GB available space on system drive. With MSDN, the
hard disk requirements are 2.8 GB and 1 GB, respectively.
CD-ROM or DVD-ROM drive
Microsoft Mouse or compatible pointing device
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Technology Updates
As technologies related to this book are updated, links to additional information will be added to the
Microsoft Press Technology Updates Web page. Visit this page periodically for updates on Visual Studio
2005 and other technologies:
http://www.microsoft.com/mspress/updates/
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Code Samples
All the code samples discussed in this book can be downloaded from the book's companion content Web
page at the following address:
http://www.microsoft.com/mspress/companion/0-7356-2181-0/
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Microsoft Press provides support for books and companion content at the following Web site:
http://www.microsoft.com/learning/support/books/
If you have comments, questions, or ideas regarding the book or the companion content, or questions that
are not answered by visiting the previous sites, please send them to Microsoft Press via e-mail to
Microsoft Press
Redmond, WA 98052-6399
Please note that Microsoft software product support is not offered through the preceding addresses.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Chapter 2: Types
Chapter 3: Inheritance
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Learning a language typically begins with core elements of language. In English, these elements include
consonants, vowels, nouns, verbs, adjectives, phrases, and sentences. They represent the building blocks of
the language; you cannot read, write, or speak English without a fundamental understanding of these
language components. The key elements of C# are symbols and tokens, keywords, expressions,
statements, functions, and classes. Effective C# programming requires, of course, a fundamental
understanding of these elements, which this chapter will provide.
As you know, a sentence in English is more than random words terminated with a period. Likewise, in C#, a
programming statement is more than a collection of random clauses. The following English sentence and C#
statement are both nonsensical:
Programming fun is C#.
for(i<5;int i=0;++i)
In both cases, the correct elements are present, but the structure is incorrect. Using either a human
language or a programming language to convey cohesive ideas, concepts, tasks, or instructions requires
organizing the words and other elements of the language correctly. In the English language, syntax (the
rules of a language) indicates where a linking verb is placed in relation to the noun object. By comparison,
C# syntax orders the clauses of a for statement. According to C# syntax, the previous for statement should
be structured like this: for(int i=0;i<5;++i). Understanding the underlying language syntax is equally
important for natural and programming languages. This chapter will also provide the basic syntax of the C#
language.
Mandarin Chinese is a tonal language, whereas English is a stress language. Learning Chinese is more than
simply assimilating new words and sentence structures. You must also learn tones because the meaning of
a Chinese word can change based on tone. Speaking Chinese with English enunciation would be confusing
and amusing at best. Similarly, C# is an object-oriented language, not a procedural language (more on this
difference later). C#, C++, Java, SmallTalk, Eiffel, and other object-oriented languages are only as effective
as your appreciation of object-oriented concepts and programming techniques. I recommend a basic
knowledge of object-oriented analysis and design concepts as a complement to the newly acquired C# skills
this book will give you.
Finally, languages do not emerge spontaneously. Natural languages have been evolving for nearly 150,000
years, and knowing the heritage of and the influences on a language can be informative and helpful. For
example, English, French, German, Yiddish, and related languages are heavily influenced by their Latin
language heritage. As such, they have common words, syntax, and structures that are characteristic of the
Latin metalanguage. The origin of C# does not date back centuries, but an understanding of its evolution is
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
invaluable.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Language Origin
From the time when the first natural language appeared, hundreds of thousands of languages have emerged.
Many of these languages are now extinct, leaving about six thousand languages that are currently spoken.
Some of these languages are similar and grouped by classification. Other languages are quite distinct, such
as Kora, which incorporates a series of click sounds and is spoken by bushmen in Africa.
A list of programming languages is modest when compared with the catalogue of natural languages.
Beginning in the 1940s with Plankalkül, more than 1,000 programming languages have been documented.
Like natural languages, the variety and diversity of these languages is impressive: the succinctness of
assembler, the verbosity of COBOL, and the efficiency of C. For a comprehensive list of programming
languages, visit this link: http://oop.rosweb.ru/Other/.
The motivations that inspire the creation of languages are diverse: FORTRAN was created for scientific
analysis, COBOL for building business applications, RPG for report generation, and so on. Some languages
serve as refinements of earlier languages. CPL combined the best ingredients of several languages, including
ALGOL, FORTRAN, and COBOL. C# is an independently developed, object-oriented language and a member
of the C family of languages. It shares similar syntax and some concepts with other C-family languages;
more important, however, C# has few if any vestiges of procedural programming, in which the basic
programming element is the procedure (that is, a named sequence of statements, such as a routine,
subroutine, or function). Unfortunately, C++ inherited many of the artifacts of procedural programming from C.
C#, however, was designed to be a purely object-oriented language.
ALGOL is arguably the most influential programming language in history. The language was introduced in
1958 but became popular when ALGOL-60 was released in 1960. ALGOL quickly became the dominant
language in Europe during the 1960s. Its impact on future languages such as Pascal, C, and Java is
undeniable—these languages' grammatical syntax borrows heavily from ALGOL. I've programmed
professionally in ALGOL, assembler, COBOL, FORTRAN, C, C++, C#, Basic (in various renditions), Forth,
JavaScript, HTML, XML, MISL, and many more—and ALGOL remains my favorite language. The major
design goals of ALGOL were portability, a formal grammar, and support for algorithms. ALGOL-68 extended
the language, but the additions increased complexity and furthered abstraction from hardware. This
abstraction prevented developers from easily accessing devices and the lower tiers of the operating
environment. Soon, languages were introduced that were less complex and not as abstracted from the
architecture. One of these new languages was C.
The journey from ALGOL to C began with CPL. CPL, a derivative of ALGOL-60, was developed at the
Computer Lab of Cambridge University. CPL was created in 1963 by David Barron, Christopher Strachey,
and Martin Richards. Although CPL is not as abstracted as ALGOL, it did maintain one characteristic of
ALGOL: complexity. Martin Richards introduced Basic CPL (BCPL) in 1967 as a lean version of CPL. Ken
Thompson of Bell Labs drafted B in 1970 as the successor to BCPL. B was lighter, faster, and more
appropriate for systems programming. C was developed by Dennis Ritchie, also of Bell Labs, in 1972. C
returned some of the abstraction removed from B while keeping that language simple and quick. Although
initially consigned to the UNIX operation system and systems programming, C is a general-purpose
language and has been used for a diverse assortment of applications across a variety of platforms and
operating systems.
FORTAN, ALGOL, and COBOL dominated the procedural programming landscape in the 1960s. On a
separate track, Simula was created between 1962 and 1965 by Ole-Johan Dahl and Kristen Nygaard at the
Norwegian Computing Center. Simula is notable for being the first object-oriented programming (OOP)
language. It was designed for simulation, but evolved into a general-purpose language. Simula introduced the
important OOP concepts of classes, inheritance, and dynamic binding.
Combining aspects of C and Simula, Bjarne Stroustrup introduced C with Classes in 1979 as an
enhancement of the C programming language. Later, under Stroustrup's stewardship, C++ was created as a
direct descendant of C with Classes and was publicly recognized in 1983. C++ rapidly became the premier
object-oriented programming language and introduced structured exception handling, templates, and much
more.
C# premiered at the Professional Developers Conference (PDC) held in Orlando, Florida, in 2000. The
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
primary architects of C# were Anders Hejlsberg, Scott Wiltamuth, Peter Sollichy, Eric Gunnerson, and Peter
Golde. C# was designed to be a fully object-oriented language focusing on developing components in a
distributed environment and was launched as part of a larger initiative by Microsoft called Microsoft .NET.
Underscoring the importance of .NET to Microsoft, Bill Gates was the keynote speaker at the PDC that year.
I attended the PDC in 2000 and was both intrigued and motivated by the introduction of .NET and C#. .NET
is emblematic of a philosophical change at Microsoft and an embracing of the standards community.
Both .NET, as defined by the Common Language Infrastructure (CLI), and C# were submitted to two
international standards organizations: ECMA and ISO/IEC. Also, .NET and .NET languages, described in the
Common Language Specification (CLS), continue the trend toward truly portable code. You can write an
application in one environment and run it anywhere else. Simultaneously, a new version of Microsoft Visual
Studio was announced: Visual Studio .NET. Visual Studio .NET provides rapid application development tools
for developing a wide variety of .NET applications.
More For information on the various standards for C# and .NET, follow these links. The current
Info ECMA standards for the C# Language Specification:
http://www.ecma-international.org/publications/standards/Ecma-334.htm. The current
ECMA standards for the Common Language Infrastructure:
http://www.ecma-international.org/publications/standards/Ecma-335.htm. The current
ISO/IEC standards for the C# Language Specification:
http://www.iso.org/iso/en/CatalogueDetailPage.CatalogueDetail?CSNUMBER=36768&ICS1
=35&ICS2=60&ICS3=. The current ISO/IEC standards for the CLI:
http://www.iso.org/iso/en/CatalogueDetailPage.CatalogueDetail?CSNUMBER=36769&scop
elist=.
Symbols and tokens are the basic constituents of the C# language. Sentences are composed of spaces,
tabs, and characters. Similarly, C# statements consist of symbols and tokens. Indeed, statements cannot
be articulated without an understanding of these basic elements. Table 1-1 provides a list of the C# tokens.
Table 1-1: C# Symbols and Tokens
Punctuator ,:;
Preprocessor directive #
Block {}
Generics <>
Nullable Type ?
Character Unicode_character
Numeric Suffix f d m u l ul lu
White Space
White space is defined as a space, horizontal tab, vertical tab, or form feed character. White space
characters can be combined; where one character is required, two or more contiguous characters of white
space can be substituted. Where white space is permitted, one or more instances of white space are
allowed.
Tabs
Tabs—horizontal and vertical—are white space characters. Refer to the preceding explanation of white space.
Punctuators
Punctuators separate and delimit elements of the C# language. Punctuators include the semicolon (;), dot
(.), colon (:), and comma (,), which are discussed in this section.
Semicolon punctuator In a natural language, sentences consist of phrases and clauses and are units of
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
cohesive expression. Sentences are terminated with a period (.). Statements consist of one or more
expressions and are the commands of the C# programming language. Statements are terminated by a
semicolon (;). C# is a free-form language in which a statement can span multiple lines of source code and
start in any position. Conversely, multiple statements can be combined on a single source code line,
assuming that each statement is delimited by a semicolon. This statement is not particularly good style, but
it is syntactically correct:
int variablea=
variableb +
variablec;
Dot punctuator Dot syntax connotes membership. The dot character (.) binds a target to a member, in
which the target can be a namespace, type, structure, enumeration, interface, or object. This assumes the
member is accessible. Membership is sometimes nested and therefore described with multiple dots.
Dot punctuator:
Target? Member
Colon punctuator The colon punctuator is used primarily to delimit a label, to describe inheritance, to
indicate the implementation of an interface, to set a generic constraint, and as part of the conditional
operator. (The conditional operator, the only ternary operator in C#, is reviewed later in this chapter.
Inheritance and generic constraints are discussed in later chapters.) Labels are tags where program
execution can be transferred. A label is terminated with a colon punctuator. The scope of a label is limited to
the containing block and any nested block. Jump to a label with the goto statement.
Label punctuator:
label_identifier: statement
Comma punctuator The comma punctuator delimits array indexes, function parameters, types of an
inheritance list, statement clauses, and most other lists of C# language elements. The comma punctuator is
separating statement clauses in the following code:
A statement clause is similar to a sentence phrase or clause, which are also sometimes delimited by
commas. A statement clause is a substatement in which multiple statement clauses can be substituted for
a single statement. Not all statements are replaceable with statement clauses—check the documentation of
the statement to be sure.
Line Terminators
Line terminators separate lines of source code. A carriage-return, line-feed, line-separator, and
paragraph-separator are the line terminators of C#. Where one line terminator is inserted, two or more are
acceptable. Line terminators can be inserted anywhere white space is allowed. The following code is
syntactically incorrect:
int variablea=var
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
iableb+variablec;
As an identifier, variablea cannot contain spaces. Thus, line terminators are also disallowed.
Comments
C# supports four styles of comments: single-line, delimited, single-line documentation, and delimited
documentation comments. Comments cannot be nested. Although comments are not mandated, liberal
comments are considered good programming style. Self-documenting code and comments in your source
code aid in later maintenance. Be kind to the maintenance program—comment! In Code Complete, Second
Edition, (Microsoft Press, 2004), Steve McConnell gives valuable best practices on programming, including
how to properly document your source code.
Single-line comments: // Single-line comments start at the symbol and end at the line terminator:
Delimited comments: /* and */ Delimited comments, also called multiline or block comments, are
bracketed by the /* and */ symbols. Delimited comments can span multiple lines of source code:
/*
Class Program: Programmer Donis Marshall
*/
class Program {
static int Main(string[] args) {
Greeting objGreeting = new Greeting();// Display Hello (French)
Console.WriteLine(objGreeting.French);
return 0;
}
}
Single-line documentation comments: /// Use documentation comments to apply a consistent format to
source code comments. Documentation comments precede types, members, parameters, delegates,
enums, and structs; they do not precede namespaces. Documentation comments use XML tags to classify
comments. These comments are exportable to an XML file using the documentation generator. The resulting
file is called the documentation file, which can be bound to a Visual Studio project to augment the
information presented in IntelliSense and the Object Browser.
Single-line documentation comments are automated in the Visual Studio IDE, which makes them more
popular than delimited documentation comments. Visual Studio IDE has Smart Comment Editing that
inserts the comment framework after immediately inputting the /// symbol.
The following code snippet shows the previous code after preceding the Main method with a single-line
documentation comment ///. From there, Smart Comment Editing completed the remainder of the comment
framework, including adding comments and XML tags for the method parameter and return. You only need to
add specific comments.
/// <summary>
///
/// </summary>
class Program {
/// <summary>
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
///
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
static int Main(string[] args) {
Greeting objGreeting = new Greeting();
Console.WriteLine(objGreeting.French
return 0;
}
}
/// <summary>
/// Starter class for Simple HelloWorld
/// </summary>
class Program {
/// <summary>
/// Program Entry Point
/// </summary>
/// <param name="args">Command Line Parameters</param>
/// <returns>zero</returns>
static int Main(string[] args) {
Greeting objGreeting = new Greeting();
Console.WriteLine(objGreeting.French);
return 0;
}
}
The C# compiler is a documentation generator. The /doc compiler option instructs the compiler to generate
the documentation file. Alternatively, you can request that the documentation file be generated in the Visual
Studio IDE. Select the Properties menu item from the Project menu. From the Properties window, switch to
the Build options. In the Build pane (see Figure 1-2), you can activate and enter the name of the XML
documentation file.
Delimited documentation tags Delimited documentation tags can be used instead of the single-line
version. Smart Comment Editing is not available with delimited documentation tags. Documentation
symbols, XML tags, and comments must be entered manually, which is the primary impediment to using
delimited documentation tags. Here is an example of delimited documentation tags:
/**<summary>
Starter class for Simple HelloWorld</summary>
*/
This is the documentation file generated by the C# compiler from the preceding source code:
The documentation generator assigns IDs to element names. T is the prefix for a type, whereas M is a prefix
for a method. Here's a listing of IDs:
E Ev
ent
F Fie
ld
M Me
tho
d
N Na
me
sp
ac
e
P Pro
per
ty
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
T Ty
pe
! Err
or
Preprocessor Directives
Use preprocessor directives to define symbols, include source code, exclude source code, name sections of
source code, and set warning and error conditions. The variety of preprocessor directives is limited when
compared with C++, and many of the C++ preprocessor directives are not available in C#. There is not a
separate preprocessor for preprocessor statements. Preprocessor statements are processed by the normal
C# compiler. The term "preproccesor" is used for historical connotations only.
Preprocessor directive:
#command expression
#re #e #pr
gio ndr ag
n egi ma
on
The preprocessor symbol and subsequent command are optionally separated with a space, but must be on
the same line. For this reason, preprocessor commands can be followed only with a single line comment.
Declarative preprocessor directives The declarative preprocessor directives are #define and #undef, which
define and undefine a preprocessor symbol, respectively. Defined symbols are implicitly true, whereas
undefined symbols are false. Declarative symbols must be defined in each compilation unit where the
symbol is referenced. Undeclared symbols default to undefined and false. The #define and #undef directives
must precede any source code. Redundant #define and #undef directives are trivial and have no affect.
Conditional preprocessor directives Conditional preprocessor directives are the #if, #else, #elif, and #endif
directives, which exclude or include subsequent source code. A conditional preprocessor directives begins
with #if and ends with #endif. The intervening conditional preprocessing directives, #else and #elif, are
optional.
The boolean_expression of the #if and #elif directive is a combination of preprocessor symbols and normal
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Boolean operators. If the boolean_expression is true, the source code immediately after the #if or #elif
directive is included in the compilation. If the boolean_expression is false, the source code is hidden. The
#else directive can be combined with an #if or #elif directive. If the boolean_expression of #if or #elif is false,
the code following the #else is included in the compilation. When true, the source code after the #else is
hidden. Here's sample code with preprocess symbols and related directives:
#define DEBUGGING
using System;
namespace Donis.CSharpBook {
class Starter{
#if DEBUGGING
static void OutputLocals() {
Console.WriteLine("debugging...");
}
#endif
static void Main() {
#if DEBUGGING
OutputLocals();
#endif
}
}
}
Finally, the #elif directive essentially nests #if conditional preprocessor directives:
#if expression
source_code
#elif expression
source_code
#else
source_code
#endif
Diagnostic directives Diagnostic directives include the #error, #warning, and #pragma directives. The #error
and #warning directives display error and warning messages, correspondingly. The diagnostic messages are
displayed in the Error List window of the Visual Studio IDE. Similar to standard compilation errors, an #error
directive prevents the program from compiling; a #warning directive does not prevent the program from
successfully compiling. Use conditional directives to conditionally apply diagnostic directives.
Diagnostic directives:
#error error_message
#warning error_message
Pragma directives:
The warning_list contains one or more warnings delimited with commas. The status of a warning included in
the warning_list remains unchanged until the end of the compilation unit unless altered in a later #error
directive.
This #pragma directive disables the 219 warning, which is the "variable is assigned but its value is never
used" warning:
Region directives Region directives mark sections of source code. The #region directive starts a region,
whereas the #endregion directive ends the region. Region directives can be nested. The Visual Studio IDE
outlines the source code using region tags. In Visual Studio, you can collapse or expand regions of source
code.
Region directives:
#region identity
source_code
#endregion
Line directives Line directives modify the line number reported in subsequent compiler errors and warnings.
There are three versions of the line directive.
Line directives:
The first #line directive shown renumbers the source code from the location of the directive until the end of
the compilation unit is reached or overridden by another #line directive. In the following code, the #line
directive resets the current line to 25:
#line 25
static void Main() {
Console.WriteLine("#line application");
int variablea=10; // 219 warning
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The second type of #line directive resets or undoes any previous #line directive. The line number is reset to
the natural line number.
The third #line directive is only tangentially related to the line number. This directive does not affect the line
number; it hides source code from the debugger. Excluding another #line hidden directive, the source code is
hidden until the next #line directive is encountered.
Blocks
Blocks define the scope of a type, where type is a class, struct, or enum. Additionally, members of the type
are listed inside the block.
Block:
Blocks are used as compound statements. Paragraphs join related sentences that convey an extended
thought or concept. A statement block combines related statements as a single entity. In this context, a
statement block is a compound statement and can contain one or more statements. Each statement of the
statement block is delimited by a semicolon. In most circumstances in which a single statement is allowed,
a statement block can be substituted. Statement blocks are prevalent as method bodies but are used with
conditional and iterative statements.
The if statement in the following code, which is a conditional statement, controls a single statement. The
Console.WriteLine is controlled by the if statement that precedes it, so a statement block is not required.
In the modified code, the if statement controls multiple statements, and a statement block is needed. Some
would suggest, and I agree, that always using statement blocks with conditional statements is a good
practice. This prevents a possible future omission when additional statements are added to the realm of the
conditional statement:
Generic Types
Generic types are templated types. A type is an abstraction of identity: a car class is an abstraction of a
type of car, an employee class is an abstraction of an employee, and a generic type is an abstraction of the
specifics of a type.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The NodeInt class partially implements and is an abstraction of a node within a linked list of integers:
class NodeInt {
public NodeInt(int f_Value, NodeInt f_Previous) {
m_Value=f_Value;
m_Previous=f_Previous;
}
// Remaining methods
The Node class is further abstracted when compared with the NodeInt class. The integer specifics of the
NodeInt class have been removed. This resulting type could be a link list of any type:
class Node<T> {
public Node(T f_Value, Node<T> f_Previous) {
m_Value=f_Value;
m_Previous=f_Previous;
}
// Remaining methods
private T m_Value;
private Node<T> m_Previous;
}
The generics symbol bounds the type parameters, which is T in the preceding program.
Nullable Types
Nullable types are value types that can be assigned a null value. Nullable types provide a consistent
mechanism for determining whether a value type is empty (null).
Nullable type:
valuetype? identifier;
Characters
C# source files contain Unicode characters, which are the most innate of symbols. (Every element, keyword,
operator, or identifier in the source file is a composite of Unicode characters.)
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Numeric Suffixes
Numeric suffixes cast a literal value to a related type. Literal integer values can have the L, U, UL, and LU
suffixes appended to them; literal real values can have the F, D, and M suffixes added. The suffixes are case
insensitive. Table 1-2 describes each suffix.
Table 1-2:
Description of
Suffixes
De Ty Su
scr pe ffix
ipti
on
Un uin u
sig t
ne or
d lon
int g
eg
er
or
un
sig
ne
d
lon
g
Lo lon l
ng g
or or
un ulo
sig ng
ne
d
lon
g
Un ulo ul
sig ng
ne
d
lon
g
Flo floa f
at t
Do do d
ubl ubl
e e
Mo de m
ne ci
y ma
l
When casting a real type using the M suffix, rounding might be required. If so, banker's rounding is used,
which rounds to the nearest possible value. If midway between two values, the even number is returned.
Gaussian rounding, albeit harder to pronounce, is another name for banker's rounding.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Here is an example:
int variable=10u;
The next statement causes a compile error. You cannot append a real suffix to an integral literal value
because they are not related types.
Escape Characters
The escape character provides an alternate means of encoding Unicode characters, especially special
characters that are not available on a standard keyboard. Escape sequences can be used as characters
within identifiers and elsewhere.
Unicode escape sequences must have four hexadecimal digits and are therefore limited to a single character.
Escape sequence:
Hexadecimal escape sequences define one or more Unicode characters and contain one or more digits.
Hexadecimal sequence:
Si Se
mp qu
le en
Es ce
ca
pe
Sin \'
gle
qu
ote
Do \"
ubl
e
qu
ote
Ba \\
ckl
as
h
Nul \0
l
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 1-3:
Predefined
Escape
Sequences
Si Se
mp qu
le en
Es ce
ca
pe
Ale \a
rt
Ba \b
ck
sp
ac
e
For \f
m
fee
d
Ne \n
w
line
Car \r
ria
ge
ret
urn
Hor \t
izo
nta
l
tab
Uni \u
co
de
ch
ara
cte
r
Ver \v
tic
al
tab
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 1-3:
Predefined
Escape
Sequences
Si Se
mp qu
le en
Es ce
ca
pe
He \x
xa
de
ci
ma
l
ch
ara
cte
r(s)
class HelloWorld {
static void Main() {
Console.Write("\u0048\u0065\u006C\u006C\u006f\n");
Console.Write("\x77\x6F\x72\x6c\x64\x21\b");
}
}
Verbatim Characters
The verbatim character prevents the translation of a string or identifier, where it is treated "as-is." To create a
verbatim string or identifier, prefix it with the verbatim character.
A verbatim string is a string literal prefixed with the verbatim character. The characters of the verbatim string,
including escape sequences, are not translated. The exception is the quote escape character, which is
translated even in a verbatim string. Unlike a normal string, verbatim strings can contain physical line feeds.
class Verbatim{
static void Main() {
string fileLocation=@"c:\datafile.txt";
Console.WriteLine("File is located at {0}",
fileLocation);
}
}
A verbatim identifier is an identifier prefixed with the verbatim character that prevents the identifier from being
parsed as a keyword. Although this is of limited usefulness, porting source code from another language—in
which the keywords are different—is a circumstance in which verbatim identifiers might be helpful.
Otherwise, it is a best practice not to use this language feature because verbatim identifiers make your code
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Can you decipher this sentence? Unless you are fluent in French, the partial translation is ineffectual at best.
The original sentence was ''L'espoir est un rêve de réveil." The following is an equally unskillful translation,
although technically acceptable:
In the preceding code, the for statement is being used as a variable name. Although technically acceptable,
it is confusing at best. The for statement is common in C# and many other programming languages. For this
reason, most developers would view the for as a statement regardless of the usage, which will inevitably lead
to confusion.
Operators
Operators are used in expressions and always return a value. There are three categories of operators: unary,
binary, and ternary. The following sections describe most of the operators in C#.
Op Sy Sa
er mb mp
ato ol le
r
Un + vari
ary abl
Plu e=
s +5;
5
Un - vari
ary abl
mi e=-
nu (-
s 10)
;
10
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Op Sy Sa
er mb mp
ato ol le
r
Bo ! vari
ole abl
an e=!
Ne tru
gat e;
ion fal
se
Bit ~ vari
wis abl
e e=
1's ~((
co uin
mp t)1)
le ;
me 42
nt 94
96
72
94
Pre ++ ++
fix vari
Inc abl
re e;
me 11
nt
Pre -- --
fix vari
De abl
cre e;
me 10
nt
Po ++ vari
stfi abl
x e
Inc ++;
re 11
me
nt
Po -- vari
stfi abl
x e --
De ;
cre 10
me
nt
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Op Sy Sa
er mb mp
ato ol le
r
Ca () vari
st abl
Op e
era =(i
tor nt)
12
3.4
5;
12
3
Fu () Fu
nct nct
ion ion
Op Cal
era l(p
tor ara
me
ter)
;
ret
urn
val
ue
Arr [] arr
ay ayn
Ind am
ex e[iI
Op nd
era ex]
tor ;
nth
ele
me
nt
Glo ::
bal
Na
me
sp
ac
e
Qu
alifi
er
Binary operators This section lists and discusses the use of binary operators.
Binary operators have two operands: a left and right operand.
Integer division truncates the floating point portion of the result.
Bitwise Shift Left (value << bitcount).
Bitwise Shift Right (value >> bitcount).
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Op Sy Sa Re
er mb mp sul
ato ol le t
r
As = vari 10
sig abl
nm e=
ent 10;
Bin + vari 15
ary abl
Plu e=
s vari
abl
e
+
5;
Bin - vari 5
ary abl
Mi e=
nu vari
s abl
e-
10;
Mu * vari 25
ltipl abl
ica e=
tio vari
n abl
e*
5;
Divi / vari 5
sio abl
n e=
vari
abl
e/
5;
Mo % vari 2
dul abl
us e=
vari
abl
e
%
3;
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Op Sy Sa Re
er mb mp sul
ato ol le t
r
Lo & vari 1
gic abl
al e=
An 5
d &
3;
Lo | vari 7
gic abl
al e=
Or 5|
3;
Bit ^ vari 6
wis abl
e e=
XO 5
R ^
3;
Nul ?? vari 2
l abl
Co eb
ale =v
sci ari
ng abl
ea
??
5
Compound operators Compound operators combine an assignment and another operator. If the expanded
expression is 'variablea=variablea operator value', the compound operator is 'variable operator= value'.
variable=variable+5;
variable+=5;
Compound operations are a shortcut and are never required in lieu of the expanded operation. Table 1-6 lists
the compound operators.
Table 1-6: List of
Compound Operators
Op Sy Sa
er mb mp
ato ol le
r
Ad += vari
diti abl
on e+
As =5;
sig
nm
ent
Su -= vari
btr abl
act e-
ion =1
As 0;
sig
nm
ent
Mu *= vari
ltipl abl
ica e*=
tio 5;
n
As
sig
nm
ent
Divi /= vari
sio abl
n e/=
As 5;
sig
nm
ent
Mo %= vari
dul abl
us e%
As =3;
sig
nm
ent
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Op Sy Sa
er mb mp
ato ol le
r
An &= vari
d abl
As e&
sig =3;
nm
ent
Or |= vari
As abl
sig e|=
nm 3;
ent
XO ^= vari
R abl
As e^
sig =
nm 3;
ent
Boolean operators Boolean expressions evaluate to true or false. The integer values of nonzero and zero
cannot be substituted for a Boolean true or false.
There are two versions of the logical And and Or operators. The && and || operators support short-circuiting,
whereas & and | do not. What is short-circuiting? If the result of the expression can be determined with the
left side, the right side is not evaluated. Without disciplined coding practices, short-circuiting might cause
unexpected side effects.
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
In the preceding code, assuming that FunctionA returns false, the entire expression evaluates to false.
Therefore, the expression short-circuits and FunctionB is not invoked.
Op Sy
er mb
ato ol
r
Eq ==
ual
s
Not !=
Eq
ual
Le <
ss
Th
an
Gr >
eat
er
Th
an
An &&
d
(Sh
ort
Cir
cui
tin
g)
Or ||
(Sh
ort
Cir
cui
tin
g)
An &
d
Or |
Le <=
ss
Th
an
or
Eq
ual
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Op Sy
er mb
ato ol
r
Gr >=
eat
er
Th
an
or
Eq
ual
Lo ^
gic
al
XO
R
Ternary operators The conditional operator is the sole ternary operator in C# and is an abbreviated if else
statement.
Conditional operator:
boolean_expression?truth_statement:false_statement
variable>5?Console.WriteLine(">5"):Console.WriteLine("<= 5");
Pointer operators Pointer operators are available in unsafe mode and support conventional pointers. The
unsafe compiler option builds a program in unsafe mode. Alternatively, in Visual Studio IDE, set the Allow
Unsafe Mode option on the Build Page of Project Settings. Table 1-8 includes the pointer operators.
Table 1-8: List of
Pointer Operators
Op Sy De
er mb scr
ato ol ipti
r on
Ast * De
eri cla
sk re
Op a
era poi
tor 1 nte
r
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Op Sy De
er mb scr
ato ol ipti
r on
Ast * Der
eri efe
sk ren
Op ce
era a
tor 2 poi
nte
r
Am & Ob
per tai
sa n
nd an
Op ad
era dre
tor ss
The above code must be compiled with the unsafe compiler option on. A more extensive review of pointers is
presented later in the book.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Identifiers
An identifier is the name of a C# entity, which includes type, method, property, field, and other names.
Identifiers can contain Unicode characters, escape character sequences, and underscores. A verbatim
identifier is prefixed with the verbatim character (as discussed earlier in this chapter).
Keywords
One of the strengths of C# is that the language offers relatively few keywords. C# keywords represent the
verbs, nouns, and adjectives of the language. The nouns of C# are instances of classes, structs, interfaces,
delegates, and namespaces. Verbs infer an action. The goto, for, while, and similar keywords have that role
in C#. The adjectives, including the public, private, protected, and static keywords, are modifiers of the C#
nouns.
Table 1-9 is an overview of the C# keywords. Extended explanations of each keyword are provided in context
at the appropriate location in the book.
Table 1-9: Overview
of C# Keywords with
Explanations
Ke Sy Ex
yw nta pla
or x nat
d ion
ab ab Th
str str e
act act cla
1 cla ss
ss ide
ide ntif
ntif ier
ier ca
nn
ot
be
ins
tan
tiat
ed.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
ab ab Th
str str e
act act me
2 ret tho
urn d
ide ide
ntif ntif
ier ier
is
not
im
ple
me
nte
d
in
the
cur
ren
t
cla
ss.
as ide Ca
ntif sts
ier obj
as ect
typ ide
e ntif
ier
to
a
rel
ate
d
typ
e
or
null
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
ba ba Ac
se se. ce
ide ss
ntif es
ier the
me
mb
er
ide
ntif
ier
of
the
ba
se
cla
ss.
Ke Sy Ex
yw nta pla
or x nat
d ion
ca ca Sw
se se itc
lab h
el sta
te
me
nts
ju
mp
s
to
a
ma
tch
ing
ca
se
lab
el.
Ke Sy Ex
yw nta pla
or x nat
d ion
ch ch Ex
ec ec ce
ke ke pti
d1 d( on
exp is
res rai
sio se
n) d
if
exp
res
sio
n
ove
rflo
ws.
ch ch Ex
ec ec ce
ke ke pti
d2 d{ on
sta is
te rai
me se
nt_ d
blo if
ck an
} y
ex
pre
ssi
on
wit
hin
sta
te
me
nt
blo
ck
ove
rflo
ws.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
Ke Sy Ex
yw nta pla
or x nat
d ion
co co De
nst nst cla
typ res
e a
ide co
ntit nst
y ant
vari
abl
e
or
me
mb
er
na
me
d
ide
ntit
y.
Co
nst
ant
ins
tan
ce
s
ca
nn
ot
be
mo
difi
ed
at
run
tim
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
co Co Ju
nti nti mp
nu nu s
e e to
the
en
d
of
a
loo
p
an
d
sta
rts
a
ne
w
iter
ati
on.
def def Th
aul aul e
t t def
aul
t
lab
el
to
ju
mp
to
in
a
swi
tch
sta
te
me
nt.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
do do Def
{ ine
sta s
te a
me do
nt_ loo
blo p
ck wit
} h
whi the
le( iter
exp ativ
res e
sio tes
n) t
at
the
en
d.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
els els Th
e e{ e
sta els
te e
me sta
nt_ te
blo me
ck nt
} is
ma
tch
ed
to
the
ne
are
st
if
sta
te
me
nt
an
d
pro
vid
es
the
fals
e
sta
te
me
nt_
blo
ck .
en en Def
um um ine
en s
um an
na en
me um
era
tio
n
typ
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
exp ex Usi
licit plic ng
it the
op us
era er-
tor defi
co ne
nve d
rsi co
ont nve
ype rsi
on
op
era
tor
req
uir
es
an
ex
plic
it
ca
st.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
ext ext A
ern ern me
ret tho
urn d
me im
tho ple
d me
nte
d
ext
ern
ally
.
fal fals A
se e Bo
ole
an
val
ue.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
Ke Sy Ex
yw nta pla
or x nat
d ion
Ke Sy Ex
yw nta pla
or x nat
d ion
Ke Sy Ex
yw nta pla
or x nat
d ion
get get Ac
ce
ss
or
me
tho
d
of
pro
per
ty
me
mb
er.
got got Ju
o1 o mp
ide s
ntif to
ier a
lab
el
na
me
d
ide
ntif
ier.
got got Ju
o2 o mp
ca s
se to
ide an
ntif ide
ier ntif
ier
lab
el
insi
de
a
swi
tch
sta
te
me
nt.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
got got Ju
o3 o mp
def s
aul to
t a
def
aul
t
ca
se
insi
de
a
swi
tch
sta
te
me
nt.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
im im Wh
plic plic en
it it ne
op ed
era ed,
tor a
co us
nve er-
rsi defi
ont ne
ype d
co
nve
rsi
on
op
era
tor
ca
sts
im
plic
itly
.
An
ex
plic
it
ca
st
is
not
req
uir
ed.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
in for Iter
ea ate
ch( ele
ele me
me nts
nt in
in an
en en
um um
era era
ble ble
_c _c
olle olle
cti cti
on) on.
Ke Sy Ex
yw nta pla
or x nat
d ion
Ke Sy Ex
yw nta pla
or x nat
d ion
is obj Ex
ect pre
is ssi
typ on
e eva
lua
tes
to
tru
e
if
obj
ect
is
rel
ate
d
to
typ
en
am
e;
oth
erw
ise
,
eva
lua
tes
to
fals
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
na na Cre
me me ate
sp sp s
ac ac a
e e na
ide me
ntif sp
ier ac
e
call
ed
ide
ntif
ier.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
ne ne All
w1 w oc
ide ate
ntif s
ier( me
co mo
nst ry
ruc for
tor) a
cla
ss
ide
ntif
ier
on
a
ma
na
ge
d
he
ap
an
d
ret
urn
s
a
ref
ere
nc
e
to
the
obj
ect
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
ne ne Me
w2 w tho
ret d
urn hid
me es
tho the
d sa
me
me
tho
d
in
the
ba
se
cla
ss.
Ke Sy Ex
yw nta pla
or x nat
d ion
Ke Sy Ex
yw nta pla
or x nat
d ion
op op Im
era era ple
tor tor me
op nts
era a
tor us
_n er-
am defi
e ne
d
op
era
tor
_n
am
e
in
the
cur
ren
t
typ
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
Ke Sy Ex
yw nta pla
or x nat
d ion
ref ref Th
typ e
e act
par ual
am par
ete am
r ete
r
is
pa
ss
ed
int
o
the
me
tho
d
an
d
ca
n
be
mo
difi
ed.
Par
am
ete
r
mu
st
be
initi
aliz
ed
bef
ore
fun
cti
on
inv
oc
ati
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
se se Ide
ale ale ntif
d d ier
ide is
ntif a
ier cla
ss
na
me
an
d
is
not
inh
erit
abl
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
set set As
sig
nm
ent
me
tho
d
of
pro
per
ty
me
mb
er.
Ke Sy Ex
yw nta pla
or x nat
d ion
Ke Sy Ex
yw nta pla
or x nat
d ion
sta sta Me
tic tic tho
ret d
urn is
me cla
tho ss-
d bo
un
d
an
d
not
as
so
cia
ted
wit
h
a
sp
ecif
ic
ins
tan
ce.
Ke Sy Ex
yw nta pla
or x nat
d ion
swi swi Co
tch tch ntr
(ex ol
pre is
ssi tra
on) nsf
{ err
sta ed
te to
me eit
nt_ her
blo the
ck ma
} tch
ing
swi
tch
lab
el,
if
an
y,
or
to
the
def
aul
t
ca
se
in
the
sta
te
me
nt_
blo
ck .
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
thi Thi Th
s s e
thi
s
obj
ect
is
a
ref
ere
nc
e
to
the
cur
ren
t
obj
ect
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
Ke Sy Ex
yw nta pla
or x nat
d ion
un un Ex
ch ch pre
ec ec ssi
ke ke on
d1 d( is
exp tru
res nc
sio ate
n) d
if
ove
rflo
we
d.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
un un If
ch ch an
ec ec ex
ke ke pre
d2 d{ ssi
sta on
te wit
me hin
nt_ sta
blo te
ck me
} nt
blo
ck
is
ove
rflo
we
d,
the
ex
pre
ssi
on
is
tru
nc
ate
d.
un un Cla
saf saf ss
e1 e ide
cla ntif
ss ier
ide ca
ntif n
ier hav
e
un
saf
e
co
de
su
ch
as
poi
nte
rs.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
un un Th
saf saf e
e2 e me
ret tho
urn d
me ca
tho n
d co
nta
in
un
saf
e
co
de
su
ch
as
poi
nte
rs.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
Ke Sy Ex
yw nta pla
or x nat
d ion
Ke Sy Ex
yw nta pla
or x nat
d ion
Ke Sy Ex
yw nta pla
or x nat
d ion
voi voi A
d1 d voi
me d
tho ret
d urn
me
an
s
tha
t
the
me
tho
d
do
es
not
ret
urn
a
val
ue.
Th
e
me
tho
d
ca
n
om
it
a
ret
urn
sta
te
me
nt
or
hav
e
an
em
pty
ret
urn
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
Ke Sy Ex
yw nta pla
or x nat
d ion
vol vol Ac
atil atil ce
e e ss
fiel es
dn to
am a
e vol
atil
e
fiel
dn
am
e
are
im
me
dia
te,
whi
ch
is
us
eful
in
a
mu
ltit
hre
ad
ed
env
iro
nm
ent
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
wh cla Th
ere ss e
cla wh
ss ere
na cla
me us
<p e
ara set
me s
ter co
s> nst
wh rai
ere nts
co on
nst the
rai cla
nt ss
na
me
. It
mu
st
be
a
ge
ner
ic
cla
ss.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
whi whi Th
le le( e
bo sta
ole te
an me
_ex nt_
pre blo
ssi ck
on) of
{ the
sta whi
te le
me loo
nt_ p
blo is
ck rep
} eat
ed
unt
il
the
bo
ole
an
_ex
pre
ssi
on
is
fals
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ke Sy Ex
yw nta pla
or x nat
d ion
yiel yiel In
d d the
iter en
ato um
r_v era
alu tor
e pat
ter
n,
the
yiel
d
ke
yw
ord
ad
ds
val
ue
s
to
be
en
um
era
ted
.
Primitives
Intrinsic data types, commonly called primitives, are predefined in C#. Primitives historically found in C-base
languages, including int, long, and many others, inure to C#. The intrinsic types are declared as C#
keywords but are alias for types in the .NET Framework Class Library. Except for the string type, the
primitives are value types and allocated on the stack as structures. The string type is a class and allocated
on the managed heap.
As indicated, primitives in C# (see Table 1-10) are value types but are nonetheless objects with a published
interface. For numeric types, the min property, max property, and Parse methods of the public interface are
particularly useful. The min and max property are invaluable for bounds checking, whereas the Parse method
converts the string to the primitive.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ty Pri De Ra
pe mit scr ng
ive ipti e
on
bo Sy Bo tru
ol ste ole e
m. an or
Bo fals
ole e
an
byt Sy 8-b 0
e ste it to
m. int 25
Byt eg 5
e er
ch Sy 16- /u0
ar ste bit 00
m. Uni 0
Ch co to
ar de /ufff
ch f
ara
cte
r
de Sy 12 ±1.
ci ste 8-b 0
ma m. it ×
l De de 10
ci ci -28
ma ma to
l l ±7.
9
×
10
28 ,
wit
h
28
to
29
digi
ts
of
pre
cisi
on
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ty Pri De Ra
pe mit scr ng
ive ipti e
on
do Sy 64- -1.
ubl ste bit 79
e m. floa 76
Do tin 93
ubl g 13
e poi 48
nt 62
32
e3
08
to
1.7
97
69
31
34
86
23
2e
30
8
poi to
nt ±3.
4
×
10
38 ,
wit
h
7
digi
ts
of
pre
cisi
on
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ty Pri De Ra
pe mit scr ng
ive ipti e
on
sb Sy 8-b -12
yte ste it 8
m. int to
SB eg 12
yte er 7
sh Sy 16- -32
ort ste bit ,76
m.I int 8
nt1 eg to
6 er 32,
76
7
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ty Pri De Ra
pe mit scr ng
ive ipti e
on
uin Sy 32- 0
t ste bit to
m. un 4,2
UIn sig 94,
t32 ne 96
d 7,2
int 95
eg
er
ulo Sy 64- 0
ng ste bit to
m. un 18,
UIn sig 44
t64 ne 6,7
d 44,
int 07
eg 3,7
er 09,
55
1,6
15
us Sy 16- 0
hor ste bit to
t m. un 65,
UIn sig 53
t16 ne 5
d
int
eg
er
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Sample C# Program
In deference to Martin Richards, the founder of the BCPL language and author of the first "Hello, World!"
program, I present a "Hello, World!" program. Actually, I offer an enhanced version that displays "Hello,
World" in English, Italian, or Spanish. The program is a console application. For simplicity, no error checking
is performed. C# is case sensitive.
using System;
namespace HelloNamespace {
class Greetings{
public static void DisplayEnglish() {
Console.WriteLine("Hello, world!");
}
public static void DisplayItalian() {
Console.WriteLine("Ciao, mondo!");
}
public static void DisplaySpanish() {
Console.WriteLine("Hola, imundo!");
}
}
class HelloWorld {
static void Main(string [] args) {
int iChoice=int.Parse(args[0]);
delGreeting [] arrayofGreetings={
new delGreeting(Greetings.DisplayEnglish),
new delGreeting(Greetings.DisplayItalian),
new delGreeting(Greetings.DisplaySpanish)};
arrayofGreetings[iChoice-1]();
}
}
}
Csc.exe is the C# compiler. The following csc command compiles the hello.cs source file to create the
executable hello.exe, which is a .NET assembly:
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
csc hello.cs
As a .NET assembly, the Hello.exe contains metadata and MISL code but not native binary.
Run the Hello application from the command line. Enter the program name and the language (1 for English, 2
for Italian, or 3 for Spanish). For example, the following command line displays "Ciao, mondo" ("Hello, world"
in Italian).
Hello 2
The source code of the Hello application has the features common to most .NET applications: a using
statement, a namespace, types, access modifiers, methods, and data.
The HelloNamespace namespace contains the Greetings and HelloWorld types. The Greetings class has
three static methods, and each method displays "Hello, World" in a different language. Static methods are
invoked on the type (classname.member), not an instance of that type. The static methods of the Greetings
type are also public and therefore visible inside and outside the class.
Delegates define a type of function pointer. The delGreeting delegate is a container for functions pointers that
point to functions that return void and have no parameters. This is not so coincidentally the function signature
of the methods in the Greetings type.
The entry point of this and any other C# application is the Main method. Command-line parameters are
passed as the args parameter, which is a string array. In the Hello program, the first element of the args
array is a number indicating the language of choice, as inputted by the user. The Hello application converts
that element to an integer. Next, the program defines an array of function pointers, which is initialized with
function pointers to methods of the Greetings class. This statement invokes a function pointer to display the
chosen Hello message:
arrayofGreetings[iChoice-1]()
The variable iChoice is an index into the delegate array. It is decremented to account for the fact that arrays
in C# are zero-based.
The remainder of the chapter discusses the important features of the Hello and any other C# application,
except for types that will be reviewed in the next chapter.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Namespaces
Namespaces provide hierarchical clarity and organization of types and other members. A container of
hundreds of classes, the .NET Framework Class Library (FCL) is an example of effective use of
namespaces. The Framework Class Library would sacrifice clarity if planned as a single namespace with a
flat hierarchy. Instead, the Framework Class Library organizes its members into numerous namespaces.
System, which is the root namespace of the FCL, contains the classes ubiquitous to .NET, such as the
Console class. Types related to data services are grouped in the System.Data namespace. Data services
are further delineated in the System.Data.SqlClient namespace, which contains types specific to Microsoft
SQL. The remaining types are organized similarly in other namespaces.
A namespace identifier must be unique within the namespace declaration space, which contains the current
namespace but not a nested namespace. A nested namespace is considered a member of the containing
namespace. Use the dot punctuator (.) to access members of the namespace.
A namespace at file scope, not nested within another namespace, is considered part of the compilation unit
and included in the global namespace. A compilation unit is a source code file. A program partitioned into
several source files has multiple compilation units—one compilation unit for each source file. Any
namespace, including the global namespace, can span multiple compilation units. For example, all types
defined at file scope are included into a single global namespace that spans separate source files.
The following code has two compilation units and three namespaces. Because of identical identifiers sharing
the same scope, errors occur when the program is compiled.
// file1.cs
namespace NamespaceZ {
public class ClassC {
}
}
// file2.cs
namespace NamespaceY {
public class ClassA {
}
}
namespace NamespaceZ {
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
In the preceding code, the global namespace has four members. NamespaceY and NamespaceZ are
members. The classes ClassA and ClassB are also members of the global namespaces. The members
span the File1.cs and File2.cs compilation units, which both contribute to the global namespace.
ClassB and ClassC are ambiguous. ClassB is ambiguous because it is defined twice in the global
namespace, once in each compilation unit. ClassC is defined in the NamespaceZ namespace in both
compilation units. Because NamespaceZ is one cohesive namespace, ClassC is also ambiguous.
The relationship between compilation units, the global namespace, and nonglobal namespaces are
illustrated in the Figure 1-3.
The using directive makes a namespace implicit. You can access members of the named namespace
directly without the fully qualified name. Do you refer to members of your family by their fully qualified names
or just their first names? Unless your mother is the queen, you probably refer to everyone directly by simply
using their first names, for the sake of convenience. The using directive means that you can treat members
of a namespace like family members.
The using directive must precede any members in the namespace where it is defined. The following code
defines the namespace member ClassA. The fully qualified name is NamespaceZ.NamespaceY.ClassA.
Imagine having to type that several times in a program!
using System;
namespace NamespaceZ {
namespace NamespaceY {
class ClassA {
public static void FunctionM() {
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Console.WriteLine("FunctionM");
}
}
}
}
namespace Application {
class Starter {
public static void Main() {
NamespaceZ.NamespaceY.ClassA.FunctionM();
}
}
}
The following using directive makes NamespaceZ.NamespaceY implicit. Now you can directly access
ClassA.
namespace Application {
using NamespaceZ.NamespaceY;
class Starter {
public static void Main() {
ClassA.FunctionM();
}
}
}
Ambiguities can occur when separate namespaces with identically named members are made implicit.
When this occurs, the affected members can be assessed only with their fully qualified names.
The using directive can also define an alias for a namespace or type. Aliases are typically created to resolve
ambiguity or as a convenience. The scope of the alias is the declaration space where it is declared. The
alias must be unique within that declaration space. In this source code, an alias is created for the fully
qualified name of ClassA:
namespace Application {
using A=NamespaceZ.NamespaceY.ClassA;
class Starter {
public static void Main() {
A.FunctionM();
}
}
}
Using directive statements are not cumulative and are evaluated independently.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
A class or struct can contain only one valid entry point method. The accessibility of the Main entry point
method is irrelevant to the invocation of that method. Even a private Main method is reachable as an entry
point method.
Entry point methods can pass command line arguments into an application as a string array. Arrays in .NET
derive from System.Array. You can use the properties and methods of System.Array to examine the
command line arguments, including the Length field to determine the count of arguments. The command
arguments start at element zero of the string array. When no arguments are passed, the arg parameter is
non-null but the array count is zero.
The return of an entry point method is cached internally for interprocess communication. If the application is
part of a system and spawned to complete a specific task, the return could represent a status code or the
result of the task. The default exit code of an application is zero. The exit code of a process is stored in the
Process Environment Block (PEB) and readable through the GetExitCodeProcess API.
using System;
namespace Application{
class StarterA{
static void Main() {
}
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
class StarterB{
static void Main() {
}
}
}
The preceding code has two valid entry points, which is inherently ambiguous. The main compiler is available
to select between multiple entry points. This command successfully compiles the program:
csc/main:Application.StarterB main.cs.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Local Variables
Local variables are objects declared in a statement block. Local variables can be declared anywhere in the
block, but must be defined before use. Local variables can be either value or reference types. A value type is
allocated storage on the stack, whereas reference types have memory allocated on the managed heap. The
storage for value types is released deterministically at the end of a statement block. Reference types are
allocated with the new keyword and removed nondeterministically by the Garbage Collector, which is a
component of the Common Language Runtime (CLR).
Variables can be declared with or without the assignment operator and they can be declared without being
initialized. That is not a good practice, however. Use the assignment operator to declare and initialize a
variable. Variables can be declared in a single declaration statement individually or by daisy-chaining the
variable names:
The scope and visibility of a local variable is the statement block, where it is declared, and any nested
statement blocks. This is called the variable declaration space, in which local variables must be uniquely
identified.
A variable that is not to be modified at run time should be const. (Const variables must be initialized at
compile time and cannot be changed later at run time.)
In the following code, several local variables are defined. The storage for variablea, variableb, variablec, and
variabled are released at the end of the function block. However, the lifetime of variablee, a local variable, is
managed by the Garbage Collector. Setting variablee to null is a hint to the Garbage Collector that the object
is no longer required, and that it should hasten cleanup:
void Function() {
int variablea=0;
int variableb=1,variablec, variabled=4;
const double PI=3.1415;
UserDefined variablee=new UserDefined();
/ function code
variablee=null;
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Nullable Types
In the previous code, a reference type is set to null. Assigning a null to an object indicates that it is unused.
This is consistent for all reference types. Can you similarly flag an integer as unused? Nulls are not
assignable to primitive value types. (It would cause a compiler error.)
int variablea=null;
Setting an integer to -1 is a possible solution, assuming that this value is outside the set of expected values.
However, this is a proprietary solution; it requires explicit documenting and is not very extensible. A
consistent solution is required for all integers and indeed for all value types.
Nullable types are a consistent solution for determining whether a value object is empty. Declare a nullable
type by adding the ? type modifier in a value type declaration. Here is an example:
double? variable1=null;
The object variable1 is a nullable type and the underlying type is double. A nullable type extends the
interface of the underlying type with the HasValue and Value properties. Both properties are public and
read-only. HasValue is a Boolean property, whereas the type of Value is the same as the underlying type. If
the nullable type is assigned a non-null value, HasValue is true and the Value property is accessible.
Otherwise, HasValue is false, and an exception is raised if the Value property is accessed. The acceptable
range of values for a nullable type includes the null value and the extents of the underlying type.
Set the default value of a nullable type with the null coalescing operator. The operator is ??. The default value
must be the same type as the underlying type. The default value is returned if the nullable type is null—
otherwise empty. This code sets the default value for variablea to zero. Otherwise, variable2 is assigned the
value of variable1:
double variable2=variable1??0;
Expressions
Expressions evaluate one or more operators. Most expressions and the operators therein return a value.
Operators have operands: Unary operators have a right-side operand, whereas binary operators have a
left-side and right-side operand. Although expression statements should assign the result of an expression,
expression statements consisting of function invocation, increment, decrement, and new operators are
exempted from this requirement.
With the exception of the assignment operator, operands of an expression are evaluated from left to right.
Expressions can contain multiple operators; operators are evaluated in order of precedence. Use
parentheses to change the precedence or simply clarify the default precedence.
Precedence Operator
1 array '[ ]', checked , function '()', member operator '.', new, postfix decrement , postfix
increment, typeof, and unchecked operators
2 unary addition '+', casting '()', one complement '~', not '!', prefix decrement, prefix
increment, unary subtraction '-'operators
6 as, is, less than '<', less than or equal to '<=', greater than '>', greater than or equal to '>='
operators
14 Assignment '=', compound '*=, /-=, %=, +=, -=, <<=, >>=, &=, ^=, and |=', and null
coalescing '??' operator
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Selection Statements
A selection statement evaluates an expression to determine what code is executed next. Based on the
expression, a selection statement transfers control to either the next or some other statement.
An if statement evaluates a Boolean expression. If the expression is true, control is transferred to the next
statement_block. If the expression is false, execution is transferred to the first statement after the
statement_block.
if(boolean_expression) {statement_block}
When combined with an else condition, the if statement has true_statement_block and
false_statement_block. The false_statement_block immediately follows the else statement. When the
boolean_expression is true, you are transferred to the true_statement_block. If it is false, control is
transferred to the false_statement_block. If nested, the else statement belongs to the nearest if statement,
and each else statement must have a matching if statement.
if(boolean_expression)
true_statement;
else
false_statement;
An alternative to nested if and else statements is the else if clause, which is particularly useful in evaluating
choices. The else if statement can also be combined with an else statement.
if(boolean_expression 1)
true_statement;
else if(boolean_expression 2)
true_statement 2;
else if(boolean_expression n)
true_statement n;
else
false_statement;
if(menuChoice=="a")
Console.WriteLine("Doing Task A");
else if(menuChoice=="b")
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
A switch statement, which is a better solution to the preceding code, jumps to the switch label that matches
a switch expression. The switch expression must resolve to an integral, char, enum, or string type. The
switch label is a constant or literal and must have the same underlying type as the switch expression.
Switch statement:
switch(switch_expression)
{
case switch_label 1:
switch_statement 1;
case case_label n:
switch_statement n;
default:
default_statement;
}
A switch statement has a switch expression that is followed by a switch block, which should contain one or
more case statements. A case statement identifies a switch label. After the switch expression is evaluated,
control is transferred to the matching switch label. A matching label has the same value as the switch
expression. Each switch label must be unique. If no switch label matches the switch expression, control is
transferred to the default label or to the first statement after the switch statement if a default label is not
provided.
Unlike C and C++, cascading between case statements is not allowed—you cannot "crash the party" of
another case statement. A break, goto, return, or throw are some of the ways to preclude falling into the
next case. There is one exception to this rule, however: You can fall through cases that have no statements.
break;
default:
Console.WriteLine("Bad choice");
break;
}
}
Any object, value, or reference type that is convertible to an integral, char, enum, or string type is acceptable
as the switch_expression, which is demonstrated in the following code. It must be a one-step conversion to
one of the acceptable types.
class Employee {
public Employee(string f_Emplid) {
m_Emplid=f_Emplid;
}
class Starter {
static void Main() {
Employee newempl=new Employee("1234");
switch(newempl) {
case "1234":
Console.WriteLine("Employee 1234");
return;
case "5678":
Console.WriteLine("Employee 5678");
return;
default:
Console.WriteLine("Invalid employee");
return;
}
}
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Iterative Statements
C# has the full repertoire of C-style iterative statements plus the foreach statement. Iterative statements
repeat statement_blocks until a condition has been satisfied.
The for statement is designed for structured iteration when a proper iterator is available. The for statement
contains three clauses. First is the initializer_clause in which the loop iterators are declared. The scope of
an iterator is the for statement and statement_block. Second is the boolean_expression that must evaluate
to a Boolean type. The expression normally compares the iterator to an end value. Third, the
iterator_expression is executed at each iteration and is usually responsible for updating iterator values. Each
clause is optional and delimited with a semicolon. The statement_block is repeated until the
boolean_expression is false.
The statement_block is repeated zero or more times. If the boolean_expression is initially false, the
statement_block is executed zero times.
for(initializer_clause;boolean_expression;iterator_expression) {
statement_block }
Both the initializer_clause and iterator_expression can contain multiple statements delimited by commas.
This allows additional flexibility and complexity than shown previously. Here is an example:
The while statement, which is an iterative statement, is more free-form than the for statement. The
statement_block of the while statement is executed while the boolean_expression is true. The
statement_block is executed zero or more times. If the boolean_expression is initially false, the
statement_block is executed zero times.
Typically, the statement_block is responsible for altering an iterator or something else where-upon the
boolean_expression evaluates to false, which ends the loop. Care should be taken to avoid unattended
infinite loops.
while(boolean_expression) { statement_block }
This is source code for selecting a choice rewritten with a while statement:
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
break;
}
}
}
A do statement is a loop with the boolean_expression after the statement_block. This is the reverse of the
while statement. The impact is that the statement_block of the do statement is repeated one or more times.
The niche for the do statement is when the statement_block needs to execute once before the
boolean_expression. The iteration of the statement_block continues while the boolean_expression is true.
This is a do statement:
do { statement_block } while(conditional_statement)
The foreach statement is a convenient mechanism for automatically iterating elements of a collection. The
alternative is manually iterating a collection with the requested enumerator object. The foreach statement is
unquestionably simpler. Collections implement the IEnumerable interface.
The foreach statement iterates the elements of the collection. As each element is enumerated, the identifier
is assigned the new element, and the statement_block is repeated. The scope of the identifier is the foreach
statement and statement_block. When the collection is fully iterated, the iteration stops, and the
statement_block no longer repeats.
The identifier is a class, struct, or interface. The identifier type should be related to the type of elements
extracted from the collection. In addition, the identifier is read-only. Even using the identifier in a context that
implies change, such as passing the identifier as a ref function parameter, is an error.
The foreach statement confirms that the enumerator or elements of the collection are disposable objects. If
so, the foreach statement calls the Dispose method on the applicable objects. Disposable objects
implement the IDisposable interface.
The break statement prematurely exits the containing switch or iterative statement and transfers control to
the statement after the statement_block. For switch statements, the break prevents fall through between
switch_labels. For an iterative statement, the break stops the iteration unconditionally and exits the loop.
Control is transferred to the statement following the iterative loop. If the switch or iterative statement_block is
nested, only the immediately statement_block is exited.
The continue statement transfers control to the end of a statement_block where execution continues. The
boolean_expression of the iterative statement then determines whether the iteration continues, and the
statement_block is repeated. This is sample code of the break statement:
Classes
This chapter introduced the fundamental ingredients of any C# program. The next chapter expands upon
classes, which is the most important of those ingredients. It is hard to envision a functional or nontrivial C#
program without at least one class. Classes are the nouns of the C# language, and it is certainly difficult to
write a great story without any nouns. It is doubly difficult to write a C# program without classes.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Chapter 2: Types
Overview
Types are the places, persons, and things of an application. As part of the object design, scenarios are often
drafted as a "day in the life" of a problem domain. The scenarios are mined for nouns, which become
potential future classes. Object-oriented programs model real-world problems, in which types represent
identities from your problem domain. An employee in a personnel program, a general ledger entry in an
accounting package, and geometric shapes in a paint application are examples of types. Types include
reference types, value types, and unsafe pointers. The topic of unsafe pointers is included in Chapter 14,
"Memory Management."
A reference type refers to an object created on the managed heap, and the lifetime of the resulting object is
controlled by garbage collection services provided by the Common Language Runtime (CLR). The reference
holds the location of an object created on the managed heap. Reference types derive from System.Object
implicitly and are created with the new keyword. The archetypal reference type is a class. Other reference
types are interfaces, arrays, and delegates.
Value types are lightweight components that are placed on the stack. Value types directly contain their
value. Value types are customarily created statically. For custom initialization, a value type can be
constituted using the new statement. This does not fabricate a value type on the managed heap. It still
resides on the stack. Value types derive from System.ValueType, which is derived from System.Object.
System.ValueType defines a value type by rewriting most of the semantics of System.Object. Primitives
such as int, float, char, and bool are archetypal value types. As a primitive, a string is a hybrid. Strictly
speaking, strings are reference types. However, strings have some of the characteristics of a value type.
Structures and enumerations complete the list of value types.
Classes and structures are the primary focus of this chapter. A class or structure is a blueprint for creating
components of similar behavior and attributes. A class instance is called an object, whereas an instance of a
structure is a value. An Employee class would describe the common attributes and behavior an employee. A
Fraction structure would calculate fractions. Each instance of an Employee or a Fraction shares common
behavior with their siblings but has a distinct state. Within a classification of objects, the GetHashCode
method returns a unique value that distinguishes a specific component from any other sibling.
View classes and structures as independent contractors. They should be isolated and selfsufficient. Types
collaborate through a published interface and hide extraneous details. Classes and structures should be fully
abstracted. This avoids dependencies between components, which leads to error-prone and
harder-to-maintain software.
Enumerations, also described in this chapter, represent a discrete set of constant values. The months of the
year are ideal for enumeration. There are a discrete number of months: 12. Months can logically be assigned
constant values from 1 to 12. Liberal use of enumerations can make code safer and more readable.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Classes
Classes are the irreplaceable ingredient of any .NET assembly. First, all executable code must be contained
in a type, usually a class. Global functions and variables are not permitted in C#, preventing problematic
dependences caused by disparate references to global entities. Second, classes published in the .NET
Framework Class Library provide important services that are integral to any .NET application.
Classes are described in a class declaration. A class declaration consists of a class header and body. The
class header includes attributes, modifiers, the class keyword, the class name, and the base class list. The
class body encapsulates the members of the class, which are the data members and member functions.
Here is the syntax of a class declaration:
attributes accessibility modifiers class classname : baselist { class body };
Attributes further describe the class. If a class is a noun, attributes are the adjectives. For example, the
Serializable attribute identifies a class that can be serialized to storage. There is an assortment of
predefined attributes. You can also define custom attributes. Attributes are optional, and there are no
defaults. Further details on attributes are contained in Chapter 11, "Metadata and Reflections."
Accessibility is the visibility of the class. Public classes are visible in the current assembly and assemblies
referencing that assembly. Internal classes are visible solely in the containing assembly. The default
accessibility of a class is internal. Nested classes have additional accessibility options, which are described
later in this chapter.
Modifiers refine the declaration of a class. For example, the abstract keyword prevents instances of the
class from being created. Modifiers are optional, and there is no default. Table 2-1 elucidates the modifiers.
Table 2-1: Class Modifier Table
Modifier Description
Unsafe Class can contain unsafe constructs, such as a pointer; requires the unsafe compiler option.
A class can inherit the members of a single base class. Multiple inheritance is not supported in the Common
Language Specification of .NET. However, a class can inherit and implement multiple interfaces. The baselist
lists the class inherited and any interfaces to be implemented. By default, classes inherit the System.Object
type. Inheritance and System.Object are expanded upon in Chapter 3, "Inheritance."
The class body encompasses the members of the class that entail the behavior and state of a class. The
member functions are the behavior, whereas data members are the state. As a design goal, classes should
expose an interface composed of the public functions of the class. The state of the class should be
abstracted and described through the behavior of the class. You manage the state of an object through its
public interface.
Classes do not require members. All members can be inherited. In addition, an empty class can function as
a valuable marker or cookie at run time when ascertaining Run-time Type Information (RTTI).
The XInt class has internal accessibility and visibility in the current assembly, but is not visible to an
external assembly. The sealed modifier means that the class cannot be refined through inheritance.
The following code uses the new statement to create an instance of a class:
Class Members
Classes are composed of members: member functions and data members. Use the dot syntax to access
members. The dot binds an instance member to an object or a static member to a class. In the following
code, Fred.name accesses the name field of the Fred object:
using System;
namespace Donis.CSharpBook{
public class Employee{
public string name;
}
public class Personnel{
static void Main(){
Employee Jim=new Employee();
Fred.name="Wilson, Jim";
}
}
}
Me De
mb scr
er ipti
on
Cla Ne
ss ste
es d
cla
ss
es
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 2-2:
Type
Members
Me De
mb scr
er ipti
on
Co Inv
nst ari
ant abl
s e
dat
a
me
mb
ers
Co Sp
nst eci
ruc aliz
tor ed
me
tho
ds
tha
t
initi
aliz
es
a
co
mp
on
ent
or
cla
ss
for
sta
tic
co
nst
ruc
tor
s
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 2-2:
Type
Members
Me De
mb scr
er ipti
on
Del Ty
eg pe-
ate saf
e
co
nta
ine
rs
of
on
e
or
mo
re
fun
cti
on
poi
nte
rs
De Sp
str eci
uct aliz
or ed
me
tho
d
tha
t
per
for
ms
cle
an
up
of
obj
ect
res
our
ce
s
up
on
gar
ba
ge
coll
ect
ion
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 2-2:
Type
Members
Me De
mb scr
er ipti
on
Ev Cal
ent lba
s ck
s
to
me
tho
ds
pro
vid
ed
by
a
su
bs
cri
ber
Fie Dat
lds a
me
mb
ers
Ind Sp
ex eci
er aliz
ed
pro
per
ty
tha
t
ind
ex
es
the
cur
ren
t
obj
ect
Int Ne
erf ste
ac d
es int
erf
ac
e
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 2-2:
Type
Members
Me De
mb scr
er ipti
on
Me Re
tho us
d abl
e
co
de
se
qu
en
ce
Op Op
era era
tor tor
s me
mb
er
fun
cti
on
s
tha
t
ove
rrid
e
im
plic
it
op
era
tor
be
hav
ior
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 2-2:
Type
Members
Me De
mb scr
er ipti
on
Pro Ge
per t
tie an
s d
set
fun
cti
on
s
pre
se
nte
d
as
a
fiel
d
Str Ne
uct ste
ure d
s str
uct
ure
wit
hin
a
cla
ss
When a member is declared, attributes can be applied and accessibility defined. Members are further
described with attributes. Accessibility sets the visibility as class member.
Member Accessibility
Members defined in a class are scoped to that class. However, the visibility of the member is defined by
accessibility keywords. The most common accessibility is public and private. Public members are visible
inside and outside of the class. The visibility of private members is restricted to the containing class. Private
is the default accessibility of a class member. Table 2-3 details the accessibility keywords:
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 2-3:
Accessibility
Ac De
ce scr
ssi ipti
bili on
ty
int Vis
ern ible
al in
co
nta
inin
g
as
se
mb
ly
int Vis
ern ible
al in
pro co
tec nta
ted inin
g
as
se
mb
ly
or
de
sc
en
da
nts
of
the
cur
ren
t
cla
ss
priv Vis
ate ible
insi
de
cur
ren
t
cla
ss
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 2-3:
Accessibility
Ac De
ce scr
ssi ipti
bili on
ty
pro Vis
tec ible
ted insi
de
cur
ren
t
cla
ss
an
d
an
y
de
sc
en
da
nts
pu Vis
bli ible
c in
co
nta
inin
g
as
se
mb
ly
an
d
as
se
mb
lies
ref
ere
nci
ng
tha
t
as
se
mb
ly
Member Attributes
Attributes are usually the first element of the member declaration and further describe a member and extend
the metadata. The Obsolete attribute exemplifies an attribute and marks a function as deprecated. Attributes
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Member Modifiers
Modifiers refine the definition of the applicable member. Modifiers are optional and there are no defaults.
Some modifiers are reserved for classification of members. For example, the override modifier is applicable
to member functions, not data members. Table 2-4 lists the available modifiers.
Table 2-4:
Modifiers
Mo De
difi scr
er ipti
on
ab A
str me
act mb
er
fun
cti
on
ha
s
no
im
ple
me
nta
tio
n
an
d
is
de
scr
ibe
d
thr
ou
gh
inh
erit
an
ce.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 2-4:
Modifiers
Mo De
difi scr
er ipti
on
Ext Im
ern ple
me
nte
d
in
a
for
eig
n
dy
na
mi
c-li
nk
libr
ary
(DL
L).
Ne Hid
w es
a
si
mil
ar
me
mb
er
or
me
mb
ers
in
the
ba
se
cla
ss.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 2-4:
Modifiers
Mo De
difi scr
er ipti
on
Ov Indi
erri cat
de es
tha
ta
fun
cti
on
in
a
der
ive
d
cla
ss
ove
rrid
es
a
virt
ual
me
tho
d
in
the
ba
se
cla
ss.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 2-4:
Modifiers
Mo De
difi scr
er ipti
on
Re Re
ad ad-
onl onl
y y
fiel
ds
are
initi
aliz
ed
at
de
cla
rati
on
or
in
a
co
nst
ruc
tor.
Se Th
ale e
d me
mb
er
fun
cti
on
ca
nn
ot
be
furt
her
refi
ne
d
thr
ou
gh
inh
erit
an
ce.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 2-4:
Modifiers
Mo De
difi scr
er ipti
on
Sta Me
tic mb
er
bel
on
gs
to
a
cla
ss
an
d
not
an
ins
tan
ce.
Virt Virt
ual ual
fun
cti
on
s
are
ove
rrid
abl
e
in
a
der
ive
d
cla
ss.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 2-4:
Modifiers
Mo De
difi scr
er ipti
on
Vol Vol
atil atil
e e
fiel
ds
are
mo
difi
abl
e
by
the
env
iro
nm
ent
,a
se
par
ate
thr
ea
d,
or
har
dw
are
.
Instance members are qualified by the object name. Here is the syntax of a fully qualified instance member:
objectname.instancemember
The lifetime of static members is closely linked to the lifetime of the application. Instance members are
inexorably linked to an instance and are accessible from the point of instantiation. Access to instance
members ceases when the instance variable is removed. Therefore, the lifetime of an instance member is a
subset of the lifetime of an application. Static members are essentially always available, whereas instance
members are not. Static members are similar to global functions and variables but have the benefit of
encapsulation. Static members can be private.
Your design analysis should determine which members are instance versus static members. For example, in
a personnel application for small businesses, there is an Employee class. The employee number is an
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
instance member of the class. Each employee is assigned a unique identifier. However, the company name
member is static because all employees work for the same company. The company name does not belong
to a single employee, but to the classification.
Static class A static class is a class that contains static members and no instance members. Because
static classes have no instance data, a static class cannot be instantiated. This is a static class:
this Object
The this reference refers to the current object. Instance members functions are passed a this reference as
the first and hidden parameter of the function. In an instance function, the this reference is automatically
applied to other instance members. This assures that within an instance member function, you implicitly
refer to members of the same object. In this code, GetEmployeeInfo is an instance member function referring
to other instance members (the this reference is implied):
Here is the same code, except that the this pointer is explicit (the behavior of the function is the same):
In the preceding code, the this reference was not required. The this reference is sometimes required as a
function return or parameter. In addition, the this reference can improve code readability and provide
IntelliSense for class members when editing the code in Microsoft Visual Studio.
Static member functions are not passed a this reference as a hidden parameter. For this reason, a static
member cannot directly access nonstatic members of the class. For example, you cannot call an instance
method from a static member function. Static members are essentially limited to accessing other static
members. Instance members have access to both instance and static members of the class.
The this reference is a read-only reference. As part of error handling, setting a this reference to null would
sometimes be beneficial. Alas, it is not allowed.
Data members are typically private to enforce encapsulation and the principles of data hiding and
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
abstraction. Abstraction abstracts the details of a class and restricts collaboration to the public interface.
The developer responsible for the class obtains implementation independence and has the freedom to modify
the implementation when required as long as the interface is unchanged. The interface is immutable—like a
contract between the class and any client. As a best practice, data members should be private and
described fully through the public interface. The class interface consists of the public member functions. Do
not make every member function public; internal functionality should remain private.
Except for constructors and destructors, members should not have the same name as the containing class.
As a policy, class names should differ from the surrounding namespace.
Constants
Constants are data members. They are initialized at compile time using a constant expression and cannot
be modified at run time. Constants have a type designation and must be used within the context of that type.
This makes a constant type-safe. Constants are usually a primitive type, such as integer or double. A
constant can be a more complex type. However, classes and structures are initialized at run time, which is
prohibited with a constant. Therefore, constant class and structures must be assigned null, which limits their
usefulness. Following is the syntax of a constant member:
const syntax:
accessibility modifier const constname=initialization;
The only modifier available is new, which hides inherited members of the same name. Constants are tacitly
static. Do not use the static keyword explicitly. The initialization is accomplished with a constant
expression. You can declare and initialize several constants simultaneously.
The following code presents various permutations of how to declare and initialize a constant data member:
The assignment to fieldc causes a compile error. The fieldd member is a nonconstant member variable and
is evaluated at run time. Thus, fieldd cannot be used in a constant expression, as shown in the preceding
code. Constant expressions are limited to literals and constant types.
Fields
As a data member, fields are the most prevalent. Instance fields hold state information for a specific object.
The state information is copied into the managed memory created for the object. Static fields are data owned
by the class. A single instance of static data is created for the class. It is not replicated for each instance of
the class. Fields can be reference or value types. Here is the syntax for declaring a field:
accessibility modifier type fieldname=initialization;
Fields support the full assortment of accessibility. Valid modifiers for a field include new, static, read-only,
and volatile. Initialization is optional but recommended. Uninitialized fields are assigned a default value of
zero or null. Alternatively, fields can be initialized in constructor functions. Like constants, fields of the same
type are declarable individually or in aggregate in the initialization list.
Initialization is performed in the textual order in which the fields appear in the class, which is top-down. This
process is demonstrated in the following code:
using System;
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
namespace Donis.CSharpBook{
internal class ZClass {
public int iField1=FuncA();
public int iField2=FuncC();
public int iField3=FuncB();
Running this code confirms that FuncA, FuncC, and FuncB are called in sequence the textual order of the
initialization. Avoid writing code dependent on the initialization sequence—writing code that way makes the
program harder to maintain without clear documentation.
Private fields exposed through accessor functions, a get and set method, are read-write. Eliminate the get or
set method to make the field read-only or write-only, respectively. However, this relies on programmer
discipline. The read-only keyword is safer.
A read-only field is enforced by the run time. These fields are different from constants. Although constants
can be initialized only at compile time, read-only fields can also be initialized in a constructor. Alternatively,
read-only fields can be initialized with nonconstant expressions. These are subtle but important nuances and
provide considerable more flexibility to a read-only field. In addition, read-only fields can be instance or static
members, whereas constant are only static.
Volatile Fields
Because of the intricacy of program execution, writes and reads to a field might not be immediate. This
latency can cause problems, particularly in a multithreaded application. Reads and writes to volatile fields
are essentially immediate. Volatile makes automatic what locks do programmatically. Locks are explained
in Chapter 9, "Threading."
Member Functions
Member functions contain the behavior of the class. Methods, properties, and indexers are the member
functions. Methods are straightforward functions that accept parameters as input and return the result of an
operation. Properties are accessor methods, a get and set method, masked as a field. An indexer is a
specialized property. Indexers apply get and set methods to the this reference, which refers to the current
object. The discussion of indexers is deferred to Chapter 6, "Arrays and Collections."
Function Code
Functions encapsulate a sequence of code and define the behavior of the class. A function is not necessarily
executed in sequential order and sometimes contains iterative and conditional transfer control statements.
Return statements provide an orderly exit to a function. Void functions can simply fall through the end of the
function. The compiler extrapolates the code paths of a function and uncovers any unreachable code, which
is subsequently flagged by the compiler. All paths must conclude with an orderly exit. The following code
includes both unreachable code and a code path without an orderly exit:
else {
Console.WriteLine("false");
}
}
The if statement is always true. Therefore, the else code is unreachable. Main returns int. However, the
method has no return. For these reasons, the application generates the following errors when compiled:
Keep functions relatively short. Extended functions are modestly harder to debug and test. A class
comprised of several functions is preferable to class of a few convoluted functions. Remoted components are
the exception: A narrower interface is preferred to minimize client calls and optimize network traffic.
Comparatively, local invocations are relatively quick and efficient.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Methods own a code sequence, local variables, and parameters. The local variables and parameters
represent the private state of the method.
Local Variables
Local variables and parameters represent the private state of the function. Local variables are reference or
value types. Declare a local variable anywhere in a method. However, a local variable must be defined before
use. Optionally, initialize local variables at declaration. There are competing philosophies as to when to
declare local variables: Some developers prefer to declare local variables at the beginning of a method or
block, where they are readily identifiable; other developers like to declare and initialize local variables
immediately prior to use. They feel that local variables are more maintainable when situated near the affected
code. Local variables are not assigned a default value prior to initialization. For this reason, using an
unassigned local variable in an expression causes a compile error. This is the syntax of a local variable:
modifier type variablename=initialization;
The const modifier is the only modifier that is applicable. Variables that are const must be initialized at
compile time.
The scope of a local variable or function parameter is the entire function. Therefore, local variables and
function parameters must have unique names. Local variables of the same type can be declared individually
or in aggregate.
Scope Local variables are declared at the top level or in a nested block within the method. Although the
scope of a local variable is the entire function, the visibility of a local variable starts where the local variable is
declared. Visibility ends when the block that the local variable is declared is exited. Local variables are
visible in descendant blocks, but not ascendant blocks. Because local variables maintain scope throughout
a function, names of local variable cannot be reused—even in a nested block. Figure 2-1 illustrates the
relationship between scope and visibility of local variables.
Figure 2-1: Figure diagramming the scope and visibility of a local variable
Local variables of a value type are removed from the stack when a function is exited. Local references are
also removed from the stack at that time, whereas the objects they referred to become candidates for
garbage collection. This assumes that no other references to the object exist at that time. If other
outstanding references exist, the object associated with the reference remains uncollectible. When a
reference is no longer needed, assign null to the reference. This is a hint to the garbage collector that the
related object is no longer needed.
Local variables are private to the current function. Functions cannot access private data of another function.
Identically named local variables in different functions are distinct and separate entities, which are stored at
different memory addresses. To share data between functions, declare a field.
Methods
Methods are the most common function member. Methods accept parameters as input, perform an
operation, and return the result of the operation. Both parameters and the return result are optional. Methods
are described through the method header, and implemented in the method body. Here is the syntax of a
method:
Method syntax:
attributes accessibility modifiers returntype methodname(parameter list) { method body };
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Any attribute that targets a method can prefix the method. The accessibility of methods included in the class
interface is public. Methods not included in the interface are typically protected or private. Methods can be
assigned the sealed, abstract, new, and other modifiers, as explained in Chapter 3. The return is the result of
the method. Methods that return nothing stipulate a void returntype. The parameter list, which consists of
zero or more parameters, is the input of the method. The method body contains the statements of the
method.
Method Return Execution of a function starts at the beginning and ends at a return. The return keyword
provides the result of function or an error code. A value or reference type can be returned from a method as
defined by the return type. Functions can contain multiple returns, each on a separate code path. More than
one return along a single code path results in unreachable code, which causes compiler warnings.
Functions with a void return type can be exited explicitly or implicitly. An empty return statement explicitly
exits a function of a void return type. For an implicit exit, execution is allowed to exit the function without a
return statement. At the end of the function block, the function simply exits. Implicit returns are reserved for
functions that return void. A function can have multiple explicit returns but only one implicit return. The end of
a function must be reached for an implicit return, whereas an explicit return can exit the function
prematurely. In the following code, Main has both an explicit and implicit exit:
// implicit return
}
A function that returns void cannot be used in an assignment. This type of method evaluates to nothing and
is thereby not assignable. This prevents a function that returns void from being used as a left- or right-value of
an assignment. Other functions are usable in assignments and more generally in expressions. A function
evaluates to the return value. When a reference type is returned, a function is available as a left- or right-value
of an assignment. Functions that return a value type are restricted to right-values of an assignment. The
following code demonstrates the use of methods in expressions:
}
}
At the calling site, the return of a function is temporarily copied to the stack. The return is discarded after
that statement. To preserve the return, assign the method return to something. Returns are copy by value.
When returning a value type, a copy of the result is returned. For reference types, a copy of the reference is
returned. This creates an alias to an object in the calling function. Look at the following code:
using System;
namespace Donis.CSharpBook{
In the preceding code, MethodA creates an instance of XInt, which is subsequently returned from the
method. The scope of the reference called inner, which is a local variable, is MethodA. After the return, outer
is an alias and refers to the inner object, which prevents the object from becoming a candidate for garbage
collection, even thought the inner reference is no longer valid.
A method returns a single item. What if you want to return multiple values? The solution is to return a
structure containing a data member for each value. Structures are lightweight and appropriate for copying by
value on the stack.
Function parameters Functions have zero or more parameters, where an empty function call operator
indicates zero parameters. Parameter lists can be fixed or variable length. Use the param keyword to
construct a variable length parameter list, which is discussed in Chapter 6. Parameters default to pass by
value. When the parameter is a value type, changes to the parameter are discarded when the method is
exited and the value is removed from the stack. In the following code, changes made in the function are
discarded when the function returns:
using System;
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
namespace Donis.CSharpBook{
public class Starter{
As a parameter, reference types are also passed by value. A copy of the reference, which is the location of
the object, is passed by value. Therefore, the function now has an alias to the actual object, which can be
used to change the object. Those changes will persist when the function exits.
This code revises the preceding code. An integer wrapper class is passed as a parameter instead of an
integer value. Changes made to the object in MethodA are not discarded later.
using System;
namespace Donis.CSharpBook{
Pass a parameter by reference using the ref modifier. The location of the parameter is passed into the
function. The ref attribute must be applied to the parameter in the function signature and at the call site. The
function now possesses the location of the actual parameter and can change the parameter directly. The
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
following code has been updated to include the ref modifier. Unlike the first revision of this code, changes
made in MethodA are not discarded.
using System;
namespace Donis.CSharpBook{
public class Starter{
What happens when a reference type is passed by reference? The function receives the location of the
reference. Because a reference contains a location to an object, the location of the location is passed into
the function. In the following code, a reference type is passed by value, which creates an alias. Because the
reference is passed by value, changes to the alias are discarded when the function exits.
using System;
namespace Donis.CSharpBook{
}
}
}
Next, the code is slightly modified to include the ref attribute. The assignment in MethodA is not discarded
when the method exits. Therefore, obj is updated to reference the object created in the method.
using System;
namespace Donis.CSharpBook{
Local variables must be initialized before use. The following code is in error. The obj reference is unassigned
but initialized in the method. However, the compiler is unaware of this and presents an error for using an
unassigned variable. The out parameter is a hint to the compiler that the parameter is being set in the
function, which prevents the compiler error. The out parameter is not required to be initialized before the
method call. These parameters are often used to return multiple values from a function.
using System;
namespace Donis.CSharpBook{
In the following code, the parameter modifier is changed to out. The program compiles and runs successfully
when the attribute is changed to out:
using System;
namespace Donis.CSharpBook{
class XInt {
class Starter{
Function Overloading
Function overloading permits multiple implementations of the same function in a class, which promotes a
consistent interface for related behavior. The SetName method would be overloaded in the Employee class to
accept variations of an employee name. Overloaded methods share the same name but have unique
signatures. Parameter attributes, such as out, are considered part of the signature. (The signature is the
function header minus the return type.) Because they are excluded from the signature, functions cannot be
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
overloaded on the basis of a different return type. The number of parameters or the type of parameters must
be different. With the exception of constructors, you cannot overload a function based on whether a member
is a static or instance member.
The process of selecting a function from a set of overloaded methods is called function resolution, which
occurs at the call site. The closest match to a specific function is called as determined by the number of
parameters and the types of parameters given at function invocation. Sometimes a function call matches
more than one function in the overloaded set. When function resolution returns two or more methods, the call
is considered ambiguous, and an error occurs. Conversely, if no method is returned, an error is also
manifested.
using System;
namespace Donis.CSharpBook{
}
}
}
Functions with variable-length parameter lists can be included in the set of overloaded functions. The function
is first evaluated with a fixed-length parameter list. If function resolution does not yield a match, the function
is evaluated with variable-length parameters.
Constructors
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
A constructor is a specialized function for initializing the state of an object. Constructors have the same
name of the class. The main purpose of a constructor is to initialize the fields and guarantee that object is in
a known state for the lifetime of the object. Constructors are invoked with the new operator. Here is the
syntax of a constructor:
Constructor syntax:
accessibility modifier typename(parameterlist)
The accessibility of a constructor determines where new instances of the reference type can be created. For
example, a public constructor allows an object to be created in the current assembly and a referencing
assembly. A private constructor prevents an instance from being created outside the class. extern is the only
modifier for constructors.
using System;
namespace Donis.CSharpBook{
case 1:
name=_name[0];
break;
case 2:
name=_name[0]+" "+_name[1];
break;
default:
// Error handler
break;
}
Classes with no constructors are given a default constructor, which is a parameterless constructor. Default
constructors assign default values to fields. An empty new statement invokes a default constructor. You can
replace a default constructor with a custom parameterless constructor. The following new statement calls
the default constructor of a class:
Constructors can be overloaded. The function resolution of overloaded constructors is determined by the
parameters of the new statement. This Employee class has overloaded constructors:
using System;
namespace Donis.CSharpBook{
Why? Although the statement is syntactically correct, the Employee class has lost the default constructor.
Adding a custom constructor to a class removes the default constructor. If desired, also add a custom
parameterless constructor to preserve that behavior.
A constructor can call another constructor using the this reference, which is useful for reducing redundant
code. A constructor cannot call another constructor in the method body. Instead, constructors call other
constructors using the colon syntax, which is affixed to the constructor header. This syntax can be used
only with constructors and not available with normal member functions. In this code, all constructors
delegate to the one argument constructor:
class Employee {
public Employee()
: this(""){
}
Constructors can also be static. Create a static constructor to initialize static fields. The static constructor is
invoked before the class is accessed in any manner. There are limitations to static constructors, as follows:
Static constructors cannot be called explicitly.
Accessibility defaults to static, which cannot be set explicitly.
Static constructors are parameterless.
Static constructors cannot be overloaded.
Static constructors are not inheritable.
The return type cannot set explicitly. Constructors always return void.
As mentioned, static constructors are called before a static member or an instance of the classes is
accessed. Static constructors are not called explicitly with the new statement. In the following code, ZClass
has a static constructor. The static constructor is stubbed to output a message. In Main, the time is
displayed, execution is paused for about five seconds, and then the ZClass is accessed. ZClass.GetField is
called to access the class and trigger the static constructor. The static constructor is called immediately
before the access, which displays the time. It is about five seconds later.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
using System;
using System.Threading;
namespace Donis.CSharpBook{
Singleton Singletons provide an excellent example of private and static constructors. A singleton is an
object that appears once in the problem domain. Singletons are limited to one instance, but that instance is
required. This requirement is enforced in the implementation of the singleton. A complete explanation of the
singleton pattern is found at http://www.microsoft.com/patterns.
The singleton presented in this chapter has two constructors. The private constructor means that an
instance cannot be created outside the class. That would require calling the constructor publicly. However,
an instance can be created inside the class. The single instance of the class is exposed as a static
member, which is initialized in the static constructor. The instance members of the class are accessible
through the static instance. Because the static constructor is called automatically, one instance—the
singleton—always exists.
A chess game is played with a single board—no more or less. This is the singleton for a chess board:
using System;
namespace Donis.CSharpBook{
static Chessboard() {
board=new Chessboard();
board.start=DateTime.Now.ToShortTimeString();
}
In Main, game is an alias for the ChessBoard.board singleton. The local variable is not another instance of
Chessboard. The alias is simply a convenience. I preferred game.player1 to Chess-Board.board.player1.
Destructors
An application can clean up unmanaged resources of an object in the destructor method. Destructors are not
directly callable and called by the run time prior to garbage collection removing the object from the managed
heap. Garbage collection is nondeterministic. A destructor is invoked at an undetermined moment in the
future. Like a constructor, the destructor has the same as the class, except a destructor is prefixed with a
tilde (~). The destructor is called by the Finalize method, which is inherited from System.Object. In C#, the
Finalize method cannot be used directly. You must use a destructor for the cleanup of object resources. For
deterministic garbage collection, inherit the IDisposable interface and implement IDisposable.Dispose. Call
dispose to relinquish managed or unmanaged resources associated with the object. Implementing a
disposable pattern is one of the topics found in Chapter 16.
Destructor syntax:
modifier ~typename()
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Destructors have performance and efficiency implications. Understand the ramifications of destructors before
inserting a destructor in a class. These topics are also covered in Chapter 16.
The WriteToFile class is a wrapper for a StreamWriter object. The constructor initializes the internal object.
The Dispose method closes the file resource. The destructor delegates to the Dispose method. The Main
method tests the class.
using System;
using System.IO;
namespace Donis.CSharpBook{
~WriteToFile() {
Dispose();
}
Properties
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Properties are get and set methods exposed as properties. Public fields do not adhere to encapsulation.
Private fields are good policy where the fields are exposed through accessor methods. A property combines
the convenience of a public field and the safety of access methods. For clients of the public interface of a
class, properties are indistinguishable from fields. The client appears to have direct access to the state of
the class or object. There several benefits of properties:
Properties are safer than public fields. Use the set method to validate the data.
Properties can be computed, which adds flexibility. Fields represent a data store and never a
calculation.
Lazy initialization can be used with properties. Some data is expensive to obtain. A large dataset
is an ideal example. Deferring that cost until necessary is a benefit.
Write- and read-only properties are supported. Public fields are fully accessible.
Properties are valid members of an interface. As part of the interface contract, a class can be
required to publish a property. Interfaces do not include fields.
A property is a get and set method. Neither method is called directly. Depending on the context of the
property, the correct method is called implicitly. As a left-value of an assignment, the set method of the
property is called. When used as a right-value, the get method of the property is called. When a property is
the target of an assignment, the set method of the property is invoked. The get method is called if a property
is used within an expression. Here is the syntax of a property:
Property syntax:
accessibility modifier type propertyname { attributes get {getbody} attributes set {setbody} }
The accessibility and any modifier included in the property header apply to both the get and set method.
However, the set or get method can override the defaults of the property. Type is the underlying type of the
property. Neither the get nor set method has a return type or parameter list. Both are inferred. The get
method returns the property type and has no parameters. The set method returns void and has a single
parameter, which is the same type of the property. In the set method, the value keyword represents the
implied parameter.
using System;
namespace Donis.CSharpBook{
Properties have no inherent data storage. If desired, the data storage must be declared in the class.
Computed properties might not require data storage. As a guideline, the property is public, whereas the data
store is private. There is considerable flexibility available. The data store of a property could consist of
several private fields. Conversely, multiple properties can share the same data store. This is demonstrated in
the following code, where the fullname, firstname, and lastname property share the prop_name data store:
using System;
namespace Donis.CSharpBook{
}
}
Read-only and write-only properties Read-only properties offer only the get method and omit the set
method. Birth date is an ideal read-only property. Conversely, write-only properties have a set only, which is
common with a password property:
Error handling Validating the state of an object is important to avoid the "garbage-in/ garbage out"
syndrome. An object or class is only as useful as the quality of the data it contains. Validation can be
performed in the set method of a property. What is the appropriate action if the validation fails? You cannot
return an error code from a set method. There are two options. First (and preferably), raise an application
exception. Second, if the property type is a reference or nullable type, set the property to null. For the person
class, an age greater than 130 is probably an error. The estimated maximum age of a person is 120. In case
someone is especially hearty, an age greater than 130 is flagged as invalid. Here are both approaches to
handling an error condition in a property:
}
}
Nested Types
Nested types are classes and structures declared inside a class. Nested classes are advantageous when
modeling systems of interconnecting components. This is an example of a nested class:
Nested types can be public, private, or other accessibility. A full complement of modifiers is available. In
addition, the new modifier is available to nested types, which hides a nested class of the base class.
Use the dot syntax to access members of the nested class. This code calls a static method of the inner
class:
Outer.Inner.FuncB();
You can build an instance of the nested class, which is an inner reference. If public, references to the nested
class can be created inside or outside the outer class. A nested class that is private can be instantiated
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
only in the outer class. The this object of the inner reference includes members of the inner class alone.
Instance members of the outer class are not accessible from the this object of the inner reference. Pass a
reference of the outer component into the constructor of the inner component. The back reference is then
available to access the instance members of the outer component from within the inner component. The
static members of the outer class, regardless of accessibility, are available in the inner class.
The Automobile class models a car engine, which is a system. An automobile system contains alternators,
ignition, steering system, and other systems. This is an abbreviated version of the Automobile class:
using System;
namespace Donis.CSharpBook{
public Automobile() {
starter=new StarterMotor(this);
}
Partial Classes
A partial class can span multiple source files. When compiled, the partial class is reassembled in the
assembly. Each source file has a complete class declaration and body. The class declaration is preceded
with the partial keyword. The class body in each source file includes only the members that the partial class
is contributing to the overall type. Code generators will benefit from partial classes. Rely on the code
generator or wizard to generate the core code. The developer can then extend the code without disturbing the
base code. Teams of developers collaborating on an application also benefit from partial classes. The class
can be parceled based on responsibility. The developer works in separate source files, which isolate
changes and focus a developer on code relevant to their responsibility.
// partial1.cs
using System;
namespace Donis.CSharpBook{
}
}
}
// partial2.cs
using System;
namespace Donis.CSharpBook{
Figure 2-2 shows the ZClass combined in the assembly, which demonstrates that the partial classes are
merged.
Structures
Structures are lightweight classes. Similar to classes, structures have behaviors and attributes. As a value
type, structures directly contain their value and are stored on the stack. Because structures reside on the
stack, keep them small. Do not cache large objects on the stack. The implementation of structures in C#
enforces the policy of using a structure as a lightweight class. The following list details the differences
between structures and classes:
Structures are sealed and cannot be inherited.
Structures cannot inherit classes and other structures.
Structure implicitly inherits from System.ValueType.
The default constructor of a structure cannot be replaced by a custom constructor.
Custom constructors of a structure must fully initialize the value of the structure.
Structures do not have destructors.
Field initialization is not allowed. Const members of a structure can be initialized.
Structure syntax:
attributes accessibility struct structname: interfacelist { structbody };
There are several attributes that target structures. Structures support the same accessibility of a class.
Structures implicitly inherit System.ValueType and cannot explicitly inherit another type. However, structures
can implement interfaces. Interfacelist is a list of interfaces the structure implements. The structbody
encompasses the member functions and data members of the structure.
The default constructor of a structure initializes each field to a default value. You cannot replace the default
constructor of a structure. Unlike a class, adding custom constructors to a structure does not remove the
default constructor. Invoke a custom constructor with the new operator. The new operator will not place the
structure on the managed heap. It is a call to the parameterized constructor of a structure. Structures are
commonly declared without the new operator. In that circumstance, the default constructor is called.
Fraction is a structure that models naturally a fraction. Fraction has two double members. It is small and
ideal for a structure.
using System;
namespace Donis.CSharpBook{
Enumeration
An enumeration is a set of discrete and related values. Enumerations expand into a type, whereas the
members of the enumeration are defined as constant. The default underlying type of an enumeration is
integer. An enumeration is never required; integer variables can be used instead. However, enumerations are
safer, more extensible, and enhance readability. The Months.GetMonth method outputs the name of a
month. The method relies on integer values.
case 2:
Console.WriteLine("Febuary");
break;
case 3:
Console.WriteLine("March");
break;
// and so on...
default:
Console.WriteLine("Invalid Month");
break;
}
}
}
Enumerations support public and other accessibility. The default underlying type is integer. The basetype
element changes the underlying type. This can be any integral type, except the character type. For the
Month enumeration, changing the underlying from integer to byte is more efficient. The memberlist includes
all the constants of the enumeration. By default, the constant members are numbered in textual order from
zero to n-1. Each member can be assigned a specific value. Here is the updated enumeration called Month:
When assigning values to enumeration members, sequential ordering is not required. In addition, duplicate
values are allowed. Some members can be set without initializing others. The value of the preceding member
is incremented and assigned to any unassigned member. ZEnum is a mixture of explicitly and implicitly
initialized members. Optional values are listed in the comments.
Enumerations are derived from the System.ValueType method, which offers important services for
enumeration types. Table 2-5 lists some of those services.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 2-5:
Important
System.Enum
Methods
Me De
tho scr
d ipti
Na on
me
sta Ret
tic urn
stri s
ng the
Ge stri
tNa ng
me rep
(Ty res
pe ent
en ati
um on
typ of
e, a
obj sp
ect ecif
val ic
ue) ite
m
of
the
en
um
era
tio
n.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 2-5:
Important
System.Enum
Methods
Me De
tho scr
d ipti
Na on
me
sta Ret
tic urn
stri s
ng[ a
] stri
Ge ng
tNa arr
me ay
s(T co
ype nta
en inin
um g
typ the
e) stri
ng
rep
res
ent
ati
on
of
eve
ry
ite
m
of
the
sp
ecif
ied
en
um
era
tio
n.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 2-5:
Important
System.Enum
Methods
Me De
tho scr
d ipti
Na on
me
sta Ret
tic urn
Arr s
ay an
Ge arr
tVa ay
lue of
s(T the
ype un
en der
um lyin
typ g
e) typ
e.
Th
e
arr
ay
co
nta
ins
the
nu
me
ric
al
rep
res
ent
ati
on
of
ea
ch
val
ue
of
the
en
um
era
tio
n.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 2-5:
Important
System.Enum
Methods
Me De
tho scr
d ipti
Na on
me
sta Ret
tic urn
Ty s
pe the
Ge un
tUn der
der lyin
lyin g
gT typ
ype e
(Ty of
pe the
en en
um um
Ty era
pe) tio
n.
Bitwise Enumeration
Bitwise enumeration is for mutually inclusive flags. Each member of the enumeration is assigned a unique
bitwise value. Apply the flags attribute to an enumeration to specify bitwise enumeration.
Combine bitwise flags using the or operator (|). Confirm the existence of a flag with the bitwise and operator (
&).
using System;
namespace Donis.CSharpBook{
All=Pension|ProfitSharing|CreditBureau|SavingsPlan
}
Equivalent values are related types that contain the same value. In the following code, integers locala and
localc are equivalent. However, the variables are not identical because they are stored at different locations
on the stack. The variables locala and localb are neither equivalent no identical. They have differenet values
and are stored at different locations on the stack:
int locala=5;
int localb=10;
int localc=5;
Assigning a value type copies the value. The target and source are equivalent after this assignment, but not
identical:
For reference types, there is synchronicity of equivalence and identity. Related references containing the
same value are equivalent and identical. Assigning a reference creates an alias, which can create unplanned
side affects. For this reason, be careful when assigning references. The following code has some unplanned
side effects:
using System;
namespace Donis.CSharpBook{
}
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Class Refinement
Classes can be refined through inheritance, which is code reuse. The common members of related classes
can be extracted and placed in a base class. Child classes inherit and refine a base class. A personnel
application might have SalariedEmployee, HourlyEmployee, and CommissionedEmployee classes that inherit
the Employee class. The Employee class holds the common members of all Employee-derived classes.
Each derived class adds members to refine the base class. Optionally, derived classes can override
members of the base class. Sealed classes cannot be inherited. Inheritance and polymorphism are closely
related. Polymorphism calls the correct method at run time from a base reference to a derived object, and it
can add considerable efficiencies to an application.
Inheritance and polymorphism and other strategies to refine class usage are reviewed in Chapter 3.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Chapter 3: Inheritance
Overview
Classes are often related to other classes. The two popular methods of relating classes are containment,
also known as composition, and inheritance. Containment is a "has a" relationship, where a class contains
or embeds another class. For example, a timestamp contains time. Inheritance is based on the "is a"
relationship, in which one class is a type of another class. For example, an hourly employee is a kind of
employee.
Inheritance involves a base type and a derived type, where a derived type is a kind of base type. Derived
types inherit the members of the base type. In essence, they are derived from the base type.
A base type represents a generalization, whereas a derived type represents a specialty. Specialties are
derivatives of generalizations. A personnel program might contain different types of employees. Hourly,
salaried, commissioned, temporary, and retired are possible types of employees. Each is a kind of
employee and distinct from any other employee specialization. For example, an hourly employee is different
from a salaried employee. Employee is a generalization of all employees and is therefore a base class.
Conversely, HourlyEmployee, SalariedEmployee, CommissionedEmployee, TemporaryEmployee, and
RetiredEmployee are specializations and are therefore derived classes.
Derived types refine base types. A base type holds the common members of the generalization. Derived
classes inherit those members and add specialty members. These members refine the base class in the
context of the derived class. The Employee class, which is a base class, defines the FullName and EmplID
members. Every employee has a name and employee number. As such, those members belong in the base
type. The SalariedEmployee class adds members for the salary and number of pay periods. This is unique to
the salaried employee specialization. The HourlyEmployee class has additional fields for the hourly rate and
the number of hours worked. Both SalariedEmployee and HourlyEmployee classes refine the Employee class
with these extra members.
Inheritance provides hierarchical clarity. The employee and derived types form a hierarchy. Without this
clarity, you face a greater challenge in programming an application. Classes are analogous to marbles in a
bin. A large bin holds hundreds of marbles. The bin is an indiscriminate jumble of marbles. From this jumble,
you might try to find the one marble that is a clearie and has gray and white stripes. You could be looking for
awhile, as shown in Figure 3-1. The marbles could be organized into separate bins for regular, boulder, and
steelie marblies. Regular marbles are further segmented into cat's eyes and clearies, which are placed in
smaller subcontainers. With this additional organization, finding the clearie with gray and white stripes is
easier. (See Figure 3-2.)
As part of the design phase, scenarios are often employed. The nouns in the scenarios are candidates for
classes. Figure 3-3 shows the nouns found in a scenario for a Personnel application. There is no order or
hierarchy. You should look for relationships. The "is a" or "is a kind of" phrases are excellent for identifying
inheritance and discerning the base to derived class relationships. When the classes are organized by
relationships, clarity blooms, as shown in Figure 3-4.
Inheritance also promotes code reuse. Without inheritance, common members would be implemented
repeatedly—once in each derived type. Modifications would require updating several classes. This is a recipe
for problems and is neither efficient nor versionable. Inheritance has a cascade effect, which is better. A
member is implemented once in a base class, and the implementation cascades to all derived classes.
Changes to a base member ripple to all descendants. Insert base members at the highest relevant point in
the hierarchy. This creates the widest cascade and efficient utilization of the member. Inserting a member
too low in the hierarchy forces related types to separately implement that member.
Reference types can inherit classes and interfaces. Inheriting a class exemplifies code reuse. The derived
class inherits the members—including the code—of the base class. Conversely, an interface has no code. It
is a contract. Types that implement the interface contract to implement every member of the interface,
whereas the interface contracts to be immutable. This is important because changes to the interface would
break any type derived from that interface. Value types and primitives are not inheritable; they are sealed.
For example, you cannot inherit an int.
Value types cannot inherit other value types or classes. The exception is the System.ValueType class.
Value types implicitly inherit the System.ValueType class. A struct can inherit interfaces.
Table 3-1:
Inheritance
Terminology
Inh Inh
eri eri
tab tin
le g
Cl Cl
ass ass
Su Su
per bcl
cla as
ss s
Par Chi
ent ld
As De
ce sc
nd en
ant da
nt
Ba Der
se ive
d
Inheritance is not a mechanism to simply drop members into a class. From a design perspective, those
members should belong in that class. For example, a timestamp has the properties of time and a stamp. A
timestamp is a kind of stamp and has a time piece. However, a timestamp is not a kind of time. A
TimeStamp class should inherit from the Stamp class but not the Time class. The "has a" relationship
between the timestamp and time infers containment. Embedding a Time class in the TimeStamp class is
the preferable implementation.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Inheritance Example
Before introducing the specifics of inheritance, an example that includes all the prerequisite elements of
inheritance might be helpful. In the code, XParent is the base class. XChild is the derived class and inherits
the XParent class. XChild inherits a method, property, and field from the base class. XChild extends XParent
by adding a method and field to this assemblage. XChild has five members: three from the base and two
from itself. In this manner, XChild is a specialty type and refines XParent. In Main, instances of the XParent
and XChild classes are created. Base methods are called on the XParent instance. Both base and derived
methods are called on the XChild instance.
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
this.GetType().ToString());
return fieldb;
}
Cross-Language Inheritance
Inheritance is language-agnostic. Managed languages can inherit classes written in another managed
language. For library developers, this expands the universe of potential clients. Developers no longer need to
maintain language-specific versions of a library or create complex workarounds. Just as important, a family
of developers is not excluded from using a certain library. Another benefit of cross-language inheritance is
collaboration. Team members collaborating on a software system can develop in the language of their
choice. The entire team is not compelled to select a single source language.
Managed languages compile to Microsoft intermediate language (MSIL) code. The Common Language
Runtime (CLR) does not perceive a Microsoft Visual Basic .NET class inheriting from a C# class. It views
one MSIL class inheriting from another MSIL class. Language independence is easier to achieve when
specific languages dissolve into a shared common language at compilation.
Cross-language inheritance fractures without compliance to the Common Language Specification (CLS)—at
least relative to the base or derived class. Language-specific and noncompliant artifacts must be wrung from
classes when cross-language inheritance is planned or expected. For example, the following class, although
perfectly okay in C#, is unworkable in Visual Basic .NET. Visual Basic .NET is case insensitive, making
MethodA in the following code ambiguous:
The following code is an example of successful cross-language inheritance. The base class is written in C#,
whereas the derived class is Visual Basic .NET.
Imports System
Imports Donis.CSharpBook
Namespace Donis.CSharpBook
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
using System;
namespace Donis.CSharpBook{
public class XParent {
public void MethodA() {
Console.WriteLine("XParent.MethodA called from {0}.",
this.GetType().ToString());
}
System.Object
System.Object is the ubiquitous base type. All managed types inherit from System.Object, either directly or
indirectly. System.Object encompasses the baseline behavior accorded to all managed types. Reference
types without an explicit base class inherit System.Object implicitly. Reference types that inherit another
type explicitly still inherit System.Object, but indirectly. You can inherit System.Object explicitly. However,
that is somewhat redundant and, therefore, is not often done. Value types inherit from System.ValueType,
which then inherits System.Object. System.ValueType overrides System.Object members to implement the
semantics of a value type.
Object.Equals Method
For reference types, the Object.Equals method compares identity. References are equal when pointing to
the same object. References that point to different objects but have the same state are not equal. You can
override the Equals method to perform a value comparison. For value types, the Equals method is already
overridden to compare values.
In Applied Microsoft .NET Framework Programming (Microsoft Press, 2001), author Jeffrey Richter mentions
four tenets of the Equals method: reflexivity, symmetry, transitivity, and consistency.
Reflexive An object is always equal to itself. The obj1.Equal (obj1) call should always return true.
Transitive If obj1.Equals(obj2) and obj2.Equals(obj3) are both true, then obj3.Equals(obj1) is true.
Consistent If obj1.Equals(obj2) is true, then obj1 and obj2 should always be equal. This assumes
that the state of neither object has changed.
The Employee class is a base class. The HourlyEmployee, CommissionedEmployee, and SalariedEmployee
classes derive from the Employee class and represent specific types of employees. This is the override of
the Equals method in the Employee class:
if(obj==null) {
return false;
}
return this.GetHashCode()==_obj.GetHashCode();
}
The preceding code also includes overloads of the operators == and !=. The default implementation of these
operators will not call the overridden Equals method. This can cause inconsistencies, where the comparison
operators behave differently from the Equals method. For this reason, both operators have been overloaded.
When overriding the Equals method, you should also override the GetHashCode method. If not, a compiler
warning occurs. Objects that are equal should have the same hash code. Therefore, equality can be based
on comparing hash codes. In the Equals method, call GetHashCode to retrieve and compare hash codes.
Object.GetHashCode Method
GetHashCode returns a hash code as a 32-bit integer. Override this method when a reference type is used
as a key in a collection. As indicated in the previous section, the Equals and GetHashCode methods should
be implemented in tandem.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The fields associated with a hash code should be immutable. If the fields are not immutable, when the fields
change, the hash code is also modified. This could necessitate updating the key in a collection. If not, the
original key is stale. Stale keys in a collection can cause problems. For this reason, hash codes should not
be transient, and the related fields should not change.
The following code shows the GetHashCode method for the Employee class. The EmplID field used for the
hash is read-only. After the Employee instance is created, EmplID cannot be modified. It is immutable.
There are a variety of algorithms for creating efficient and distributed hash codes—some quite complex. For
simplicity, this implementation of GetHashCode simply returns the EmplID property:
Hash codes are recyclable. When a reference is garbage collected, the hash code is returned to the
available pool. The hash code is then assignable to a future instance. For this reason, you should remove
dead objects from any collections.
Object.GetType Method
The GetType method returns a Type instance, which describes the current object. Type objects are useful in
interrogating the composition of an associated object. Members, such as GetMethods and GetFields, return
the underlying architecture of a type. This process is called Reflection.
The following code demonstrates Reflection. The public methods of System.Object are enumerated and
displayed. As expected, the methods of Table 3-3 are shown. This excludes the Object.MemberwiseClone
method, which is not a public method.
Table 3-3: Member
Accessibility
Me Ou De
mb tsi riv
er de ed
Ac W Cl
ce orl ass
ss d
Mo
difi
er
pu Ye Ye
blic s s
priv No No
ate
pro No Ye
tec s
ted
int Ye Ye
ern s s
al (thi (thi
s s
as as
se se
mb mb
ly) ly)
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Me Ou De
mb tsi riv
er de ed
Ac W Cl
ce orl ass
ss d
Mo
difi
er
int Ye Ye
ern s s
al (thi
pro s
tec as
ted se
mb
ly)
using System;
using System.Collections;
using System.Reflection;
namespace Donis.CSharpBook{
Object.ToString Method
The ToString method returns a string representation of an instance. The default return is the fully qualified
name of the type. Value types override ToString to return the value of the instance. The following code
displays the default string representation of a value and a reference type:
int locala=10;
Console.WriteLine(locala.ToString()); // 10
Object obj=new Object();
Console.WriteLine(obj.ToString()); // System.Object
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
In the Employee class, ToString is overridden to return the name of the employee:
Object.MemberwiseClone Method
MemberwiseClone returns a new instance of an object. A shallow copy is performed. An object is cloned by
performing a bitwise copy of each member. If a member is a value type, the values are copied and there is no
side effect. For a member that is a reference type, the reference—not the object—is copied. The result is
that the reference members of both objects point to the same memory, as shown in Figure 3-5. When this
occurs, any changes in the original object will affect the cloned object, and vice versa.
MemberwiseClone is protected and cannot be overridden. The method is typically called from a derived class
when implementing the ICloneable interface. The ICloneable interface defines an interface for cloning
objects. The only member is the ICloneable.Clone method.
The following code shows the implementation of the ICloneable.Clone method in the Employee class. As
expected, the code delegates to the MemberwiseClone method. In the Employee class, emplName is a
member and a reference type. In Main, obj2 is a clone of obj1. The hash of both objects is displayed. This
confirms the separate identities of each object. Nonetheless, changes to the employee name affect both
objects, which is a nasty side effect. Both objects point to the same name in memory.
obj2.GetHashCode().ToString());
Console.WriteLine(obj2.EmplID+": "+obj2.FullName);
// 5678: Donis Marshall
}
}
propID=id;
}
Object.ReferenceEquals Method
The ReferenceEquals method compares identity. If the objects are the same, ReferenceEquals returns true.
Otherwise, false is returned. ReferenceEquals is not virtual and cannot be overridden in the derived class.
The following code compares an original object and a cloned object. The objects have the same state but
different identities. Because the identities are distinct, the ReferenceEquals method returns false.
Employee Class
This is the complete listing of the Employee class, including the overridden methods of the System.Object
class:
using System;
using System.Collections;
namespace Donis.CSharpBook{
class Employee {
propID=id;
}
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
if(obj==null) {
return false;
}
return this.GetHashCode()==_obj.GetHashCode();
}
}
}
Implementing Inheritance
Inheritance requires a base class and a derived class, where the derived class inherits from the base class.
The members of the base class are inherited into the derived type. Derived classes can override and extend
the interface inherited from the base type.
In the class header, the colon stipulates inheritance and prefixes the base list. The base list is a
comma-delimited list that includes at most one base class and any number of interfaces. Classes are
limited to inheriting a single class. However, a class can inherit multiple interfaces. C++ developers are
probably familiar with multiple inheritance. C++ allows multiple inheritance of classes. Excluding multiple
inheritance was an important design decision for C#. Personally, I endorse that decision. First, multiple
inheritance is not used that often. Second, multiple inheritance often causes more problems than are
resolved. Third, multiple inheritance allows for the increased probability of ambiguousness of members.
A base type must have the same or greater accessibility than the derived type. A compiler error occurs in
the following code. The accessibility of the base class is narrower than the derived class, which is an error.
.NET supports only public inheritance. C++ supported public, protected, and private inheritance. Despite this
flexibility in C++, practically all inheritance was public inheritance. Most C++ developers were not familiar
with or ever used any other kind of inheritance. Thus, protected and private inheritance will probably not be
missed.
Access Modifiers
Access modifiers set the visibility of members to the outside world and the derived type. Table 3-3 describes
member accessibility.
Are private members of the base class inherited? Private members are indeed inherited but are not visible to
the derived type and are not directly accessible in the derived class. These members are accessible through
the public and protected functions of the base class. Therefore, a derived type has two realms of private
variables. One realm includes private members that are defined in a derived class. These members are
visible in the derived type. The other realm includes inherited private members, which are not visible to the
derived type.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Overriding
A derived class can override behaviors in the base class, which allows a derived type to substitute different
implementations where appropriate. The Pay method of the Employee class is overridden in
CommissionedEmployee to include commissions in each paycheck. In the SalariedEmployee class, the Pay
method is overridden to calculate pay by prorating annual income. Methods, properties, and indexers can be
overridden. Fields and static members of the base cannot be overridden in the derived class. However,
derived types can hide fields and static members of the base class.
When is a child class allowed to override the parent? Most parents hope that their children inherit their
sterling behavior. However, the behavior of the parent is merely a suggestion to the child. Children are often
inclined to override the behaviors of the parents. The parent is hopeless in preventing this substitution. In
many object-oriented languages, that is the model. Child or derived classes can override the behavior of the
parent or base class. The derived class might override a behavior inappropriately and destabilize the base
class. This is called the fragile base class problem: a problem that is acute with class libraries where a child
class is more likely to inherit a type and unknowingly or incorrectly override a base member. The model is
different in C#. By default, functions cannot be overridden. Parent classes must tacitly approve the overriding
of a method. In addition, the child class must acknowledge its intent to override the method. This prevents
the child from unknowingly overriding a member of the base class.
The virtual keyword indicates that a member can be overridden in a child class. It can be applied to methods,
properties, indexers, and events. In the derived class, the override keyword indicates the intention to override
a virtual member of the base class. The virtual and override keywords complement each other. The virtual
keyword propagates to descendants. A virtual method is overridable in a derived class and descendants.
Overriding is not an all-or-nothing condition. Using the base keyword, the public and protected members of
the base class are accessible in the derived type. The syntax is base.member. You cannot skip levels with
the base keyword. For example, you cannot access a member in a grandfather class. The base.base.
member syntax is illegal. In the following code, the SalariedEmployee.Pay method calls the Employee.Pay
method to display an employee name on a paycheck:
public Employee() {
}
propID=id;
}
// partial listing
}
// partial listing
Override and overload are different concepts. When a member of a base class is overridden, the signature of
the base and the signature of the derived member are the same. Overloading requires different signatures. In
a derived class, a function can be overloaded, overridden, or both. When a member is overridden with a
different signature, a compiler warning occurs in the derived type. This prevents an accidental overload,
where method overriding was intended. This is shown in the following code, where MethodA is overloaded in
the YClass class but not overridden. In Main, both the base and the derived implementation are called.
using System;
namespace Donis.CSharpBook{
}
}
Overriding Events
For completeness, the following is sample code for overriding an event. Events are discussed in Chapter 8,
"Delegates and Events."
New Modifier
The new modifier hides a member of the base class. New is the default modifier when a member is replicated
in a derived type. However, unless the new keyword is stated explicitly, a compiler warning is presented to
prevent the inadvertent hiding of a member of the base class. Explicit use of the new keyword prevents the
warning. A virtual member and overridden member are related, which is important to polymorphism. The
derived member and hidden member are, however, unrelated.
NET supports hide-by-signature and hide-by-name techniques. C# only supports hide-by-signature, where a
single member is hidden using the new modifier. Hide-by-name hides the entire interface of a member, which
may entail several functions. This option is available in Visual Basic .NET.
In the following code, ZClass is the base class and contains MethodA and MethodB members. Both are
virtual methods, and MethodB calls MethodA.
YClass is a derived class and inherits from ZClass. MethodA is overridden in the derived type, but MethodB
is not overridden. Therefore, two versions of MethodA exist: a base version and a derived version. When
YClass.MethodB is called, which version of MethodA is invoked in the method? Because MethodA is virtual,
the most derived method is called. YClass.MethodA is invoked. With a virtual method, the compiler prefers
the most derived method.
using System;
namespace Donis.CSharpBook{
The following code is almost identical to the previous code. However, ZClass.MethodA is not overridden in
the derived class. Instead, YClass hides the base class implementation of MethodA with the new modifier.
ZClass.MethodA and YClass.MethodA are now unrelated. Therefore, the compiler will not delegate to
YClass.MethodA for an implementation inherited from the parent. For this reason, YClass.MethodB calls
ZClass.MethodA. This prevents functions inherited from the base class in calling unrelated functions, which
could cause the fragile base class problem.
using System;
namespace Donis.CSharpBook{
class ZClass {
public virtual void MethodA() {
Console.WriteLine("ZClass.MethodA");
}
Interestingly, although the new modifier hides the base class member, you can still access the base class
implementation with the base keyword. This is demonstrated in the following code:
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Virtual methods are overridable in all descendants. The new modifier ends the propagation of this
characteristic. The new modifier changes virtual methods to nonvirtual. The method cannot be overridden in
further descendants. A function can be declared as both new and virtual. In that circumstance, the function
hides the base method and is overridable to descendants.
The following code demonstrates that a member with the new modifier cannot be overridden:
/* ERROR
Base fields and static members cannot be overridden in a derived class. However, both can be hidden with
the new modifier. Hiding field and static members can cause confusion. When you use this feature, I
recommend thorough supporting documentation in your code.
In the following code, ZClass has a static method and field. Their purpose is to count ZClass instances.
YClass inherits ZClass and hides the two static members of the base type. The new members count the
instances of the YClass. Therefore, there are simultaneous counts—the base class and derived class
counters. In Main, multiple instances of the ZClass and YClass are created. Both counters are then
displayed.
using System;
namespace Donis.CSharpBook{
}
*/
}
Abstract
Abstract classes are concepts. Object-oriented applications model the real world, which is replete with
concepts. For developers of a graphics program, a geometric shape is a concept. Have you even seen a
geometric shape? No. Rather, you have seen types of geometric shapes, such as triangles, ellipses,
rectangles, and lines. Geometric shape is a description of a category—a kind of shape. Conversely, a
rectangle is tangible: A television, a box, and this book are actual rectangles.
The Employee class is an abstract class. Why? In the personnel domain, employee is a categorization of
employees. Each employee is specifically an hourly, salary, or commissioned employee. How an employee
is paid is determined by the type of employee. Payroll calculations are conducted into the derived type, such
as HourlyEmployee. The Employee base type is a container of general information such as the employee
name. However, without pay specifics, the Employee class is incomplete. Most employees value getting
paid. For that reason, Employee is a concept and abstract.
The abstract keyword makes a class abstract. Abstract classes exist extensively for inheritance. You
cannot create an instance of an abstract class. Nonabstract classes are concrete classes. You can create
an instance of a concrete class. Static classes, value types, and interfaces do not support the abstract
modifier.
In the next example, Employee is the base class and is abstract. HourlyEmployee is the derived class and is
concrete.
Members can also be abstract. Methods, properties, indexers, and event members can be abstract.
However, static members cannot be abstract. In addition, classes with abstract members must also be
abstract. An abstract member has a signature but no function body. Virtual is implied with abstract
functions. Abstract members must be implemented in the derived type.
In the real world, a parent can ask a child to do something with or without instructions. The parent can
provide instruction to the child to complete the request. Alternatively, a parent might provide no directions.
The child must accomplish the task. However, the parent does not care about the details of the
implementation. An abstract function is an example of the latter. Abstract methods are a means of assuring
that derived types implement required methods. The derived type inherits no implementation (instructions)
from the base type. Derived types must override all abstract functions. If not, the derived type is incomplete
and in error.
In the following code, Employee mandates that derived types implement the CalculatePay method. Because
the class has an abstract method, the Employee class is also abstract:
Sealed
Sealed classes are the reverse of abstract classes. Abstract types must be inherited. You cannot create an
instance of an abstract type. Conversely, sealed types cannot be inherited and are concrete. Sealed classes
cannot be refined by a derived type. Sealed classes are terminating nodes in the class hierarchy.
The sealed modifier can also be applied to instance methods, properties, events, and indexers. It cannot be
applied to static members. Sealed members are allowed in sealed and nonsealed classes. A sealed
member must override a virtual or implied virtual member, such as an abstract member. However, a sealed
member itself cannot be overridden. It is sealed. The sealed modifier must be combined with the override
modifier. Although sealed members cannot be overridden, a sealed member in a base class can be hidden in
a derived type with the new modifier. As a bonus, the CLR can perform optimizations on sealed members.
The following code demonstrates both a sealed class and a sealed member. In this example, the
HourlyEmployee class cannot be further refined. In addition, the HourlyEmployee.Pay method cannot be
overridden.
During the instantiation of a derived type, the default or parameterless instance constructor of the based type
is called by default. If the base class does not have a default constructor, a compile error occurs unless the
derived class calls a constructor in the base class that has a parameter. In the following code, ZClass does
not have a default constructor and will not compile:
Constructors in a derived class can explicitly call constructors in the base class. This is particularly useful
for calling parameterized constructors in the base class. Base class constructors are called from the derived
class using a constructor initializer list. The initializer list can only be affixed to instance constructors in the
derived type. The derived constructor is responsible for initializing the derived type and possibly calling the
base class constructor.
The constructor initializer list follows the derived constructor name and colon. The base keyword refers to the
base class constructor. The parameterlist 2 determines which base class constructor is called. Parameters of
the derived constructor can be used in the parameter list of the base class constructor.
In the following example, the YClass constructor explicitly calls the one-argument constructor of the base
class:
Instances of a derived type are created inside out. The base element is created first, and then the derived
elements. In support of this model, the constructors are walked bottom-up. This facilitates passing
parameters from derived constructors to base constructors. After the parameters are passed up the class
hierarchy, the constructors are executed top-down, beginning with the constructor in the root class.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The following code confirms that constructor initializers are called bottom-up but the constructors are invoked
top-down starting with the constructor in the base class:
using System;
namespace Donis.CSharpBook{
This is the output from the code, which confirms the sequencing of base constructor initializers and the
actual invocation of constructors:
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
C:\>insideout
XClass constructor initializer
YClass constructor initializer
ZClass constructor
YClass constructor
XClass constructor
Objects are not fully created until the constructor has finished completely. Therefore, the this reference of the
derived type cannot be used as a parameter in the base class constructor initializer list:
Destructors are called in reverse order of constructors. Derived objects are destroyed outside-in, where the
most-derived component is destroyed first. Correspondingly, destructors are called bottom-up. Because
destructors are parameterless, there is no information to pass between them. In C#, base class destructors
are called automatically. Do not attempt to call the base class destructor explicitly.
The following code has three classes—each with a destructor—which form a class hierarchy. At the end of
the program, the destructors are called bottom-up, which confirms the sequencing of destructors.
using System;
namespace Donis.CSharpBook{
Console.WriteLine("YClass destructor");
}
}
C:\>destructors
XClass destructor
YClass destructor
ZClass destructor
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Interfaces
An interface is a contract and defines the requisite behavior of generalization of types. For example, vehicle
behavior includes ignition on, ignition off, turn left, turn right, accelerate, and decelerate. A car, truck, bus,
and motorcycle are included in the vehicle category. As such, they must encapsulate baseline behavior
representative of all vehicles. The vehicle interface defines that baseline. Specific vehicle types implement
that baseline behavior differently than others. A car accelerates differently from a motorcycle. A motorcycle
turns differently from a bus. An interface mandates a set of behaviors, but not the implementation. The
derived type is free to implement the interface in an appropriate manner. Interfaces must be inherited. You
cannot create an instance of an interface.
Any class or structure that inherits an interface commits to implementing the members of that interface. An
interface is an array of related functions that must be implemented in a derived type. Members of an interface
are implicitly public and abstract.
An interface is similar to an abstract class. Both types must be inherited. You cannot create an instance of
either. Abstract members require implementation in the derived type. Interface members require
implementation in a derived type. Although abstract and interface members are similar, there are several
differences:
An abstract class can contain some implementation. Interfaces have no implementation.
Abstract classes can inherit other classes and interfaces. Interfaces can only inherit other
interfaces.
Abstract classes can contain fields. Interfaces cannot have state.
Abstract classes have constructors and destructors. Interfaces have neither.
Interfaces can be inherited by structures. Abstract classes are not inheritable by structures.
Interfaces support multiple inheritance. Abstract classes support single inheritance.
When choosing between defining an interface versus an abstract class that has all abstract members, select
the interface. With an interface, you can still inherit other types. Further-more, interfaces are clearer. The
ZClass in the following code is simply not as clear as a comparable interface:
public interface IZ {
void MethodA (int a);
void MethodB (int a);
void MethodC (int a);
void MethodD (int a);
}
An interface can begin with an attribute. Use attributes to further describe an interface, such as the
ObsoleteAttribute. For accessibility, independent interfaces can be public or internal. Nested interfaces can
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
also be protected or private. Interfaces can be nested in classes and structs, but not in other interfaces.
Interfaces support the unsafe modifier. For nested interfaces, the new modifier is also applicable. Baselist is
a list of zero or more interfaces that this interface inherits. The interface body contains the members of the
interface, which consists of methods, properties, indexers, and events. As mentioned, interface members
are implicitly public and abstract.
Interfaces can inherit from other interfaces. The inherited members are added to the members of the current
interface. The inherited interface essentially extends the current interface. A type inheriting the interface
must implement the aggregate interface, which includes the members of the current interface and members
that interface inherited from other interfaces. If a member appears in both the current interface and an
inherited interface, there is no ambiguity. An identical member in a current interface hides the same member
in an inherited interface. However, a compiler warning is generated. Add the new modifier to the member in
the current interface to avoid the warning.
In the following code, IZ and IY are interfaces. The IY interface inherits IZ. MethodB is a member of both
interfaces. For that reason, IY.MethodB hides IZ.MethodB with the new modifier. Types implementing the IY
have to implement the MethodA, MethodB, and MethodC methods. These are members of both interfaces.
public interface IZ {
void MethodB();
void MethodC();
}
A type can inherit multiple interfaces. The type must implement the members of each interface that is
inherited. Those interfaces may have identical members. For example, assume that IZ and IY interfaces
contain a MethodA method. The single implementation in the derived type satisfies the requirements for both
IZ.MethodA and IY.MethodA. Therefore, the same method in multiple interfaces is not ambiguous. The
implementation in the type consolidates all requirements for the interface member.
Implementing Interfaces
Members of an interface are implicitly public. The implementation in the type must also be public.
In the following code, the Car class inherits the IVehicle interface. As required, the members of the IVehicle
interface are implemented in the Car class.
void TurnRight();
}
A class that inherits multiple interfaces probably falls into multiple categorizations. An amphibious vehicle is
a combination of a vehicle and a boat. In the following code, the Amphibious class inherits both the IVehicle
and IBoat interfaces. The members of both interfaces are implemented in the Amphibious class. As
discussed, duplicate interface members are implemented only once.
using System;
namespace Donis.CSharpBook {
You can associate the implementation of an interface member with a specific interface. This is called explicit
interface member implementation. Explicit interface member implementation binds an interface member to a
particular interface, which requires prefixing the member with the interface name. Members implemented in
this manner are not accessible through the derived type. For this reason, the accessibility modifier is omitted
from explicitly implemented interface member. These members are available only via an interface cast. The
modifiers abstract, virtual, override, or static are also not allowed.
In the following code, the ZClass inherits the IA interface. The IA.MethodA is an explicitly implemented
interface member, whereas IA.MethodB is implemented regularly. Because it is explicitly implemented,
MethodA is prefixed with the interface name. Because the implementation of MethodA is hidden, it cannot be
called from a ZClass instance type. However, you can cast the instance to the underlying interface, which is
interface IA. The method can then be called on that interface.
public interface IA {
void MethodA();
void MethodB();
}
void IA.MethodA() {
Console.WriteLine("IA.MethodA");
}
}
}
Explicit interface implementation is most helpful when duplicate members require separate implementations.
Members inherited from multiple interfaces might require separate implementation. Normally, they are
consolidated with a single implementation. Another possibility is duplicate members between the interface
and derived type. You can implement duplicate members separately with explicit interface implementation.
Explicit interface implementation provides separate implementation without ambiguity.
The following code is for the amphibious vehicle. Steering a boat is different from steering a car. Therefore,
TurnLeft and TurnRight are explicitly implemented for the IBoat interface. TurnLeft and TurnRight are also
implemented explicitly for the IVehicle interface. To assign defaults, Amphibious delegates to
IVehicle.TurnLeft and IVehicle.TurnRight from identical functions in the class.
using System;
namespace Donis.CSharpBook {
Amphibious marinevehicle=new
Amphibious();
marinevehicle.IgnitionOn();
marinevehicle.TurnLeft();
IBoat boatmaneuvers=marinevehicle;
boatmaneuvers.TurnLeft();
marinevehicle.IgnitionOff();
}
}
void IVehicle.TurnLeft() {
Console.WriteLine("Turn vehicle left.");
}
void IVehicle.TurnRight() {
Console.WriteLine("Turn vehicle right.");
}
void IBoat.TurnLeft() {
Console.WriteLine("Turn boat left.");
}
void IBoat.TurnRight() {
Console.WriteLine("Turn boat right.");
}
Another reason to use explicit interface implementation is to hide some portion of a class or struct. The
explicitly implemented members are hidden from clients of the class. You must cast the type to the interface
to access the hidden interface members.
In the following code, the Car class inherits and implements the IVehicle and IEngine interface. The two
interfaces have no overlapping members. A driver doesn't usually interface with the engine directly. That is
usually left to the mechanic. Therefore, the IEngine interface is hidden in the Car class.
using System;
namespace Donis.CSharpBook{
auto.IgnitionOn();
auto.IgnitionOff();
// Access engine.
IEngine e=auto.AccessEngine();
// Inspect engine.
}
}
AccessEngine().Ignition();
}
void IEngine.Alternator(){
Console.WriteLine("Alternator.");
}
void IEngine.Ignition() {
Console.WriteLine("Ignition");
}
void IEngine.Transmission(){
Console.WriteLine("Transmission");
}
}
}
Reimplementation of Interfaces
A child class can reimplement an interface that was implemented in a base class. In the following code,
ZClass inherits interface IZ. YClass inherits ZClass and reimplements the IZ interface. This interface
reimplementation in the derived class hides the implementation of the base class. Unless the new modifier is
specified, warnings will occur in the derived class.
public interface IZ {
void MethodA();
void MethodB();
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Polymorphism
Polymorphism is one of the major goals of inheritance. With polymorphism, related types can be treated in a
generic manner. In a graphics program, instead of calling separate algorithms or operations for rectangle,
ellipse, and triangle shapes, you leverage their commonality and call a generic algorithm or operation. This is
more extensible and maintainable than handling each type of geometric shape differently.
using System;
namespace Donis.CSharpBook{
shape1.Draw();
shape2.Draw();
shape3.Draw();
}
}
The two primary advantages to polymorphism are late binding and extensibility:
Late binding is when a specific function call is decided at run time. Early binding sets the function
call at compile time, which is not always feasible. For example, in a competent graphics program,
users decide the objects to draw at run time. The previous program does not accommodate this.
The same objects are drawn every time.
Extensible code adapts easily to future changes. In the preceding code, the Draw method is
called separately for each kind of geometric type. A rectangle is drawn differently from an ellipse.
As the program evolves into the future, more geometric shapes are likely to be added. It could
eventually support 50 types of shapes. An extensible process for drawing a variety of shapes is
needed. Do you want to call Draw 50 different times—once for each type of shape?
Polymorphism starts with a base class reference to an instance of a derived type. Assign an instance of a
derived type to a base class reference. When a virtual function is called on the base class reference, the
compiler automatically delegates to the overridden method in the derived instance. Virtual and overridden
methods have a special relationship. Virtual methods delegate to overridden methods when possible.
BaseReference.VirtualMethod delegates to different implementations depending on the derived object
assigned to the base class reference.
In the following code, Geoshape is the base class reference, and Draw is the virtual method. At run time, two
derived instances are created and assigned to the base class reference. Geoshape references shape[0] and
shape[1] are assigned an Ellipse and Rectangle object, respectively. The Draw method is invoked on the
Geoshape base class reference. At that time, the compiler delegates to the Draw methods overridden in the
derived types (base.virtualfunc-> derived. overriddenfunc). Therefore, a virtual function has different behavior
depending on the kind of object assigned to the reference.
shapes[0].Draw(); // Geoshape.Draw()->Ellipse.Draw()
shapes[1].Draw(); // Geoshape.Draw()->Rectangle.Draw()
In the following code, the DrawShape method draws a geometric shape. Can you predict which type.Draw is
called? The method has one parameter, which is the Geoshape base class. This is a reference to a base
class. When a Rectangle instance is passed as a parameter, it is a derived instance to a base reference.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
This is one of the requirements of polymorphism. The base reference points to Rectangle instance, which
means Rectangle.Draw is called. When the Triangle instance is passed as a parameter, Triangle.Draw is
called. Therefore, one statement, shape.Draw, calls different implementations for related types. The
shape.Draw statement is extensible.
The following code demonstrates polymorphism. There are related types: Triangle, Ellipse, and Rectangle.
The common method is Draw. Each Draw method is implemented differently in the derived class. At the
command line, clients indicate which shapes to draw. This command draws a rectangle, ellipse, triangle,
and another rectangle. Therefore, the decision about what to draw is deferred to run time:
shapes r e t r
The program defines a collection of base references. At compile time, we know that clients will draw shapes
but we don't know the specific shape. Therefore, a collection of Geoshape base class references is defined,
which is a generalization of all shape types. Plus, polymorphism begins with base references to derived
instances. In the first loop, derived instances are created and added to the collection of base class
references. In the second loop, the array of Geoshape base class references is iterated. Rectangle.Draw,
Ellipse.Draw, or Triangle.Draw is called based on the type of derived object assigned to the base reference.
Therefore, each call to the Geoshape.Draw could initiate a different operation. This is the polymorphic
behavior.
using System;
using System.Collections.Generic;
namespace Donis.CSharpBook{
}
else if(shape.ToUpper()=="T") {
obj=new Triangle();
}
else {
continue;
}
shapes.Add(obj);
}
Geoshape is an abstraction and not concrete. You cannot draw a generic geometric shape. For that reason,
in the Geoshape class, the Draw method should also be abstract. There is an added benefit of an abstract
Draw in the base class. Derived types are forced to implement a specific Draw method. They are not
inheriting the Draw method of the base class, which does nothing:
Interface Polymorphism
Interface polymorphism employs base interfaces instead of base classes. The result is the same. Because it
has some implementation, the Geoshape class cannot be entirely replaced with an interface. However, the
Draw abstract method can be lifted from the class and placed in an IDraw interface:
using System;
using System.Collections.Generic;
namespace Donis.CSharpBook{
The new modifier interrupts normal polymorphism and can cause unexpected results. Unlike an overridden
member, the compiler will not delegate from a virtual member to a member that has the new modifier. They
are considered unrelated.
The following code demonstrates the potential problem. ZClass.MethodA method is virtual and polymorphic.
YClass.MethodA overrides ZClass.MethodA. XClass derives from YClass. In the XClass, the new modifier
defines an unrelated MethodA. XClass.MethodA is also marked avirtual. WClass.MethodA overrides
XClass.MethodA. ZClass.MethodA and YClass.MethodA define a group of related methods. XClass.MethodA
and WClass.MethodA define a second group. In Main, several related objects are instantiated. The derived
instances are cached in an array of ZClass references, which is an array of base class references. In the
loop, MethodA is called through the derived objects.
using System;
namespace Donis.CSharpBook{
The following code shows the result of running the application. Notice that XClass.MethodA and
WClass.MethodA are not called. The new modifier on the XClass.MethodA method interrupts the
polymorphic hierarchy.
C:\ >newapp
ZClass.MethodA
YClass.MethodA
YClass.MethodA
YClass.MethodA
YClass.MethodA
ZClass.MethodA
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Casting
Casting a derived object to a base type is always safe. As shown in the previous section, this is the
common cast for polymorphism and is legitimate in all circumstances. Derived types extend base types.
Because derived types encompass everything about the base type, a derived-to-base-type cast is
guaranteed to be safe. You can even cast a derived instance to an abstract base class. This was
demonstrated earlier. Casting from a derived object to a base reference provides a base view of the instance.
You are limited to members of the base type. The refinements of the derived type are not visible through a
base reference to a derived instance, although the derived class implementation of an abstract base class
method will be visible. After the cast, the base reference is an alias to the original derived object.
Casting a value type to a base interface has different semantics. The result of casting a reference type to an
interface is an alias. However, when casting a value type to an interface, a separate entity is created.
Interfaces are reference types. Boxing always occurs when casting a value type to a reference type. This
includes casting a value type to an interface. Boxing creates a copy of the value type, which is placed on the
managed heap. The original and copy are unrelated. Changes to one will not affect the other.
In the following code, the ZStruct structure implements the IAdd interface. Structures are value types. The
IAdd.Increment method increments a counter. In Main, a ZStruct local variable is cast to an IAdd interface,
which is legitimate. Boxing happens, and a copy of the ZStruct is placed on the managed heap. Changes to
one will not affect the other. When the counts are displayed, the variables have different counts that confirm
their separate identities.
using System;
namespace Donis.CSharpBook{
}
}
}
}
A base class or interface can be used as a function parameter or return value. In this circumstance, you can
substitute any related type for the function parameter or return value. There are three major reasons to use a
base class or interface as a parameter or return value:
It generalizes a function call or return. The function parameter or return can be used with different
types.
A specific parameter or return type may not be known at compile time. A base reference supports
late binding, where the type is selected at run time.
Returning a base class or interface restricts access to an object. This is especially useful for
class libraries that want to hide internal implementation.
The following code is an example of a class library—albeit a rather small library. The library contains a single
class, which is the ZClass. It is marked as internal and visible solely within the class library. IExposed
defines the public face of the ZClass type and clients of the library. LibraryClass.Method returns a ZClass
instance through the IExposed public interface. This prevents clients from accessing the other methods of
the ZClass. Those methods are internal and reserved for use in the class library.
using System;
namespace Donis.CSharpBook{
}
}
As shown several times in this chapter, casting a derived object to a base reference is safe. However, you
cannot implicitly cast a base object to a derived reference. The derived type might have members not defined
in the base type. Therefore, the derived reference might access members not available to the base object.
For this reason, the cast is unsafe. An explicit cast can force the compiler to accept the unsafe cast of a
base object to a derived reference. Because the cast remains unsafe, an exception is raised at run time at
the cast. You have simply deferred the problem from compile time to run time.
using System;
namespace Donis.CSharpBook{
obj.MethodA();
obj.MethodB();
}
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Inheritance Operators
The is and as operators are convenient tools for testing the pedigree of an instance. These operators confirm
the presence of a class or interface somewhere in the hierarchy of an instance. This confirmation is useful for
promoting type-safe function calls. You can also develop algorithms that vary based on class types, for
example, to display extra menu items for managers.
The is operator returns true if the expression type and target type are related. If the expression type and
target type are unrelated, false is returned.
The following code displays a menu. If employee is a manager, additional menu items are displayed. The is
operator confirms that the employee is a manager.
using System;
namespace Donis.CSharpBook{
get {
return propDepartment;
}
}
}
}
The as operator evaluates an expression in the context of the target type. If the expression type and target
type are related, an instance of the target type is returned. If the expression type and target type are
unrelated, null is returned.
Here is the previous code rewritten with the as operator (this is a partial listing):
using System;
namespace Donis.CSharpBook{
Console.WriteLine("Task 1");
Console.WriteLine("Task 2");
IManager mgr=person as IManager;
if(mgr != null) {
Console.WriteLine("\n[{0} Menu]\n",
mgr.Department);
Console.WriteLine("Task 3");
}
}
}
// Partial listing
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Attribute Inheritance
By default, custom attributes are not inheritable. The AttributeUsage attribute can make an attribute
inheritable. For the AttributeUsage attribute, set the Inherited option to true to enable inheritance.
Inheriting classes with attributes sometimes can cause interesting behavior. In the following code, ZClass is
the base class, and YClass is the derived class. Both classes are adorned with the PrincipalPermission
attribute, which identifies the users or roles that can call functions in a particular class. In the example, the
ZClass function can be called by managers; YClass function calls are limited to accountants. The YClass
inherits MethodA from the ZClass. Who can call YClass.MethodA? Because MethodA is not overridden in
the derived class, YClass is relying on the implementation of MethodA in the base class, which includes any
applicable attributes. Therefore, only managers can call the YClass.MethodA, whereas YClass.MethodB is
available to accountants and not managers, as demonstrated in the following code:
using System;
using System.Security;
using System.Security.Permissions;
using System.Security.Principal;
using System.Threading;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
GenericIdentity g=new GenericIdentity("Person1");
GenericPrincipal p=new GenericPrincipal(g,
new string [] {"Manager"});
Thread.CurrentPrincipal=p;
ZClass.MethodA();
YClass.MethodA();
// YClass.MethodB(); // Security exception.
}
}
[PrincipalPermission(SecurityAction.Demand,
Role="Manager")]
public class ZClass {
[PrincipalPermission(SecurityAction.Demand,
Role="Accountant")]
public class YClass: ZClass {
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Visual Studio .NET 2005 includes several enhancements. Improved editing capabilities have been added,
such as Code Expansion and Auto IntelliSense. Code snippets have also been improved. Refactoring is an
exciting new feature and the friend of every developer. New build alternatives such as MSBuild are available
for professional developers. Microsoft has also added a pantheon of new project and template types, such as
Smart Devices, to Visual Studio. These new Visual Studio 2005 options provide developers with additional
choices for managed development.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Chapter 6: Generics
Chapter 7: Iterators
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Visual Studio 2005 has additional build and deployment options. The Microsoft Build Engine (MSBuild) is a
new build environment, which is integrated into Visual Studio 2005, but also available separately. MSBuild
projects are XML files that choreograph the build process of an application. ClickOnce Deployment deploys a
desktop application from a centralized server and has several benefits when compared with traditional
Windows Installer technology, such as self-updating. It is the perfect merger of desktop and Web
technology, which provides hands-free distribution of desktop applications. Finally, Windows Installer
technology remains available and is improved with the introduction of Microsoft Installer 2.0.
Visual Studio 2005 is more online-enabled than before. Developers can easily participate in the developer
community, submit questions to Microsoft, and visit a variety of online resources. In addition, Help is now
online-enabled.
With the exception of debugging, this chapter demonstrates many of the new features of Visual Studio 2005.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The Visual Studio 2005 IDE has many rapid application development (RAD) tools. The Visual Studio Code
Editor, Microsoft IntelliSense, Solution Explorer, Class View, Object Browser, and the class diagram are
essential components of the user interface and contribute to improved developer productivity, accuracy, and
efficiency. Many of the tools have been updated, such as IntelliSense and the Object Browser. Other tools,
including the class diagram, are new.
Start Page
Start Page is the gateway screen into Visual Studio. For many developers, the Start Page is the first window
presented in Visual Studio. The Start Page, shown in Figure 4-1, is completely redesigned in Visual Studio
2005. Visual Studio normally starts with the Start Page. If the page is not visible, open the Start Page from
the View Menu. Select the Other Windows submenu and then the Start Page menu command.
Recent Projects This pane lists the recently opened projects. Select an item in the project list to
open that project. The Open and Create buttons at the bottom of this pane open or create a new
project.
MSDN: Visual C# This pane contains links to recent news on Visual Studio and C#. Each topic
is previewed. Click the related link to view the complete article.
Getting Started This pane contains helpful links for new Visual Studio developers.
Visual Studio Headlines This pane allows developers to submit feedback directly to Microsoft.
Community Integration
The Visual Studio community is an important resource for both novice and experienced developers of
managed and unmanaged code. The availability of the aggregate knowledge of developers is of great benefit
to everyone. Visual Studio 2005 has a menu dedicated to the developer community. The Community menu
provides convenient access to the developer community, which makes participation easier. The Community
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ask a Question This command redirects developers to the Microsoft Community Forum, where
questions are posted to Microsoft. You can also view previous posts and responses.
Check Question Status This command redirects developers to the Microsoft Community Forum,
where the status of previous posts can be checked.
Send Feedback This command redirects developers to the MSDN Product Feedback Center.
Developer Center This command redirects developers to the Microsoft Visual C# Development
Center, where C#-related topics are discussed.
Codezone Community This command redirects users to the Microsoft Codezone page, which
features a Web site each month that makes substantial technological contributions to the
developer community. It also lists number of online communities.
Partner Products Catalog This command redirects users to the Microsoft Visual Studio Industry
Partner program. You can search for a particular Microsoft partner, product, or solution from this
page.
Community Search This command searches online for downloadable project templates, code
snippets, macros, and other helpful resources.
Creating Projects
Developing an application in Visual Studio typically starts with the creation of a project. Projects are the
basic organization component of Visual Studio, in which files, resources, reference, and other constituents of
an application are grouped. Related projects are sometimes grouped into solutions, which can contain
projects of different types and languages. For example, during the development cycle, it is common to group
applications and related libraries in the same solution. The option to add a new project to an open solution or
creating the project in a new solution has changed. Figure 4-2 shows the New Project dialog box. Toward the
bottom of the dialog box is a drop-down list box containing the choices Create New Solution and Add To
Solution. This option is not available unless a solution is already open. If desired, select the Create Directory
For Solution option box to create a separate directory for the solution.
Solution Explorer
You can configure both solutions and projects from the Solution Explorer window. If not visible, display the
Solution Explorer window from the View menu. Solution Explorer is shown in Figure 4-3. In this example, the
solution is Airline Seats, which has two projects: Airline Seats and Person. Developers can change the
properties of a solution or any project. From Solution Explorer, open the shortcut menu on the solution or
project icon and then select the Properties menu item. In the Project properties, configure the application
settings, configure the build environment, configure debug settings, resources, define default application
settings, view or add references to the project, publish the ClickOnce manifests, or sign assemblies. The
user interface of Project properties has changed and been renamed as the Project Designer window, shown
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
in Figure 4-4.
In the Solution Explorer window, the AssemblyInfo.cs file has been moved to the Properties folder. The
AssemblyInfo.cs file contains assembly attributes that define assembly level metadata, such as the version
number and culture.
The location of Solution Explorer and some other Visual Studio windows default to a docking window. In
Visual Studio 2005, these windows can be tabbed or set to auto hide, which is an improvement. Figure 4-5
shows Solution Explorer in a tabbed window. Display the shortcut menu on the window header to change the
alignment to Tabbed Document.
Project Types
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
In Visual Studio 2005, a wide assortment of projects are available. Common projects, such as a Windows
Application and Class Library, are still available. However, new project types have also been introduced. Table
4-1 lists the standard project types available.
Table 4-1:
Project Types
Te De
mp scr
lat ipti
e on
Na
me
Wi Cre
nd ate
ow a
s Wi
Ap nd
plic ow
ati s
on For
ms
de
skt
op
ap
plic
ati
on
usi
ng
thi
s
te
mp
lat
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-1:
Project Types
Te De
mp scr
lat ipti
e on
Na
me
Cla Cre
ss ate
Lib a
rar ma
y na
ge
d
dy
na
mi
c-li
nk
libr
ary
(DL
L)
usi
ng
thi
s
te
mp
lat
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-1:
Project Types
Te De
mp scr
lat ipti
e on
Na
me
Wi Cre
nd ate
ow cu
s sto
Co m
ntr co
ol ntr
Lib ols
rar for
y Wi
nd
ow
s
For
ms
ap
plic
ati
on
s
usi
ng
thi
s
te
mp
lat
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-1:
Project Types
Te De
mp scr
lat ipti
e on
Na
me
We Cre
b ate
Co cu
ntr sto
ol m
Lib We
rar b
y ser
ver
co
ntr
ols
for
AS
P.
NE
T
ap
plic
ati
on
s
usi
ng
thi
s
te
mp
lat
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-1:
Project Types
Te De
mp scr
lat ipti
e on
Na
me
Co Cre
ns ate
ole a
Ap co
plic ns
ati ole
on ap
plic
ati
on
usi
ng
thi
s
te
mp
lat
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-1:
Project Types
Te De
mp scr
lat ipti
e on
Na
me
Wi Cre
nd ate
ow a
s Wi
Ser nd
vic ow
e s
Ser
vic
e
ap
plic
ati
on,
whi
ch
is
a
da
em
on
pro
ce
ss
tha
t
ca
n
run
acr
os
s
log
on
se
ssi
on
s,
usi
ng
thi
s
te
mp
lat
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-1:
Project Types
Te De
mp scr
lat ipti
e on
Na
me
Em Cre
pty ate
Pro a
jec cu
t sto
m
pro
jec
t,
whi
ch
is
dev
oid
of
the
nor
ma
l
file
s,
wit
h
thi
s
te
mp
lat
e.
Th
e
dev
elo
per
is
res
po
nsi
ble
for
buil
din
g
the
pro
jec
t
fro
m
es
se
nti
ally
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-1:
Project Types
Te De
mp scr
lat ipti
e on
Na
me
Cry Cre
sta ate
l a
Re Wi
por nd
ts ow
Ap s
plic For
ati ms
on ap
plic
ati
on
tha
t
co
nta
ins
a
Cry
sta
l
Re
por
t
usi
ng
thi
s
te
mp
lat
e.
Web applications are no longer included with the standard templates detailed in Table 4-1. Web applications
are created from the File menu. Select the New submenu and the Web Site menu command. The New Web
Site dialog box appears, displaying Web application templates. Table 4-2 lists the available Web templates.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-2:
Web Project
Types
Open table as
spreadsheet
Te De
mp scr
lat ipti
e on
AS Cre
P. ate
NE an
T AS
We P.
b NE
Sit T
e We
b
ap
plic
ati
on
usi
ng
thi
s
te
mp
lat
e.
AS Cre
P. ate
NE an
T AS
We P.
b NE
Ser T
vic We
e b
Ser
vic
e
usi
ng
thi
s
te
mp
lat
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-2:
Web Project
Types
Open table as
spreadsheet
Te De
mp scr
lat ipti
e on
Per Cre
so ate
nal an
We AS
b P.
Sit NE
e T
Sta 2.0
rter sta
Kit rter
We
b
sit
e,
incl
udi
ng
an
initi
al
ho
me
pa
ge,
usi
ng
thi
s
te
mp
lat
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-2:
Web Project
Types
Open table as
spreadsheet
Te De
mp scr
lat ipti
e on
Em Cre
pty ate
We a
b cu
Sit sto
e m
We
b
pro
jec
t,
whi
ch
is
dev
oid
of
the
nor
ma
l
file
s,
usi
ng
thi
s
te
mp
lat
e.
Th
e
dev
elo
per
is
res
po
nsi
ble
for
buil
din
g
the
We
b
sit
e
fro
m
es
se
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-2:
Web Project
Types
Open table as
spreadsheet
Te De
mp scr
lat ipti
e on
AS Cre
P. ate
NE a
T ne
Cry w
sta We
l b
Re sit
por e
ts tha
We t
b incl
Sit ud
e es
a
sa
mp
le
Cry
sta
l
Re
por
t
usi
ng
thi
s
te
mp
lat
e.
Visual Studio 2005 also includes a variety of special-purpose project templates. Some of these templates
are detailed in Table 4-3.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-3:
Special-Purp
ose Project
Templates
Te De
mp scr
lat ipti
e on
Sm Thi
art s
De pro
vic jec
e t
Pro typ
jec e
t incl
Ty ud
pe es
the
Po
ck
et
PC
20
03,
Sm
art
ph
on
e
20
03,
an
d
Wi
nd
ow
s
CE
5.0
cat
eg
ory
of
te
mp
lat
es,
whi
ch
tar
get
mo
bile
an
d
em
be
dd
ed
dev
ice
s.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-3:
Special-Purp
ose Project
Templates
Te De
mp scr
lat ipti
e on
SQ Cre
L ate
Ser a
ver dat
Pro ab
jec as
t e
pro
jec
t
tar
get
ing
a
Mi
cro
sof
t
SQ
L
Ser
ver
20
05
dat
ab
as
e
usi
ng
thi
s
te
mp
lat
e.
In
thi
s
pro
jec
t,
dev
elo
per
s
ca
n
cre
ate
a
vari
ety
of
cla
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-3:
Special-Purp
ose Project
Templates
Te De
mp scr
lat ipti
e on
Sta Thi
rter s
Kit pro
Pro jec
jec t
t typ
Ty e
pe incl
s ud
es
the
Scr
ee
n
Sa
ver
Sta
rter
Kit
an
d
Mo
vie
Col
lec
tio
n
Sta
rter
Kit
te
mp
lat
es.
Th
e
Scr
ee
n
Sa
ver
Sta
rter
Kit
is
a
sa
mp
le
scr
ee
n
sav
er
tha
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-3:
Special-Purp
ose Project
Templates
Te De
mp scr
lat ipti
e on
Set Thi
up s
An typ
d e
De incl
vel ud
op es
me the
nt Set
Pro up,
jec We
t b
Ty Set
pe up,
Me
rge
Mo
dul
e,
Set
up
Wi
zar
d,
CA
B,
an
d
Sm
art
De
vic
e
CA
B
pro
jec
t
te
mp
lat
es.
Th
es
e
are
te
mp
lat
es
for
cre
ati
ng
set
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-3:
Special-Purp
ose Project
Templates
Te De
mp scr
lat ipti
e on
Ext Ext
en en
sibi sibi
lity lity
te
mp
lat
es
incl
ud
e
the
Vis
ual
Stu
dio
Ad
d-I
n
an
d
Sh
are
d
Ad
d-I
n
te
mp
lat
es.
Th
es
e
are
te
mp
lat
es
for
cre
ati
ng
ad
d-i
ns,
whi
ch
are
ma
na
ge
d
DL
Ls
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Adding References
Developers add application references to projects to access foreign types contained in those applications.
The Add Reference dialog box in Visual Studio 2005 has some new windows. To add a reference from the
Project menu, choose the Add Reference menu command. Add Web Reference adds a reference to a Web
service application. References can also be added from Solution Explorer. Open a shortcut menu on the
Reference folder. From the menu, choose the Add Reference or Add Web Reference menu command. The
Add Reference command now has five windows. References can be added from any of the windows.
The .NET window lists system libraries of the Common Language Runtime (CLR) and .NET
Framework Class Library.
The COM window lists registered COM servers, which contain unmanaged code.
The Projects window lists references to other projects in the current solution.
The Browse Window allows developers to browse for references.
The Recent window lists recent references that have been added to this solution.
Data Menu
In Visual Studio 2005, the Data menu has been added to the menu system. From this menu, developers can
add a new data source to the project or show available data sources. When adding new data sources, you
are presented with the Data Source Configuration Wizard, shown in Figure 4-6.
The Data Source Configuration Wizard works with three types of data sources:
A Database data source is a connection to a database, which returns a dataset to the application.
A dataset is a disconnected client-side representation of data at the data source.
A Web Service data source retrieves data from a Web service. This option adds a Web reference
to the specified Web service application.
An Object data source retrieves data from a managed object.
Visual Studio offers an array of windows for a variety of purposes. The potential windows include Code Editor
windows, a variety of toolboxes, Server Explorer, Solution Explorer, Property windows, and much more. Most
of these windows are movable, but can also be docked. In Visual Studio, it has been easy to misplace
windows or muddle the interface while attempting to dock a window. Visual Studio 2005 provides visual clues
to aid in moving and docking windows, including a guide diamond with docking arrows to help dock windows
correctly.
Switching between project files and windows is also improved. Visual Studio 2005 has the IDE Navigator to
help toggle between open files and windows. Pressing Ctrl+Tab opens the IDE Navigator. Once there, you
can navigate between open files and windows. This IDE Navigator dialog box is shown in Figure 4-7. When
the window is open, use Ctrl+arrow keys or Ctrl+Tab to navigate the items in the window.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
AutoRecover
AutoRecover is another feature that is added to Visual Studio 2005. This option periodically saves a project
and prevents the loss of data when Visual Studio is exited abnormally. Figure 4-8 shows the AutoRecover
window of the Options dialog box. Open this window from the Tools menu and Options menu command.
AutoRecover is found in the Environment settings. In the AutoRecover window, set the frequency of auto
saves and the length of time backups are retained.
When re-entering Visual Studio after an abnormal termination, the Microsoft Visual Studio Recovered Files
dialog box automatically displays. (See Figure 4-9.) In the dialog box, confirm the files to recover.
Class Hierarchies
Visual Studio 2005 offers several visual tools that depict class hierarchies. These tools also depict individual
types, such as classes, structures, and interfaces. This includes showing relationships between types,
including inheritance. The Class View and Object Browser have been updated with new features. The class
diagram is new and adds a capable object-modeling tool to Visual Studio 2005.
The Class View window provides a visual representation of the types included in projects of the current
solution. Expand the project and then expand class nodes to display the class hierarchy. Each item in the
class hierarchy has a shortcut menu with helpful commands. For example, the shortcut menu can display
classes in a class diagram. The Class View window can now show classes in referenced applications. Open
the Project References folder to find references and view their classes. Figure 4-10 shows the Class View
window, including the Project Reference folder. If the Class View window is closed, open it from the View
menu.
The Class View window has two panes: The top pane contains the hierarchy, whereas the bottom pane
displays the details of whatever item is selected in the class hierarchy. Across the top of the class view
window are four icons: New Folder, Forward, Back, and Class View Settings. Beneath the icons is the
search combo box, which is used to search the hierarchy for a symbol. This is especially helpful for
extensive class hierarchies. Type the search text and press Enter to initiate a search. All symbols—such as
classes, methods, fields, and properties, which contain some part of the search text—are returned in the
Class View window.
Object Browser
The Object Browser is an alternative to the Class View window. The Object Browser displays the class
hierarchy of the current project, referenced applications, and system libraries. If the Object Browser is not
visible, open it from the View menu. Figure 4-11 shows the Object Browser window.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The Object Browser window contains three panes. The left pane contains the class hierarchy. The two right
panes list the members and a description of the selected type, respectively. The Object Browser toolbar has
the Browser Scope, Edit Custom Component Set, Back, Forward, Add Reference, and Object Browser
Settings buttons. The Browser Scope button filters the class hierarchy. You can display classes from the
current solution, custom component set, .NET Framework, or all classes. Browse a class in a foreign
assembly using the Edit Custom Component Set button. The Add Reference button adds a reference to the
project for the application of the selected type. Above the class hierarchy is a combo box for searching. Use
the search box to find specific types in the class hierarchy.
Class Diagram
Visual Studio 2005 introduces the class diagram. Class diagrams are created by the Class Designer, which
is an internal component of Visual Studio 2005. Developers can model object-oriented applications using
class diagrams. This tool will assuredly continue to evolve in the future and is destined to be a developer
favorite. For me, it has already become one of the most frequently used tools in Visual Studio. Class
diagrams present a visual representation of a type and type hierarchy. It's important to note that class
diagrams are not static. Developers can add new types, create new relationships, insert members, delete
members, and much more. The class diagram is synchronized with the source code of the application.
Changes to types modeled in the class diagram are immediately reflected in code. Conversely, changes in
the source code immediately appear in a relevant class diagram. Therefore, the application and diagram
consistently remain in sync. Updated design documents are one of the seminal benefits of the class
diagram. Where do design documents vanish to after the project implementation has begun? This is a
common problem that plagues developers of object-oriented applications. For many applications, original
design documents are not available or have not been updated as the software evolved. Often, design
documents quickly become stale and sometimes vanish shortly after the implementation phase. Class
diagrams help update these important documents, including changes to the original design. Conversely,
class diagrams can be saved as images preserving a snapshot of the design. With the class design, the
design and implementation phase is truly iterative, which translates into better-developed applications. It also
makes maintaining applications exponentially easier.
Class diagrams provide a high-level perspective of an application, which is beneficial throughout the life cycle
of the application. This is particularly useful with complex systems that entail hundreds of classes and
dozens of relationships. Reviewing code in this circumstance is a tedious, time-consuming process. It also
does not transfer the clarity of understanding that only a synoptic view of an application affords. Class
diagrams present a general overview, in which introspection is available as needed. This information is
particularly invaluable to programmers maintaining an application. The class diagram represents a new
starting point for anyone who has to maintain a product with Visual Studio.
The entire class hierarchy can be presented in the class diagram. However, developers control how much
information is presented in the class diagram. You can view one class, a dozen types, or everything in the
class diagram. It depends on developer discretion. In addition, multiple class diagrams can be added to
group-related types or simply to reduce the amount of information presented in any particular diagram. In the
diagram, you can view all types: classes, structures, and interfaces. Relationships between types, such as
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
In Visual Studio 2005, there are several ways to create a class diagram. One way is to select the Add New
Item command on the Project menu. From the Add New Item dialog box, choose Class Diagram. In the edit
box, name the class diagram. (Class diagram files are automatically given the .cd file extension.) You can
also open a new class diagram from the Solution Explorer or Class View window. In Solution Explorer, open
a shortcut menu for the project name or source file and then choose View Class Diagram. This creates a
new class diagram that contains all the classes found in the project or source file. In Class View, open a
shortcut menu for the project name or class and choose View Class Diagram.
Class diagrams have a surface. There are several ways to add existing or new types to this surface, as listed
here. Types added to the class diagram surface are represented by shapes.
Drag a type from the Class View window or the Object Browser to the class diagram surface.
Drag a file from Solution Explorer to the class diagram surface.
Add a new type from the Class Designer toolbox.
Figure 4-12 shows a class diagram with a single class on the surface. The single class is contained in a
shape. Shapes have a header and a details pane. Click once on the class label in the shape header to
change the name of the class. Double-click the class label to view the related source code. There is also an
expand button or collapse button. The expand button is the double-down arrow and shows the details of the
type. The collapse button is the double-up arrow and hides the details of the type. The expand and collapse
buttons toggle, depending on whether the details are expanded or collapsed.
Types can be removed from a class diagram. Select the shape for the type and open the shortcut menu from
the class header. Choose Remove From Diagram. The type is removed from the diagram, but remains in the
program. The Delete Code option is not the same. This option not only removes the class from the class
diagram but also deletes it from the project. An easier method of removing a class from the diagram is
simply to select the type and then press Delete.
The class detail shows the members of the class. Members are grouped by type, where fields, methods,
properties, and other member types are grouped. Each group can be collapsed or expanded. From the
shortcut menu, members can be refactored, deleted, copied, pasted, and more. Members can also be
viewed and maintained in the Class Details window, as shown in Figure 4-13. If the Class Details window is
not visible, display it from the shortcut menu of any shape in the class diagram.
In the Class Details window, you can view, change, or add new members to the class. You can change the
name, type, or accessibility of a type. The Hide option hides or displays a member in the class diagram. The
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
shortcut menu of the class header has the Show All Members command, which shows all members,
including any hidden members.
The Class Details toolbox has four buttons. The New Member button adds a Method, Property, Field, Event,
Constructor, Destructor or constant to a class. The Navigate To Methods button selects that method
category in the Class Details window. This is helpful for large classes with dozens of members. The final
three buttons navigate to properties, fields, and events, respectively.
After the buttons for adding new types, the inheritance and association buttons appear next on the toolbox.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The inheritance button creates an inheritance line, which links a base class and a derived class. The
association button creates an association line that defines the relationship between an embedded class and
the owning class. The comment button is the last item on the toolbox and adds comments to the class
diagram.
Inheritance
Inheritance is visualized in the class diagram. Inheritance lines depict the base-to-derived-type relationship.
Implicit inheritance of System.Object and System.ValueType are not shown in the class diagram. Figure 4-16
shows class inheritance in the class diagram. In the figure, XClass1 inherits the ZClass class. The
inheritance line is the arrow that starts at the base class and ends at the derived class. Delete the
inheritance line to remove the inheritance relationship. Alternatively, you can remove the inheritance line from
the shortcut menu. Open a shortcut menu on the inheritance line and then select Delete Code.
You can define new base and derived classes in the class diagram. Select the inheritance line in the
toolbox. Drag the inheritance line from the base class to the derived class. This assumes that both the base
and derived classes are already on the class diagram. If the base class is not on the class diagram, drag the
class from the Class View window onto the derived class shape in the class diagram. This both creates the
inheritance relationship and adds the derived class to the class diagram.
Interface inheritance and class inheritance are similarly shown in the class diagram. Add interface
inheritance to a type using the inheritance line. To fully implement the interface, the class diagram adds
stubs for each member of the interface in the derived type. In the Code Editor, developers can replace the
stubs with the appropriate implementation. Interface inheritance is not depicted as an inheritance line.
Instead, interface inheritance is displayed as a lollipop atop the derived type. This is displayed in Figure 4-17,
in which the ZClass class inherits the IA interface. You can change how the interface is implemented using
the shortcut menu of the interface label. This includes whether to implement the interface implicitly or
explicitly.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Relationship lines, which are the inheritance and association lines, can be rerouted. Simply click the line
and drag the mouse to reroute. Lines can be rerouted multiple times. Figure 4-18 shows a rerouted
inheritance line. Dragging the endpoints of the line repositions the relationship line on the class shape. The
mouse cursor looks like a cross when positioned over the endpoint of a relationship line. The shortcut menu
of a relationship line can hide, reroute, delete, or display the properties of the relationship.
Class diagrams can find the base or derived class of a type. Open the shortcut menu on the class shape
header. The Show Base Class command finds the base class of the type in the class diagram. If not already
present, the base class is added to the class diagram. The Show Derived Classes command selects the
derived type.
Association
Association lines define a "has-a" relationship, in which a class owns another class. The class is embedded
as a property. Figure 4-19 shows an association relationship in the class diagram. In the figure, the XClass
class owns the YClass class, which is embedded as a property. The association line looks slightly different
from the inheritance line.
Select the association line in the class diagram toolbox to create a new relationship. Both classes and
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
interfaces can be embedded. Drag the association line from the owning type to the embedded type. A
property is created in the owning class for the embedded class. You can then switch to the Code Editor and
implement the property, which should return an instance of the embedded type. When the association line is
visible, the embedded member is not shown in the class details pane of the shape. If desired, you can show
the embedded member and hide the association line. Open the shortcut menu of the association line and
select the Show As Property command. To reverse that decision, open the shortcut menu and select Show
As Association.
This walkthrough demonstrates the class diagram—particularly creating new types and relationships.
1. Create a new class library. In Visual Studio 2005, from the File menu, choose the New submenu
to create a new project. You can also accomplish this by clicking New Project on the Standard
toolbar. The Class Library template is selected from a C# project. The project is named Personnel.
2. Add a class diagram to the project. Open the Project menu and select the Add New Item menu
item. From the Add New Item dialog box, add a class diagram. The class diagram is named
Employee.
3. Add a new interface using the class diagram toolbox. Name the interface IEmployee. Other
defaults are accepted.
4. Add a new abstract class using the class diagram toolbox. Name the class IEmployee.
5. Add another class using the class diagram toolbox. Name this class HourlyEmployee.
6. Add a new struct using the class diagram toolbox. Name the struct Name. Structs are depicted in
the class diagram as rectangles with square corners.
From the Class Details window, add three members to the IEmployee interface. (See Figure 4-20.) The
EmployeeInfo method returns a string. The Age property is an integer and gets and sets the age of an
employee. Finally, the Fullname property is of the Name type and gets the Employee name.
The Employee class should inherit and implement the IEmployee interface. Select the inheritance line in the
class diagram toolbox. Drag the inheritance line from the Employee class to the IEmployee interface. The
interface members now appear in the class details of the Employee class. In addition, Employee is given the
stubbed implementations of the interface members:
set
{
// Partial listing
Derived types of the Employee class must have a calculate pay operation. Add an abstract CalculatePay
method to the Employee class. The method returns a decimal. In the header of the Employee class, open a
shortcut menu. Select the Add submenu and the Method menu command. Name the new method
CalculatePay. In the Class Details window, set decimal as the return type. The method should be abstract.
Open the shortcut menu for the CalculatePay method and change the Inheritance Modifier to abstract. A
message box is displayed asking you to confirm this decision. Click Yes.
The HourlyEmployee class should inherit the Employee class. This relationship is created with the
inheritance line. Drag the inheritance line from the HourlyEmployee class to the Employee class.
Finally, add a Pay method to the HourlyEmployee class. The Pay method has a single parameter, which is
the hours worked. In the Class Details window, expand the row for the Pay method to expose the Add
Parameter item. Select the Add Parameter row and enter Hours as the parameter name. Change the type to
decimal. To complete the class, add a HourlyRate property to the class. It is a decimal type.
The Name struct has two string properties: FirstName and LastName. Both properties can be added in the
Class Details window.
The final class diagram for the Personnel application is shown in Figure 4-21.
This is code created per the class diagram. The functions are stubbed. Implementing the stubbed methods
is the only remaining step:
set;
}
Name Fullname
{
get;
set;
}
string EmployeeInfo();
}
{
throw new Exception(
"The method or operation is not implemented.");
}
set
{
throw new Exception(
"The method or operation is not implemented.");
}
}
#endregion
}
set
{
}
}
The Error List window is new to Visual Studio 2005. It displays edit and compile errors, warnings, and
general messages. Unique icons are assigned to each type of message. For example, error messages are
decorated with a red circle that contains an x. If there are compile errors, compiling a program automatically
displays the Error List window. (See Figure 4-22.) You can also display the Error List window from the View
menu.
The Error List window displays different categories of messages. The Error, Warning, and General buttons
hide or show a particular category of messages. The Error List buttons also indicate the number of
messages in each category. For each error, the error number, description, and location are shown.
Double-clicking an error message in the error list will jump to the related source code. In addition, you can
use the column headers to sort the error list. Column headers can be dragged to change column order.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Code Editor
In Visual Studio 2005, the Code Editor has been enhanced. Developers can add, format, and edit code with
the Code Editor. Code is often organized by file, region, and color. Some of the improvements to the Code
Editor include better IntelliSense, code snippets, smart tags, and additional formatting options.
IntelliSense
IntelliSense helps developers enter code correctly and efficiently. It minimizes the keystrokes required to
enter code while improving developer accuracy. IntelliSense is available in both the Code Editor and the
immediate mode command window. IntelliSense is actually a group of related features that includes the
completion list, parameter information, quick info, and complete word.
IntelliSense is a dynamic drop-down list called the completion list and appears as developers type new
commands or words. The completion list automatically appears after a space separator or dot for a member
is typed. You can also force a completion list at a dot or the Ctrl+spacebar shortcut key. The completion list
contains items for namespaces, types, type members, language keywords, and code snippets. As text is
entered, the first matching item in the list is selected. As more text is entered, the match is refined. When
the desired item is selected, press Tab to insert. Assuming that the IntelliSense For Most Recently Used
Members option is enabled, recently used items that match the input text are selected first. Function
overloads are not shown in the completion list. For example, there are 19 overloads of the Console.WriteLine
method. However, WriteLine appears in the member list just once. The overload versions of a function, if any,
are displayed with the IntelliSense for parameter info.
Parameter Info, a form of IntelliSense, displays the parameters of a function, including the overloaded
versions of the function, which have different signatures. Use the arrows to cycle through the available
overloads (shown in Figure 4-23). Parameter Info is prompted at the open parentheses of a function. Within
function parentheses, the Ctrl+Shift+spacebar key-stroke prompts Parameter Info.
IntelliSense in Visual Studio 2005 auto detects generics types and arguments. (See Figure 4-24.) A generic
dictionary is defined that has string keys and integer values. When creating a new instance of the generic
type, IntelliSense filters the completion list. The completion list automatically highlights the correct generic,
key, and value types.
There are other circumstances in which IntelliSense filters the completion list:
Interfaces
Base classes
Attributes
As and is operators
Catch clauses
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Add using Have you ever attempted to use a type without the proper using statement? It requires stopping,
determining the correct namespace, and then explicitly entering the namespace before the type or adding a
using statement. The Add Using feature of IntelliSense avoids the distraction of resolving unbound types.
This is yet another way IntelliSense improves developer productivity.
When an unbound type is entered, a smart tag, which appears as a narrow red box, is placed beneath the
last character. The smart tag is shown when the cursor is in or immediately adjacent to the unbound type.
From the smart tag menu, you have two choices: Either insert the using statement or prefix the unbound
type with the required namespace. Figure 4-25 shows the smart tag menu. Alternatively, open a shortcut
menu on the unbound type, choose the Resolve submenu, and then choose from the two menu choices.
Surround With
Visual Studio allows developers to surround code with an item on the completion list. You can surround a
block of code with a region. A block of statements can be surrounded with a for loop. The Surround With
feature surrounds selected text with something from the completion list. First select the text to be
surrounded. Open a shortcut menu on the selected text and choose the Surround With command. The
Surround With command will display the completion list. Select the item that should surround the text.
Visual Studio has always allowed developers to customize the color scheme of the user interface. Visual
Studio 2005 .NET extends custom formatting to user types, user keywords, and other information. This is
done in the Options dialog box, which is opened from the Tools menu. In the dialog box, switch to the
Environment window and select Fonts and Colors. Figure 4-26 shows the Fonts and Colors window.
Visual Studio 2005 provides developers additional control over code formatting. Developers control code
indentation, new line spacing, code spacing, and the wrapping of blocks. This is done in the Options dialog
box on the Tools menu. In the dialog box, open the Formatting window in the Text Editor section under the
C# node. (See Figure 4-27.) Except for the General node, the bottom-right pane is the code preview area, in
which options can be reviewed before accepting.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Change Tracking
Change tracking distinguishes saved from unsaved code. Visual Studio 2005 colorizes the far left pane to
indicate the status of code. Saved code is highlighted in green. Unsaved code is marked in yellow. Original
code, which is code unchanged since the source file was opened, is not highlighted. This is demonstrated in
Figure 4-28.
Profile
Almost every aspect of the Visual Studio 2005 user interface is customizable. Some developers spend
considerable time creating the ideal look and feel. They might want to preserve these settings to reapply
when necessary. User interface settings are saved in a profile, which includes window and text settings. The
profile is particularly useful when reinstalling Visual Studio because the user interface settings are lost at
that time. The profile is a convenient tool for reasserting preferred user preferences. The profile is helpful
when you want to configure the Visual Studio user interface similarly on different machines. Developers can
share profiles with other developers to identically configure their machines. This is helpful when developers
are working together on a project.
Profiles can be exported or imported in the Import And Export Settings Wizard dialog box on the Tools
menu. In the dialog box, you can import, export, or reapply the defaults of the user interface. (See Figure 4-29
.) Exporting the user interface settings creates a profile, whereas importing reads a profile that is applied to
the user interface. The Import And Export Settings Wizard dialog box guides the user through the process.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Code Snippets
Developers are connoisseurs of code and tend to collect useful code in a variety of places. While other
people collect coins, books, or PEZ dispensers, developers collect code snippets. Why? Developers do not
like typing. There is some irony considering how much typing is involved in programming. If you want to
improve developer productivity, reduce the amount of typing. This also reduces the number of typos and
resulting compiler errors, which require an inordinate amount of developer attention. Code reuse is another
reason why developers collect code. Even unique applications are largely composed of small but common
code snippets. These are the same code elements enlisted in almost every program: loops, classes,
exception handling, and so on. Developers traditionally cut and paste code to prevent repetitive typing.
However, this has considerable limitations, including limited persistence. There should be a more viable
solution.
That solution is code snippets, introduced in Visual Studio 2005, which are capsules of reusable source
code. A code snippet is an alias for code. Insert a code snippet to insert the aliased code into the source file
at the cursor or selection. Code snippets improve developer efficiency and accuracy.
There are multiple sources for snippets. Visual Studio 2005 has a set of default snippets for common tasks,
such as the for, try, and while snippets. Snippets are available online to be downloaded, and developers can
create custom snippets.
Snippet types are not mutually exclusive. For example, the while snippet is both an Expansion and Surround
With template.
Of course, copying and pasting code remains available. The main disadvantages are that the Clipboard is a
shared resource—the contents of the Clipboard can be altered by another application. Clipboard data is not
retained across logon sessions. Visual Studio lets developers copy source code directly to the toolbox. The
code is then preserved on the toolbox for future placement. This is not as elegant as a snippet but
sometimes quicker and more convenient—especially when a custom snippet is required. Code placed on the
toolbox is preserved between Visual Studio and logon sessions. Unlike the Clipboard, the toolbox is not a
shared resource and is available only to Visual Studio.
There are two methodologies for placing code on the toolbox. Select the code and copy to the Clipboard.
Next click on the toolbox and paste the code. You can also drag the code onto the toolbox. A button is then
created on the toolbox for the code. Multiple passages of code can be added to the toolbox. Moving the
mouse over a code button in the toolbox displays the saved code. Click a code button to insert the code at
the cursor. As a best practice, create a separate tab on the toolbox to group code. Intermingling code with
nonrelated buttons can be confusing. A toolbox populated with source code is shown in Figure 4-30. A tab
called Code was added to the toolbox to group the source code.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
There are several ways to insert code snippets into source code. IntelliSense is probably the simplest
method. Code snippets appear in the completion list as a torn page. Use a complete word to select the code
snippet and then press Tab to insert. Alternatively, double-click the snippet in the completion list.
Use the following techniques to select and insert code snippets. Inserting a snippet is called code expansion.
Insert a code snippet using IntelliSense. The completion list includes snippets.
Insert a code snippet from a menu using the Insert Snippet menu command. Insert Snippet is also
available from the shortcut menu in the Code Editor. You will be prompted to insert the name of
the snippet. Find the Insert Snippet menu command on the Edit menu and the IntelliSense
submenu. The resulting completion list includes only code snippets. This is called the snippet
picker.
Ctrl+K and then Ctrl+X is the keyboard shortcut for the Insert Snippet menu command.
Insert a code snippet using Auto IntelliSense. Position the cursor where the snippet should be
inserted, type the name of snippet, and then press Tab twice.
Some code snippets are templates and contain editable fields. After inserting the code snippet, a developer
customizes the template by assigning values to each field. The first field is selected automatically when the
code snippet is inserted. Other fields in the code template are color-highlighted. There can be multiple
instances of a field in the same code snippet. For example, there are several instances of the i field in the
code snippet of the for keyword. The first instance of a field is color-highlighted and editable. Remaining
instances are notated with a dotted border. Tab moves to the next field, whereas Shift-Tab moves to the
previous file. You can double-click to select a field. Fields with tooltips display the tip when the field is
selected. Figure 4-31 shows the code snippet of the for keyword. It has the ‘i' and length fields. Figure 4-32
presents the snippet with the fields customized.
When using a Surround With template, select the target code first and then insert the snippet. The snippet
will surround the code. For example, the following code increments a counter:
int count = 0;
Console.WriteLine(++count);
if (count == 10)
{
break;
}
This is the result of selecting the code and inserting the code snippet for the while keyword:
while (true)
{
int count = 0;
Console.WriteLine(++count);
if (count == 10)
{
break;
}
}
Default Snippets
Visual Studio 2005 has default snippets for tasks that are routine for developers. The default is a mixture of
expansion and surround with code snippets. Default snippets appear in the IntelliSense completion list and
the code snippet picker. Table 4-4 lists some of the default code snippets.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
#if Thi
s
co
de
sni
pp
et
sur
rou
nd
s
co
de
wit
h
#if
an
d
#e
ndi
f
dir
ect
ive.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
#re Thi
gio s
n co
de
sni
pp
et
sur
rou
nd
s
co
de
wit
h
a
#re
gio
n
an
d
#e
ndr
egi
on
dir
ect
ive.
~ Thi
s
co
de
sni
pp
et
ins
ert
s
a
de
str
uct
or.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
attr Thi
ibu s
te co
de
sni
pp
et
ins
ert
s
a
de
cla
rati
on
for
a
cu
sto
mi
ze
d
attr
ibu
te,
whi
ch
is
a
cla
ss
der
ive
d
fro
m
Sy
ste
m.
Att
rib
ut
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
ch Thi
ec s
ke co
d de
sni
pp
et
sur
rou
nd
s
co
de
wit
h
a
ch
ec
ke
d
blo
ck.
cla Thi
ss s
co
de
sni
pp
et
ins
ert
s
a
cla
ss
de
cla
rati
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
cto Thi
r s
co
de
sni
pp
et
ins
ert
s
a
co
nst
ruc
tor.
cw Thi
s
co
de
sni
pp
et
ins
ert
s
a
Co
ns
ole
.W
rite
Lin
e
sta
te
me
nt.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
do Thi
s
co
de
sni
pp
et
sur
rou
nd
s
co
de
wit
h
a
do
whi
le
blo
ck.
els Thi
e s
co
de
sni
pp
et
ins
ert
s
an
els
e
blo
ck.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
en Thi
um s
co
de
sni
pp
et
ins
ert
s
an
en
um
de
cla
rati
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
eq Thi
ual s
s co
de
sni
pp
et
ove
rrid
es
the
Eq
ual
s
me
tho
d
inh
erit
ed
fro
m
the
Sy
ste
m.
Ob
jec
t
typ
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
exc Thi
ept s
ion co
de
sni
pp
et
ins
ert
s
the
de
cla
rati
on
of
an
ap
plic
ati
on
ex
ce
pti
on,
whi
ch
is
der
ive
d
fro
m
Sy
ste
m.
Ex
ce
pti
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
for Thi
s
co
de
sni
pp
et
sur
rou
nd
s
co
de
wit
h
a
for
loo
p.
for Thi
ea s
ch co
de
sni
pp
et
sur
rou
nd
s
co
de
wit
h
a
for
ea
ch
loo
p.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
forr Thi
s
co
de
sni
pp
et
sur
rou
nd
s
co
de
wit
h
a
de
cre
me
nti
ng
for
loo
p.
if Thi
s
co
de
sni
pp
et
sur
rou
nd
s
co
de
wit
h
an
if
blo
ck.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
ind Thi
exe s
r co
de
sni
pp
et
ins
ert
s
an
ind
ex
er
fun
cti
on.
int Thi
erf s
ac co
e de
sni
pp
et
ins
ert
s
an
int
erf
ac
e
de
cla
rati
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
iter Thi
ato s
r co
de
sni
pp
et
ins
ert
s
an
iter
ato
r.
int Thi
eri s
nd co
ex de
sni
pp
et
ins
ert
s
a
na
me
d
iter
ato
r
an
d
ind
ex
er.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
inv Thi
ok s
e co
de
sni
pp
et
ins
ert
s
an
d
inv
ok
es
an
eve
nt.
loc Thi
k s
co
de
sni
pp
et
sur
rou
nd
s
co
de
wit
h
a
loc
k
blo
ck.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
mb Thi
ox s
co
de
sni
pp
et
ins
ert
s
the
Me
ss
ag
eB
ox.
Sh
ow
sta
te
me
nt.
na Thi
me s
sp co
ac de
e sni
pp
et
sur
rou
nd
s
co
de
wit
h
a
na
me
sp
ac
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
pro Thi
p s
co
de
sni
pp
et
ins
ert
s
a
pro
per
ty
an
d
ba
cki
ng
fiel
d.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
pro Thi
pg s
co
de
sni
pp
et
ins
ert
s
a
rea
d-o
nly
pro
per
ty.
Re
ad-
onl
y
pro
per
tie
s
hav
e
onl
y
a
get
me
tho
d.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
si Thi
m s
co
de
sni
pp
et
ins
ert
s
an
ent
ry
poi
nt
me
tho
d
tha
t
is
sta
tic
an
d
ret
urn
s
an
int
eg
er.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
str Thi
uct s
co
de
sni
pp
et
ins
ert
s
a
str
uct
de
cla
rati
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
sv Thi
m s
co
de
sni
pp
et
ins
ert
s
an
ent
ry
poi
nt
me
tho
d
tha
t
is
sta
tic
an
d
ret
urn
s
voi
d.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
swi Thi
tch s
co
de
sni
pp
et
ins
ert
s
a
swi
tch
sta
te
me
nt.
try Thi
s
co
de
sni
pp
et
ins
ert
s
a
try-
cat
ch
blo
ck.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
tryf Thi
s
co
de
sni
pp
et
ins
ert
s
a
try-
fin
ally
blo
ck.
un Thi
ch s
ec co
ke de
d sni
pp
et
sur
rou
nd
s
co
de
wit
h
an
un
ch
ec
ke
d
blo
ck.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
un Thi
saf s
e co
de
sni
pp
et
ins
ert
s
an
un
saf
e
blo
ck.
usi Thi
ng s
co
de
sni
pp
et
sur
rou
nd
s
co
de
wit
h
a
usi
ng
blo
ck.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-4:
Default Code
Snippets
Co De
de scr
Sn ipti
ipp on
et
Na
me
whi Thi
le s
co
de
sni
pp
et
sur
rou
nd
s
co
de
wit
h
a
whi
le
loo
p.
Use the Code Snippets Manager to manage snippets, including adding, removing, importing, and searching
for snippets.
The Code Snippets Manager is available on the Tools menu. (See Figure 4-33.) The folders shown in the
Code Snippets Manager are snippet directories. Open a folder to view individual snippets. For each snippet,
the follow information is provided:
Description of the code snippet
Alias or shortcut of the code snippet
Snippet type
Author of the snippet
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The Add button in the dialog box adds another directory to the list of code snippet directories. A code
snippets directory contains code snippet files, which are XML files with the .snippet suffix. For C#, the
default snippets directories are My Code Snippets, Refactoring, and Visual C#. The Remove button removes
a snippet directory from the list. The Import button imports a code snippet file. Use the Search Online button
to browse for code snippets. This button opens a general search window, in which developers can search for
the code snippets. When you find the code snippet you're searching for, you can download it to the local
machine.
Downloading snippets found online can pose security problems. A snippet is not guaranteed to have
innocuous XML. In addition, the snippet might hide malicious scripts. The Help URL element defined in the
snippet might stealthily launch a script. Also, code snippets can silently add references. Malicious calls are
then possible to functions in the reference. Long snippets can hide potentially harmful code. Be careful when
downloading code snippets with database access, code-access security, role-base security, or validation
code. As a best practice, carefully review the source code of code snippets that are downloaded before using
in an application.
Creating Snippets
You can create custom code snippets to encapsulate reusable code not found in a default snippet, which
allows you to maintain private libraries of reusable code. Different industries might benefit from specialized
code snippets. For example, a developer of medical software may have a need for medical code snippets.
Developers of legal software could benefit from legal code snippets. I am confident that vendors will publish
industry-specific code snippets in the future.
Custom code snippets are created in XML files. Microsoft publishes an XML schema for code snippets. Code
snippet files should have the .snippet extension. When the schema is set, Visual Studio 2005 offers
IntelliSense on the XML elements and attributes, which helps considerably in creating properly formed code
snippet files. You can group custom snippets in directories. Add these directories to the Code Snippets
Manager to make using the custom snippets more convenient.
The XML schema for code snippets defines the structure of a code snippets file. For a complete explanation
of the schema, visit the Code Snippet Schema Reference at
http://msdn2.microsoft.com/en-us/library/ms171418(en-us,vs.80).aspx. Some parts of the schema are
discussed in the next few pages.
<CodeSnippets xmlns="
http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
<CodeSnippet>
<!-Insert code snippet here. -->
</CodeSnippet>
Header Element
The Header element has child elements, which together provide the basic information about the code
snippet. The title, author, description, shortcut, and snippet type are some of the details that can be
provided. The title is the name of the code snippet as it appears in the Code Snippet Picker, whereas the
shortcut name appears in the IntelliSense completion list. The description of the snippet found in the Code
Snippets Manager is read from this element.
This is the Header element for an example snippet for StringBuilder types:
<Header>
<Title>StringBuilder</Title>
<Shortcut>sb</Shortcut>
<Description>Creates a new String Builder</Description>
<Author>Donis Marshall</Author>
</Header>
This is an example of the References and Import elements. The System.Text namespace contains the
StringBuilder type:
<Snippet>
<References>
<Reference>
<Assembly>System.Text.Dll</Assembly>
</Reference>
</References>
<Imports>
<Import>
<Namespace>System.Text</Namespace>
</Import>
</Imports>
Snippet element. There are literal and object fields. The Literal element declares a literal field, which is a field
that is fully contained in the code snippet. This would include string and numeric literals. The Object element
also declares a field, which is a field defined outside the snippet. This usually describes a type.
The Literal and Object elements can contain ID, Default, Tooltip, Type, and Function child elements:
The ID element is the name of the field.
The Default element is the default value of the field.
The Tooltip element is the tooltip of the field.
The Type element sets the type of the field.
The Function element names a function. This is a function to call whenever the field is selected.
<Declarations>
<Literal>
<ID>Name</ID>
<Default>mytext</Default>
<ToolTip>Name of new StringBuilder</ToolTip>
</Literal>
<Literal>
<ID>Size</ID>
<ToolTip>Capacity of String</ToolTip>
</Literal>
</Declarations>
Each line of code is placed in a <![CDATA]> element. The code must be written in the syntax of the chosen
language.
CDATA syntax:
In snippet code, fields are bounded with dollar signs ($field$). This is the snippet code for the StringBuilder
snippet:
<Code Language="CSharp">
<![CDATA[StringBuilder $Name$=]]>
<![CDATA[ new StringBuilder($Size$);]]>
</Code>
</Header>
<Snippet>
<References>
<Reference>
<Assembly>System.Text.Dll</Assembly>
</Reference>
</References>
<Imports>
<Import>
<Namespace>System.Text</Namespace>
</Import>
</Imports>
<Declarations>
<Literal>
<ID>Name</ID>
<Default>mytext</Default>
<ToolTip>Name of new StringBuilder</ToolTip>
</Literal>
<Literal>
<ID>Size</ID>
<ToolTip>Capacity of String</ToolTip>
</Literal>
</Declarations>
<Code Language="CSharp">
<![CDATA[StringBuilder $Name$=]]>
<![CDATA[new StringBuilder($Size$);]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>
directory is eventually added as a code snippet directory using the Code Snippets Manager.
This walkthrough creates a code snippet that reflects the methods of a type. This code tests the proposed
snippet:
// Snippet starts
Type t = typeof(Object);
string typename = t.Name;
string typenamespace = t.Namespace;
MethodInfo [] methods = t.GetMethods();
// Snippet ends
Console.WriteLine("Type Name:"+typename);
Console.WriteLine("Namespace Name:"+typenamespace);
foreach (MethodInfo method in methods)
{
Console.WriteLine(method.Name);
}
23. </Snippet>
24. The code snippet has four fields, which name the type, type instance, namespace, and MethodInfo
array. The type instance is an object field. The other fields are literals and contained within the
code snippet. Defaults for the fields are gleaned from the code used to test the code snippet.
Fields are defined within the Declarations element with literal and object elements:
25.
26. <Declarations>
27. <Literal>
28. <ID>Instance</ID>
29. <Default>t</Default>
30. <ToolTip>Name of instance.</ToolTip>
31. </Literal>
32. <Literal>
33. <ID>TypeName</ID>
34. <Default>typename</Default>
35. <ToolTip>Name of type.</ToolTip>
36. </Literal>
37. <Literal>
38. <ID>TypeNamespace</ID>
39. <Default>typenamespace</Default>
40. <ToolTip>Namespace of type.</ToolTip>
41. </Literal>
42. <Literal>
43.
44. <ID>Methods</ID>
45. <Default>Methods</Default>
46. <ToolTip>Type methods</ToolTip>
47. </Literal>
48. <Object>
49. <ID>Type</ID>
50. <Default>Object</Default>
51. <ToolTip>object type</ToolTip>
52. <Type>System.Object</Type>
53. </Object>
54. </Declarations>
55. Paste the code for the snippet into <![CDATA]> elements, which are placed within the Code
element. Add extra line feeds to separate lines of source code. After pasting the code, substitute
the field names into the code:
56.
57. <Code Language="CSharp">
58. <![CDATA[Type $Instance$ = typeof($Type$);
59. ]]>
60. <![CDATA[string $TypeName$ = $Instance$.Name;
61. ]]>
62. <![CDATA[string $TypeNamespace$ = $Instance$.Namespace;
63. ]]>
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Refactoring
Refactoring helps developers re-engineer their code and removes much of the tedium of maintaining an
application. Considerable time is spent re-engineering during the lifetime of an application. This sometimes
starts as early as the development phase and accelerates as the application matures. This includes—but is
not limited to—renaming variables, moving methods, changing method signatures, and redesigning classes.
The objective is to improve the application. The process is tedious. Finding and changing a variable in a
software system that spans dozens of source files can be challenging. Another example is changing the
signature of a commonly used method. Method calls throughout the application must be located and
corrected. These changes are made to improve an application. However, if executed poorly, these changes
have the opposite effect. Worst-case scenario: Maintenance might introduce bugs into otherwise pristine
applications. Visual Studio 2005 introduces refactoring as a tool to help developers re-engineer code quickly
and accurately. Refactoring is a multifaceted tool. It assists in the renaming of variables, changing of method
signatures, extrapolating of interfaces, converting fields to properties, and much more.
A Refactoring menu is available in the Visual Studio 2005 user interface that shows the refactoring
operations. Table 4-5 list the various refactoring operations. The refactoring menu is also available in the
Code Editor using a shortcut menu.
Table 4-5: Refactoring Operations
Operations Description
Extract Method The Extract Method operation creates a new method that encapsulates the
selected code.
Encapsulate Field The Encapsulate Field operation creates a property that abstracts the selected
field.
Extract Interface The Extract Interface operation extracts an interface from a type.
Promote Local The Promote Local Variables To Parameters operation promotes a local
Variables To Parameters variable to a parameter of the current method.
Remove Parameters The Remove Parameters operation removes a parameter from the parameter
list of a function. Call sites for the function are updated to reflect the removed
parameter.
Reorder Parameters The Reorder Parameters operation reorders the parameters of a function. Call
sites for the function are updated for the new sequence of parameters.
The Preview Changes dialog box is invaluable because it provides you with an opportunity to preview
refactoring changes before applying them. Figure 4-35 displays the Preview Changes dialog box.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Refactoring can span multiple projects in the same solution, which occurs with project-to-project references.
A project-to-project reference is a reference in which both the referencing and referenced assemblies are built
in projects of the same solution. The Projects window in the Add Reference dialog box inserts a
project-to-project reference.
Refactoring Walkthrough
This walkthrough demonstrates some of the features of refactoring, including the renaming of variables and
extracting of interfaces. The walkthrough refactors the Airline Seats application. This application manages
the first class and coach standby lists of an airline flight. This is the user interface (shown in Figure 4-36)
presented to flyers on overhead monitors at the airport gate.
This is the interface created from the Extract Interface refactoring operation. It is saved to a separate file. In
the file, change the namespace containing the interface appropriately. For the sample application, the
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
interface IPerson
{
string FirstName { get; }
Refactoring also updated the Person class to inherit the IPerson interface:
Visual Studio 2005 supports conventional deployment based on Windows Installer technology. With
Windows Installer technology, an application is packaged in a Setup.exe file and then deployed to the local
machine or a public share. The Setup.exe program installs the application on the local machine. ClickOnce
deployment is introduced in Visual Studio 2005 as an alternate deployment strategy. ClickOnce deploys an
application from a central location and combines a desktop application with Web-based delivery system.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
MSBuild
MSBuild does not require Visual Studio. This is ideal for lab environments in which Visual Studio is not
installed. Individual builds are organized in project files. Build project files are XML files and have a .proj
extension. As an XML file, the build project is platform-independent and extensible. For a complete
explanation of MSBuild, consult the MSBuild Reference at the Microsoft Developer Network (MSDN) at
http://winfx.msdn.microsoft.com/library/default.asp?url=/library/en-us/dv_fxgenref/html/.asp.
Build projects consist of items, properties, and tasks, as described in the following sections.
Items
Items are the input of the build process. Items are created as child elements of the ItemGroup element.
Items sharing the same name are considered a collection. Item collections are addressable in the build
project as @(ItemCollectionName). Item collections are primarily used as parameters in build tasks.
For example, the Compile element is a standard item. It defines the source files that are included or
excluded from the build. The Include attribute specifies a file to be included in the build. The Exclude
attribute, naturally, excludes files. The following code defines a Compile collection, which includes two items:
<ItemGroup>
<Compile Include="source1.cs"/>
<Compile Include="source3.cs" Exclude="source2.cs"/>
</ItemGroup>
Properties
Properties are configuration data used during the build process. Properties represent individual values and
cannot be grouped into collections. Properties are defined as child elements of the PropertyGroup element.
Refer to properties in the project using the $(property) syntax:
<PropertyGroup>
<ApplicationVersion>1.2.3.4</ApplicationVersion>
</PropertyGroup>
There are several reserved properties in the MSBuild environment. Table 4-6 lists the reserved properties.
Table 4-6: Reserved Properties
Property Description
MSBuildProjectDirectory This property is the absolute path to the MSBuild project file.
MSBuildProjectFile This property is the name and extension of the MSBuild project file.
MSBuildProjectExtension This is the file extension of the MSBuild project file. It should include the
. prefix.
MSBuildProjectFullPath This property is the fully qualified name of the MSBuild project file.
MSBuildProjectName This property is the name of the MSBuild project file without the
extension.
MSBuildProjectDefaultTargets This property is the list of targets specified in the project element of the
MSBuild project file.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Property Description
MSBuildExtensionsPath This property is the supplemental directory for custom target files, which
is found under the Program Files directory.
Tasks
Tasks are the build operations of the MSBuild project and child elements of the Target element. Tasks can
accept parameters, which are the attributes of the Task element. Item collections and properties are valid
parameters to tasks. Create multiple targets to batch groups of build operations. The MSBuild tool can
invoke different targets. For example, you can create targets for release versus debug builds.
Tasks are written in a managed language and are available to any MSBuild project. Developers can author
specialty tasks in managed code. Build tasks are classes that implement the ITask interface.
Table 4-7 list the defaults tasks available in the MSBuild environment.
Table 4-7:
Default Tasks
Ta De
sk scr
ipti
on
AL Thi
s
tas
k
inv
ok
es
the
As
se
mb
ly
Lin
ker
(AL
)
too
l.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-7:
Default Tasks
Ta De
sk scr
ipti
on
As Thi
pN s
etC tas
om k
pile inv
r ok
es
as
pn
et_
co
mp
iler
.ex
e,
whi
ch
pre
co
mp
iles
AS
P.
NE
T
ap
plic
ati
on
s.
As Thi
sig s
nC tas
ult k
ure cre
ate
s
an
ite
m
for
a
cul
tur
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-7:
Default Tasks
Ta De
sk scr
ipti
on
Co Thi
py s
tas
k
co
pie
s
file
s
to
a
sp
ecif
ied
dir
ect
ory
.
Cre Thi
ate s
Ite tas
m k
co
pie
s
ite
ms
bet
we
en
coll
ect
ion
s.
Cre Thi
ate s
Pro tas
per k
ty co
pie
s
pro
per
tie
s.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-7:
Default Tasks
Ta De
sk scr
ipti
on
Cs Thi
c s
tas
k
inv
ok
es
cs
c.e
xe,
whi
ch
is
the
C#
co
mp
iler
.
Del Thi
ete s
tas
k
del
ete
s
file
s.
Ex Thi
ec s
tas
k
ex
ec
ute
s
an
ap
plic
ati
on
or
co
m
ma
nd.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-7:
Default Tasks
Ta De
sk scr
ipti
on
Fin Thi
dU s
nd tas
erP k
ath det
er
mi
ne
s
whi
ch
ite
ms
are
fou
nd
wit
hin
a
sp
ecif
ied
dir
ect
ory
an
d
its
su
bdi
rec
tori
es.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-7:
Default Tasks
Ta De
sk scr
ipti
on
Ge Thi
ner s
ate tas
Ap k
plic cre
ati ate
on s
Ma an
nif ap
est plic
ati
on
ma
nife
st
or
nat
ive
ma
nife
st
for
a
Cli
ck
On
ce
ap
plic
ati
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-7:
Default Tasks
Ta De
sk scr
ipti
on
Ge Thi
ner s
ate tas
Bo k
ots loc
tra ate
pp s,
er do
wnl
oa
ds,
an
d
ins
tall
s
an
ap
plic
ati
on.
Ge Thi
ner s
ate tas
De k
plo cre
ym ate
ent s
Ma a
nif de
est plo
ym
ent
ma
nife
st
for
a
Cli
ck
On
ce
ap
plic
ati
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-7:
Default Tasks
Ta De
sk scr
ipti
on
Ge Thi
ner s
ate tas
Re k
so cre
urc ate
e s.
res
our
ce
s
file
s
fro
m.
txt
an
d.
res
x
file
s.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-7:
Default Tasks
Ta De
sk scr
ipti
on
Ge Thi
tAs s
se tas
mb k
lyId ret
ent urn
ity s
as
se
mb
ly
me
tad
ata
an
d
sto
res
the
res
ult
s
in
an
ite
m
coll
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-7:
Default Tasks
Ta De
sk scr
ipti
on
Ge Thi
tFr s
am tas
ew k
ork obt
Pat ain
h s
the
pat
h
of
the
.N
ET
Fra
me
wor
k
as
se
mb
lies
.
Ge Thi
tFr s
am tas
ew k
ork obt
SD ain
KP s
ath the
pat
h
of
the
.N
ET
Fra
me
wor
k
SD
K.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-7:
Default Tasks
Ta De
sk scr
ipti
on
LC Thi
s
tas
k
co
nve
rts
a.
licx
to
a.
lice
ns
e
file.
Ma Thi
ke s
dir tas
k
ma
ke
s
a
dir
ect
ory
.
MS Thi
Bui s
ld tas
k
buil
ds
an
MS
Bui
ld
pro
jec
t
fro
m
an
oth
er
buil
d
pro
jec
t.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-7:
Default Tasks
Ta De
sk scr
ipti
on
Re Thi
ad s
Lin tas
es k
Fro rea
mF ds
ile MS
Bui
ld
ite
ms
fro
m
a
file.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-7:
Default Tasks
Ta De
sk scr
ipti
on
Re Thi
gis s
ter tas
As k
se rea
mb ds
ly the
me
tad
ata
of
an
as
se
mb
ly
an
d
reg
ist
ers
the
as
se
mb
ly
as
a
CO
M
ser
ver,
whi
ch
allo
ws
CO
M
clie
nts
to
ac
ce
ss
the
as
se
mb
ly.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-7:
Default Tasks
Ta De
sk scr
ipti
on
Re Thi
mo s
ve tas
Dir k
del
ete
s
a
dir
ect
ory
.
Re Thi
sol s
ve tas
As k
se det
mb er
lyR mi
efe ne
ren s
ce whi
ch
as
se
mb
lies
de
pe
nd
on
an
oth
er
as
se
mb
ly.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-7:
Default Tasks
Ta De
sk scr
ipti
on
Re Thi
sol s
ve tas
Co k
mR res
efe olv
ren es
ce the
loc
ati
on
of
typ
e
libr
ary
(TL
B)
file
s.
Re Thi
sol s
ve tas
Ke k
yS det
our er
ce mi
ne
s
the
str
on
g
na
me
of
a
ke
y
so
urc
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-7:
Default Tasks
Ta De
sk scr
ipti
on
Sg Thi
en s
tas
k
is
a
wra
pp
er
for
the
XM
L
Ser
iali
zat
ion
Ge
ner
ato
r
To
ol (
sg
en.
exe
).
Sig Thi
nFi s
le tas
k
sig
ns
a
file
wit
h
a
cer
tific
ate
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-7:
Default Tasks
Ta De
sk scr
ipti
on
To Thi
uc s
h tas
k
set
s
the
ac
ce
ss
an
d
mo
difi
cat
ion
tim
e
of
a
file.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-7:
Default Tasks
Ta De
sk scr
ipti
on
Unr Thi
egi s
ste tas
rAs k
se unr
mb egi
ly ste
rs
an
as
se
mb
ly
fro
m
the
reg
istr
y.
Aft
erw
ard
,
the
as
se
mb
ly
is
no
lon
ger
ava
ilab
le
to
CO
M
clie
nts
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-7:
Default Tasks
Ta De
sk scr
ipti
on
Vb Thi
c s
tas
k
inv
ok
es
vb
c.e
xe,
whi
ch
is
the
Vis
ual
Ba
sic
.N
ET
co
mp
iler
.
Vc Thi
Bui s
ld tas
k
wra
ps
vc
bui
ld.
exe
,
whi
ch
buil
ds
a
Vis
ual
C+
+
pro
jec
t.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 4-7:
Default Tasks
Ta De
sk scr
ipti
on
Wri Thi
teL s
ine tas
sT k
oFi writ
les es
the
pat
hs
of
the
sp
ecif
ied
ite
ms
to
a
file.
Project File
A project file is a generalized skeleton of an MSBuild project. An MSBuild project can have any number of
tasks, item collections, and properties. This is entirely dependent on the requirements of the actual project.
Ta Description
sk
Ta Description
sk
Ta Description
sk
MSBuild Walkthrough
This section provides a walkthrough of a normal MSBuild project. The project contains two tasks: The first
task compiles an assembly from the available source files, and the second task creates a DLL from a source
file. The project then compiles the remaining sources files. This requires a reference to the DLL. The
walkthrough MSBuild project is documented with steps. Here is the project, followed by a description of each
step:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Step Two -->
<ItemGroup>
<Compile Include="source1.cs"/>
<Compile Include="source2.cs"/>
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
</ItemGroup>
<!-- Step Three -->
<PropertyGroup>
<DebugType>none</DebugType>
</PropertyGroup>
<PropertyGroup>
<AssemblyName>sample.exe</AssemblyName>
</PropertyGroup>
<!-- Step Four -->
<Target Name="Application1">
<Csc Sources="*.cs" OutputAssembly="$(AssemblyName)"
DebugType="$(DebugType)"/>
</Target>
<!-- Step Five-->
<Target Name="Application2">
<Csc Sources="source3.cs" TargetType="library"/>
<Csc Sources="@(compile)" References="source3.dll"
OutputAssembly="$(AssemblyName)" DebugType="$(DebugType)"/>
</Target>
</Project>
Step 1 establishes the schema for the MSBuild schema.
Step 2 creates an item collection for the Compile element. The source files source1.cs and
source2.cs are named in the collection.
Step 3 sets the defaults for the DebugType and AssemblyName properties.
Step 4 creates target Application1, which has a single task. The task compiles the source files in
the current directory. It uses both the AssemblyName and DebugType properties.
Step 5 creates target Application2, which contains two tasks: The first task compiles source3.cs
and places the results in a library assembly; the second task compiles the item collection and
references the library assembly.
This MSBuild command line reads the sample.proj project file. The output is the donis.exe assembly. The
debugtype property is assigned full, whereas the assemblyname property is assigned donis.exe. This
overwrites the default values of those properties. Finally, MSBuild invokes the Application1 target:
C:\>msbuild /p:debugtype=full,assemblyname=donis.exe
sample.proj /target:application1
The following MSBuild command executes the tasks of the Application2 target:
ClickOnce applications are self-updating, which is particularly useful for applications that require frequent
updates. Deploying applications that require frequent updates within the Windows Installer technology model
was a time-consuming process. ClickOnce applications are deployed once and updated online
automatically. Updates require online connectivity.
Another benefit includes the security privileges required to perform an installation. Windows Installer
technology sometimes required administrative permissions, but ClickOnce deployment does not require
administrative permissions to deploy the application.
ClickOnce deployment publishes an application at a Web site on an ASP.NET server. The client machine
must support the .NET Framework. This is more likely in an intranet deployment, in which the desktops are
controlled. It is also feasible in an extranet environment, in which requirements can be published and a
reasonable level of compliance can be expected. ClickOnce applications can be deployed in a traditional
manner, but require online connectivity for self-updating.
ClickOnce deployment has two modes of deployment. The online-only mode executes the application from
the Web, which requires the client to be connected to the server computer whenever the application is
executed. The full installation mode executes the installation on the client computer. This is similar to a
traditional installation from an .msi or .cab file. Updates still require online connectivity.
ClickOnce deployment uses an application manifest (which is an XML file) to define dependent assemblies,
files, and security permissions required for the application. ClickOnce deployment also uses a deployment
manifest. This manifest contains deployment configuration information, such as the current version, the
location of required files, deployment mode, and update policies.
Create application and deployment manifests with the Manifest Generation and Editing (MageUI) tool, which
is distributed in the .NET Framework. To create a new application manifest, choose the File menu and then
choose the New submenu. From the submenu, select Application Manifest The Name window of the
application manifest appears first. This window contains the general information on the deployment. The
Airline Seats application is published as a ClickOnce application. Figure 4-41 shows a view of the Name
window in the MageUI tool for the Airline Seats application.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The Files window lists the files included in the deployment. (See Figure 4-42.)
The Permissions Required window sets the security required to execute the deployed application.(See Figure
4-43.)
You can also open the deployment manifest using the MageUI tool. The Names windows for both the
deployment and application manifests are the same. For the deployment manifest, the two most important
windows are the Deployment and Update Options windows.
The Deployment Options window sets the ClickOnce deployment mode and the URL of the published
application. (See Figure 4-44.)
The Update Options window configures how updates are managed for an application deployed with
ClickOnce technology. Figure 4-45 shows the Update Options window.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Fortunately, ClickOnce deployment is automated in Visual Studio 2005. This includes the creation of both
manifest files and Web server configuration. In Visual Studio 2005, developers use the Publish Wizard to set
up an application for ClickOnce deployment. Open the project shortcut menu and choose Publish to launch
the Publish Wizard.
The Publish Wizard has several steps. The first step provides the URL in which the application is published.
Figure 4-46 shows the first window of the Publish Wizard.
Step 2 of the Publish Wizard sets the online connectivity requirements of the deployed application. (See
Figure 4-47.)
Step 3 (shown in Figure 4-48) is the confirmation window, in which a developer can review the ClickOnce
settings and confirm the deployment.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
More sophisticated collections are sometimes needed. The .NET Framework offers ArrayList, Queue, Stack,
HashTable, and other useful collections. Specialty collections are also available, including BitVector32,
HybridDictionary, and NameValueCollection.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Arrays are represented in most programming languages. Most developers have some familiarity with the
concept of arrays. Arrays are employed in a variety of ways. A personnel program would contain an array of
employees. Graphic programs might have one array for each type of geometric object, such as ellipses,
rectangles, or triangles. An accounting and scheduling application for automobile repair likely would have an
array of automobile repair tickets. The Space Shuttle program would have an array of astronauts.
Arrays are intrinsic to the language. Other collections, such as Stack, Queue, and Hashtable, are not native
to the language. As such, ease of use is one of the benefits of arrays. Another benefit is familiarity. Arrays
are available and functionally similar in almost every programming language. Few developers have not worked
with arrays.
An array is a container, which is an abstraction of a data structure. As a container, an array holds data
items called elements. Elements of an array are always related, such as an array of apples or an array of
oranges. An array might consist of SalariedEmployee, HourlyEmployee, and CommissionedEmployee
instances. However, an array of apples and employees is probably invalid because those objects are
probably unrelated. In addition, an array is a data structure that is a composite of elements that reside in
contiguous memory.
Arrays are reference types, and the memory for the array is allocated on the managed heap. Even an array
of value types is allocated on the managed heap and not on the stack. An array of 30 integer values would
have the same number of 32-bit slots allocated in contiguous memory for the array elements. With arrays of
reference types, the objects are not stored in contiguous memory; the references are stored in contiguous
memory. However, the objects themselves are stored in noncontiguous memory, which is pointed to by the
reference. Figure 5-1 shows the difference in memory allocation between arrays of reference versus value
types.
Elements are relative to the beginning of the array and are identified with indexes, which are either integer or
long types. Indexes are also commonly called indices or subscripts, and placed inside the indexing operator
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
([ ]). Arrays are zero-based where the index is actually an offset. Array indexes are offsets from the
beginning of the array to a particular element. Therefore, the first element is at the start of the array, which is
an offset of zero. For an array of five elements, a proper index is from zero to four. Therefore, the last element
of the array is at index n -- 1. This is a common cause of fencepost errors. Fencepost errors occur when
referring to array elements with indexes outside the bounds of an array. If an array has five elements,
accessing element six is a fencepost error.
As mentioned, arrays are immutable. This means that an array is statically sized, and the dimensions
cannot be changed at run time. The System.Array.Resize method, which is a generic method, seemingly
resizes an array. However, appearances can be deceiving. Array.Resize creates an entirely new array that is
the new size. The new array is initialized with the elements of the source array. Afterward, the original array
is discarded.
Single-dimensional arrays are indigenous to the run time. There are specific MSIL instructions for vectors,
including newarr, ldelem, ldlen, and stelem. There are no built-in instructions for multidimensional arrays.
This direct manipulation of single-dimensional arrays makes them more efficient. In addition, some of the
members of the System.Array type, which is the underlying type for all arrays, cannot be applied to
multidimensional arrays. Conversely, all the methods and properties of the System.Array type are applicable
to single-dimensional arrays.
The System.Array type is the underpinning of all arrays. It is an abstraction of an array. Instances of arrays
are instances of the System.Array type. Thus, arrays are implicitly reference types. Arrays can access
many of the instance methods and properties of the System.Array type. System.Array is a combination of
original methods and the implementation of a series of interfaces. Table 5-1 lists the interfaces that
System.Array implements.
Table 5-1:
Interfaces
Implemented
at
System.Array
Int De
erf scr
ac ipti
e on
ICl Thi
on s
ea int
ble erf
ac
e
defi
ne
s
a
me
tho
d
to
clo
ne
an
ins
tan
ce
of
an
arr
ay.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-1:
Interfaces
Implemented
at
System.Array
Int De
erf scr
ac ipti
e on
ICo Thi
llec s
tio int
n erf
ac
e
defi
ne
s
me
tho
ds
to
co
unt
the
nu
mb
er
of
ele
me
nts
of
an
arr
ay
an
d
for
thr
ea
d
sy
nc
hro
niz
ati
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-1:
Interfaces
Implemented
at
System.Array
Int De
erf scr
ac ipti
e on
IEn Thi
um s
era int
ble erf
ac
e
defi
ne
s
a
me
tho
d
tha
t
en
um
era
tes
the
ele
me
nts
of
a
coll
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-1:
Interfaces
Implemented
at
System.Array
Int De
erf scr
ac ipti
e on
ILi Th
st e
int
erf
ac
e
defi
ne
s
me
tho
ds
to
ac
ce
ss
an
ind
ex-
ba
se
d
coll
ect
ion
.
So
me
me
mb
ers
of
thi
s
int
erf
ac
e
are
not
pu
blic
ly
im
ple
me
nte
d.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Create arrays as local variables or fields. Use arrays also as method parameters and return types. The
rationale of using an array as a local variable, field, method parameter, or return type is the same as any
type. Arrays are passable into or out of a method as a parameter. The semantics of an array parameter are
the same as any parameter. Because an array is a reference type, it is passed by reference. Therefore, the
content of the array can be changed in the called method. Parameter modifiers, such as the ref modifier, are
assignable to array parameters.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Arrays
Following is the syntax for declaring a one-dimensional array. A vector has a single index. When declaring
an array, the indexing operator on the right-hand side sets the size of the array.
type [] arrayname 1;
typea [] arrayname 2=new typeb[n];
typea [] arrayname 3=new typeb[n] {ilist};
typea[] arrayname 4=new typeb[] {ilist};
typea[] arrayname 5={ilist};
The first syntax declares a reference to an array that is not initialized. You can later initialize the array
reference using the right hand side (RHS) syntax of declaring an array. This is sample code of the first
syntax of declaring an array. It declares an integer array named zArray. The array is then assigned an array
of 10 integers that initializes the references. However, the array elements of 10 integers are not initialized.
Array elements default to zero or null: zero for value types and null for reference type elements. In this code,
the array elements are set to zeroes:
int [] zArray;
zArray=new int[10];
The second syntax declares and initializes a reference to an array. The array reference is assigned a
reference to the new instance. An array of a value types must be initialized to an array of the same value
type. Otherwise, typea and typeb must be identical. Even if typea and typeb are convertible, the declaration
is not allowed. Bytes are convertible to integers. However, an array of bytes is not convertible to an array of
integers. The size of the array is n. The elements are indexed from zero to n -- 1.
byte aValue=10;
int bValue= aValue; // convertible
int [] zArray=new byte[5]; // not convertible
int [] yArray=new int[5]; // convertible
Unlike value types, an array of reference types can be initialized to an array of the same or derived types. In
the declaration, typeb must be the same or a derivation of typea, which is demonstrated in the following code:
The third syntax declares, initializes, and assigns values to the array elements. The initialization list (ilist)
contains the initial value for the elements of the array, where the values are comma-delimited. The number of
values in the list should match the number of elements in the array exactly—no more and no less.
The fourth syntax also declares, initializes, and assigns values to array elements. However, in this
circumstance, the initialization list sets the number of elements. The array size is not stipulated in this
syntax. The compiler counts the number of items in the initialization list to set the length of the array.
The fifth syntax is an abbreviation of the fourth syntax, where the array type and number of elements are
inferred from the initialization list.
int [] yArray={1,2,3,4,5};
// Remainder of class...
}
Array Elements
Indexing operators refer to elements of an array. With an index, the indexing operator returns a specific
element of the array. When an indexing operator is used on the left hand side (LHS), the element value is
changed. On the RHS, the indexing operator returns the value of the element.
The following for loop lists the elements of an array. The element and indexing operator appear as an l-value
to total the array. It is used as an r-value to display each element:
int [] zArray={1,2,3,4,5};
int total=0;
for(int count=0;count<zArray.Length;++count) {
total+=zArray[count]; // l-value
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Multidimensional Arrays
You are not limited to one-dimensional arrays. Multidimensional arrays are rectangular arrays and have
multiple dimensions and indices. Two-dimensional arrays, which consist of rows and columns, are the most
prevalent kind of multidimensional array. Each row contains the same number of columns, thus making the
array rectangular. From a geometric perspective, the x-axis consists of rows and the y-axis consists of
columns. Multidimensional arrays are stored in row-major order and are processed in row-major order.
The total number of elements in a multidimensional array is the product of the indices. For example, an array
of 5 rows and 6 columns has 30 elements. The Array.Length property returns the total number of elements in
the array. The Array.GetLength method returns the number of elements per index. The indices are numbered
from zero. For a two-dimensional array, row is the zero dimension, whereas column is the one dimension.
GetLength(0) would then return the number of rows in the multidimensional array.
This is the syntax to declare a two-dimensional array. Notice the indexing operator. Row and column
indexes in the indexing operator are delimited with a comma.
The following code shows various declarations of multidimensional arrays. The initialization list of a
multidimensional array includes nested initialization lists for each row. If an array has two rows, the
initialization list includes two nested initialization lists. This is the syntax of a nested initialization list:
{ {nlist}, {nlist}, {nlist} …}
The following code shows the various declaration syntaxes, including nested initialization lists:
To access an element of a multidimensional array, specify a row and column index in the indexing operator.
It can be used on the LHS and RHS to set or get the value of an element, respectively. The following code
calculates the total of the elements. This requires enumerating all the elements of a multidimensional array.
int total=0;
for(int row=0;row<zArray.GetLength(0);++row) {
for(int col=0;col<zArray.GetLength(1);++col) {
total+=zArray[row, col]; // LHS
int number=zArray[row, col]; // RHS
Console.WriteLine(number);
}
}
Console.WriteLine("\nThe total is {0}.",
total);
We have been focusing on two-dimensional arrays. However, arrays can have more than two dimensions. In
fact, there is no limit to the number of dimensions. Three- and four-dimensional arrays are less common than
two-dimensional arrays, but are seen nonetheless. More dimensions are rarely enunciated. Most developers
find multidimensional arrays beyond two indices mind-numbing to manage and manipulate. Additional
dimensions require added comma-delimited indexes when the array is, declared, defined, and used.
How is the preceding code interpreted? A multidimensional array can be viewed as a hierarchical array that
consists of layers. Each layer represents a different level of array nesting. The previous example defines a
single-dimensional array, which aggregates two nested arrays. The nested arrays each contain three other
arrays. Each of these lower nested arrays contains two elements.
{ 1 }; // layer 1
{{ 1 },{ 2 }}; // layer 2
{{{ 1 }, { 2 },{ 3 }},{{{ 1 }, { 2 },{ 3 }}; // layer 3
{{{{1,2}, {1,2},{1,2}} , {{1,2},{1,2},{1,2}}}}; // layer 4
The following code demonstrates a practical use of a multidimensional array. The program maintains the
grades of students. Each student attends two classes. Each class has a class name and grade. Object
elements are defined to make the array generic. Strings, integers, reference types, or anything else can be
placed in an object array. Everything is derived from System.Object in managed code. The downside is
boxing and unboxing of grades, which are value types.
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
{{"Algebra",95}, { "History",70}},
{{"Biology",100}, { "English",92}}};
for(int iName=0;iName<names.Length;++iName) {
Console.WriteLine("\n{0}\n", names[iName]);
for(int iCourse=0;iCourse<2;++iCourse) {
Console.WriteLine("{0} {1}",
grades[iName, iCourse, 0],
grades[iName, iCourse, 1]);
}
}
}
}
}
Jagged Arrays
The most frequently found definition of a jagged array is an array of arrays. More specifically, a jagged array
is an array of vectors. Although other arrays are rectangular, a jagged array, as the name implies, is jagged
as each vector in the array can be of different length. With jagged arrays, first define the number of rows or
vectors in the jagged array. Second, declare the number of elements in each row.
The syntax for declaring a jagged array is similar to a multidimensional array. Instead of a single bracket ([r,c
]), jagged arrays have two brackets ([r][]). When declaring a jagged array, the number of columns is not
specified and is omitted. The number of elements in each row is set individually. This is the syntax of a
jagged array:
The rows of the jagged array are initialized to one-dimensional arrays. Because the rows are assigned
distinct arrays, the length of each row may vary. Therefore, a jagged array is essentially an array of vectors:
jarray[row]=new type[elements];
Here is sample code that employs a jagged array. Each row of the jagged array has an increasing number of
elements. The first nested loop creates and initializes each row of the jagged array. At the end, the values of
each row are totaled and displayed.
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
int [][] jagged=new int [7][];
int count=0;
for(int row=0;row<jagged.GetLength(0);++row) {
Console.Write("\nRow {0}:", row);
jagged[row]=new int[row+1];
for(int index=0; index<row+1; ++index) {
++count;
jagged[row][index]=count;
Console.Write(" {0}", count);
}
}
Console.WriteLine("\n\nTotals");
for(int row=0;row<jagged.GetLength(0);++row) {
int total=0;
for(int index=0; index<jagged[row].GetLength(0);
++index) {
total+=jagged[row][index];
}
Console.Write("\nRow {0}: {1}",
row, total);
}
}
}
}
Vectors and multidimensional arrays are both collections. As such, arrays implement a combination of an
array and some collection-specific interfaces, which are encapsulated in the System.Array type.
System.Array implements the ICollection, IEnumerable, IList, and ICloneable interfaces. System.Array also
implements array-specific behaviors, such as the Array.GetLength method.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
System.Array
The System.Array type houses the fundamental methods and properties that are essential to an array. This
includes sorting, reversing, element count, synchronization, and much more. Table 5-2 lists the methods of
the System.Array type. Many of the methods are static, which is noted in the syntax. In addition, some
methods are for single-dimensional arrays and are not usable with multidimensional arrays. This fact is noted
in the method description in Table 5-2.
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
As
Re stati
ad c
Onl ReadO
y nlyCo
Thi llect
s ion<T
is >
a
ge AsRea
ner dOnly
ic <T>(
me
tho
d T[]
tha sourc
t eArra
ret y)
urn
s
a
rea
d-o
nly
wra
pp
er
for
an
arr
ay.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Bin
ary stati
Se c
arc int
h Binar
Thi ySear
s ch(
me
tho Array
d sourc
co eArra
nd y,
uct
s
a objec
bin t
ary searc
se hValu
arc e)
h
for
stati
a
c
sp
int
ecif
Binar
ic
ySear
val
ch<T>
ue
(
in
a
sor T[]
ted sourc
on eArra
e-d y,
im
en T
sio value
nal )
arr
ay.
Th
ere
are
sev
era
l
ove
rlo
ad
s
for
thi
s
me
tho
d.
Th
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Cle
ar stati
Thi c
s void
me Clear
tho (Arra
d y
set sourc
s eArra
a y,
ran
ge int
of index
ele ,
me int
nts lengt
to h)
zer
o,
null
,
or
fals
e.
Clo
ne seale
Thi d
s objec
me t
tho Clone
d ()
clo
ne
s
the
cur
ren
t
arr
ay.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Co
nst stati
rai c
ne void
dC Const
opy raine
dCopy
Thi {
s
me Array
tho sourc
d eArra
co y,
pie
s
a int
ran sourc
ge eInde
of x
ele
me Array
nts desti
fro natio
m nArra
the y,
so
urc
int
e
desti
arr
natio
ay
nInde
int
x,
o
a
de int
sti lengt
nat h)
ion
arr
ay.
Yo
u
set
the
so
urc
e
ind
ex
an
d
de
sti
nat
ion
ind
ex,
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Co
nve stati
rtAl c
l <dest
Thi inati
s onTyp
is e>
a
ge Conve
ner rtAll
ic <sour
me ceTyp
tho e,
d
tha
t desti
co natio
nve nType
rts >(
the
typ sourc
e eType
of sourc
an eArra
arr y,
ay.
Conve
rter<
sourc
eType
,
desti
natio
nType
>
conve
rter)
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Co
py stati
Thi c
s void
me Copy(
tho
d Array
co sourc
pie eArra
s y,
ele
me
nts Array
fro desti
m natio
the nArra
so y,
urc
e int
arr lengt
ay h)
to
the
de stati
sti c
nat void
ion Copy(
arr
ay. Array
Th sourc
e eArra
sp y,
ecif
ied
nu int
mb sourc
er eInde
of x,
ele
me Array
nts desti
is natio
co nArra
pie y,
d.
Th int
ere desti
are natio
fou nInde
r x,
ove
rlo int
ad lengt
s h)
to
thi
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Co
py void
To CopyT
Thi o(Arr
s ay
me desti
tho natio
d nArra
co y,
pie
s int
the index
cur )
ren
t void
on CopyT
e-d o(Arr
im ay
en desti
sio natio
nal nArra
arr y,
ay
to long
the index
de )
sti
nat
ion
arr
ay
sta
rtin
g
at
the
sp
ecif
ied
ind
ex.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Cre
ate stati
Ins c
tan Array
ce Creat
Thi eInst
s ance(
me
tho Type
d array
cre Type,
ate
s
an int
ins lengt
tan h)
ce
of
stati
an
c
arr
Array
ay
Creat
at
eInst
run
ance(
tim
e.
Type
Thi
array
s
Type,
me
tho
d int
ha rows,
s
sev int
era cols)
l
ove
rlo
ad
s.
On
e-d
im
en
sio
nal
an
d
two
-di
me
nsi
on
al
ver
sio
ns
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Exi
sts stati
c
Thi bool
s Exist
is <T> {
a
ge T []
ner sourc
ic eArra
me y,
tho
d
tha Predi
t cate<
co T>
nfir match
ms )
tha
t
an
ele
me
nt
ma
tch
es
the
co
ndi
tio
ns
set
in
the
pre
dic
ate
fun
cti
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Fin
d stati
Thi c T
s Find<
is T>(
a
ge T[]
ner sourc
ic eArra
me y,
tho
d
tha Predi
t cate<
find T>
s match
the )
firs
t
ele
me
nt
tha
t
ma
tch
es
the
co
ndi
tio
ns
set
in
the
pre
dic
ate
fun
cti
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Fin
dAl stati
l c
Thi T[]
s FindA
is ll<T>
a (
ge
ner T[]
ic sourc
me eArra
tho y,
d
tha
t Predi
ret cate<
urn T>
s match
all )
the
ele
me
nts
tha
t
ma
tch
the
co
ndi
tio
ns
set
in
the
pre
dic
ate
fun
cti
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Fin
dIn stati
dex c
int
Thi FindI
s ndex<
is T>(
a
ge T[]
ner sourc
ic eArra
me y,
tho
d
tha Predi
t cate<
ret T>
urn match
s )
the
ind
stati
ex
c
to
int
the
FindI
firs
ndex<
t
T>(
ele
me
nt T[]
tha sourc
t eArra
ma y,
tch
es int
the start
co ingIn
ndi dex,
tio
ns
set Predi
in cate<
the T>
pre match
dic )
ate
fun stati
cti c
on. int
FindI
ndex(
T[]
sourc
eArra
y,
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Fin
dL stati
ast c T
FindL
Thi ast<T
s >{
is
a T[]
ge sourc
ner eArra
ic y,
me
tho
d Predi
tha cate<
t T>
ret match
urn )
s
the
las
t
ele
me
nt
tha
t
ma
tch
es
the
co
ndi
tio
ns
set
in
the
pre
dic
ate
fun
cti
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Fin
dL stati
ast c
Ind int
ex FindL
Thi astIn
s dex(T
is []
a sourc
ge eArra
ner y,
ic
me Predi
tho cate<
d T>
tha match
t )
ret
urn
s stati
the c
ind int
ex FindL
to astIn
the dex(T
las []
t sourc
ele eArra
me y,
nt
tha
int
t
start
ma
ingIn
tch
dex,
es
the
co Predi
ndi cate<
tio T>
ns match
set )
in
the
pre stati
dic c
ate int
fun FindL
cti astIn
on. dex(T
[]
sourc
eArra
y,
int
start
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
For
Ea publi
ch c
Thi stati
s c
is void
a ForEa
ge ch<T>
ner (
ic
me T[]
tho array
d ,
tha
t
per Actio
for n<T>
ms actio
an n)
act
ion
on
ea
ch
ele
me
nt
of
the
arr
ay,
wh
ere
act
ion
ref
ers
to
a
fun
cti
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Ge
tEn seale
um d
era IEnum
tor erato
Thi r
s GetEn
me umera
tho tor()
d
ret
urn
s
an
en
um
era
tor
tha
t
im
ple
me
nts
the
en
um
era
tor
pat
ter
n
for
coll
ect
ion
s.
Yo
u
ca
n
en
um
era
te
the
ele
me
nts
of
the
arr
ay
wit
h
the
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Ge
tLe int
ngt GetLe
h ngth(
Thi int
s dimen
me sion)
tho
d
ret
urn
s
the
nu
mb
er
of
ele
me
nts
for
a
di
me
nsi
on
of
an
arr
ay.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Ge
tLo long
ng GetLo
Le ngLen
ngt gth(i
h nt
Thi dimen
s sion)
me
tho
d
ret
urn
s
the
nu
mb
er
of
ele
me
nts
as
a
64-
bit
int
eg
er
for
a
di
me
nsi
on
of
an
arr
ay.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Ge
tLo int
wer GetLo
Bo werBo
un und(i
d nt
Thi dimen
s sion)
me
tho
d
ret
urn
s
the
low
er
bo
un
d
of
a
di
me
nsi
on,
whi
ch
is
us
uall
y
zer
o.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Ge
tUp int
per GetUp
Bo perBo
un und(i
d nt
Thi dimen
s sion)
me
tho
d
ret
urn
s
the
up
per
bo
un
d
of
a
di
me
nsi
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Ge
tVa objec
lue t
GetVa
Thi lue(i
s nt
me index
tho )
d
ret
urn objec
s t
the GetVa
val lue(p
ue arams
of int[]
an indic
ele es)
me
nt
at
the
sp
ecif
ied
ind
ex.
Thi
s
me
tho
d
ha
s
sev
era
l
ove
rlo
ad
s.
A
on
e-d
im
en
sio
nal
ver
sio
n
an
d
a
mu
ltidi
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Ind
ex stati
Of c
Thi int
s Index
me Of(Ar
tho ray
d sourc
ret eArra
urn y,
s
the objec
ind t
ex find)
of
the Ge
firs ner
t ic
ele ver
me sio
nt n:
in
a
stati
on
c
e-d
int
im
Index
en
Of<T>
sio
(T[]
nal
sourc
arr
eArra
ay
y,
tha
t
ha T
s value
the )
sp
ecif
ied
val
ue.
Thi
s
me
tho
d
ha
s
sev
era
l
ove
rlo
ad
s.
A
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Init
iali void
ze Initi
Thi alize
s ()
me
tho
d
initi
aliz
es
eve
ry
ele
me
nt
of
the
arr
ay.
Th
e
def
aul
t
co
nst
ruc
tor
of
ea
ch
ele
me
nt
is
call
ed.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
La
stI stati
nd c
ex int
Of LastI
Thi ndexO
s f(Arr
me ay
tho sourc
d eArra
ret y,
urn
s objec
the t
ind value
ex )
of
the Ge
las ner
t ic
ele ver
me sio
nt n:
tha
t
stati
ma
c
tch
int
es
LastI
the
ndexO
sp
f<T>(
ecif
T[]
ied
sourc
val
eArra
ue
y,
in
a
on T
e-d value
im )
en
sio
nal
arr
ay.
Thi
s
me
tho
d
ha
s
sev
era
l
ove
rlo
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Re
siz stati
e c
Thi void
s Resiz
is e<T>(
a
ge ref
ner T[]
ic sourc
me eArra
tho y,
d
tha
t int
ch newSi
an ze)
ge
s
the
siz
e
of
a
on
e-d
im
en
sio
nal
arr
ay.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Re
ver stati
se c
Thi void
s Rever
me se(Ar
tho ray
d sourc
rev eArra
ers y)
es
the
ord stati
er c
of void
ele Rever
me se(Ar
nts ray
in sourc
a eArra
on y,
e-d
im int
en index
sio ,
nal int
arr lengt
ay. h)
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Set
Val void
ue SetVa
Thi lue(o
s bject
me value
tho ,
d int
set index
s )
the
val
ue void
of SetVa
a lue(o
sp bject
ecif value
ic ,
ele
me param
nt s
of int[]
the indic
cur es)
ren
t
on
e-d
im
en
sio
nal
arr
ay.
Thi
s
me
tho
d
ha
s
sev
era
l
ove
rlo
ad
s.
Tw
o
of
the
ove
rlo
ad
s
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Sor
t stati
Thi c
s void
me Sort(
tho Array
d sourc
sor eArra
ts y)
the
ele
me stati
nts c
of void
a Sort<
on T>(
e-d
im T[]
en sourc
sio eArra
nal y)
arr
ay.
Thi
s
me
tho
d
ha
s
sev
era
l
ove
rlo
ad
s.
A
no
ng
en
eri
c
ver
sio
n
an
d
a
ge
ner
ic
ver
sio
n
of
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-2:
System.Array
Members
De Sy
scr nta
ipti x
on
Tru
eF stati
orA c
ll bool
Thi TrueF
s orAll
is <T>(
a
ge T[]
ner array
ic ,
me
tho
d Predi
tha cate<
t T>
ret match
urn )
s
tru
e
if
all
ele
me
nts
of
an
arr
ay
ma
tch
the
co
ndi
tio
ns
set
in
the
pre
dic
ate
fun
cti
on.
The following sections offer sample code and additional descriptions for some of the System.Array methods.
Array.AsReadOnly Method
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The following code creates and initializes an integer array. The second element of the array is then modified,
which demonstrates the read-write capability of the collection. Array .AsReadOnly is called to wrap the array
in a read-only collection. The ReadOnlyCollection type is found in the System.Collections.ObjectModel
namespace. After displaying the elements of the read-only collection, the code attempts to modify an
element. Since the collection is read-only, a compile error occurs at this line:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
int [] zArray={1,2,3,4};
zArray[1]=10;
ReadOnlyCollection<int> roArray=Array.AsReadOnly(zArray);
foreach(int number in roArray) {
Console.WriteLine(number);
}
roArray[1]=2; // compile error
}
}
}
Array.Clone Method
In the following code, the CommissionedEmployee type inherits from the Employee type. An array of
commissioned employees is defined and then cloned with the Clone method. The clone type is an array of
Employees. Because Clone returns an object array, which is unspecific, you should cast to a specific array
type. The cast from System.Object is not type-safe, and an incorrect cast would cause an exception.
Polymorphism is employed in the foreach loop to call the Pay method of the derived class.
using System;
using System.Collections.Generic;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
CommissionedEmployee [] salespeople=
{new CommissionedEmployee("Bob"),
new CommissionedEmployee("Ted"),
new CommissionedEmployee("Sally")};
Employee [] employees=
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
(Employee [])salespeople.Clone();
foreach(Employee person in
employees) {
person.Pay();
}
}
}
Array.CreateInstance Method
The following code demonstrates both the CreateInstance and SetValue methods. CreateInstance creates a
new array at run time. This requires some degree of reflection, which will be discussed in Chapter 10,
"Metadata and Reflection." This code reads the array type, method to call, and initial values for each element
from the command line. CreateInstance creates a new array using the type name read from the command
line. In the for loop, Activator.CreateInstance creates instances of the element type, where values from the
command line are used to initialize the object. In the foreach loop, the elements of the array are enumerated
while calling the method stipulated in the command-line arguments as the second parameter.
using System;
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
using System.Reflection;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(string [] argv){
Assembly executing=Assembly.GetExecutingAssembly();
Type t=executing.GetType(argv[0]);
Array zArray=Array.CreateInstance(
t, argv.Length-2);
for(int count=2;count<argv.Length;++count) {
System.Object obj=Activator.CreateInstance(t, new object[] {
argv[count]});
zArray.SetValue(obj, count-2);
}
foreach(object item in zArray) {
MethodInfo m=t.GetMethod(argv[1]);
m.Invoke(item, null);
}
}
}
Console.WriteLine(m_Info);
}
A typical command line and results of the application are shown in Figure 5-2.
Figure 5-2: A command line and the results from running the application
Array.FindAll Method
Several System.Array methods use predicates, such as the Exists, Find, FindAll, and FindLastIndex
methods. Predicates are delegates initialized with functions that find matching elements. The predicate
function is called for each element of the array. A conditional test is performed in the function to isolate
matching elements; true or false is returned from the predicate indicating that a match has or has not been
found, respectively.
Predicate methods are generic methods. The type parameter indicates the element type. The return value is
the result of the comparison.
The following code finds all elements equal to three. MethodA is the predicate method, which compares each
value to three.
Array.Resize Method
The Resize method resizes a one-dimensional array.
Here is sample code for resizing an array. The elements added to the array are initialized to a default value.
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
int [] zArray={1,2,3,4};
Array.Resize<int>(ref zArray, 8);
foreach(int number in zArray) {
Console.WriteLine(number);
}
}
}
}
System.Array Properties
System.Array has several properties that are useful when working with arrays. Table 5-3 lists the various
properties.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-3:
System.Array
Properties
Open table as
spreadsheet
De Sy
scr nta
ipti x
on
IsF
ixe virtu
dSi al
ze bool
Thi IsFix
s edSiz
pro e{
per
ty get;}
ret
urn
s
tru
e
if
the
arr
ay
is
a
fixe
d
siz
e.
Ot
her
wis
e,
fal
se
is
ret
urn
ed.
Al
wa
ys
tru
e
for
arr
ay
s.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-3:
System.Array
Properties
Open table as
spreadsheet
De Sy
scr nta
ipti x
on
IsR
ea virtu
dO al
nly bool
Thi IsRea
s dOnly
pro {
per
ty get;}
ret
urn
s
tru
e
if
the
arr
ay
is
rea
d-o
nly
.
Ot
her
wis
e,
fal
se
is
ret
urn
ed.
Al
wa
ys
fal
se
for
arr
ay
s.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-3:
System.Array
Properties
Open table as
spreadsheet
De Sy
scr nta
ipti x
on
IsS
ync virtu
hro al
niz bool
ed IsSyn
Thi chron
s ized{
pro
per get;}
ty
ret
urn
s
tru
e
if
the
arr
ay
is
thr
ea
d-s
afe
.
Ot
her
wis
e,
fal
se
is
ret
urn
ed.
Al
wa
ys
fal
se
for
arr
ay
s.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-3:
System.Array
Properties
Open table as
spreadsheet
De Sy
scr nta
ipti x
on
Le
ngt int
h Lengt
Thi h{
s
pro get;}
per
ty
ret
urn
s
the
nu
mb
er
of
ele
me
nts
in
the
arr
ay.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-3:
System.Array
Properties
Open table as
spreadsheet
De Sy
scr nta
ipti x
on
Lo
ng Long
Le LongL
ngt ength
h {
Thi
s get;}
pro
per
ty
ret
urn
s
the
nu
mb
er
of
ele
me
nts
in
the
arr
ay
as
a
64-
bit
val
ue.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-3:
System.Array
Properties
Open table as
spreadsheet
De Sy
scr nta
ipti x
on
Ra
nk int
Thi Rank{
s
pro get;}
per
ty
ret
urn
s
the
ran
k
of
the
arr
ay,
whi
ch
is
the
nu
mb
er
of
di
me
nsi
on
s.
For
ex
am
ple
,a
two
-di
me
nsi
on
al
arr
ay
ha
s
a
ran
k
of
two
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-3:
System.Array
Properties
Open table as
spreadsheet
De Sy
scr nta
ipti x
on
Sy
nc virtu
Ro al
ot objec
Thi t
s SyncR
pro oot{
per
ty get;}
ret
urn
s
a
sy
nc
hro
niz
ati
on
obj
ect
for
the
cur
ren
t
arr
ay.
Arr
ay
s
are
not
inh
ere
ntl
y
thr
ea
d-s
afe
.
Sy
nc
hro
niz
e
ac
ce
ss
to
the
arr
ay
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Many of the preceding properties are used in sample code. The SyncRoot property is particularly important
and not included in previous sample code.
Array.SyncRoot Property
The purpose of the SyncRoot object is to synchronize access to an array. Arrays are unsafe data structures.
As documented, the IsSynchronized property always returns false for an array. Accesses to arrays are
easily synchronized with the lock statement, where the SyncRoot object is the parameter.
In the following code, the array is a field in the Starter class. The DisplayForward and DisplayReverse
methods list array elements in forward and reverse order correspondingly. The functions are invocated at
threads, in which the SyncLock property prevents simultaneous access in the concurrent threads.
using System;
using System.Threading;
namespace Donis.CSharpBook{
public class Starter{
Array.Reverse(zArray);
}
}
}
}
Comparable Elements
System.Array methods require elements to be instances of comparable types.
Array.IndexOf
Array.LastIndexOf
Array.Sort
Array.Reverse
Array.BinarySearch
Comparable types implement the IComparable interface, which requires the implementation of the
CompareTo method. The CompareTo method returns zero when the current and target instances are equal. If
the current instance is less than the target, a negative value is returned. A positive value is returned if the
current instance is greater than the target. The preceding methods call IComparable.CompareTo to perform
the required comparisons to sort, reverse, and otherwise access the array in an ordered manner.
A run-time error occurs in the following code when Array.Sort is called. Why? The XClass does not
implement the IComparable interface.
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
XClass [] objs={new XClass(5), new XClass(10),
new XClass(1)};
Array.Sort(objs);
}
}
Here is the proper code, where the XClass implements the IComparable interface. This program runs
successfully.
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
XClass [] objs={new XClass(5), new XClass(10),
new XClass(1)};
Array.Sort(objs);
foreach(XClass obj in objs) {
Console.WriteLine(obj.Number);
}
}
}
Many of the methods and properties of System.Array are mandated in interfaces that the type implements.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ICollection Interface
The ICollection interface is one of the interfaces implemented in System.Array type; it returns the count of
elements and supports synchronization of collections. The members of the ICollection interface are as
follows:
CopyTo method
Count property
IsSynchronized property
SyncRoot property
ICloneable Interface
System.Array implements the ICloneable interface. This is the interface for duplicating an object, such as an
array. The only member of this interface is the Clone method.
IEnumerable
System.Array type implements the IEnumerable interface. IEnumerable.GetEnumerator is the sole member
of this interface. GetEnumerator returns an enumerator object that implements the IEnumerator interface.
The enumerator provides a consistent interface for enumerating any collection. The benefit is the ability to
write a generic algorithm, which requires enumerating the elements of a collection in a consistent manner.
For example, the foreach statement uses an enumerator object to consistently iterate all collection types.
using System;
using System.Collections;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
int [] numbers={1,2,3,4,5};
IEnumerator e=numbers.GetEnumerator();
while(e.MoveNext()) {
Console.WriteLine(e.Current);
}
}
}
}
IList Interface
System.Array type implements the IList interface. However, only part of this implementation is publicly
available. The private implementation of some IList members effectively removes those members from the
public interface. These are members contrary to the array paradigm, such as the RemoveAt method. Arrays
are immutable. You cannot remove elements from the middle of an array.
Table 5-4 lists the IList interface and indicates the public versus private implementation in System.Array.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Me Pu
mb bli
er c
Na or
me Pri
vat
e
Ad Pri
d vat
e
Cle Pu
ar blic
Co Pri
nta vat
ins e
Ind Pu
ex blic
Of
Ins Pri
ert vat
e
Re Pri
mo vat
ve e
Re Pri
mo vat
ve e
At
Indexers
You can treat instances as arrays by using indexers. Indexers are ideal for types that wrap collections, in
which additional functionality is added to the type that augments the collection. Access the object with the
indexing operator to get or set the underlying collection. Indexers are a combination of an array and a
property. The underlying data of an indexer is often an array or a collection. The indexer defines a set and get
method for the this reference. Indexers are considered a default property because the indexer is a nameless
property. Internally, the compiler inserts the get_Item and set_Item method in support of indexers.
Indexers are fairly typical properties. However, there are some exceptions. Here are some of the similarities
and differences between indexers and properties:
Indexers can be overloaded.
Indexers can be overridden.
Indexers can be added to interfaces.
Indexers support the standard access modifiers.
Indexers cannot be a static member.
Indexers are nameless and associated with the this reference.
Indexers parameters are indices. Properties do not have indices.
Indexers in the base class are accessed as base[indices], while a similar property is accessed
base.Property.
Except for static accessibility, indexers have the same accessibility and modifiers of a normal property.
Indexers cannot be static. The parameters are a comma-delimited list of indexes. The list includes the type
and name of each parameter. Indexer indices can be nonintegral types, such as string and even Employee
types.
The following is example code for indexers. The Names type is a wrapper of an array of names and ages.
The indexer is read-only in this example and provides access to the array field. Per the parameters of the
indexer, the indexer manipulates object elements and has a single index. However, the _names array is
two-dimensional, which requires two indexes. Flexibility is one of the benefits of indexers versus standard
arrays. In the sample code, the parameter of the indexer represents the row, whereas the column is a
constant.
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
Names obj=new Names();
Console.WriteLine(obj[1]);
}
}
{"Ben", 35},
{"Donis", 29}};
Indexers can be overloaded based on the parameter list. Overloaded indexers should have a varying number
of parameters or parameter types. The following code overloads the indexer property twice. The first property
is read-only and returns the name and age information. The second overload is a read-write property that
sets and gets the age of a person. This property, which uses a string parameter, also demonstrates
non-numerical indexes.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
Names obj=new Names();
obj["Donis"]=42;
Console.WriteLine(obj["Donis"]);
}
}
get {
int index=FindName(sIndex);
return _names[index, 1];
}
set {
int index=FindName(sIndex);
_names[index, 1]=value;
}
}
++index) {
if((string)(_names[index,0])==sIndex) {
return index;
}
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
params Keyword
The params keyword is a parameter modifier, which indicates that a parameter is a one-dimensional array of
any length. This defines a variable-length parameter list. The params modifier can be applied to only one
parameter in the parameter list, which must be the final parameter. The ref and out modifiers cannot be
applied to the params modified parameter. You have probably used methods of the Microsoft .NET
Framework class library (FCL) that have variable-length parameter lists, such as Console.WriteLine.
Console.WriteLine accepts a variable number of arguments. It has a param parameter.
Initialize the params-modified argument with an implicit or explicit array. This is done at the call site. For
implicit initialization, the C# compiler consumes the optional parameters that follow the fixed parameter list.
The fixed parameters are the parameters that precede the params-modified parameter in the function
signature. The optional arguments are consolidated into an array. If there are six optional arguments after the
fixed arguments, an array of six elements is created and initialized with the values of the optional arguments.
Alternatively, an explicit array can be used. Finally, the params argument can be omitted. When omitted, the
params parameter is assigned an empty array. An empty array is different from a null array. Empty arrays
have no elements but are valid instances.
In the following code, Names is a static method, where the params modifier is applied to the second
parameter. Therefore, the employees argument is a single-dimensional string array, and the Names method
offers a variable-length parameter list.
For a variable-length parameter list, the number of parameters, which includes the parameter array, is set at
the called site. The following code shows the Names method being called with varying numbers of
parameters. In both calls, the first parameter is consumed by the company parameter. The remaining
parameters are used to create an array, which is assigned to the params parameter. A three-argument array
is created for the first method call, whereas the second method creates a six-argument array that is
assigned to the params parameter.
The following code calls the Names method with an explicit array. This is identical to calling the method with
three optional arguments.
In this statement, the Names method is called without a params argument. For the omitted parameter, the
compiler creates an array with no elements, which is subsequently passed to the method.
Names("Fabrikam");
Variable-length methods can be overloaded similar to any method. You can even overload a method with a
fixed number of parameters with a method with a variable number of parameters. Where there is ambiguity,
the method with the fixed number of parameters is preferred and called.
In the following code, the Names method is overloaded with three methods. The first two overloads have a
variable-length parameter list, whereas the final method has a fixed-length parameter list. In Main, the first
two calls of the Names method are not ambiguous. The final call is ambiguous and can resolve to either the
first or third overload. Because the third overload has a fixed-length parameter list, it is called instead of the
first overloaded method.
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
Names("Fabrikam","Fred", "Bob", "Alice");
Names("Fabrikam",1234, 5678, 9876, 4561);
Names("Fabrikam","Carter", "Deborah");
}
Array Conversion
You can cast between arrays. Arrays are implicit System.Array types. For that reason, regardless of type or
the number of dimensions, any array can be cast to System.Array. All arrays are compatible with that type.
When casting or converting between arrays, the source and destination array are required to have the same
dimensions. In addition, an array of value types is convertible only to arrays of the same dimension and type.
Arrays of reference types are somewhat more flexible. Arrays of reference types can be converted to arrays
of the same or ascendant type. This is called array covariance. Array reference types are covariant, whereas
arrays of value types are not.
Arrays can be inserted as function parameters and returns. In these roles, proper conversion is important.
In the following code, the ZClass has two static methods, which both have an array parameter. The ListArray
method has a System.Array parameter, which accepts any array argument. For this reason, the ListArray
method is called in Main with different types of array arguments. The Total method limits array arguments to
one-dimensional integer arrays.
using System;
namespace Donis.CSharpBook{
public class Starter{
Arrays can also be returned from functions, which is one means of returning more than a single value. You
can return multiple values as elements of an array. Returning an array gives the calling function a reference
to the array, which provides direct access to the array. Arrays are not returned on the stack.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Collections
Arrays are the most popular collection. However, the .NET FCL offers a variety of other collections with
different semantics. Collections are abstractions of data algorithms. ArrayList is an abstraction of a dynamic
array, the Stack collection abstracts a stack data structure, the Queue collection abstracts queues, the
Hashtable collection abstracts a lookup table, and so on. Each collection exposes both unique and standard
interfaces. The unique interface is specific to the collection type. For example, the Stack type has pop and
push methods, whereas the Queue type has Dequeue and Enqueue methods. Collections minimally
implement the ICollection, IEnumerable, and ICloneable interfaces. (These interfaces were described earlier
in this chapter.) The nongeneric collection classes are implemented in the System.Collections namespace.
Generic collections are reviewed in Chapter 6, "Generics."
What follows is a detailed explanation of each collection found in the Collections namespace. The
explanations are complemented with sample code, which demonstrates the uniqueness of each collection.
ArrayList Collection
An array list is a dynamic array. Although indigenous arrays are static, elements can be added or removed
from an ArrayList at run time. Elements of the ArrayList are not automatically sorted. Similar to
single-dimensional arrays, the elements of an ArrayList are accessible using the indexing operator and
indices.
In addition to the standard collection interfaces, ArrayList implements the IList interface.
Table 5-6 lists the ArrayList-specific methods and properties. The static members of ArrayList are
thread-safe, whereas instance members are not. The common collection interfaces—ICollection,
IEnumerable, and ICloneable—are not discussed in detail. (These interfaces and their members were
reviewed earlier in the chapter.)
Table 5-6: ArrayList Members
Constructors
ArrayList()
ArrayList(
ICollection
sourceCollection)
ArrayList(int capacity)
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Adapter
This method creates a wrapper for an IList collection. static ArrayList
Adapter(
IList list)
Add
This method adds an element to the end of the ArrayList collection. virtual int Add(object
value)
AddRange
This method adds a range of elements to the ArrayList collection. The virtual void
elements are input from an ICollection type, such as a regular array. AddRange(
ICollection
elements)
BinarySearch
This method performs a binary search for a specific value in a sorted array. virtual int
BinarySearch(
object value)
virtual int
BinarySearch(
object value,
IComparer
comparer)
virtual int
BinarySearch(
int index,
int count,
object value,
IComparer
comparer)
Capacity
This property gets or sets the number of properties allowed in the collection. virtual int Capacity
The default capacity is 16 elements. Capacity is different from the number of {
elements. The capacity is automatically increased as elements are added. get; set}
When the number of elements exceeds the current capacity, the capacity
doubles. Capacity is for better memory management of elements in the
collection.
Clear
This method removes all the elements of the collection. virtual void Clear()
Contains
This method returns true if the specified item is found in a collection. If the virtual bool
item is not found, false is returned. Contains(object item)
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Count
This property returns the number of elements in the collection. virtual int Count{
get; }
FixedSize
This method creates a wrapper for an ArrayList or IList collection, in which static ArrayList
elements cannot be added or removed. FixedSize(
ArrayList
sourceArray)
static IList
FixedSize(
IList sourceList)
GetRange
This method returns a span of elements from the current array. The result is virtual ArrayList
stored in a destination ArrayList. GetRange(
int index, int
count)
IndexOf
This method returns the index of the first matching element in the collection. virtual int IndexOf(
object value)
virtual int
IndexOf(object value,
int startIndex)
virtual int
IndexOf(object value,
int startIndex,
int count)
Insert
This method inserts an element into the collection at the specified index. virtual void
Insert(int index,
object value)
InsertRange
This method inserts multiple elements into the collection at the specified virtual void
index. InsertRange(
int index,
ICollection
sourceCollection)
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
IsFixedSize
This property returns true if the collection is fixed-length. Otherwise, the virtual bool
property returns false. IsFixedSize{
get;}
IsReadOnly
This property returns true if the collection is read-only. Otherwise, the virtual bool
property returns false. IsReadOnly{
get;}
Item
This property gets or sets the element at the index. virtual object
this[int index] {
get;set;}
LastIndexOf
This method returns the index of the last matching element in the collection. virtual int
LastIndex(
object value)
virtual int
LastIndexOf(object
value,
int startIndex)
virtual int
LastIndexOf(object
value,
int startIndex,
int count)
ReadOnly
This method creates a read-only wrapper for an IList object. static ArrayList
ReadOnly(
ArrayList
sourceArray)
static IList
ReadOnly(
IList sourceList)
Remove
This method removes the first element in the collection that matches the virtual void Remove(
value. object value)
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
RemoveAt
This method removes the element at the index from the collection. virtual void
RemoveAt(
int index)
RemoveRange
This method removes a range of elements from a collection. virtual void
RemoveRange(
int index,
int count)
Repeat
This method returns an ArrayList with each element initialized to the same static ArrayList
value. Count is the number of times to replicate the value. Repeat(
object value,
int count)
Reverse
This method reverses the order of elements in the collection. virtual void
Reverse()
SetRange
This method copies elements from a collection into the same elements in virtual void
the ArrayList collection. SetRange(
int index,
ICollection
sourceCollection)
Sort
This method sorts an ArrayList. virtual void Sort()
virtual void
Sort(int index,
int count,
IComparer
comparer)
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Synchronized
This method returns a thread-safe wrapper of an ArrayList or IList object. static ArrayList
Synchronized(
ArrayList
sourceArray)
static IList
Synchronized(
IList sourceList)
ToArray
This method copies elements from the current array into a new collection. virtual object []
ToArray()
virtual Array
ToArray(Type type)
TrimToSize
This method sets the capacity to the number of elements in the collection. virtual void
TrimToSize()
The following code uses various ArrayList methods and properties. It creates a new ArrayList, which is then
initialized with command-line parameters. The Add method is called to add elements to the ArrayList. The
ArrayList is then sorted and cloned. Then the values at the elements of the cloned ArrayList are doubled.
Afterward, the cloned ArrayList is enumerated and every element is displayed.
using System;
using System.Collections;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(string [] argv){
al2[count]=((int)al2[count])*2;
}
foreach(int number in al2) {
Console.WriteLine(number);
}
}
}
}
BitArray Collection
The BitArray collection is a composite of bit values. Bit values are 1 and 0, where 1 is true and 0 false. This
collection provides an efficient means of storing and retrieving bit values.
Table 5-7 list the BitArray-specific methods and properties. The static members of the BitArray are
thread-safe, whereas instance members are not.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-7:
BitArray
Members
Me Sy
mb nta
er x
Na
me
Co
nst BitAr
ruc ray(b
tor ool
Th []
e bits)
Bit
Arr
ay BitAr
co ray(i
nst nt
ruc []
tor bits)
is
ove
BitAr
rlo
ray(i
ad
nt
ed.
count
Th
,
es
e
are bool
so defau
me lt)
of
the
ove
rlo
ad
ed
co
nst
ruc
tor
s.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-7:
BitArray
Members
Me Sy
mb nta
er x
Na
me
An
d BitAr
Thi ray
s And(B
me itArr
tho ay
d value
per )
for
ms
a
bit
wis
e
An
d
on
the
cur
ren
t
an
d
Bit
Arr
ay
par
am
ete
r.
Th
e
res
ult
is
pla
ce
d
in
the
ret
urn
ed
Bit
Arr
ay.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-7:
BitArray
Members
Me Sy
mb nta
er x
Na
me
Ge
t bool
Thi Get(i
s nt
me index
tho )
d
ret
urn
s
a
sp
ecif
ic
bit
in
the
Bit
Arr
ay
coll
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-7:
BitArray
Members
Me Sy
mb nta
er x
Na
me
IsR
ea virtu
dO al
nly bool
Thi IsRea
s dOnly
pro {
per
ty get;}
ret
urn
s
tru
e
if
the
coll
ect
ion
is
rea
d-o
nly
.
Ot
her
wis
e,
the
pro
per
ty
ret
urn
s
fal
se.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-7:
BitArray
Members
Me Sy
mb nta
er x
Na
me
Ite
m virtu
Thi al
s objec
pro t
per this[
ty int
get index
s ] {
or
set get;s
s et;}
the
bit
at
the
ind
ex.
Le
ngt publi
h c
Thi int
s Lengt
pro h{
per
ty get;
get set;
s }
or
set
s
the
nu
mb
er
of
bit
s
in
the
coll
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-7:
BitArray
Members
Me Sy
mb nta
er x
Na
me
Not
BitAr
Thi ray
s Not()
me
tho
d
ne
gat
es
the
bit
s
of
the
Bit
Arr
ay
coll
ect
ion
.
Th
e
res
ult
is
pla
ce
d
in
the
ret
urn
ed
Bit
Arr
ay.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-7:
BitArray
Members
Me Sy
mb nta
er x
Na
me
Or
Thi BitAr
s ray
me Or(Bi
tho tArra
d y
per value
for )
ms
a
bit
wis
e
Or
on
the
cur
ren
t
an
d
Bit
Arr
ay
par
am
ete
r.
Th
e
res
ult
is
pla
ce
d
in
the
ret
urn
ed
Bit
Arr
ay.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-7:
BitArray
Members
Me Sy
mb nta
er x
Na
me
Set
Void
Thi Set(i
s nt
me index
tho ,
d bool
set value
s )
a
sp
ecif
ic
bit
in
the
coll
ect
ion
.
Set
All void
Thi SetAl
s l(boo
me l
tho value
d )
set
s
all
the
bit
s
of
the
coll
ect
ion
to
tru
e
or
fal
se.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-7:
BitArray
Members
Me Sy
mb nta
er x
Na
me
Xor
BitAr
Thi ray
s Xor(
me
tho BitAr
d ray
per value
for )
ms
an
ex
clu
siv
e
OR
on
the
cur
ren
t
coll
ect
ion
an
d
the
Bit
Arr
ay
par
am
ete
r.
IEn Ge
um tEn
era um
ble era
me tor
mb
ers
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-7:
BitArray
Members
Me Sy
mb nta
er x
Na
me
ICl Clo
on ne
ea
ble
me
mb
ers
ICo Co
llec py
tio To,
n Co
me unt
mb ,
ers IsS
ync
hro
niz
ed
,
an
d
Sy
nc
Ro
ot
The following code demonstrates the BitArray collection. The Employee class contains a BitArray collection
that tracks employee enrollment in various programs, such as the health plan and credit union. This is
convenient because enrollment is either true or false and never maybe. In the Employee class, properties are
provided to set and get enrollment in various programs.
using System;
using System.Collections;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
Employee ben=new Employee();
ben.InProfitSharing=false;
ben.InHealthPlan=false;
Employee valerie=new Employee();
valerie.InProfitSharing=false;
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Participation("Ben", ben);
Participation("Valerie", valerie);
}
public Employee() {
eflags.SetAll(true);
}
}
}
return eflags.Get(2);
}
}
}
}
Hashtable Collection
The Hashtable collection is a collection of key/value pairs. Entries in this collection are instances of the
DictionaryEntry type. DictionaryEntry types have a Key and Value property to get and set keys and values.
In addition to the standard collection interfaces, the Hashtable collection implements the IDictionary,
ISerializable, and IDeserializationCallback interfaces.
The entries are stored and retrieved in order based on a hash code of the key.
Table 5-8:
Hashtable
Members
Me Sy
mb nta
er x
Na
me
Co
nst Hasht
ruc able(
tor )
Th Hasht
e able(
Ha int
sht capac
abl ity)
e
co
nst Hasht
ruc able(
tor int
is capac
ove ity,
rlo
ad
float
ed.
loadF
Th
actor
es
)
e
sy
nta
xe
s
are
so
me
of
the
ove
rlo
ad
ed
co
nst
ruc
tor
s.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-8:
Hashtable
Members
Me Sy
mb nta
er x
Na
me
Ad
d virtu
Thi al
s void
me Add(o
tho bject
d key,
ad
ds objec
an t
ele value
me )
nt
to
the
coll
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-8:
Hashtable
Members
Me Sy
mb nta
er x
Na
me
Co
nta virtu
ins al
bool
Thi Conta
s ins(
me
tho objec
d t
ret key)
urn
s
tru
e
if
the
ke
y
is
fou
nd
in
the
coll
ect
ion
. If
the
ke
y
is
not
pre
se
nt,
fal
se
is
ret
urn
ed.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-8:
Hashtable
Members
Me Sy
mb nta
er x
Na
me
Co
nta virtu
ins al
Ke bool
y Conta
Thi insKe
s y(
me
tho objec
d t
ret key)
urn
s
tru
e
if
the
ke
y
is
fou
nd
in
the
coll
ect
ion
. If
the
ke
y
is
not
pre
se
nt,
fal
se
is
ret
urn
ed.
Ide
nti
cal
to
the
Co
nta
ins
me
tho
d.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-8:
Hashtable
Members
Me Sy
mb nta
er x
Na
me
Co
nta virtu
ins al
Val bool
ue Conta
Thi insVa
s lue(
me
tho objec
d t
ret value
urn )
s
tru
e
if
the
val
ue
is
fou
nd
in
the
coll
ect
ion
. If
the
val
ue
is
not
pre
se
nt,
fal
se
is
ret
urn
ed.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-8:
Hashtable
Members
Me Sy
mb nta
er x
Na
me
Eq
uali IEqua
tyC lityC
om ompar
par er
er Equal
ityCo
mpare
r {
get;}
.
Ge
tHa virtu
sh al
Thi int
s GetHa
me sh(
tho
d objec
ret t
urn key)
s
the
ha
sh
co
de
for
the
sp
ecif
ied
ke
y.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-8:
Hashtable
Members
Me Sy
mb nta
er x
Na
me
Ge
tO virtu
bje al
ctD void
ata GetOb
jectD
Thi ata(
s
is Seria
the lizat
me ionIn
tho fo
d info,
im
ple
me Strea
nte mingC
d ontex
to t
ser conte
iali xt)
ze
the
coll
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-8:
Hashtable
Members
Me Sy
mb nta
er x
Na
me
IsF
ixe virtu
dSi al
ze bool
Thi IsFix
s edSiz
pro e{
per
ty get;}
ret
urn
s
tru
e
if
the
coll
ect
ion
is
fixe
d
siz
e.
Ot
her
wis
e,
fal
se
is
ret
urn
ed.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-8:
Hashtable
Members
Me Sy
mb nta
er x
Na
me
IsR
ea virtu
dO al
nly bool
Thi IsRea
s dOnly
pro {
per
ty get;}
ret
urn
s
tru
e
if
the
coll
ect
ion
is
rea
d-o
nly
.
Ot
her
wis
e,
fal
se
is
ret
urn
ed.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-8:
Hashtable
Members
Me Sy
mb nta
er x
Na
me
IsS
ync virtu
hro al
niz bool
ed IsSyn
Thi chron
s ized{
pro
per get;}
ty
ret
urn
s
tru
e
if
the
coll
ect
ion
is
sy
nc
hro
niz
ed.
Ite
m virtu
Thi al
s objec
pro t
per this[
ty objec
get t
s key]{
or
set get;
s set;}
a
val
ue
rel
ate
d
to
a
ke
y.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-8:
Hashtable
Members
Me Sy
mb nta
er x
Na
me
Ke
yE virtu
qu al
als bool
KeyEq
Thi uals(
s
me objec
tho t
d item,
co
mp
are objec
s t
a key)
ke
y
to
a
val
ue.
If
eq
ual
,
tru
e
is
ret
urn
ed.
Ot
her
wis
e,
fal
se
is
ret
urn
ed.
Thi
s
me
tho
d
is
pri
ma
rily
us
ed
to
co
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-8:
Hashtable
Members
Me Sy
mb nta
er x
Na
me
Ke
ys virtu
Ret al
urn IColl
s ectio
a n
coll Keys{
ect
ion get;}
tha
t
co
nta
ins
the
ke
ys
of
the
Ha
sht
abl
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-8:
Hashtable
Members
Me Sy
mb nta
er x
Na
me
On
De virtu
ser al
iali void
zat OnDes
ion erial
izati
Thi on(
s
me objec
tho t
d sende
is r)
call
ed
wh
en
de
ser
iali
zat
ion
is
co
mp
let
ed.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-8:
Hashtable
Members
Me Sy
mb nta
er x
Na
me
Re
mo virtu
ve al
Thi void
s Remov
me e(
tho
d objec
re t
mo key)
ves
an
ele
me
nt
wit
h
the
sp
ecif
ied
ke
y
fro
m
the
coll
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-8:
Hashtable
Members
Me Sy
mb nta
er x
Na
me
Sy
nc stati
hro c
niz Hasht
ed able
Thi Synch
s roniz
me ed(
tho
d Hasht
ret able
urn sourc
s eTabl
a e)
thr
ea
d-s
afe
wra
pp
er
of
the
coll
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-8:
Hashtable
Members
Me Sy
mb nta
er x
Na
me
Val
ue virtu
s al
Ret IColl
urn ectio
s n
a value
coll s{
ect
ion get;}
tha
t
ha
s
the
val
ue
s
of
the
Ha
sht
abl
e.
IEn Ge
um tEn
era um
ble era
me tor
mb
ers
ICl Clo
on ne
ea
ble
me
mb
ers
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-8:
Hashtable
Members
Me Sy
mb nta
er x
Na
me
ICo Co
llec py
tio To,
n Co
me unt
mb ,
ers IsS
ync
hro
niz
ed,
an
d
Sy
nc
Ro
ot
The following code extends the previous sample code for the BitArray collection. The program creates a
Hashtable, in which employee identifiers are the keys. The values associated with the keys are instances of
the Employee type.
using System;
using System.Collections;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
Hashtable employees=new Hashtable();
employees.Add("A100", new Employee(
"Ben", true, false, true));
employees.Add("V100", new Employee(
"Valerie", false, false, true));
Participation((Employee) employees["A100"]);
Participation((Employee) employees["V100"]);
}
if(person.InProfitSharing) {
Console.WriteLine(" Participating in"+
" Profit Sharing");
}
if(person.InHealthPlan) {
Console.WriteLine(" Participating in"+
" Health Plan");
}
if(person.InCreditUnion) {
Console.WriteLine(" Participating in"+
" Credit Union");
}
}
}
InCreditUnion=creditUnion;
}
using System;
using System.Collections;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
Hashtable zHash=new Hashtable();
zHash.Add("one", 1);
zHash.Add("two", 2);
zHash.Add("three", 3);
zHash.Add("four", 4);
IDictionaryEnumerator e=
zHash.GetEnumerator();
while(e.MoveNext()){
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Console.WriteLine(
"{0} {1}",
e.Key, e.Value);
}
}
}
}
Queue Collection
Queue collections abstract FIFO data structures. The initial capacity is 32 elements. Queue collections are
ideal for messaging components.
Table 5-9:
Queue
Members
Me Sy
mb nta
er x
Na
me
Co
nst publi
ruc c
tor Queue
()
publi
c
Queue
(
IColl
ectio
n
sourc
eColl
ectio
n)
publi
c
Queue
(int
capac
ity)
publi
c
Queue
(int
capac
ity,
float
facto
r)
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-9:
Queue
Members
Me Sy
mb nta
er x
Na
me
Cle
ar virtu
Thi al
s void
me Clear
tho ()
d
re
mo
ves
all
the
ele
me
nts
of
the
coll
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-9:
Queue
Members
Me Sy
mb nta
er x
Na
me
Co
nta virtu
ins al
bool
Thi Conta
s ins(o
me bject
tho value
d )
ret
urn
s
tru
e
if
the
sp
ecif
ied
val
ue
is
fou
nd
in
the
coll
ect
ion
. If
the
val
ue
is
not
fou
nd,
fal
se
is
ret
urn
ed.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-9:
Queue
Members
Me Sy
mb nta
er x
Na
me
De
qu virtu
eu al
e objec
Thi t
s Deque
me ue()
tho
d
re
mo
ves
an
d
ret
urn
s
the
firs
t
ele
me
nt
of
the
qu
eu
e.
En
qu virtu
eu al
e void
Thi Enque
s ue(
me
tho objec
d t
ad eleme
ds nt)
an
ele
me
nt
to
the
qu
eu
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-9:
Queue
Members
Me Sy
mb nta
er x
Na
me
Pe
ek virtu
Thi al
s objec
me t
tho Peek(
d )
ret
urn
s
the
firs
t
ele
me
nt
of
the
qu
eu
e
wit
ho
ut
re
mo
vin
g
it.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-9:
Queue
Members
Me Sy
mb nta
er x
Na
me
Sy
nc stati
hro c
niz Queue
ed Synch
Thi roniz
s ed(
me
tho Queue
d sourc
ret eQueu
urn e)
s
a
thr
ea
d-s
afe
wra
pp
er
for
a
qu
eu
e
obj
ect
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-9:
Queue
Members
Me Sy
mb nta
er x
Na
me
To
Arr virtu
ay al
Thi objec
s t[]
me ToArr
tho ay()
d
cre
ate
s
a
ne
w
arr
ay
initi
aliz
ed
wit
h
the
ele
me
nts
of
the
qu
eu
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-9:
Queue
Members
Me Sy
mb nta
er x
Na
me
Tri
mT virtu
oSi al
ze void
Thi TrimT
s oSize
me ()
tho
d
set
s
the
ca
pa
cit
y
to
the
nu
mb
er
of
ele
me
nts
in
the
coll
ect
ion
.
IEn Ge
um tEn
era um
ble era
me tor
mb
ers
ICl Clo
on ne
ea
ble
me
mb
ers
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-9:
Queue
Members
Me Sy
mb nta
er x
Na
me
ICo Co
llec py
tio To,
n Co
me unt
mb ,
ers IsS
ync
hro
niz
ed,
an
d
Sy
nc
Ro
ot
This is sample code of the Queue collection. Customers are added to the queue and then displayed.
using System;
using System.Collections;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
Queue waiting=new Queue();
waiting.Enqueue(new Customer("Bob"));
waiting.Enqueue(new Customer("Ted"));
waiting.Enqueue(new Customer("Kim"));
waiting.Enqueue(new Customer("Sam"));
while(waiting.Count!= 0) {
Customer cust=
(Customer) waiting.Dequeue();
Console.WriteLine(cust.Name);
}
}
propName=cName;
}
SortedList
The SortedList collection is a combination of key/value entries and an ArrayList collection, where the
collection is sorted by the key. The collection is accessible per the key or an index.
Table 5-10:
SortedList
Members
Me Sy
mb nta
er x
Na
me
Co
nst Sorte
ruc dList
tor ()
Th
e
Sor Sorte
ted dList
Lis (ICom
t parer
co compa
nst rer)
ruc
tor
Sorte
is
dList
ove
(
rlo
ad
ed. IDict
Th ionar
es y
e sourc
are eColl
so ectio
me n)
of
the
ove
rlo
ad
ed
co
nst
ruc
tor
s.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-10:
SortedList
Members
Me Sy
mb nta
er x
Na
me
Ad
d virtu
Thi al
s void
me Add(o
tho bject
d key,
ad
ds objec
an t
ele value
me )
nt
to
the
coll
ect
ion
.
Ca
pa virtu
city al
int
Thi Capac
s ity{
pro
per get;
ty set;}
get
s
or
set
s
the
ca
pa
cit
y
of
the
coll
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-10:
SortedList
Members
Me Sy
mb nta
er x
Na
me
Cle
ar virtu
Thi al
s void
me Clear
tho ()
d
re
mo
ves
all
the
ele
me
nts
of
the
coll
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-10:
SortedList
Members
Me Sy
mb nta
er x
Na
me
Co
nta virtu
ins al
bool
Thi Conta
s ins(o
me bject
tho value
d )
ret
urn
s
tru
e
if
the
sp
ecif
ied
val
ue
is
fou
nd
in
the
coll
ect
ion
. If
the
val
ue
is
not
fou
nd,
fal
se
is
ret
urn
ed.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-10:
SortedList
Members
Me Sy
mb nta
er x
Na
me
Co
nta virtu
ins al
Ke bool
y Conta
Thi insKe
s y(
me
tho objec
d t
ret key)
urn
s
tru
e
if
the
ke
y
is
fou
nd
in
the
coll
ect
ion
. If
the
ke
y
is
not
pre
se
nt,
fal
se
is
ret
urn
ed.
Ide
nti
cal
to
the
Co
nta
ins
me
tho
d.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-10:
SortedList
Members
Me Sy
mb nta
er x
Na
me
Co
nta virtu
ins al
Val bool
ue Conta
Thi insVa
s lue(
me
tho objec
d t
ret value
urn )
s
tru
e
if
the
val
ue
is
fou
nd
in
the
coll
ect
ion
. If
the
val
ue
is
not
pre
se
nt,
fal
se
is
ret
urn
ed.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-10:
SortedList
Members
Me Sy
mb nta
er x
Na
me
Ge
tBy virtu
Ind al
ex objec
Thi t
s GetBy
me Index
tho (
d
ret int
urn index
s )
the
val
ue
at
the
ind
ex.
Ge
tKe virtu
y al
Thi objec
s t
me GetKe
tho y(
d
ret int
urn index
s )
the
ke
y
at
the
sp
ecif
ied
ind
ex.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-10:
SortedList
Members
Me Sy
mb nta
er x
Na
me
Ge
tKe virtu
yLi al
st IList
Thi GetKe
s yList
me ()
tho
d
ret
urn
s
all
the
ke
ys
in
the
coll
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-10:
SortedList
Members
Me Sy
mb nta
er x
Na
me
Ge
tVa virtu
lue al
Lis IList
t GetVa
Thi lueLi
s st()
me
tho
d
ret
urn
s
all
the
val
ue
s
of
the
Sor
ted
Lis
t
in
a
ne
w
coll
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-10:
SortedList
Members
Me Sy
mb nta
er x
Na
me
Ind
ex virtu
Of al
Ke int
y Index
Thi OfKey
s (
me
tho objec
d t
ret key)
urn
s
the
ind
ex
of
a
ke
y
fou
nd
in
the
coll
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-10:
SortedList
Members
Me Sy
mb nta
er x
Na
me
Ind
ex virtu
Of al
Val int
ue Index
Thi OfVal
s ue(
me
tho objec
d t
ret value
urn )
s
the
ind
ex
to
the
firs
t
ins
tan
ce
of
thi
s
val
ue
in
the
coll
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-10:
SortedList
Members
Me Sy
mb nta
er x
Na
me
IsF
ixe virtu
dSi al
ze bool
Thi IsFix
s edSiz
pro e{
per
ty get;}
ret
urn
s
tru
e
if
the
coll
ect
ion
is
fixe
d
siz
e.
Ot
her
wis
e,
fal
se
is
ret
urn
ed.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-10:
SortedList
Members
Me Sy
mb nta
er x
Na
me
IsR
ea virtu
dO al
nly bool
Thi IsRea
s dOnly
pro {
per
ty get;}
ret
urn
s
tru
e
if
the
coll
ect
ion
is
rea
d-o
nly
.
Ot
her
wis
e,
fal
se
is
ret
urn
ed.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-10:
SortedList
Members
Me Sy
mb nta
er x
Na
me
Ite
m virtu
Thi al
s objec
pro t
per this[
ty objec
get t
s key]
or
set {get;
s set;}
the
val
ue
of
thi
s
ke
y.
Ke
ys publi
Thi c
s virtu
pro al
per IColl
ty ectio
ret n
urn Keys{
s
the get;}
ke
ys
of
the
Sor
ted
Lis
t
coll
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-10:
SortedList
Members
Me Sy
mb nta
er x
Na
me
Re
mo virtu
ve al
Thi void
s Remov
me e(
tho
d objec
re t
mo key)
ves
an
ele
me
nt,
whi
ch
is
ide
ntifi
ed
by
the
ke
y,
fro
m
the
coll
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-10:
SortedList
Members
Me Sy
mb nta
er x
Na
me
Re
mo virtu
ve al
At void
Thi Remov
s eAt(
me
tho int
d index
re )
mo
ves
an
ele
me
nt
at
the
sp
ecif
ic
ind
ex.
Set
ByI virtu
nd al
ex void
Thi SetBy
s Index
me (
tho
d int
set index
the ,
val objec
ue t
of value
the )
ele
me
nt
at
the
sp
ecif
ied
ind
ex.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-10:
SortedList
Members
Me Sy
mb nta
er x
Na
me
Sy
nc stati
hro c
niz Sorte
ed dList
Thi Synch
s roniz
me ed(
tho
d Sorte
ret dList
urn sourc
s eList
a )
thr
ea
d-s
afe
wra
pp
er
for
a
qu
eu
e
obj
ect
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-10:
SortedList
Members
Me Sy
mb nta
er x
Na
me
Tri
mT virtu
oSi al
ze void
Thi TrimT
s oSize
me ()
tho
d
tri
ms
the
ca
pa
cit
y
to
the
act
ual
nu
mb
er
of
ele
me
nts
in
the
coll
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-10:
SortedList
Members
Me Sy
mb nta
er x
Na
me
Val
ue virtu
s al
Thi IColl
s ectio
pro n
per Value
ty s{
ret
urn get;}
s
the
val
ue
s
of
the
coll
ect
ion
.
IEn Ge
um tEn
era um
ble era
me tor
mb
ers
ICl Clo
on ne
ea
ble
me
mb
ers
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-10:
SortedList
Members
Me Sy
mb nta
er x
Na
me
ICo Co
llec py
tio To,
n Co
me unt
mb ,
ers IsS
ync
hro
niz
ed,
an
d
Sy
nc
Ro
ot
This following program is an application that tracks auto repair tickets. Each ticket, which is an instance of
the AutoRepairTicket, is added to a sorted list. The key is the customer name. The value is the actual
ticket. After populating the SortedList type, the CustomerReport method lists the open tickets.
using System;
using System.Collections;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
SortedList tickets=new SortedList();
AutoRepairTicket ticket=NewTicket("Ben");
tickets.Add(ticket.Name, ticket);
ticket=NewTicket("Donis");
tickets.Add(ticket.Name, ticket);
ticket=NewTicket("Adam");
tickets.Add(ticket.Name, ticket);
CustomerReport(tickets);
}
DateTime.Now);
}
}
}
Stack Collection
Stack collections abstract LIFO data structures in which the initial capacity is 32 elements.
Me Sy
mb nta
er x
Na
me
Cle
ar virtu
Thi al
s void
me Clear
tho ()
d
re
mo
ves
all
the
ele
me
nts
of
the
coll
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-11:
Stack
Members
Me Sy
mb nta
er x
Na
me
Co
nta virtu
ins al
bool
Thi Conta
s ins(o
me bject
tho value
d )
ret
urn
s
tru
e
if
the
sp
ecif
ied
val
ue
is
fou
nd
in
the
coll
ect
ion
. If
the
val
ue
is
not
fou
nd,
fal
se
is
ret
urn
ed.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-11:
Stack
Members
Me Sy
mb nta
er x
Na
me
Pe
ek virtu
Th al
e objec
Pe t
ek Peek(
me )
tho
d
pre
vie
ws
the
las
t
ele
me
nt
on
the
sta
ck.
Th
e
ele
me
nt
is
ret
urn
ed
wit
ho
ut
re
mo
val
fro
m
the
sta
ck.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-11:
Stack
Members
Me Sy
mb nta
er x
Na
me
Po
p virtu
Thi al
s objec
me t
tho Pop()
d
ret
urn
s
an
d
re
mo
ves
the
top
ele
me
nt
of
the
sta
ck.
Pu
sh virtu
Thi al
s void
me Push(
tho objec
d t
pu obj)
sh
es
an
oth
er
ele
me
nt
on
the
sta
ck.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-11:
Stack
Members
Me Sy
mb nta
er x
Na
me
Sy
nc stati
hro c
niz Stack
ed Synch
Thi roniz
s ed(
me
tho Stack
d sourc
ret eStac
urn k)
s
a
thr
ea
d-s
afe
wra
pp
er
for
the
Sta
ck
coll
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-11:
Stack
Members
Me Sy
mb nta
er x
Na
me
To
Arr virtu
ay al
Thi objec
s t[]
me ToArr
tho ay()
d
ret
urn
s
the
Sta
ck
coll
ect
ion
as
a
reg
ula
r
arr
ay.
The following code adds numbers to a Stack collection. The values of the collection are then enumerated
and displayed.
using System;
using System.Collections;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
Stack numbers=new Stack(
new int [] {1,2,3,4,5,6});
int total=numbers.Count;
for(int count=0;count<total;++count) {
Console.WriteLine(numbers.Pop());
}
}
}
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Specialized Collections
In addition to the common collections that most developers use, the .NET FCL offers specialized collections.
These collections are found in the System.Collections.Specialized namespace. Although these collections
are used infrequently, they are valuable in certain circumstances.
Table 5-12:
Specialized
Collections
Me De
mb scr
er ipti
Na on
me
Bit Thi
Ve s
cto is
r32 an
arr
ay
of
32
bit
s.
It
is
si
mil
ar
to
a
Bit
Arr
ay,
but
limi
ted
to
32
bit
s.
Be
ca
us
e
of
thi
s
arr
ay'
s
refi
ne
d
us
e,
Bit
Ve
cto
r32
str
uct
ure
s
are
mo
re
effi
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-12:
Specialized
Collections
Me De
mb scr
er ipti
Na on
me
Hy Thi
bri s
dDi coll
cti ect
on ion
ary is
a
co
mb
ina
tio
n
of
a
Lis
tDi
cti
on
ary
an
d
Ha
sht
abl
e.
It
op
era
tes
as
a
Lis
tDi
cti
on
ary
wh
en
co
nta
inin
g
a
sm
all
nu
mb
er
of
ele
me
nts
.
For
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-12:
Specialized
Collections
Me De
mb scr
er ipti
Na on
me
Na Thi
me s
Val is
ue a
Col coll
lec ect
tio ion
n of
ke
ys
an
d
val
ue
s,
in
whi
ch
bot
h
the
ke
y
an
d
val
ue
are
stri
ng
s.
Th
e
coll
ect
ion
is
ac
ce
ssi
ble
via
an
ind
ex
or
ke
y.
A
ke
y
ca
n
ref
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-12:
Specialized
Collections
Me De
mb scr
er ipti
Na on
me
Or Not
der do
ed cu
Dic me
tio nte
nar d
y at
the
tim
e
of
thi
s
bo
ok.
Stri Thi
ng s
Col is
lec a
tio coll
n ect
ion
of
stri
ng
s.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 5-12:
Specialized
Collections
Me De
mb scr
er ipti
Na on
me
Stri Thi
ng s
Dic is
tio a
nar co
y mb
ina
tio
n
of
the
Ha
sht
abl
e
an
d
Stri
ng
Col
lec
tio
n
coll
ect
ion
s,
in
whi
ch
bot
h
the
ke
y
an
d
val
ue
are
stri
ng
s.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Generics
The next chapter is about Generics, which improves upon the performance and other factors related to
nongeneric collections. The collections reviewed in this chapter manipulate object types. For this reason,
when populating collections with value types, boxing is required. Excessive boxing is both a performance
and memory issue. Another problem is type-safeness. You often cast elements that are object types to a
specific type. The cast is not type-safe and can cause run-time errors when done incorrectly.
Generics types are classes with type parameters. The type parameter acts as a placeholder for a future
type. When a generic type is declared, type arguments are substituted for the type parameters. The type
argument is the actual type. At that time, the generic is type-specific, which resolves many of the problems
of a nongeneric type. Generic methods, like generic types, have type parameters. However, the type
argument can be inferred from the way the method is called.
Chapter 6: Generics
Overview
The definition of generic, as found in the Merriam-Webster's Collegiate Dictionary, is "adj. of a whole genus,
kind, class, etc.; general; inclusive." Based on this definition, a person is generic, whereas Donis Marshall is
quite specific. A city is generic, whereas Seattle is specific. For developers, a data algorithm is generic,
whereas the implementation is specific. A stack is generic, whereas a stack of integers is specific. A
spreadsheet is generic, whereas a spreadsheet of strings is specific. To lesser extent, general abstractions
can use generic implementation. An arithmetic class is generic, whereas integer calculations are specific.
In .NET, a type is specific, such as a class or structure. A StackInt class is a specific type and represents
a specialization of the stack pattern, which targets integers. A stack of strings or floats would require
additional implementations of the same algorithm, such as StackString and StackFloat.
"using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
StackInt stack=new StackInt(5);
stack.Push(10);
stack.Push(15);
Console.WriteLine("Pushed 3 values");
stack.ListItems();
stack.Pop();
Console.WriteLine("Popped 1 value");
stack.ListItems();
}
}
Inheritance polymorphism, which is polymorphism using inheritance and virtual functions, is an alternate
solution to type specialty. In a personnel application, ExemptEmployee, HourlyEmployee, and
TempEmployee are specific classes that are related through inheritance, where Employee is the generic
base class. Using it solves some of the aforementioned problems, such as code reuse. This is a potential
solution to generic abstraction but not a generic data algorithm. Abstracting the stack algorithm based on
inheritance encourages bad design. Is a stack a kind of integer? Is an integer a kind of stack? Neither
statement is true. For a stack solution, inheritance polymorphism is a contrived solution at best.
A simple solution is generic implementation, which is single implementation for all types—a stack of anything
, versus a stack of integers. This is the function of collections in the .NET Framework class library (FCL),
which are general-purpose containers. All types—value or reference—are derived directly or indirectly from
System.Object, making it the ubiquitous type. System.Object can be anything. General-purpose collections
are containers of System.Object instances. These collections are discussed in Chapter 5, "Arrays and
Collections."
In the following code, a stack of integers is implemented using the stack collection class from the .NET FCL.
The stack collection is reused as a stack of strings, which demonstrates the amorphous nature of the stack
collection class.
using System;
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
using System.Collections;
namespace Donis.CSharpBook{
public class Starter{
}
}
General-purpose collections (described in the previous chapter) are valuable, but there are some drawbacks.
Performance is the first problem. These collections manage instances of System.Object. Casting is required
to access, remove, and insert items into a collection. For integers, boxing occurs as items are added to the
collection. Unboxing happens when items are accessed in the collection. Boxing occurs if a collection
contains value types—integers are value types. Frequent boxing can prompt earlier garbage collection, which
is especially expensive. The incremental penalty for boxing and unboxing can be substantial for a collection
of value types. For a collection of reference types, there is the cost of down-casting, which is lighter. The
second problem is type-safeness. Although the StackInt type is type-safe, the general-purpose collection of
integers is not. The stack collection stores elements in memory as System.Object. As a System.Object
type, the behavior of each element is limited to ToString, GetHashCode, Equals, and GetType. Casting is
required for expanded behavior. However, you can cast to anything, which is inherently type-unsafe. Incorrect
casts cause run-time exceptions. The third problem is clarity. Frequent casting, as required with collection
classes, obfuscates the source code. Generics are the solution.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Generics are parameterized types and methods. Each parameter is a placeholder of a yet unspecified type.
The polymorphic behavior of the generic type or method is conveyed through parameters, which is called
parametric polymorphism. There is a single implementation of the algorithm, which is abstracted through
parameters. Many developers were introduced to this concept in C++ as parameterized templates. Other
languages also support this feature. However, generics in .NET avoid some of the problems emblematic of
parametric polymorphism in these other languages.
Generics address the shortcomings of generic collections. Generics perform better because needless boxing
and unboxing are eliminated. At run time, generic types with value type arguments expand to type-specific
instances. For example, a generic stack with an integer type parameter becomes an actual stack of
integers, which make generics inherently type-safe. Generic types become specific types, which avoids the
necessity of casting. Because casting is avoided, clarity is enhanced.
Generic Types
Generics are types with parameters, and parameters are placeholders for future types. Generic types include
classes, structures, and interfaces. The essence of the type remains. The persona of a class, even a generic
class, remains a class—it is simply a class with type parameters.
Type Parameters
Parameters appear after the class header and are placed in anchor brackets (< and >). The type parameters
are accessible to the class declaration, header, body, and constraints. Because collections are typically
general-purpose, generics are ideal for implementing collections. .NET provides generic collections for
arrays, stacks, queues, and other common data structures. However, there is no predefined spreadsheet
collection, which is a collection of rows and columns. Sounds like an opportunity to me.
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
int count=1;
Sheet<int> asheet=new Sheet<int>(2);
for(byte row=1; row<3; ++row) {
for(byte col=1; col<3; ++col) {
asheet[row,col]=count++;
}
}
for(byte row=1; row<3; ++row) {
for(byte col=1; col<3; ++col) {
Console.WriteLine("R{0} C{1}= {2}",
row, col, asheet[row,col]);
}
}
Console.WriteLine("Current[{0},{1}] = {2}",
asheet.R, asheet.C, asheet.Current);
asheet.MoveDown();
asheet.MoveRight();
Console.WriteLine("Current[{0},{1}] = {2}",
asheet.R, asheet.C, asheet.Current);
}
}
class Sheet<T> {
public Sheet(byte dimension) {
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
if(dimension<0) {
throw new Exception("Invalid dimensions");
}
m_Dimension=dimension;
m_Sheet=new T[dimension, dimension];
for(byte row=0; row<dimension; ++row) {
for(byte col=0; col<dimension; ++col) {
m_Sheet[row,col]=default(T);
}
}
public T Current {
get {
return m_Sheet[curRow-1, curCol-1];
}
set {
m_Sheet[curRow-1, curCol-1]=value;
}
}
}
}
Sheet is a generic type and collection, with a single parameter. For generics with a single parameter, by
convention, T is the name of the parameter. (T is for type.) In the sheet generic type, the type parameter is
used as a function return, field type, and local variable. When the sheet is instantiated, the specific type is
specified. This defines a spreadsheet of strings, which has two rows and columns:
Review the constructor of the sheet generic type. Notice the use of the default keyword:
}
}
The preceding for loop initializes the internal collection of the Sheet generic type. However, the
implementation is not based on a specific type. A generic type implementation has placeholders instead of
specific types. The challenge is initializing a nonspecific type, which could be a reference or value type. The
default expression returns null for a reference type and bitwise zero for a value type. This is the syntax of the
default expression:
zerovalue default(type)
The Sheet generic type has a single parameter. Generics can have multiple parameters. The type parameter
list is contained within the anchor brackets. Parameters in the type parameter list are comma-delimited. If a
generic has two parameters, the parameters are normally named K and V, for key and value. Here is sample
code of a generic type that has two parameters:
}
}
This code creates an instance of the ZClass, which has two parameters:
Generic types have parameters. Those parameters can themselves be generic types. In the following code,
XClass is generic and is also inserted as a nested parameter:
using System;
namespace Donis.CSharpBook{
The syntax of nested parameters can be somewhat complicated. What if the type parameter was
extrapolated even further? You could have a type parameter that is a generic, which has a type parameter
that is also generic, and so on, which could become somewhat labyrinthine. If the declaration is repeated, it
would be particularly problematic. An alias is a good solution. Alias the declaration containing a nested
parameter and rely on the alias in the code. The following alias is used for this purpose:
Constructed Types
Generic types are also called constructed types. There are open constructed and closed constructed types.
An open constructed type has at least one type parameter. The type parameter is a placeholder, which is
unbound to a specific type. The implementation of a generic type is an example of an open constructed type.
This is an open constructed type:
}
}
For a closed constructed type, all type parameters are bound. Bound parameters are called type arguments
and are assigned a specific type. Closed constructed types are used in several circumstances: to create an
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
instance of a generic type, inherit a generic type, and more. In the following code, ZClass<int, decimal> is a
closed constructed type. The first parameter is bound to an integer, whereas the second is a decimal. The
type arguments are int and decimal.
Generic Methods
A generic method can declare method-specific generic parameters. These parameters can be used in the
method header or body. An open method has type parameters, which are nonspecific. A closed method has
type arguments, whereas specific types are substituted for type parameters. For a generic method, the type
parameters are listed after the function header. The type parameter list is enclosed in anchor brackets,
whereas each parameter is delimited with commas.
using System;
namespace Donis.CSharpBook{
When calling a generic method, provide the type arguments as a substitute for type parameters. There is an
alternate syntax to calling a generic method, called type inference, in which the type argument is inferred
from the actual method parameters. The type argument can then be omitted. Basically, the generic method
is called similarly to any method. The benefit is ease of use. However, type inference disguises the true
nature of the call, which could be relevant when debugging an application.
In the previous code, ZClass.MethodA is a generic method. The following statement calls MethodA using
type inference:
ZClass.MethodA(20);
Overloaded Methods
Generic methods can be overloaded, which creates an interesting dilemma. Is a method ambiguous based
on extrapolation alone? In the following code, MethodA is overloaded:
}
public void MethodA(U arg) {
}
Both functions have a single type parameter. The type parameters are different, but each parameter could be
anything, including both parameters being identical. This makes certain renditions of MethodA ambiguous.
For example, they both could have a single integer parameter. However, the C# compiler is concerned with
actual ambiguity and does not highlight, as an error or warning, potential ambiguousness related to
overloading generic methods. A compile error is manifested when MethodA is called, if ever, in an ambiguous
manner. This is a potential land mine for developers of libraries. Test every permutation of type parameters to
predict and avoid potential ambiguousness for clients of a library.
In the following code, MethodA is called ambiguously. Interestingly, MethodA is known to be ambiguous at
the generic type instantiation. However, the compiler error occurs on the next statement at the call site.
using System;
namespace Donis.CSharpBook{
Generic methods can overload nongeneric methods. If the combination of a generic and nongeneric method
is ambiguous, the nongeneric method is called. The compiler prefers nongeneric methods over generic
methods of the same signature.
The following code contains a generic and nongeneric MethodA. When MethodA is called with an integer
parameter, the call would otherwise be ambiguous. Because the ambiguous methods are generic and
nongeneric methods, the nongeneric MethodA is simply called. Then MethodA is called with a double, which
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
using System;
namespace Donis.CSharpBook{
Console.WriteLine("ZClass.MethodA()");
}
}
}
Generic types, similar to any type, have a this reference, which is a reference to the current object and the
same type of that object. If the object is an XClass instance, the this reference is also of the XClass type.
The this reference is used implicitly to refer to instance members and explicitly as function returns and
parameters. The this reference of a generic type is associated with the closed constructed type, which is
normally defined at generic type instantiation.
The following code displays the type of a this reference. The following is a this reference of a generic type,
which is Donis.CSharpBook.ZClass`1[System.Int32]. From the type, you know that the closed constructed
type has a single parameter that is a 32-bit integer.
using System;
namespace Donis.CSharpBook{
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
class ZClass<T> {
public T MethodA() {
T var=default(T);
Console.WriteLine(this.GetType().ToString());
return var;
}
}
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Constraints
When a generic type is implemented, the deferred specialty of the type parameter is unknown. Managed
code has plenty of cool features, but they do not include foretelling the future. Therefore, within the
implementation of a generic type, a type parameter could eventually be anything. We are assured that the
type parameter inherits from System.Object—nothing else. .NET guarantees that all types are a derivation of
System.Object. For this reason, type parameters are implied System.Object types and must behave
accordingly. This limits type parameters to the public interface of the System.Object, which assures type
safeness.
In the following code, ZClass has a single parameter, which is T. My intention is that T is an array of some
type. ZClass.MethodA will enumerate that array while displaying each value. System.Array is the underlying
type of an array and implements the IEnumerable interface. This is the required interface for enumeration
and an essential ingredient of a foreach loop. Unfortunately, the C# compiler does not know my intention.
Regardless of my future intention, T is an implied System.Object, which does not implement the
IEnumerable interface. Therefore this code does not compile.
class ZClass<T> {
This problem is resolved with a generic constraint. Constraints define the future intention of a type parameter.
The following program uses a constraint, which is a where clause, to indicate the intention of the T
parameter. It is intended that the T parameter is an IEnumerable type. With this understanding, the program
compiles and executes successfully:
using System;
using System.Collections;
namespace Donis.CSharpBook{
}
}
}
}
Derivation Constraint
The derivation constraint states the derivation of a type parameter. The type parameter must be derived from
that constraint, which is enforced by the C# compiler. This allows the compiler to relax the restriction that
access to type parameters is limited to the System.Object. The public interface of the constrained type
parameter is expanded to include the derivation type. A type can inherit from a single class because multiple
inheritance is not available in C#. For this reason, a type parameter can optionally have a single constraint.
However, each parameter can have separate derivation constraints, in which each constraint is
space-delimited.
Because the constraint is enforced by the compiler, the type parameter can only be used accordingly. This
avoids an unsafe usage of the type parameter. The compiler assures that all access to the type parameter is
type-safe as defined in the constraint. This is different in C++, in which the compiler performs no such
type-checking on type parameters. In C++, you can basically do anything with a type parameter. Errors are
uncovered when the parameter template is expanded at compile time, deep in the bowels of the expansion
code. This can lead to cryptic error messages that are hard to debug. Anyone that has used the Active
Template Library (ATL) and had expansion errors can attest to this. The C# compiler also updates Microsoft
IntelliSense per the derivation constraint. A type parameter reflects the IntelliSense of any constraints on
that parameter.
ZClass is a generic type and is defined in the following code. It has a K and V type parameter, each with a
separate constraint. Per the constraints, the K parameter must be derived from XClass, while the V
parameter should be derived from YClass. Main has three generic type instantiations. The first two are okay,
but the third causes compile errors. The problem is the first type argument. WClass is not derived from
XClass, which is a requirement of the first parameter per the constraint.
using System;
namespace Donis.CSharpBook{
// good
ZClass<XClass, YClass> obj=
new ZClass<XClass, YClass>();
// good
ZClass<XClass, WClass> obj2=
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
// bad
ZClass<WClass, YClass> obj3=
new ZClass<WClass, YClass>();
}
}
class ZClass {
public T MethodA<T>() where T:XClass {
return default(T);
}
}
Generic types can function as constraints, which include both open and closed constructed types. This is
demonstrated in the following code. XClass is a nongeneric type. YClass is a generic type with a single
parameter. ZClass is also a generic type, also with a single parameter, where YClass<XClass> is the
constraint on that parameter. YClass<XClass> is a closed constructed type. In Main, an instance of the
YClass is created, where XClass is the type parameter. Next, an instance of the ZClass is created, and the
parameter type is YClass<XClass>, which is required by the constraint mentioned earlier. ZClass.MethodA
is then called, where the YClass instance is passed as a parameter.
using System;
namespace Donis.CSharpBook{
Console.WriteLine("YClass::MethodB");
}
}
A type parameter can be used as a constraint. In this circumstance, one type of parameter is constraining
another. You are stating that a parameter is derived from another parameter. In this code, the T1 parameter
must be derived from the eventual T2 type argument:
using System;
using System.Collections;
namespace Donis.CSharpBook{
}
}
}
}
Nodes are the ideal generic type. You can have nodes of integers, floats, employees, or even football teams.
When creating a link list, each node has reference to the next and previous node. The node should reference
nodes of the same type. For an employee node, the next and previous node must also be an employee. This
relationship between nodes is enforced with a recursive constraint:
// partial implementation
public T Previous {
get {
return default(T);
}
set {
}
}
public T Next {
get {
return default(T);
}
set {
}
}
}
The type parameter cannot exceed the visibility of the constraint. In the following code, XClass has internal
accessibility and is visible in the current assembly alone. The T parameter is public and is visible outside the
current assembly. The accessibility of the T parameter exceeds XClass. For this reason, it is an error to use
XClass as a constraint on the type parameter.
In addition, value types cannot be used as constraints, which is a good introduction to a limitation with
generics. Look at the following code, which will not compile. Why not?
As with any parameter, the T parameter is an inferred System.Object type. System.Object does not have an
operator *. Therefore, "number*number*number" will not compile. An integer constraint should resolve the
problem, which would confirm that the type parameter is an integer. Integers have an operator *. However,
value types and primitives are not valid constraints. The following code will not compile:
The inability to use standard operators with value types is a major limitation to generics. The workaround is
implementing named operators, such as Add, Multiply, and Divide, as members of the generic type.
In addition to value types, there are other restrictions on constraints. The following types cannot be used as
constraints:
Sealed classes
Open constructed types
Primitive types
System.Array
System.Delegate
System.Enum
System.ValueType
Interface Constraints
Interfaces can also be constraints, which requires that the type argument implement the interface. Although
a type parameter can have at most one derivation constraint, it can have multiple interface constraints. This
is logical because a class can inherit a single base class but can implement many interfaces. The syntax for
an interface constraint is identical to a derivation constraint. Class and interface constraints can be
combined in a list of constraints. However, a class constraint should precede interface constraints in the list.
Interface and derivation constraints share many of the same rules and restrictions, such as the visibility of
the interface constraint exceeding that of the type parameter.
In the following code, the find capability has been added to the Sheet collection. The Find method returns an
array of cells that contain a certain value. A comparison is made between the cell and value, where both are
the type indicated in the type argument. Types that implement the IComparable interface support
comparisons. If a comparison is equal, IComparable .CompareTo returns 0. To support this behavior, an
interface constraint for IComparable is added to the type parameter. This is a partial implementation of the
Sheet collection that shows Find and related methods. (Some of the code shown previously is omitted.)
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
Sheet<int> asheet=new Sheet<int>(5);
for(byte row=1; row<6; ++row) {
for(byte col=1; col<6; ++col) {
asheet[row,col]=row*col;
}
}
Cell [] found=asheet.Find(6);
foreach(Cell answer in found) {
Console.WriteLine("R{0} C{1}",
answer.row, answer.col);
}
}
}
This code works, but there is a subtle problem. The IComparable interface manipulates objects, which
causes boxing and unboxing when working with value types. This could become expensive in a large
collection of value types. In the preceding code, the type argument is an integer, which is a value type. This
causes boxing with the IComparable interface. Generic interfaces obfuscate this problem. .NET Framework
2.0 includes several general-purpose generic interfaces for developers. This is the class header updated for
the IComparable generic interface:
A value type constraint restricts a type parameter to a value type. Value types are derived from the
System.ValueType type. Primitives and structures are examples of value types. The exception is the Nullable
type. The Nullable type is a value type, but it is not allowed with a value type constraint. A value type
constraint is a constraint using the struct keyword.
The following code demonstrates the value type constraint. The commented source line uses a reference
type, which would cause compile errors because of the value type constraint.
using System;
using System.Collections;
namespace Donis.CSharpBook{
A reference type constraint restricts a type parameter to a reference type. Reference types are generally
user-defined types, including classes, interfaces, delegates, and array types. A reference type constraint
uses the class keyword.
The following code has a reference type constraint. Although this code is similar to the code presented in the
previous section, a reference type constraint is used instead of a value type constraint. For this reason, the
illegal line has moved. You cannot use an integer type with a reference type parameter.
using System;
using System.Collections;
namespace Donis.CSharpBook{
}
}
class ZClass<T> {
public void MethodA() {
T obj=new T();
}
}
This code does not compile. The problem is the default constructor. Although prevalent, not every type has a
default constructor. A default constructor, or a constructor with no arguments, assigns a default state to an
object. The default constructor is called with a parameterless new operator. However, because a default
constructor is not guaranteed, the new operator is not universally applicable. Therefore, the new operator is
disallowed on type parameters.
The solution is the constructor constraint. The derivation constraint does not help with constructors because
derived types do not inherit constructors for the base class. Constructor constraints mandate that a type
parameter have a default constructor, which is confirmed at compile time. This allows the new operator to be
used with the type parameter. The constructor constraint is added to the where clause and is a new
operator. When combined with other constraints, the default constructor constraint must be the last item in
the constraint list. The constructor constraint applies only to the default constructor. You are still prevented
from using constructors with arguments.
Here is sample code of the constructor constraint. The constructor constraint is used on the ZClass.
using System;
namespace Donis.CSharpBook{
Casting
You may need to a cast a generic type. Since generic types are implicit System.Object types, they can
always be cast to that type. In addition, generic types can be cast to the derivation constraint, which is also
a type. The derivation constraint assures that the generic type is a descendant of the constraint. This
assures a safe cast. Finally, generics can be cast to any interface even if the interface is not included in an
interface constraint. Since there is no restriction on casting to interfaces, it is not type-safe. For that reason,
care should be taken to cast to an implemented interface.
In the following code, ZClass is a generic type. It has a single type parameter (T) that has three constraints:
YClass type derivation, IA interface derivation, and the constructor constraint. An instance of the T
parameter is created in the method called Cast. The instance is then cast in succession to the YClass, IA,
and IB interface. The first two casts work as expected. The third cast fails spectacularly. The type parameter
is not related to IB. However, the compiler does not notice. Therefore, an exception is raised at run time,
which is the worst possible time and underscores the type-unsafe nature of interface cast of generic types.
Generic type parameters cannot be assigned a null or zero. There is no assurance that a type parameter is
either a reference or value type, which prevents safely assigning a null or zero value to a type parameter.
However, you can test a type parameter against null but not zero. If the comparison succeeds, the type
parameter is a reference type. Otherwise, the type parameter is a value type.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Inheritance
Generic and nongeneric classes can inherit a generic type. In addition, a generic type can be the base class
to a generic or nongeneric type. Some basic rules apply. For example, the derived class cannot be a closed
constructed type. Table 6-1 lists all the possible permutations.
Table 6-1: Inheritance Table for Generic Types
Generic (open) Generic (open) Permitted when the derived class consumes the type parameters of
the base class
This sample code shows some of the permitted and not permitted combinations:
/*
public class AClass<Z> : BClass<Y> { [ illegal ]
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
When inheriting an open constructed type, the constraints of the base class must be repeated in the derived
type. Furthermore, the derived type can provide additional constraints on type parameters declared at the
base type. This is not applicable to closed constructed types because closed constructed types do not have
type parameters or constraints.
Overriding Methods
Methods that have a type parameter can be overridden, regardless of where the type parameter is declared.
These methods can also override other methods. Table 6-2 lists the various combinations of overriding
generic and nongeneric methods. If a base class is nongeneric or closed, overriding methods cannot have
type parameters. Conversely, if the base class is open, overriding methods can employ type parameters.
Table 6-2:
Combination of
Overriding Generic
Methods
Ba De Co
se riv m
Me ed me
tho Me nts
d tho
d
No Ge Per
ng ner mit
en ic ted
eri (op
c en)
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 6-2:
Combination of
Overriding Generic
Methods
Ba De Co
se riv m
Me ed me
tho Me nts
d tho
d
No Ge Per
ng ner mit
en ic ted
eri (cl
c os
ed)
Ge No Not
ner ng per
ic en mit
(op eri ted
en) c
Ge Ge Per
ner ner mit
ic ic ted
(op (op ;
en) en) mu
st
us
e
the
sa
me
typ
e
par
am
ete
rs
Ge Ge Not
ner ner per
ic ic mit
(op (cl ted
en) os
ed)
Ge No Per
ner ng mit
ic en ted
(cl eri
os c
ed)
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 6-2:
Combination of
Overriding Generic
Methods
Ba De Co
se riv m
Me ed me
tho Me nts
d tho
d
Ge Ge Per
ner ner mit
ic ic ted
(cl (cl
os os
ed) ed)
Ge Ge Not
ner ner per
ic ic mit
(cl (op ted
os en)
ed)
using System;
namespace Donis.CSharpBook{
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
When a generic method overrides another generic method, it inherits the constraints of that method. The
overriding method cannot change the inherited constraints.
Nested Types
You can nest a generic type inside a nongeneric type. Conversely, a nongeneric type can be nested in a
generic type. More intriguing is nesting generic types inside of generic types. The nested generic type can
consume the type parameters of the surrounding type. A type parameter of the surrounding type cannot be
redefined as a new type parameter in the nested type. However, the nested generic type can declare entirely
new type parameters.
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ZClass<int>.Nested<double> obj=
new ZClass<int>.Nested<double>();
obj.MethodA(10, 12.34);
}
}
Static Members
Generic types can have static members. If present, a closed constructed type owns a set of any static
members. Therefore, there are possibly multiple instances of the static members—one for each closed
constructed type. Static members are not referable from the open constructed type. Static members are
usually accessed through the class name. With generic types, static members are accessible using the
closed constructed type notation. Static constructors, which are called implicitly, initialize the static fields in
the context of the current closed constructed type.
classname is the name of the generic type; argumentlist is a comma-delimited list of type arguments;
staticmember is the name of the static member.
Static members are frequently used as counters. The following code counts the instances of generic types.
There are several generic type instantiations—each using a closed constructed type. The static count is
specific to each closed constructed type. The count counts the number of instances of a closed constructed
type.
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
ZClass<int> obj1=new ZClass<int>();
ZClass<double> obj2=new ZClass<double>();
ZClass<double> obj3=new ZClass<double>();
ZClass<int>.Count(obj1);
ZClass<double>.Count(obj2);
}
}
public ZClass() {
++counter;
}
Several articles describing generics bemoan the extra cost of static fields in generic types. This is a
misplaced concern. A generic type often supplants the explicit implementation of separate classes:
StackCircle, StackTriangle, and so on. As separate classes, static members would also be replicated.
Alternatively, a general-purpose collection would have a single set of static members but incur other costs,
such as boxing and unboxing. From every perspective, the overhead from extra sets of static members from
generic types is comparable to or better than any alternate solution.
Operator Functions
You can implement operator functions for generic types. Operator member functions are static members.
Therefore, the rules of static members also apply to operator member functions. An operator member
function cannot be a generic method. However, the operator member function can use type parameters of the
generic type.
An operator+ has been added to the Sheet generic type. It adds two Sheet collections. The results of the
calculations are placed in a third sheet. Only integral sheets can be added. Because type parameters
cannot be constrained by a value type, a helper function called Add is provided:
total[(byte) row,(byte)col]=
sheet1.Add(sheet1[(byte) row,(byte)col],(int)
(object) (sheet2[(byte) row,(byte)col]));
}
}
return total;
}
This is the signature of the operator+ function in the Sheet generic type:
An operator+ is a binary operator with two operands. Notice that one operand is a closed constructed type,
whereas the other is an open constructed type. Why? The operator+ requires that one of the operands be
the containing class, which is the open constructed type, so there is a chance of adding incompatible
sheets. This explains the cast:
sheet2 is the right-hand parameter of the operator+ function. The unknown type in sheet2 is being cast back
to an integer. If the type is incompatible, an exception is raised at that moment. Exception handling could
mitigate this problem. However, to keep the code simple, the problem is left unresolved. This code is meant
as demonstration only.
Serialization
Serialization persists the state of an object to a stream. Serializing the instance of a generic type is similar
to a regular type. This book does not provide a detailed overview of serialization. This section presents
targeted information on serializing generic types.
Serialization is done mostly with the SerializationInfo object. For generic types, there are additional
overloads of the SerializationInfo.AddValue and SerializationInfo.GetValue methods for object types. This
requires casting to and from object types.
For serialization, the generic type must be adorned with the Serializable attribute.
The GetObjectData method implements the serialization of an object. This includes serializing both the
metadata and instance data of the type. GetObjectData has a SerializationInfo and StreamingContext
parameter. The SerializationInfo.AddValue method is called to serialize generic type content:
To deserialize, add a two-argument constructor to the generic type. The arguments are a SerializationInfo
and StreamingContext parameter. Call the SerializationInfo.GetValue method to rehydrate the instance:
Objects can be serialized in different formats, which is accomplished with formatters, such as the
BinaryFormatter type. The SoapFormatter type cannot be used with generic types. Serialization also
requires creating an appropriate stream, such as a FileStream. The stream is where the instance is
serialized or deserialized. Call BinaryFormatter.Serialize to serialize a generic type instance. Conversely,
call BinaryFormatter.Deserialize to deserialize.
The following program accepts a command-line argument. The Set command instructs the program to
serialize an instance of the ZClass generic type to a file. A Get command asks the program to deserialize
the ZClass generic type.
using System;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
namespace Donis.CSharpBook{
if(args[0].ToLower()=="set") {
ZClass<int> obj=new ZClass<int>(5);
binary.Serialize(file, obj);
return;
}
if(args[0].ToLower()=="get") {
ZClass<int> obj=(ZClass<int>)
binary.Deserialize(file);
Console.WriteLine(obj.GetValue());
return;
}
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
public T GetValue() {
return fielda;
}
private T fielda=default(T);
}
Generics Internals
Generics are economical and expeditious, especially when compared with past implementations of
parametric polymorphism. The disparity is found in the compile-time and run-time semantics of code
expansion in generics. This section focuses on improvement in this area as compared with parameterized
types in C++, which has a widely-recognized implementation of parametric polymorphism.
Although an inspection of parameterized templates in C++ might uncover cursory similarities with generics,
there are considerable differences between them. These differences make generics more efficient and
better-performing than parameterized templates. The exact implementation of templates is specific to each
C++ compiler. However, the concepts of parameterized templates are similar in all implementations.
The major difference between generics and parameterized templates is that the latter is purely compile-time
based. Instances of parameterized templates expand into separate classes and are inlined at compile time.
The Standard Template Library (STL) of C++ offers a stack collection. If ellipse, rectangle, triangle, and curve
instances of the stack are defined, the stack template expands into separate classes—one for each type.
The expansion occurs at compile time. What happens when two stacks of circles are defined separately? Is
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
there a consolidation of the code? The answer is no, which can lead to significant code bloating.
The compile-time expansion has some shortcomings. This makes the parameterized templates specific to
C++. In .NET, generic types expand at run time and are not language-specific. Therefore, generics are
available to any managed language. The Sheet generic type presented in this chapter is usable in Microsoft
Visual Basic .NET. With C++, the particulars of the template, such as the parameterized types, are lost at
compile time and not available for later inspection. Managed code, including generics, undergoes two
compilations. The first compilation, administered by the language compiler, emits metadata and Microsoft
intermediate language (MSIL) code specific to generic types. Because the specifics of the generic type are
preserved, it is available for later inspection, such as reflection. There are new metadata and MSIL
instructions that target generic types. The second compilation, performed by the just-in-time compiler (jitter),
performs the code expansion. The jitter is part of the Common Language Runtime (CLR).
The CLR performs an intelligent expansion of generic types, unlike C++, which blindly expands
parameterized types. Intelligent expansion is conducted differently for value type versus reference type
arguments.
When a generic type with a value argument is defined, it is expanded into a class at run time, where the
specific type is substituted for the parameter throughout the class. The resulting class is cached in memory.
Future instances of the generic type with the same type argument reference the existing class. In this
circumstance, there is code sharing between the separate generic type instantiations. Additional class
expansion is prevented and unnecessary code bloating is avoided.
If the type argument is a reference type, the CLR conducts intelligent expansion differently. The run time
creates a specialized class for the reference type, where System.Object is substituted for the parameter.
The new class is cached in memory. Future instances of the generic type with any reference type parameter
references this same class. Generic type instantiations that have a reference type parameter share the
same code.
Look at the following code. How many specialized classes are created at run time?
The preceding code results in three specialized classes. The Sheet<int> instantiations share a class.
Sheet<double> is a separate class. Sheet<XClass> and Sheet<YClass> also share a class.
Generic Collections
As discussed in the previous chapter, the .NET Framework Class Library includes general-purpose collection
classes for commonplace data algorithms, such as a stack, queue, dynamic array, and dictionary. These
collections are object-based, which affects performance, hinders type-safeness, and potentially consumes
the available memory. The .NET FCL includes parameterized versions of most of the collections.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The parameterized data algorithms are found in the System.Collections.Generic namespace. Generic
interfaces are also included in the namespace. Table 6-3 lists some of the types and interfaces members of
this namespace.
Table 6-3:
Generic
Types and
Interfaces
De Ty
scr pe
ipti
on
Dy Lis
na t<T
mi >
c
arr
ay
LIF Sta
O ck
list <T
>
FIF Qu
O eu
list e<
T>
Col Dic
lec tio
tio nar
n y<
of K,
ke V>
y/v
alu
e
pai
rs
Co ICo
mp mp
are ara
s ble
a <T
cur >
ren
t
an
d
oth
er
obj
ect
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 6-3:
Generic
Types and
Interfaces
De Ty
scr pe
ipti
on
Co ICo
mp mp
are are
s r<T
two >
obj
ect
s
Ret IEn
urn um
s era
an ble
en <T
um >
era
tor
Def IEn
ine um
s era
an tor
en <T
um >
era
tor
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Enumerators
The next chapter pertains to enumerators, a topic that is a natural extension of this chapter. Enumerators
are typically used to iterate collections, which have been the focus of the last two chapters. Enumerators are
implemented and typically exposed from an enumerable object. Enumerable objects implement the
IEnumerable interface. The IEnumerable.GetEnumerator method returns an enumerator object.
The foreach statement is the most visible expression of enumeration in a C# program. The target of the
foreach statement is not any object—it has to be an enumerable object.
The next chapter documents the enumerator pattern, including how to implement an enumerator and
enumerable object.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Chapter 7: Iterators
Overview
Stacks, queues, dictionaries, dynamic arrays, normal arrays, and various other collections were introduced
in previous chapters. Collections are assemblages of related types. There could be a dynamic array of
Employee types, which would include HourlyEmployee, ExemptEmployee, and CommissionedEmployee
instances. There could be a queue of bank requests, such as DepositTransaction, WithdrawalTransaction,
and InterestTransaction types. Inheritance relates these types at compile time, whereas polymorphism
binds them at run time. Collections are also assemblages of similar items. Enumerating the elements of a
collection is a recurrent and valuable behavior. Enumeration iterates the items in a collection. A report that
lists new employees would need to enumerate the employee collection. Bank statements require
enumerating transactions of a particular bank client.
Enumeration is a valuable tool to almost every developer, encouraging some standardization. Enumerable
nongeneric types implement the IEnumerator and IEnumerable interfaces, whereas generic types implement
the IEnumerator<T> and IEnumerable<T> generic interfaces. IEnumerable interfaces return unique
enumerator objects to clients. An enumerator object implements the IEnumerator interface, which defines
the behavior of enumeration. Collections are enumerable objects, which implement one of the IEnumerable
interfaces and adhere to the enumeration pattern.
Collections are usually enumerated in a loop. Most collections offer collection-specific interfaces to
enumerate elements. The ArrayList type has indexes. The Stack has the Pop method. Hashtables have the
Keys and Values properties. Collections also offer a standardized approach to enumeration with the
IEnumerator interface. Loops are often harbingers of subtle bugs. The foreach statement is a
compiler-generated loop that uses IEnumerator objects to iterate error-free enumerable collections. This
reduces the code required to iterate a collection and makes developer code more robust.
The following is a straightforward foreach loop. The colors array is an array of strings. Arrays are collections
and are therefore enumerable. Instead of writing a for loop, with indexes and a counter to manage, the
foreach loop keeps everything simple. No indexes. No counters. The foreach loop automatically enumerates
each element of the collection.
Here is another example of a foreach loop. This code manipulates a Stack collection—a more complex
structure than a simple array. However, stacks are also collections, meaning that the foreach statement is
available.
Implementing the IEnumerator interface in an enumerable object can be challenging—especially for complex
data structures. Iterators have been introduced for this reason. Iterators provide a standardized
implementation of the enumerator pattern and prevent individual developers from re-creating the same wheel.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Enumerable Objects
Enumerable objects implement the IEnumerable interface. The GetEnumerator method is the only member
of this interface. It returns an enumerator, which is used to enumerate nongeneric collections.
Each invocation of the GetEnumerator method returns a unique enumerator. The enumerator is a state
machine that minimally maintains a snapshot of the target collection and a cursor. The cursor points to the
current item of the collection. The snapshot is a static copy of the collection. What happens if a collection is
modified while being enumerated? An exception occurs. You could lock the collection during enumeration,
but it might cause substantial performance degradation. As a best practice, an enumerator should capture
the collection as a snapshot. This isolates the enumerator from changes to the original collection. In
addition, the snapshot collection is read-only. The GetEnumerator method should be thread-safe,
guaranteeing that a unique enumerator is returned, which references an isolated collection regardless of the
thread context.
Enumerators
Enumerators are part of the enumeration pattern and normally implemented as a nested class within the
collection type. The primary benefit of nested classes is access to the private members of the outer class.
This access allows you to avoid breaking the rules of encapsulation to allow the enumerator class to access
the data store of the collection. The data store is undoubtedly a private member of the collection class.
Enumerators implement the IEnumerator interface, which has three members. This is the IEnumerator
interface:
The Current property returns the current element of the collection. MoveNext moves the Current property to
the next element. If the iteration has completed, MoveNext returns false. Otherwise, MoveNext returns true.
Notice that there is not a MovePrevious method; enumeration is forward only. The Reset method returns the
enumeration to the beginning of the collection.
The enumerator is the state machine representing the enumeration. Part of the state machine is the cursor,
which is the collection index or locator. The cursor is not necessarily an integral value, but normally it is. For
a link list, the cursor may be a node, which is an object. When the enumerator is created, the cursor initially
points before the first element of the collection. Do not access the Current property while the cursor is in this
initial state. Call MoveNext first, which positions the cursor at the first element of the collection.
The following is a typical constructor of an enumerator. In this example, the constructor makes a snapshot of
the collection. For reasons of simplicity, the collection is a basic array. The cursor is then set to –1, which is
before the first element of the collection.
The MoveNext method increments the value of the cursor. This action moves the Current property to the next
element of the collection. If the list has been fully iterated, MoveNext returns false. Collections are not
circular, where MoveNext might cycle through a collection. Circular collections are not within the enumerator
pattern. The Current property is not valid after the collection has been fully enumerated. For this reason, do
not use the Current property after MoveNext returns false.
The Reset method resets the enumeration. The cursor is updated to point to before the collection again.
Resetting the cursor also automatically resets the Current property.
With the cursor as a reference to the current item, the Current property provides access to the current
element of the collection. The Current property must be read-only. Implement the get method of the property
but not the set method. The implementation should check for fence-post errors. If the index is before or after
the collection, the appropriate exception should be thrown.
Enumerator states Enumerators can be in one of four possible states. Table 7-1 lists the enumerator states.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 7-1:
Enumerator
States
St De
ate scr
ipti
on
Bef Thi
ore s
is
the
sta
te
of
the
en
um
era
tor
bef
ore
en
um
era
tio
n
ha
s
sta
rte
d
or
aft
er
it
ha
s
be
en
res
et.
Th
e
firs
t
call
to
Mo
ve
Ne
xt
ch
an
ge
s
the
sta
te
fro
m
Bef
ore
to
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 7-1:
Enumerator
States
St De
ate scr
ipti
on
Ru Thi
nni s
ng is
the
sta
te
wh
en
Mo
ve
Ne
xt
is
cal
cul
ati
ng
the
ne
xt
ele
me
nt (
Cur
ren
t)
of
the
iter
ati
on.
Wh
en
Mo
ve
Ne
xt
ret
urn
s
tru
e,
the
ne
xt
ele
me
nt
ha
s
be
en
en
um
era
ted
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 7-1:
Enumerator
States
St De
ate scr
ipti
on
Su Th
sp e
en sta
de te
d of
the
en
um
era
tor
bet
we
en
en
um
era
tio
ns.
Cal
ling
Mo
ve
Ne
xt
ch
an
ge
s
the
sta
te
fro
m
Su
sp
en
de
d
to
Ru
nni
ng
whi
le
cal
cul
ati
ng
the
ne
xt
Cur
ren
t
pro
per
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 7-1:
Enumerator
States
St De
ate scr
ipti
on
Aft Thi
er s
is
the
sta
te
aft
er
en
um
era
tio
n
ha
s
co
mp
let
ed.
Re
set
ret
urn
s
the
en
um
era
tio
n
to
Bef
ore
,
an
d
en
um
era
tio
n
ca
n
be
res
tart
ed.
Enumerator Example
Sample code for an enumerator class and client are provided as follows. This is also the complete listing for
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
some of the partial code presented earlier in this segment. The SimpleCollection is a thin wrapper for a basic
array. Actually, it is somewhat redundant because arrays are already fully functional collections, including
exposing an enumerator. However, this simple example is ideal for demonstrating the enumerator pattern.
The enumerator pattern recommends isolation of the underlying collection. In this code, the enumerator is
created as a nested class, in which a snapshot of the collection is made in the constructor. The isolated
collection is created from a copy of the array. In addition, the Current property is read-only, which prevents
changes to the collection data.
In Main, an instance of the SimpleCollection is created. It is initialized with an integer array. The collection is
then iterated using the IEnumerator interface.
using System;
using System.Collections;
namespace Donis.CSharpBook{
IEnumerator enumerator=simple.GetEnumerator();
while(enumerator.MoveNext()) {
Console.WriteLine(enumerator.Current);
}
}
}
As mentioned, the SimpleCollection class makes a copy of the collection in the enumerator. This isolates
the collection from contamination caused by modifications. This is the recommended approach for volatile
collections. SimpleCollection is volatile because the elements of the underlying collection can be modified. If
the collection is static, a copy may not be necessary. Isolation protects a collection against changes. If the
collection is static, this is not an issue, and isolation is not required.
Here is the SimpleCollection class rewritten for a static collection. The underlying collection is read-only,
which makes it static. When the enumerator is created, the this reference of the enumerable object is
passed to the constructor. Future and unique enumerators share this reference to access the same
collection—maybe simultaneously. The state machine encapsulates the back reference and a cursor. The
back reference is the this reference to the outer object. With the back reference, the enumerator gains
access to members of the outer class, including the collection, which allows the enumerator to iterate the
collection directly.
if(cursor == -1) {
throw new InvalidOperationException(
"Enumeration not started");
}
return oThis.items[cursor];
}
}
Collections that have inconstant changes are not ideal for either of the enumerator models presented:
contentious or static. Copying the collection per each enumerator is costly when changes are infrequent.
The static model is inappropriate because the collection may indeed change, albeit not regularly. A
versioned collection is the solution and combines traits of the contentious and static models for
implementing enumerators.
The Main method is changed to test the versioning. The collection is modified in the method after the
enumerator has been obtained. After the modification, the Current property is used, which causes the
expected exception:
using System;
using System.Collections;
namespace Donis.CSharpBook{
IEnumerator enumerator=simple.GetEnumerator();
enumerator.MoveNext();
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Console.WriteLine(enumerator.Current);
enumerator.MoveNext();
simple[4]=10;
Console.WriteLine(enumerator.Current); // Exception raised
enumerator.MoveNext();
}
}
if(cursor>(oThis.items.Length-1)) {
throw new InvalidOperationException(
"Enumeration already finished");
}
if(cursor == -1) {
throw new InvalidOperationException(
"Enumeration not started");
}
return oThis.items[cursor];
}
}
IEnumerator Problem
Several techniques to implement the enumerator pattern have been shown. The varied strategies share
common problems. Enumerators, which implement the IEnumerator interface, manipulate System.Object
types. This is the heart of the problem.
There is a performance penalty from boxing and unboxing value types.
Alternatively, there is a performance cost to downcasting to reference types.
Frequent boxing stresses the managed heap.
Casting to and from System.Object is required, which is not type-safe.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
These problems were identified in the previous chapter. The solution was generic types. That is the solution
here also. The .NET Framework offers generic versions of the IEnumerable and IEnumerator interfaces.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Generic Enumerators
Nongeneric enumerable objects and enumerators are oblique. There is a lack of type specialty, which leads
to performance problems and other issues. You can implement enumerable objects and enumerators using
generic interfaces, which avoid some of the problems mentioned earlier. Implement the IEnumerable<T>
interface for generic enumerable objects. For generic enumerator objects, implement the IEnumerator<T>
interface. Both IEnumerable<T> and IEnumerator<T> are generic interfaces found in the .NET FCL in the
System.Collections .Generic namespace. IEnumerable<T> and IEnumerator<T> inherit their nongeneric
counterparts IEnumerable and IEnumerator, respectively. This means that generic enumerable objects and
enumerators are available generically or nongenerically.
IEnumerable<T> Interface
Generic enumerable objects implement the IEnumerable<T> interface. This is the IEnumerable<T> interface:
As shown, IEnumerable<T> inherits IEnumerable, which is the nongeneric version of the same interface.
This includes a nongeneric version of the GetEnumerator method. Enumerators inheriting IEnumerable<T>
must therefore implement a generic and nongeneric GetEnumerator method. The two GetEnumerator
methods differ in return type only. As you know, the return type alone is insufficient for overloading a method.
To prevent ambiguousness, one of the GetEnumerator methods must have explicit interface member
implementation.
This is sample code of the GetEnumerator methods for a generic enumerable object. (The nongeneric version
of GetEnumerator is implemented explicitly.)
IEnumerator IEnumerable.GetEnumerator() {
return new Enumerator<T>(this);
}
The generic version of GetEnumerator naturally returns a generic enumerator, which implements the
IEnumerator<T> interface.
IEnumerator<T> Interface
Generic enumerators implement the IEnumerator<T> interface, shown here:
Current is a read-only property and is the only direct member of the IEnumerator<T> generic interface. The
remaining members are inherited from the IDisposable and IEnumerator interfaces. The IDisposable
interface defines generic enumerators as disposable. This requires implementing the IDisposable.Dispose
method. The IEnumerator interface adds the nongeneric enumerator interface. The MoveNext and Reset
methods do not require a generic implementation and are therefore not defined in the generic portion of the
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
interface. A second Current property, a nongeneric version, is inherited from the IEnumerable interface.
Therefore, IEnumerator has overloaded Current properties, where both should be implemented in the
enumerator.
This is sample code of a generic and nongeneric implementation of the Current property. The nongeneric
Current property delegates to the generic version.
if(cursor>(oThis.items.Length-1)) {
throw new InvalidOperationException(
"Enumeration already finished");
}
if(cursor == -1) {
throw new InvalidOperationException(
"Enumeration not started");
}
return oThis.items[cursor];
}
}
object IEnumerator.Current {
get {
return Current;
}
}
The Dispose method supports deterministic garbage collection. This method is called explicitly to clean up
for an object. In this circumstance, the method is called to clean up resources assigned to an enumerator.
Dispose methods of enumerators are most frequently called by iterators, which is the next topic of this
chapter. In the Dispose method, set the state of the enumeration to After and perform any necessary
cleanup. Some enumerators track the state using a flag (an enumeration type), where the flag is updated in
the Dispose method and elsewhere in the enumerator. The code presented earlier does not employ a state
flag. If the cursor is beyond the collection, the After state is assumed. Conversely, a cursor of -1 indicates
the Before state. Based on these assumptions, this is one implementation of a Dispose method for an
enumerator:
Earlier in the chapter, a version collection model of enumeration was presented. Here is the versioned
collection example redone with generic interfaces. The following code also completes some of the partial
code presented earlier in this section. In Main, the collection is enumerated in a generic and nongeneric
manner. In the second foreach loop, the simple collection is cast to the nongeneric IEnumerable interface.
This instructs the foreach statement to call the nongeneric GetEnumerator method, which returns a
nongeneric enumerator. The nongeneric enumerator is then used to iterate the simple collection.
using System;
using System.Collections;
using System.Collections.Generic;
namespace Donis.CSharpBook{
foreach(int number in
(IEnumerable) simple) {
Console.WriteLine(number);
}
}
}
++version;
items[index]=value;
}
}
IEnumerator IEnumerable.GetEnumerator() {
Console.WriteLine(
"IEnumerator GetEnumerator()");
return new Enumerator<T>(this);
}
if(cursor>(oThis.items.Length-1)) {
throw new InvalidOperationException(
"Enumeration already finished");
}
if(cursor == -1) {
throw new InvalidOperationException(
"Enumeration not started");
}
return oThis.items[cursor];
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
object IEnumerator.Current {
get {
return Current;
}
}
private T [] items=null;
private int version;
}
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Iterators
Iterators implement the enumerator pattern. This chapter has demonstrated different implementation
strategies for enumerators. The chosen implementation depends on the answer to relevant questions. Should
the enumerator be implemented as a nested class? Should a version collection be used? Should the
implementation be generic or nongeneric? Having every developer answer these questions and author a
proprietary enumerator is not a productive use of their collective mental prowess. Almost assuredly, the
various implementations of the enumerator pattern are within an acceptable delta in performance and
memory requirements. Anyway, a certain number of developers simply visit the MSDN Web site, from which
they copy and paste the enumerator implementation. Therefore, the various implementations are undoubtedly
quite similar. Iterators standardize the enumerator pattern for developers.
When implementing enumerators ourselves, the real challenge is complex enumerations. As shown, forward
iterations are usually straightforward. Reverse iterations are more complex, however. Coding an enumerator
to iterate a sparse collection, nodal list, or binary tree can also be daunting. What about enumerating a
temporary collection? This often requires maintaining multiple enumerators, such as a forward and reverse
enumerator. Does this sound like fun? Actually, I would gladly pass the baton to the compiler.
.NET 2.0 introduced the iterator, which is a compiler-manifested enumerator. When the enumerable object
calls GetEnumerator, either directly or indirectly, the compiler generates and returns an appropriate iterator
object. Optionally, the iterator can be a combined enumerable and enumerator object. You are not
completely excluded from the process. Developers can affect the enumeration in an iterator block. The
essential ingredient of an iterator block is the yield statement.
Yield Statement
The following code demonstrates one of the obvious benefits of the yield statement and an iterator block:
brevity. The previous sample code of a generic enumerator required more than 50 lines of code. The following
example implements a similar enumerator using the yield statement, which requires three lines of code:
In the previous code, the C# compiler implements the enumerator. You are still coding the IEnumerable
interface. Optionally, the compiler can implement both the enumerable and enumerator object in the iterator.
If the iterator block returns IEnumerable, the compiler responds by creating an enumerable and enumerator
object. Iterator blocks are explained soon. This removes even having to implement the GetEnumerator
method.
For clients, iterators are similar to enumerators. Clients call GetEnumerator or other iterator methods to
obtain an iterator object. You then use the IEnumerator or IEnumerator<T> interface to enumerate the
collection. There is one big difference between iterators and enumerators: Iterators do not implement the
Reset method. Calling the Reset method on an iterator causes an exception.
The pivotal statement of an iterator is yield. This is the syntax of the yield statement:
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The yield return statements iterate the next element of a collection. The statement expression is assigned to
the Current property. Enumerators start in the Before state. The initial MoveNext method calls the first yield
statement. After the Current property is set, the enumerator is suspended. The next MoveNext calls the next
yield. This pattern continues until the enumeration is finished. The iterator block is not called anew for each
MoveNext. Between yield statements of the same iterator block, enumeration is suspended. The iterator is a
state machine that maintains the state of the enumeration between calls to the MoveNext method.
The yield break statements finish an enumeration, which ultimately changes the enumerator state to after.
The Dispose method is then called to clean up the enumerator. This is sample code of the yield break
statement. The yield break statement stops the enumeration after the fourth element.
Iterator Blocks
Iterator blocks contain the logic to enumerate a collection. This includes one or more yield statements.
Methods, properties, and operator functions can be iterator blocks. Iterator blocks are not executed
continuously and are sometimes suspended. The function is suspended between successive yield
statements, which are controlled by the MoveNext method. As mentioned, the iterator block maintains the
state machine of the enumerator between iterations.
There are restrictions on iterator blocks. For example, iterator blocks cannot have a return statement. Only
yield return statements are allowed:
When an iterator block is exited, the Dispose method of the enumerator is called. Internally, the iterator
creates the enumerator in a using block. This provides an opportunity to clean up for the enumerator when
the enumeration is completed.
Iterator Internals
Iterators are implemented as nested classes, which are created by the C# compiler. The nested class
maintains the state of the current enumeration. It persists the enumeration state between yield statements.
Iterators are created by the language compiler, not by the Common Language Runtime (CLR). Neither
Microsoft intermediate language (MSIL) nor metadata has changed to especially accommodate iterators. The
nested class for an enumerator is a normal class, which is created for a method that contains an iterator
block. If three methods within a class have enumerator blocks, the compiler adds three nested classes to
that class.
If the iterator method returns either the IEnumerator<T> or IEnumerable<T> interfaces, the name of the
nested class has the <T> suffix.
The compiler adds three nested classes, one for each enumerator method, to the ZClass type. The various
nested classes represent the state machine for different enumerators. Figure 7-1 shows ZClass and the
nested classes.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Figure 7-1: A view of the ZClass type, which includes the nested enumerator classes
The nested enumerator class has several private fields. The local variables of the iterator method are lifted to
fields of the nested class. The fields maintain the state of these local variables throughout the enumeration.
The nested class also has three special purpose fields. The state of the enumeration, such as running or
after, is in the <>1__state field. The <>2__current field is the result of the last iteration. It is the current item.
The <4>__field is a back pointer to the outer class. If the iterator method is static, the back pointer is not
initialized; it is initialized only for instance methods. Combined, the lifted and specialty fields are the state of
the enumeration.
Figure 7-2 shows the fields of a typical nested class for an enumerator.
Iterator Examples
This section presents several examples of iterators to demonstrate the flexibility of iterators and provide
techniques for using them.
using System;
using System.Collections.Generic;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
ZClass obj=new ZClass();
foreach(int item in obj) {
Console.Write(item);
}
}
}
}
}
The preceding code alternates between yielding list1 and list2. As the iteration moves between collections,
the even and odd numbers are intermixed. The result is 0123456789. ZClass does not inherit the
IEnumerable interface. However, it adheres to the enumerator pattern by implementing the GetEnumerator
method. This is sufficient for defining enumerable objects. The following examples implement the iteration
pattern but not explicitly the interfaces.
Reverse iteration The following example iterates a collection forward and reverse. Two iterators are
exposed for this reason. GetEnumerator exposes the standard forward iterator. The reverse iterator is
implemented in the Reverse property. Because the property returns IEnumerable, the Reverse property is
both an enumerable and enumerator object. When an iterator method returns IEnumerable, the nested class
generated by the compiler is implemented as both an enumerable and enumerator object. In Main, there are
two foreach loops: The first foreach loop uses the forward iterator, whereas the reverse iterator is requested
in the second loop:
using System;
using System.Collections.Generic;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
Console.WriteLine("Forward List");
ZClass obj=new ZClass();
foreach(int item in obj) {
Console.Write(item);
}
Console.WriteLine("\nReverse List");
foreach(int item in obj.Reverse) {
Console.Write(item);
}
}
}
}
}
}
Temporary collections Temporary collections are calculated at run time and are useful in a variety of
circumstances. The list of prime numbers, the records of a file, and fields in a dataset are examples of
collections that can be calculated. Temporary collections can be populated lazily. You can read a flat file or
dataset on demand at run time to hydrate a temporary collection.
The following code enumerates days from the current date until the end of the month, which is calculated
using DateTime structure. The method ToEndOfMonth is enumerable by virtue of returning the IEnumerable
interface and possessing a yield statement. Each repeat of the while loop extrapolates the next day until the
end of the month is reached. The yield statement iterates the days as they are calculated.
using System;
using System.Collections;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
foreach(string day in ToEndOfMonth()) {
Console.WriteLine(day);
}
}
int currMonth=date.Month;
while(currMonth==date.Month) {
string temp=currMonth.ToString()+
"/"+date.Day.ToString();
date=date.AddDays(1);
yield return temp;
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
}
}
}
Complex iteration Link lists are complex data structures. Iterators are particularly useful when iterating
complex data structures, such as a link list. Each item in the list is considered a node. Nodes maintain a
reference to the previous and next node. From any node, you can walk the link list either forward or
backward. For several reasons, the iteration is more complex. First, data structure must be iterated forward
and backward during the same iteration. Second, fence posts are not as definable. Fence posts in arrays,
stacks, queues, and other sequenced containers are easily found, which helps avoid fence post error
exceptions. Finally, the iteration can start from any node in the link list, not necessarily just the beginning or
end of the list.
Below is a partial listing of the Node class. The key code is in the GetEnumerator method. In the first while
loop, the link list is iterating in reverse—from the current node to the beginning of list. This is accomplished
by walking the prevNode member of the node class. The current node is enumerated next. Finally, the
second while loop iterates the link list going forward from the current note to the end of the list. The nextNode
members are walked.
this.prevNode=node;
this.nextNode=node.nextNode;
node.nextNode=this;
if(node.nextNode==null) {
lastNode=null;
}
}
private T m_Info;
private static Node<T> lastNode=null;
private static Node<T> firstNode=null;
private Node<T> prevNode=null;
private Node<T> nextNode=null;
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Delegates are frequently used as callbacks, in which a function is called upon something happening. The
Event model promotes an effective paradigm for callbacks, where there are publishers and subscribers.
Publishers expose something that happens—better known as an event. Subscribers subscribe to an event
with a delegate. The delegates of the subscriber are called when the event occurs.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
A delegate is an abstraction of one or more function pointers. Delegates are derived from
System.MulticastDelegate, which is a reference type. System.MulticastDelegate is derived from
System.Delegate. The delegate classes offer a public interface for initializing, adding, removing, and invoking
delegates. An instance of a delegate is an object that abstracts the semantics of a function pointer. The
object encapsulates a function pointer and a target object. When a delegate is invoked, the delegate calls
the function on that object.
Delegates have a signature and a return type. A function pointer dropped into the delegate must have a
compatible signature, which makes the function pointer type-safe. The return type should also match.
Delegate covariance, which is discussed later in the chapter, provides some flexibility with the signature.
You assign function pointers to delegates based on signature, not type. Regardless of the object or type that
binds the function, it is assignable to a delegate of the same signature.
Functions called with a delegate are given the security context of the caller, which prevents a delegate from
performing a task not available to a lower-privilege caller. Delegates can be initialized with pointers to
functions that are implemented anywhere. The only limitation is the signature. Callers need to be careful
when invoking delegates containing function pointers to unknown sources, where there could be unexpected
implementation. Use code access security to protect delegates.
Delegates are useful as general function pointers, callbacks, events, and threads. As a general function
pointer, a delegate can be a method parameter, function return, class member, or local variable. Callbacks
are valuable in many scenarios, including promoting peer-to-peer relationships, in which objects swap
function pointers to send bilateral messages. Events support a publisher/subscriber model. The publisher
notifies subscribers of events, whereas the subscriber registers functions to be called when the event occurs.
Finally, a delegate is a path of execution for a thread, which is an asynchronous function call. This chapter
focuses on general function pointers, callbacks, and events. Threads are discussed only in this context.
Many .NET applications are event-driven. Event-driven architecture is common in the .NET environment with
Microsoft Windows Forms, ASP.NET, and XML Web Service applications. The lifetime of an event-driven
application is spent waiting for events to handle, such as a paint event for Windows Forms, a page load
event in an ASP.NET application, or a session event in an XML Web Service application. Although
procedural applications execute linearly, event-driven code runs disjointedly. The sequence of code in an
event-driven application is determined at run time from the user interface and other input sources. Poorly
designed or implemented procedural code is commonly referred to as spaghetti code. Poorly implemented
code for an event-driven application is called ravioli code and it can literally run in circles. The randomness of
an event-driven application makes poorly written code harder to maintain and debug.
Events are the most practical application of delegates. Events mark an occurrence as defined by an
application. For proper application design, events in an application should mirror events in the problem
domain. Events represent a wide variety of actions and include predefined and custom events. Predefined
events include button click, page load, timer, unhandled exception, and so on. Custom events include events
for monitoring hardware devices, starting and stopping communications, overdrawing a checking account,
setting alarms, and so on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The event model includes subscribers and publishers: A publisher is an object or type that exposes an
event; a subscriber registers for an event with a delegate. The delegate contains the response to the event.
When an event is raised, publishers invoke the delegates of any subscribers. The Observer pattern, which
includes the event patterns, documents the best practices of an event-driven environment. Use the following
link to view the Observer pattern on the Microsoft Developer Network (MSDN):
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/observerpattern.asp.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Delegates
The common blueprint for using delegates includes steps to define, create, and invoke a delegate.
1. Define a classification of a delegate using the delegate keyword:
2.
3. public delegate int DelegateClass(string info);
4.
5. Create an instance of a delegate using the new keyword. In the constructor, initialize the delegate
with a function pointer. You can also create and initialize a delegate implicitly without the new
keyword:
6.
7. DelegateClass obj=new DelegateClass(MethodA);
8. DelegateClass obj2=MethodA; // implicit
9. Invoke the delegate with the call operator "()". The call operator is convenient, but makes it harder
to discern a delegate invocation from a regular function call. Alternatively, invoke the delegate with
the Invoke method, which clearly distinguishes invoking a delegate from a normal function call:
10.
11. obj("1");
12. obj.Invoke("2"); // Alternative.
13. As with any object, when the delegate is no longer needed, set the delegate to null:
14.
15. obj=null;
using System;
namespace Donis.CSharpBook{
}
}
Define a Delegate
The delegate keyword is for defining new delegates. The delegate statement looks like a function signature.
Rather, it defines a new delegate type. Function pointers matching the delegate signature and return type
can be stored into the delegate. Therefore, only functions with similar signatures can be called through the
delegate. This code defines a new delegate, which internally becomes a class named DelegateClass:
Accessibility is limited to the valid accessibility of classes, such as public or private. The remainder of the
syntax defines the signature and return type of the delegate. delegatename is the name of the delegate
classification.
Because defining a delegate creates a new class, delegates can be defined anywhere a class is appropriate.
A delegate can be defined in a namespace as a namespace member and within a class as a nested class,
but not as a class field or a local variable within a method.
Create a Delegate
As a class, use the new keyword to create an instance of a delegate. As mentioned, delegates are derived
from the System.MulticastDelegate reference type. Multicast delegates are repositories of zero or more
function pointers. The list of pointers in a multicast delegate is called the invocation list. When a delegate
hosts multiple function pointers, the functions are called on a first-in, first-out (FIFO) basis.
The delegate constructor is not overloaded and has a single parameter, which is the target method. For an
instance method, use the object.method format. If static, use the class.method format. If the method and
delegate are contained in the same class, neither the object nor class name is required. The following code
initializes a delegate in a variety of ways:
using System;
namespace Donis.CSharpBook{
You can assign a function pointer directly to a delegate and omit the new operator, which is called delegate
inference. Delegate inference infers a delegate signature from the function pointer, creates a new delegate,
initializes the source delegate with the function pointer in the constructor, and assigns the source delegate
to the target delegate. The source delegate and the target delegate should have compatible signatures.
Although the code is more concise, the intent is not as obvious. Here is the previous code, changed for
delegate inference:
DelegateClass del1=Constructors.MethodA;
DelegateClass del2=MethodA;
The signatures of delegates are not entirely rigid. Through contravariance and covariance, there is some
flexibility. The signature of the function pointer does not have to match the delegate signature exactly.
using System;
namespace Donis.CSharpBook{
delegate ZClass DelegateClass(BClass obj);
public class Starter{
public static void Main(){
DelegateClass del=MethodA;
}
The signature of the delegate and function pointer are not exactly the same. This is okay because the
parameter of the delegate (BClass) refines the parameter of the function (AClass), which is contravariance. In
addition, the return type of the function (YClass) refines the return type of the delegate (ZClass). This is
covariance.
Invoking a Delegate
Delegates are invoked through the call operator or the Invoke method. The C# compiler translates the call
operator into an invocation of the Invoke method, which has the same signature as the delegate and calls
the function pointers contained in the delegate. The parameters of the delegate become the parameters and
input to the function calls. If the delegate contains a function pointer, the return from that function becomes
the return of the delegate. When multiple function pointers are stored in the delegate, the return of the last
function becomes the return of the delegate.
Arrays of Delegates
Arrays of delegates can be the impetus of elegant solutions that would otherwise be unavailable. Creating an
array of delegates is the same as creating an array of any type. Selecting from a set of tasks at run time is
an example of when an array of delegates is essential to an elegant solution. The switch statement is one
solution, in which each case of the switch statement is assigned to a task. Suppose that there are about
three lines of code associated with each task. In the next revision of the application, 30 additional tasks are
added, which results in an additional 90 lines of code. This solution provides linear growth and is not
particularly scalable. With an array of delegates, regardless of the number of tasks, the solution is one line
of code. Now and in the future, this solution remains one line of code. This solution is more scalable than the
switch statement approach. In the following code, an array of delegates facilitates invoking a task from a
menu of choices:
using System;
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
namespace Donis.CSharpBook{
System.MulticastDelegate Class
Delegates default to multicast delegates, which inherit from the System.MulticastDelegate class. A multicast
delegate is similar to a basket. Multiple function pointers or delegates can be dropped into the basket. The
list of delegates is stored in an invocation list, which can even include multiple instances of the same
delegate. When you execute the contents of the basket, the function pointers of the delegates are called
FIFO. Multicast delegates are useful for invoking a chain of functions.
Combining delegates Multiple delegates are combined using the Combine method, the plus operator (+),
or the += assignment operator. Combine is a static method. In C#, the Combine method is called with two
delegate parameters or an array of delegates. The method returns a reference to the combined delegate.
The following code combines two delegates separately wrapping functions MethodA and MethodB. When the
Combine delegate is invoked, MethodA is run first and MethodB second. This is the order in which the
function pointers are added to the multicast delegate. Both the Combine method and the += assignment
operators are shown, with the Combine statement commented to avoid duplication.
using System;
namespace Donis.CSharpBook{
public delegate void DelegateClass();
public class Starter{
public static void Main(){
DelegateClass del=MethodA;
del+=MethodB;
// del=(DelegateClass) DelegateClass.Combine(
// new DelegateClass [] {MethodA, MethodB});
del();
}
public static void MethodA() {
Console.WriteLine("MethodA...");
}
Removing delegates To remove delegates from a multicast delegate, use the Remove method, the minus
operator (-), or the -= assignment operator. Remove is a static method that accepts the source delegate as
the first parameter and the delegate to remove as the second parameter. Be careful not to inadvertently
remove all delegates from a multicast delegate, which causes a run-time error when invoked. The following
code removes MethodB from a multicast delegate. When the delegate is invoked, MethodA is invoked, but
not MethodB. The three methodologies are shown, with the Combine statement and operator minus
commented to avoid duplication.
using System;
namespace Donis.CSharpBook{
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
// del=(DelegateClass) DelegateClass.Remove(del,
(DelegateClass) MethodB);
// del-=MethodB;
del();
}
public static void MethodA() {
Console.WriteLine("MethodA...");
}
Invocation List
Multicast delegates maintain an invocation list, which has an entry for each delegate of the multicast
delegate. Entries are added to the invocation list in the same order in which delegates are added.
GetInvocationList returns the invocation list as an array of delegates.
This code retrieves the list of delegates in a multicast delegate, in which each delegate is a wrapper of a
function. The name of each function is displayed. The Delegate.Method property returns a MethodInfo type,
which encapsulates the function abstracted by the delegate.
using System;
namespace Donis.CSharpBook{
public delegate void DelegateClass();
public class Starter{
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
}
public static void MethodA() {
Console.WriteLine("MethodA...");
}
The delegate signature can contain reference parameters. When invoked, the reference is passed to the
called functions. Reference parameters are a way for functions on an invocation list to share state. Value
parameters are not shareable across the functions. Because the invocation list is called in sequence,
beginning with the first delegate and function, each successive entry in the chain can view changes in the
reference parameter. The next item in the chain can view those changes and possibly change the state
again. In this way, when the multicast delegate is invoked, the state information is propagated along the
invocation list. Furthermore, functions in invocation lists that have the same target object or class share the
state of the object or class. The following code uses the reference parameter of a multicast delegate as a
counter. The delegate has a value and reference parameter. The value parameter is lost between function
calls in the invocation list. However, the reference parameter persists.
using System;
namespace Donis.CSharpBook{
valCount+=2;
refCount+=2;
}
}
}
You can access the invocation list to execute each delegate and function therein directly. There are two
reasons to invoke the invocation list directly. First, invoke the delegates explicitly to obtain the return of each
delegate. When the invocation list is invoked implicitly, only the return of the last function is garnered.
Second, invoke the invocation list directly in special circumstances (for example, to modify how exceptions
are handled in a multicast delegate, which is described later).
The following code uses the invocation list to calculate a factorial. The Incrementer method increments a
number, which is a reference parameter. The incremented value is returned from the method. Five delegates
are created and initialized with the Increment method and combined into a multicast delegate. The foreach
loop iterates the invocation list, multiplying the results of each function to calculate a factorial.
using System;
namespace Donis.CSharpBook{
IncrementDelegate [] values=
{ Incrementer, Incrementer,
Incrementer, Incrementer,
Incrementer};
IncrementDelegate del=(IncrementDelegate)
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
IncrementDelegate.Combine(
values);
long result=1;
short count=1;
foreach(IncrementDelegate number
in del.GetInvocationList()) {
result=result*number(ref count);
}
Console.WriteLine("{0} factorial is {1}",
del.GetInvocationList().Length,
result);
}
}
}
Methods and properties Several members of delegate object, such as GetInvocationList, have already
been introduced. Table 8-1 is a list of some of the public properties and methods of the MulticastDelegate
type. Some of the members are inherited from System.Delegate.
Table 8-1:
Delegate
Members
Me De
mb scr
er ipti
on
Be Inv
gin ok
Inv es
ok a
e del
eg
ate
as
yn
chr
on
ou
sly
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 8-1:
Delegate
Members
Me De
mb scr
er ipti
on
Co Co
mb mb
ine ine
s
del
eg
ate
s
int
o
a
mu
ltic
ast
del
eg
ate
.
Thi
s
is
a
sta
tic
me
tho
d.
Cre Def
ate ine
Del s
eg a
ate del
eg
ate
at
run
tim
e.
Thi
s
is
a
sta
tic
me
tho
d.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 8-1:
Delegate
Members
Me De
mb scr
er ipti
on
Dy Dy
na na
mi mi
cIn call
vo y
ke inv
ok
es
a
del
eg
ate
tha
t
wa
s
cre
ate
d
at
run
tim
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 8-1:
Delegate
Members
Me De
mb scr
er ipti
on
En Re
dIn qu
vo est
ke s
the
res
ult
s
of
a
del
eg
ate
tha
t
wa
s
ex
ec
ute
d
as
yn
chr
on
ou
sly
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 8-1:
Delegate
Members
Me De
mb scr
er ipti
on
Inv Ex
ok ec
e ute
s
a
del
eg
ate
,
whi
ch
call
s
all
fun
cti
on
s
co
nta
ine
d
in
the
del
eg
ate
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 8-1:
Delegate
Members
Me De
mb scr
er ipti
on
Me Thi
tho s
d is
a
pro
per
ty
tha
t
ret
urn
s
the
Me
tho
dIn
fo
typ
e
of
the
las
t
fun
cti
on
in
the
inv
oc
ati
on
list
.
Me
tho
dIn
fo
pro
vid
es
a
de
scr
ipti
on
of
a
fun
cti
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 8-1:
Delegate
Members
Me De
mb scr
er ipti
on
Re Re
mo mo
ve ves
a
del
eg
ate
fro
m
a
mu
ltic
ast
del
eg
ate
.
Thi
s
is
a
sta
tic
me
tho
d.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 8-1:
Delegate
Members
Me De
mb scr
er ipti
on
Re Re
mo mo
ve ves
All the
inv
oc
ati
on
list
of
a
del
eg
ate
fro
m
the
inv
oc
ati
on
list
of
an
oth
er
del
eg
ate
.
Th
e
res
ult
is
ret
urn
ed.
Thi
s
is
a
sta
tic
me
tho
d.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 8-1:
Delegate
Members
Me De
mb scr
er ipti
on
Tar Thi
get s
is
a
pro
per
ty
tha
t
ret
urn
s
the
ins
tan
ce
of
the
las
t
fun
cti
on
in
the
inv
oc
ati
on
list
.
For
sta
tic
fun
cti
on
s,
Tar
get
is
null
.
In addition, the delegate itself can be generic and closed when the delegate is created. Functions added to a
generic delegate need to match the signature of the closed generic delegate.
using System;
namespace Donis.CSharpBook{
public delegate void DelegateClass(int data);
public class Starter{
public static void Main(){
DelegateClass del1=MethodA<int>;
del1(5);
DelegateClass del2=MethodA;
del1(10); // inferred
}
using System;
namespace Donis.CSharpBook{
public delegate void DelegateClass<T>(T data);
public class Starter{
public static void Main(){
DelegateClass <string> del=MethodA;
del("data");
}
Asynchronous Invocation
Delegates are invoked synchronously via the Invoke method or call operator. Delegates can also be invoked
asynchronously on a separate worker thread. A delegate performing a time-consuming task might improve
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
application performance by running on a separate thread. This is especially true for Windows Forms
applications, in which responsiveness of the user interface is a concern. The user interface could freeze for
an extended period time while a synchronous delegate is performing an extended task. Other types of
operations benefit from asynchronous delegates: network services, methods dependent on hardware devices,
timed routines, and more. Synchronous solutions are simpler. However, this often falls short of the
sophistication required for a professional application. Developers must be concerned with thread
synchronization in asynchronous executions, which undoubtedly adds complexity. There is much more to
writing robust multithread applications then simply spawning threads.
When a delegate is defined, the C# compiler adds several methods for invoking function pointers. The Invoke
method is for synchronous invocation, while the BeginInvoke and EndInvoke methods are included for
asynchronous invocation. The default for the call operator is synchronous execution.
BeginInvoke Method
BeginInvoke is callable on single-cast delegates alone. A single-cast delegate is a multicast delegate that
contains a single function pointer. BeginInvoke adds the operation to a queue, where it is then serviced by a
separate thread from a Common Language Runtime (CLR) managed thread pool. Therefore, functions on the
invocation list run on a different thread from the caller of the delegate. The thread pool for asynchronous
execution has a maximum of 25 threads per processor.
BeginInvoke begins with the parameters of the delegate signature. If the delegate signature names four
arguments, those are the first four parameters of BeginInvoke. The next parameter is the completion routine.
This routine is called back when the asynchronous call has completed. The final parameter is a state object.
The completion routine and state objects are optional and either can be set to null. The return is an
IAsyncResult object, which maintains the state of the object. IAsyncResult has four properties, which are
listed in Table 8-2.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 8-2:
IAsyncResult
Properties
Pr De
op scr
ert ipti
y on
As Th
ync e
Sta sta
te te
obj
ect
tha
t
ha
s
info
rm
ati
on
on
the
as
yn
chr
on
ou
s
op
era
tio
n.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 8-2:
IAsyncResult
Properties
Pr De
op scr
ert ipti
y on
As Us
ync e
Wa the
itH Wa
an itH
dle an
dle
to
blo
ck
unt
il
the
as
yn
chr
on
ou
s
op
era
tio
n
is
co
mp
let
ed.
Co Indi
mp cat
let es
eS tha
ync t
hro the
no op
usl era
y tio
n
co
mp
let
ed
sy
nc
hro
no
usl
y.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 8-2:
IAsyncResult
Properties
Pr De
op scr
ert ipti
y on
IsC Indi
om cat
ple es
ted tha
t
the
as
yn
chr
on
ou
s
op
era
tio
n
ha
s
co
mp
let
ed.
The following code highlights the IAsyncResult and delegate state object:
using System;
using System.Threading;
namespace Donis.CSharpBook{
public delegate void DelegateClass();
Thread.Sleep(100);
lock(state) {
Console.WriteLine("Back in Main");
Console.WriteLine(state.message);
}
}
class DelegateStateBag {
public string message;
}
}
Interestingly, the not completed message is received before MethodA has even started. This confirms that
BeginInvoke does not directly invoke the function in the delegate. Rather, it adds a request to the thread
pool queue, which is eventually handled. There is a Thread .Sleep statement near the end of the Main
method. If removed, a race condition would exist between Main and the completion routine called Callback.
What is the source of the race condition? Note the WaitHandle.WaitOne command in Main, which blocks
Main until the asynchronous operation is complete. As the completion routine, Callback also waits for the
asynchronous operation to finish. When the asynchronous operation finishes, the Main and Callback
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
routines both resume, and the race begins. Callback needs to update the state object before Main displays
the contents of the state object. The Thread.Sleep statement is a primitive way of removing the race
condition and allowing the Callback routine to lock the shared state object first.
EndInvoke Method
EndInvoke provides that the result of an asynchronous operation is callable in the completion routine or the
thread of the delegate caller. EndInvoke has the same signature as the delegate without the value
parameters and has an additional final parameter, which is an IAsyncResult object. To obtain the signature
of the EndInvoke method, remove the value parameters from the delegate signature and add an IAsyncResult
parameter. The IAsyncResult parameter is the same IAsyncResult object that is returned from BeginInvoke.
The return type of EndInvoke is identical to the return of the delegate.
Calling EndInvoke before functions of the asynchronous delegate has finished executing blocks the caller
until the operation is completed. For each BeginInvoke, there should be a complementing EndInvoke to
confirm the results of the operation, detect an exception, and to allow the CLR to properly clean up the
delegate. Calling the EndInvoke method more than once on the same delegate yields undefined results and
should be avoided.
using System;
using System.Threading;
namespace Donis.CSharpBook{
public delegate int DelegateClass(out DateTime start,
out DateTime stop);
ar.AsyncWaitHandle.WaitOne();
elapse);
}
stop=DateTime.Now;
return (stop-start).Seconds;
}
}
}
The preceding text explains how to invoke a delegate asynchronously and to obtain the results. Figure 8-1
diagrams the relationship and sequence of operations. This includes the delegate, the BeginInvoke method,
the EndInvoke method, and the delegate completion routine.
Delegate Internals
Delegates may appear to be magical to developers because so much is hidden. The magician is the C#
compiler. This section explains the magic of the compiler. Why should you care? Understanding how
delegate are implemented internally should help in the how, where, and when to use delegates.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The C# compiler builds a class from the definition of a delegate. The class shares the name of the delegate
and has four methods: a constructor, Invoke, BeginInvoke, and EndInvoke. Importantly, the delegate class
is derived from System.MulticastDelegate. This is a typical definition of a delegate:
As viewed in ildasm, C# creates the ADelegate class from this delegate. (See Figure 8-2.)
Members of the delegate class, such as Invoke and BeginInvoke, were described earlier in this chapter.
However, it is comforting to actually see them. It is not magic after all. There may be a slight surprise: The
constructor of the delegate has two arguments. The code in this chapter shows the delegate constructor
being called with a single parameter. The C# compiler helps us here: It interprets the single parameter as a
target object and function pointer, which are the parameters of the two argument constructors. Also notice
that the signatures of Invoke, BeginInvoke, and EndInvoke were defined as described earlier in this chapter.
Exceptions
What happens if an unhandled exception is raised in a multicast delegate? If the delegate is invoked inside a
protected block of code, the exception is trapped. The invocation list is called until an exception occurs.
Functions later in the invocation list do not execute. Let us assume that the invocation list has pointers to
MethodA, MethodB, MethodC, and MethodD. They were also added to the delegate in that order. If an
unhandled exception is raised in MethodB, MethodC and MethodD are not called. The situation might appear
to be more complicated if the exception is raised in a delegate that is running asynchronously. In this
circumstance, the exception is raised on a different thread. However, the run time will route the exception
back to the calling thread, as evidenced in the following code. Therefore, the exception is handled as if the
delegate were called synchronously.
using System;
using System.Threading;
namespace Donis.CSharpBook{
Anonymous Methods
An anonymous method is a nameless method. Sometimes a function is intended exclusively for a delegate.
Without an anonymous method, you would have to define a separate function as part of a class. The function
can then be assigned to the delegate. Assigning an anonymous method prevents creating a separate
method. This is cleaner and more convenient than a named method. Anonymous methods can substitute for
delegates as parameters, return values, and in other situations.
Define an anonymous method with the delegate keyword. This code assigns an anonymous method to a
delegate:
using System;
using System.Threading;
namespace Donis.CSharpBook{
In the preceding example, the anonymous method does not have a signature. The signature of the
anonymous method is inferred from the delegate signature, which is another example of delegate inference.
Despite not having a signature, the return of the anonymous method must match the delegate return. The
advantage of an anonymous method without a signature is its compliance with almost any delegate. The
disadvantage is that anonymous methods defined without a signature cannot access the parameters of the
delegates. This means that the anonymous method cannot utilize the parameters. For this reason,
anonymous methods without a signature cannot be employed where delegates have out parameters. Out
parameters must be initialized in the called method, which is impossible in an anonymous method. When an
anonymous method is called through the delegates, the parameters must still be provided, even if ignored
inside the method. Of course, if the delegate has no parameters, none should be provided.
The C# compiler performs several tasks to assign an anonymous method without a signature to a delegate:
1. Validates the signature of the delegate—no out parameters
2. Infers the signature of the anonymous method from the delegate
3. Confirms that the return type of the delegate is compatible with the anonymous method
4. Creates a new delegate that is initialized with a function pointer to the anonymous method
Anonymous methods can be assigned a signature, which is appended to the delegate keyword. This
restricts the anonymous method to a delegate with a matching signature and return type. Like conventional
methods, the parameters are initialized with the parameters from the delegate call. This is an example of an
anonymous method with a signature:
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
using System;
using System.Threading;
namespace Donis.CSharpBook{
As demonstrated in the preceding code, anonymous methods can be used for delegates. The exception is
the -= assignment operator. Because the anonymous method is unnamed, it cannot be used to remove a
named method from a multicast delegate. This is the exception to freely substituting delegates with
anonymous methods.
Actually, anonymous methods are not nameless. The C# compiler implements the anonymous method as
the named method in the containing class. The generated method is both static and private. The wrapper
delegate is initialized with this method. The following format is used to name anonymous methods:
<functionname>uniqueid
The function name is the function in which the anonymous method is implemented. The unique identifier is a
combination of a letter and number. The method has the same signature as the target delegate.
The wrapper delegate initialized with the anonymous method is also added to the class as a static and
private field. This is the format of the wrapper delegate. The identifier is a compiler-generated number,
whereas # is a character added to the name of the wrapper delegate.
<>id__CachedAnonymousMethodDelegate#
using System;
namespace Donis.CSharpBook{
Figure 8-3 is a disassembly of this application program. It shows the wrapper delegates and anonymous
methods.
Outer Variables
Anonymous methods can refer to local variables of the containing function and class members within the
scope of the method definition. Local variables used in an anonymous method are called outer variables.
The scope of a local variable is closely linked to the method in which the local is declared. When the method
ends, local variables are removed from memory. Local variables used in an anonymous method are captured,
which extends the lifetime of the variable. The lifetime of a captured or outer variable is of the same as that of
the delegate. The outer variable is removed when the delegate is garbage-collected. In the following code, the
local variable of MethodA is captured in the anonymous method. The lifetime of the local variable would
usually end when MethodA is exited. However, the delegate is garbage-collected until the end of the
program, which extends the lifetime of the local variable for the entire program.
using System;
namespace Donis.CSharpBook{
public delegate void DelegateClass(out int arg);
DelegateClass del=MethodA();
int var;
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
del(out var);
Console.WriteLine(var);
}
Because the lifetime of outer variables is aligned with the delegate, it remains persistent across invocations
of the anonymous method. In the following code, the anonymous method is invoked thrice through the
delegate. As a local variable, local would be initialized on each call to the method. However, as an outer
variable, it is initialized once in MethodA and maintains its value across multiple calls to the anonymous
method. Therefore, the value displayed is 3.
using System;
namespace Donis.CSharpBook{
DelegateClass del=MethodA();
int var;
del(out var);
del(out var);
del(out var);
Console.WriteLine(var);
}
The C# compiler creates a private nested class for anonymous methods that capture local variables,
whereas anonymous methods that contain no outer variables are implemented as private static methods.
The nested class has a public field for each outer variable used in the anonymous method. The local variable
is persisted to this public field. It also contains a named method for the anonymous method. The named
method has the same signature as the related delegate. This is the method used to initialize the wrapper
delegate. Figure 8-4 illustrates the nested class created for an anonymous method.
Figure 8-4: Disassembly of nested class for an anonymous method that uses outer variables
Locals are fixed variables and are not normally available to garbage collection. Local variables are removed
from memory when the applicable method is exited. When a local variable is captured, it becomes a movable
variable. Movable variables are placed on the managed heap. As such, outer variables then become available
for garbage collection. For this reason, captured variables must be pinned or otherwise fixed before being
used with unsafe code.
Anonymous methods can use generic parameters of the class or delegate. However, anonymous methods
cannot define new generic parameters and constraints. Here are two examples of anonymous methods using
generic parameters:
Although anonymous methods are similar to other methods, they also have some limitations (some have
already been mentioned):
Do not attempt to jump out of an anonymous method.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Events
An event is an occurrence that an object or class wants to notify someone else about. (The someone else is
either another object or class.) A button notifies a form object that someone clicked on the button. A timer
notifies some other object that a time period has elapsed. An application domain notifies an application of an
unhandled event. A Web application notifies an object of an application event. You can create custom
events, such as a device notifying a monitoring application that data transmission has started or stopped.
This list of events is somewhat eclectic, which underscores that there is no strict definition. Events are
literally anything worth notifying someone else about. Public events are exposed as public members of a
publisher class, such as a button class. You can also have private and protected events that are used within
the realm of the containing or derived class.
Any object or class interested in an event can subscribe. Subscribers register for an event by submitting a
delegate. The delegate must be single cast and contain a single function pointer. That function is the
subscriber's response to the event. When the event is raised, the publisher calls the function, affording the
subscriber an opportunity to respond to the event. For this reason, the function is called an event handler.
Events can have multiple subscribers. A device could publish a power-off event in which multiple applications
might want to be notified and respond.
What happens when there are no subscribers to a particular event? In other words, if a tree falls in the woods
and no one hears it, does it make a sound? If the tree is an event, no sound is made. Events that have no
subscribers are not raised.
Publishing an Event
Both classes and objects can publish events. Static events are published at classes, whereas objects
publish instance events. Multicast delegates underlie events. You must create a delegate for the event or
leverage a predefined event. Events are defined with the event keyword. This is the syntax for defining an
event:
accessibility event delegatename eventname
Events are defined as a field in a class. The accessibility of an event is typically public or protected.
Protected events restrict subscribers to child objects. A public event is available to every interested
subscriber. Delegate name is the underlying delegate of the event, which defines the signature of the event.
Event name is the identity of the event.
As a best practice, event handlers should return void and have two arguments. EventHandler is a predefined
delegate created for this purpose. It is included in the .NET Framework class library (FCL) and prevents you
from having to declare a separate delegate for the standard event type. The first parameter is the object that
raised the event. The second parameter is an EventArgs derived instance, which offers optional data that
further explains the event.
Internally, events declared in a class become a field of that class. Delegates used to define the event, which
is a multicast delegate, are types used for the field. The C# compiler also inserts methods to add and
remove subscribers to the event. The names of the methods are add_EventName and remove_EventName,
respectively. The fourth member added to the class is the event itself. See Figure 8-5 for a disassembly of an
event class.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Subscribe
The publisher/subscriber relationship is a one-to-many relationship. For each publisher there can be zero or
more subscribers. Conversely, a subscriber can subscribe to multiple events. For example, a form can
subscribe to a button click and a text change event. Subscribers add a delegate to an event to register for
the event. The delegate is a wrapper for the function to be called when the event is raised. Subscribe to an
event using the add method or the += assignment operator:
using System;
namespace Donis.CSharpBook{
class Publisher {
public event EventHandler MyEvent;
}
Raising an Event
At the discretion of a publisher, an event is raised and the methods of the subscribers are called back. Raise
an event with the call operator "()". Adding the call operator to the event raises the event. Because a
delegate underlies an event, the Invoke method inherited from a multicast delegate is another means to
raising an event. The signature of both the call operator and the Invoke method match the delegate of the
event. Any parameters are passed to the called methods of the subscribers. If an event returns a value as
defined by the event delegate, the function of the last subscriber sets the return. Do not raise an event for
events without subscribers. Events with no subscribers are null, which can be tested. An application that
raises an event that has no subscribers will incur an exception because of a null reference. This is the proper
way to raise an event:
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
EventArgs
Events sometime provide subscribers with custom information pertaining to the event. The mouse click event
provides the x and y coordinates of the mouse pointer in the MouseEventArgs class. The
DataRowChangeEventArgs class provides the affected row and action of several database-related events,
such as the RowChanged event. The PaintEventArgs class is instantiated in a paint event. It gives
developers the clip rectangle and graphics object for the paint event. Custom information about an event is
defined in an EventArgs derived class. MouseEventArgs, DataRowChangeEventArgs, PaintEventArgs, and
other related classes derive from the EventArgs class. These classes represent the state information for the
event.
The EventArgs derived class that contains the state information of the event is typically passed as the
second parameter of the event. State information can also be specified in the other parameters of events.
This is code for a bank account. The NSF event is raised when the account is overdrawn. The
BankEventArgs class provides the bank account balance and amount of the transaction that would overdraw
the account.
using System;
namespace Donis.CSharpBook{
get {
return propTransaction;
}
}
}
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Exception Handling
This chapter focused on delegates and events. In the next chapter we will cover the importance and benefits
of exception handling.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
What is an exception? Exceptions are exceptional events or error conditions within an application and are
categorized as system or application exceptions. System exceptions are raised by the Common Language
Runtime (CLR) and include null reference, out of memory, divide by zero, and stack overflow exceptions.
Application exceptions, considered custom exceptions, are thrown by the application, not by the CLR. Some
exceptional events or error conditions are detectable by application logic, not by the runtime. Application
exceptions are useful in those circumstances. As an example, constructors that fail are not always
detectable by the CLR. In addition, constructors implicitly return void, which prevents returning an error code.
For these reasons, throwing an application exception in the failed constructor is the best solution.
Exceptions should be used only for exceptions. Exception handling is not a substitute for transfer of control
or a goto statement. Exceptions are expensive when compared with conventional transfer of control.
Whenever possible, applications should preempt exceptions and avoid the accompanying costs. Performing
data validation, where incorrect values can cause exceptions, is one measure that prevents exceptions. Data
validation and, if necessary, returning an error code is undoubtedly cheaper than raising an exception.
The overuse of exceptions makes code harder to read and maintain. Errors that frequently occur should not
be handled with exception handling, but with error codes or returns. Remember, exceptions should be
reserved for exceptional events.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Exception Example
A common exception is dividing by zero. Exceptions are generally linked to an action. For dividing by zero,
the action is a divisor of zero. Integer division, where the divisor is zero, triggers a divide by zero exception.
(However, floating-point division by zero does not cause an exception; instead, infinity is returned.) The
following code causes an unhandled divide by zero exception, which terminates the application:
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
int var1=5, var2=0;
var1/=var2; // exception occurs
}
}
}
Place code that is likely to raise an exception in a try block because code in a try block is protected from
exceptions. Exceptions raised in the try block are trapped. The stack is then walked by the CLR, searching
for the appropriate exception handler. Code not residing in a try block is unprotected from exceptions. In this
circumstance, the exception eventually evolves into an unhandled exception. As demonstrated in the
previous code, an unhandled exception is apt to abort an application.
In the following code, the divide by zero exception is caught in a try block. Trapping, catching, and handling
an exception are separate tasks. The catch statement consists of a catch filter and block. The
DivideByZeroException filter catches the divide by zero exception. The catch block handles the exception,
which is to display the stack trace. The proximity of the infraction is included in the stack trace. Execution
then continues at the first statement after the catch block.
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
try {
int var1=5, var2=0;
As part of the common exception model, .NET advocates structured exception handling, which is essentially
the try and catch statements. C-based and Java developers are probably familiar with structured exception
handling. For Visual Basic developers, this is a seismic shift from unstructured exception handling. The
unstructured model, such as on error goto and on error resume, is thankfully now obsolete.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Try Statement
Try blocks are observers; they watch for exceptions in protected code. Place code prone to raising
exceptions in a try block. Do not attempt to protect an entire application in a try block—there are more
convenient and practical means of accomplishing the same feat, as described later in this chapter.
As mentioned, exceptions are stack-based. The following code contains a fence-post error that happens
when the bounds of the array are exceeded. The result is an exception in the unprotected MethodA method.
Main calls MethodA, where the scope of Main encompasses MethodA.
For this reason, the try block in Main extends protection to MethodA. The exception is trapped in Main.
using System;
namespace Donis.CSharpBook{
public class Starter{
Try statements must be paired with either a catch statement or a finally statement. There can be zero or
more catch statements attached to a try statement; there are zero or one finally statements. If both catch
and finally are present, the catch statement should precede the finally statement. The following code has
various combinations of try, catch, and finally statements:
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
// try..catch
try {
}
catch(Exception e) {
}
// try..finally
try {
}
finally{
}
// try..catch..finally
try {
}
catch(Exception e) {
}
finally{
}
// try..catches..finally
try {
}
catch(Exception e) {
}
catch {
}
finally{
}
Catch Statement
Catch statements filter and handle exceptions. When an exception is raised, the filter sets which exceptions
are handled at that scope in the stack. If the filter matches the exception, the exception is suppressed, and
control is transferred to the adjoining catch block to be handled. The CLR searches the stack for an
appropriate filter. If a matching filter is not found, the exception becomes an unhandled exception.
Exceptions must be derived from System.Exception. The catch filter defines an exception type and catches
exception of that type or any descendants. The exception object contains details of the exception, such as a
user-friendly message describing the exception. The exception object caught in the filter is accessible only
in the catch block. System.Exception is the base class of .NET exceptions and the generic filter.
A System.Exception filter catches all managed exceptions. Derived classes of System.Exception catch
more-specific exceptions. In the previous code, the DivideByZeroException filter caught integer divide by zero
exceptions and nothing else. A try block can be appended to multiple catches for catching distinct
exceptions. The catches are ordered from specific to generic. The following code has several exception
filters, from specific to generic:
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
try {
int var1=5, var2=0;
var1/=var2;
}
catch(DivideByZeroException except) {
Console.WriteLine("Exception "+except.Message);
}
catch(System.ArithmeticException except) {
Console.WriteLine("Exception "+except.Message);
}
catch(Exception except) {
Console.WriteLine("Exception "+except.Message);
}
}
}
}
In the preceding code, DivideByZeroException is very specific and catches only divide by zero exceptions.
ArithmeticException is less specific and catches a variety of arithmetic exceptions, including the divide by
zero exception. Exception catches all managed exceptions, which includes divide by zero and arithmetic
exceptions. In the preceding code, the exception is caught at the DivideByZeroException catch handler.
The catch filter is optional, and the default is catch all. Although System.Exception catches any managed
exceptions, catch all catches both managed and unmanaged exceptions. In most circumstances, native
exceptions are mapped to managed exceptions, which are thrown in the RaiseTheException native function.
RaiseTheException uses the RaiseException API to construct the managed exception. Exceptions not
mapping to managed exceptions are outside this normal mechanism. A catch with no catch filter is a catch
all. This code has a catch all:
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
try {
int var1=5, var2=0;
var1/=var2;
}
catch(DivideByZeroException except) {
Console.WriteLine("Exception "+except.StackTrace);
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
catch {
// catch remaining managed and unmanaged exceptions
}
}
}
}
Propagating Exceptions
Exceptions are not always handled locally where the exception is caught. It is sometimes beneficial to catch
an exception and then propagate the exception. Propagating an exception is catching and then rethrowing
the exception. Rethrowing an exception continues the search along the call stack to find an appropriate
handler. Here are some reasons to propagate an exception:
There is a centralized handler for the exception. There are several reasons for implementing
centralized handlers, including code reuse. Instead of handling an exception in various locations in
an application, concentrate code for certain exceptions in a centralized handler. Wherever the
exception is raised, the proper response is to record the exception and then delegate to the
centralized handler. A central handler can be used to handle all exceptions in a single place.
Resources required to handle the exception are not available locally. For example, an exception is
raised because of an invalid database connection. However, the correct connection string is read
from a file not available where the exception occurs. The solution is to propagate the exception to
a handler that has access to the file resource.
Propagate unwanted exceptions caught in the umbrella of the exception filter. This would be
useful for catching all DataException types with the exception of the DuplicateNameException.
One solution would be to write 12 individual catches—one for each of the data exceptions except
for the DuplicateNameException exception. A better solution is to catch the DataException type
and propagate the DuplicateNameException when necessary. This is one catch statement versus
12 catch statements and eliminates redundant code.
Catch an exception to gather information or to report on an exception, and then propagate the
exception.
To propagate an exception, rethrow the same exception or another exception in the catch block. An empty
throw statement propagates the caught exception. Alternatively, throw a different exception.
Exceptions might propagate through several layers of an application. Ultimately, the exception could
percolate to the user interface level. As an exception percolates, the exception becomes less specific.
Exceptions from the lower echelon of an application contain detailed information appropriate to the
application developer, but probably not relevant to the user. Internal exceptions might contain security and
other sensitive information not appropriate for a benign (or malicious) user. Record the facts of the internal
exception if logging is preferable. Exceptions that reach the user should present user-relevant information: a
user-friendly message, steps to resolve the exception, or even a customer support link.
When an original exception is rethrown, you can preserve the that exception in the InnerException attribute.
Successive InnerException attributes form a chain of exceptions from the current exception to the original
exception. The InnerException can be initialized in the constructor of the new exception. Here is sample
code that propagates an exception and sets the inner exception:
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
try {
MethodA();
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
catch(Exception except) {
Console.WriteLine(except.Message);
}
}
Finally Statement
The finally block is the termination handler. When an exception is raised, protected code after the infraction
is not executed. What if it is cleanup code? If cleanup code is not executed, resources are left dangling.
Termination handlers are the solution. Code that must execute, regardless of an exception occurring, is
placed in a termination handler. When an exception is raised, the finally blocks within scope are called
before the stack is unwound. Note that the termination handler is executed even when there is no occurrence
of an exception. Execution simply falls through the try block into the attached finally block. In the termination
handler, you could close a file, release a database connection, or otherwise manage resources.
using System;
using System.IO;
namespace Donis.CSharpBook{
public class FileWriter{
public static void Main(){
StreamWriter sw=null;
try {
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
sw=new StreamWriter("date.txt");
sw.Write(DateTime.Now.ToLongTimeString());
throw new ApplicationException("exception");
// dangling code
}
finally {
sw.Close();
Console.WriteLine("file closed");
}
}
}
}
The CLR refers to the Exception Information Table to track protected code in an efficient manner. Because of
the Exception Information Table, there is no overhead associated with an exception unless an exception
occurs.
An Exception Information Table is constructed for every managed application. The table has an entry for
each method in the program. Each entry is an array, where the array elements describe the filters and
handlers of that method. Entries in the array represent a catch filter, user-filtered handler, catch handler, or
termination handler. User-filtered handlers use the when clause and are available in Visual Basic .NET, but
not in C#.
When an exception occurs, the CLR consults the Exception Information Table. The entry for the method
hosting the exception is searched for a filter that matches the exception. If the array is empty or a matching
filter is not found, the entry of the outer method is examined next. When the boundary of the application is
reached, the exception is considered unhandled.
Try blocks can be nested. The order of evaluation is more complex with nested try blocks. This is the order
of execution when an exception occurs:
1. The first step is to find an appropriate try block. If an exception is raised outside a protected
block, the exception is not trapped and is therefore unhandled.
2. From the try block, walk the stack until a matching catch filter is found. If a matching filter is not
found, the exception is unhandled. This defines the scope of the exception.
3. Before the stack is unwound, finally blocks within the scope of the exception are run. The
innermost finally blocks are executed first.
4. The catch block of the filter is executed as the exception handler.
5. Execution continues at the first statement after the catch block.
6. Finally blocks at scope of exception are executed.
Figure 9-1 diagrams the sequence when an exception is raised in a nested try block.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
using System;
namespace Donis.CSharpBook{
public class Starter{
Console.WriteLine("outer - try");
try {
Console.WriteLine("inner - try");
throw new ApplicationException("exception");
}
finally {
Console.WriteLine("inner - finally");
}
}
catch(Exception except){
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Console.WriteLine("outer - catch");
}
finally {
Console.WriteLine("outer - finally");
}
}
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
System.Exception
System.Exception is the base exception class. All exceptions in .NET are derived from System.Exception.
System.SystemException is the base class for system exceptions raised by the CLR, such as
System.Data.DataException or System.FormatException. SystemException is derived directly from
System.Exception. System.SystemException does not refine System.Exception. However, it is an important
marker that distinguishes between system and application exception, as demonstrated in the following code:
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
try {
int var1=5, var2=0;
var1/=var2; // exception occurs
}
catch(Exception except) {
if(except is SystemException) {
Console.WriteLine("Exception thrown by runtime");
}
else {
Console.WriteLine("Exception thrown by application");
}
}
}
}
}
System.Exception Functions
Exception 1 is the default constructor. Exception 2 constructor sets the user-friendly message of the
exception. Exception 3 constructor also sets the inner exception, which is the originating exception. Exception
4 deserializes an exception raised remotely.
The Exception class has several other helpful functions. Table 9-1 lists the important methods of the class.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ToString Concatenates the name of the exception object with the user-friendly message
The following code calls GetBaseException and outputs the error message of the initial exception. If the
current exception is the first exception in a chain of exceptions, GetBaseException returns null.
Alternatively, you can walk InnerException properties back to the first exception.
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
try {
MethodA();
}
catch(Exception except) {
Exception original=except.GetBaseException();
Console.WriteLine(original.Message);
}
}
catch(Exception except) {
throw new ApplicationException( "Inner Exception", except);
}
}
System.Exception Properties
System.Exception has a full complement of attributes providing information on the exception. Table 9-2
describes the properties of the Exception class.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Pr De Ty Re
op scr pe ad/
ert ipti Wr
y on ite
Pr De Ty Re
op scr pe ad/
ert ipti Wr
y on ite
HR Th int R/
es e W
ult HR
ES
UL
T,
whi
ch
is
a
32-
bit
err
or
co
de
co
m
mo
n
to
CO
M,
as
sig
ne
d
to
the
ex
ce
pti
on.
Thi
s
is
a
pro
tec
ted
pro
per
ty.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Pr De Ty Re
op scr pe ad/
ert ipti Wr
y on ite
Inn Wh Ex R
erE en ce
xce ex pti
pti ce on
on pti
on
s
are
pro
pa
gat
ed,
the
inn
er
ex
ce
pti
on
rep
res
ent
s
the
pre
vio
us
ex
ce
pti
on.
Me Us stri R
ss er-f ng
ag rie
e ndl
y
me
ss
ag
e
de
scr
ibin
g
the
ex
ce
pti
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Pr De Ty Re
op scr pe ad/
ert ipti Wr
y on ite
So Na stri R/
urc me ng W
e of
ap
plic
ati
on
or
obj
ect
wh
ere
ex
ce
pti
on
oc
cur
red
.
Pr De Ty Re
op scr pe ad/
ert ipti Wr
y on ite
Tar Ref Me R
get ere tho
Sit nc dB
e e as
to e
the
me
tho
d
wh
ere
ex
ce
pti
on
is
rai
se
d.
The Message and InnerException properties are settable in constructors of the Exception class.
The following code uses some of the properties of the Exception class. In Main, MethodA is called, and an
exception is raised. The exception is caught and handled in Main. In the catch block, the exception flag is
set to false. Leveraging the TargetSite property, MethodA is then called again successfully. The TargetSite
property returns a MethodBase type, which can be used to late bind and invoke a method.
using System;
using System.Reflection;
namespace Donis.CSharpBook{
public class Starter{
public static bool bException=true;
public static void Main(){
try {
MethodA();
}
catch(Exception except) {
Console.WriteLine(except.Message);
bException=false;
except.TargetSite.Invoke(null, null);
}
}
if(bException) {
throw new ApplicationException("exception message");
}
}
}
}
Application Exceptions
Application exceptions are custom exceptions and are thrown by the application, not by the CLR.
Application exceptions are derived from System.ApplicationException or System.Exception.
System.ApplicationException adds nothing to System.Exception. While System.SystemException is a
marker for system exceptions, System.ApplicationException brands application exceptions. A custom
exception derived from System.Exception accomplishes the same feat. When several custom exceptions are
planned, create a custom base exception class to categorize the exceptions. For convenience and
maintainability, deploy application exceptions together in a separate assembly.
Do not create an application exception for an existing exception. Research the available system exceptions
to avoid replicating an existing exception.
To raise an application exception, use the throw statement. You can also throw system exceptions. Thrown
exceptions are considered software exceptions. The CLR treats software exceptions as standard exceptions.
throw syntax:
throw exceptioninstance 1;
throw 2;
The second syntax is specialized: It is available in the catch block, but nowhere else. This version of the
throw statement rethrows as an exception caught in the catch block. However, the best policy is to add
additional context to an exception before propagating the exception object. Propagating exceptions is
reviewed later in this chapter.
Application exceptions are typically prompted by an exceptional event. What is an exceptional event? A
strict definition does not exist. You define the basis of the event using whatever criteria are appropriate.
Remember, raising an exception simply for transfer of control or a nonexceptional event is bad policy. In an
application, the following could be considered exceptional events where throwing an application exception is
warranted:
Constructor fails to initialize the state of an object.
A property does not pass validation.
Null parameters.
An exceptional value is returned from a function.
ConstructorException is an application exception. Throw this exception when a constructor fails. It refines
the System.Exception base class with name of the type and time of exception. In addition, the Message
property is assigned a congruous message. This is the code for the ConstructorException class:
using System;
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
namespace Donis.CSharpBook{
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
try {
ZClass obj=new ZClass();
}
catch(ConstructorException except) {
Console.WriteLine(except.Message);
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Console.WriteLine("Typename: "+except.Typename);
Console.WriteLine("Occured: "+except.Time);
}
}
}
class ZClass {
public ZClass() {
// initialization fails
throw new ConstructorException(this);
}
}
}
Exception Translation
In some circumstances, the CLR catches an exception and rethrows a different exception. The inner
exception of the translated exception contains the original exception. Invoking a method dynamically using
reflection is one such circumstance. Exceptions raised in methods invoked by MethodInfo.Invoke are
automatically trapped and converted to TargetInvocationException? NET documentation in MSDN will always
confirm when exception translation will happen. Here is an example of exception translation:
using System;
using System.Reflection;
namespace Donis.CSharpBook{
}
}
}
.NET applications often host COM components or expose managed components to COM clients. These
applications must be prepared to handle and possibly throw COM exceptions, respectively. The prevalence
of COM components makes COM interoperability an important consideration for managed applications into
the foreseeable future.
COM Exceptions
COM components should sandbox exceptions, which protects COM clients from potential language-specific
exceptions. COM methods return an HRESULT structure, which is the result code of the method. An
HRESULT is a 32-bit structure, where the severity bit is the high-order bit. The severity bit is set if an
exception is raised. The Win32 SDK defines constants representing various HRESULT codes.
E_NOINTERFACE, E_INVALIDARG, E_OUTOFMEMORY, S_OK, and S_FALSE are common HRESULT
codes. E_codes are error codes indicating that an exception was raised or some other exceptional event
happened. S_codes are success codes where no failure occurred.
When managed components call methods on COM objects, the CLR consumes the resulting HRESULT. If
the HRESULT represents a known COM error (E_code), the CLR maps the HRESULT to a managed
exception. For example, E_POINTER maps to the NullReferenceException, which is a managed exception.
An error code from an unknown HRESULT is mapped to a COMException object. No exception is thrown if
the HRESULT is a success code (S_code). Table 9-3 shows the common translations of HRESULT to
managed exceptions.
Table 9-3:
COM
Exception
Table
CO Ma
M na
Ex ge
ce d
pti Ex
on ce
pti
on
CO Ov
R_ erfl
E_ ow
OV Ex
ER ce
FL pti
O on
W
CO Thr
R_ ea
E_ dSt
TH op
RE Ex
AD ce
ST pti
OP on
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 9-3:
COM
Exception
Table
CO Ma
M na
Ex ge
ce d
pti Ex
on ce
pti
on
E_ Inv
NO alid
INT Ca
ER stE
FA xce
CE pti
on
E_ Not
NO Im
TI ple
MP me
L nte
dE
xce
pti
on
E_ Ou
OU tOf
TO Me
FM mo
EM ryE
OR xce
Y pti
on
E_ Nul
PO lRe
INT fer
ER en
ce
Ex
ce
pti
on
COM components implement Error objects to provide extended error information to clients. An Error object
implements the IErrorInfo interface. Members of the IErrorInfo interface correlate to members of the
COMException class and are therefore accessible to the managed client. Table 9-4 maps members of the
Error object to the COMException class.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 9-4:
IErrorInfo to
COMExceptio
n Mapping
IEr CO
ror ME
Inf xc
o ept
Me ion
mb Me
er mb
er
IEr CO
rorI ME
nfo xce
::G pti
etD on.
es Me
cri ss
pti ag
on e
IEr CO
rorI ME
nfo xce
::G pti
etS on.
our So
ce urc
e
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 9-4:
IErrorInfo to
COMExceptio
n Mapping
IEr CO
ror ME
Inf xc
o ept
Me ion
mb Me
er mb
er
If CO
IEr ME
rorI xce
nfo pti
::G on.
etH Hel
elp pLi
Fil nk
e
is
no
n-z
ero
,
IEr
rorI
nfo
::G
etH
elp
Fil
e+"
#"+
IEr
rorI
nfo
::H
elp
Co
nte
xt
The following code is a partial listing from an ATL project that publishes a COM component. The COM
component exposes the CComponentZ::MethodA. Using the AtlReportError API, CComponentZ::MethodA
builds an Error object to return extended error information to the client. The method also returns a custom
error code in the HRESULT, which will be unknown to the CLR.
#include "stdafx.h"
#include "ComponentZ.h"
#include ".\componentz.h"
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
// CComponentZ
STDMETHODIMP CComponentZ::MethodA(void) {
// TODO: Add your implementation code here
The following code is managed code, in which the ATL component is called from a managed COM client.
Because the HRESULT is unknown, the error code appears as a COMException exception.
using System;
using System.Runtime.InteropServices;
namespace COMClient {
class Program {
static void Main(string[] args) {
try {
COMLib.CComponentZClass com_object =
new COMLib.CComponentZClass();
com_object.MethodA();
}
catch (COMException except){
Console.WriteLine(except.ErrorCode);
Console.WriteLine(except.HelpLink);
Console.WriteLine(except.Message);
}
catch (Exception) {
}
}
}
}
TypeException is an application exception, which should be thrown when an object is the wrong type.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
TypeException has two overloaded constructors. Both constructors set the HResult property of the exception
to the E_NOTIMPL error code (0x80004001). The one-argument constructor accepts a type object, which is
the required type that was not implemented. The name of the type is added to the error message of the
exception. This is the code for the TypeException class:
using System;
namespace Donis.CSharpBook{
public TypeException()
: base("object type wrong") {
HResult=unchecked((int) 0x80004001); // E_NOTIMPL
}
The Delegator class uses the TypeException class. Delegator delegates method calls of Delegator .MethodA
to an external object. For the delegation to be successful, the external object must also implement the
MethodA method, which is defined in the ZInterface. Appropriately, the code in Delegator.MethodA confirms
that the external object implements the ZInterface. If not, the TypeException is thrown. Otherwise,
Delegator.MethodA proceeds with the delegation:
using System;
namespace Donis.CSharpBook{
interface ZInterface {
void MethodA();
}
YClass creates an instance of the Delegator class. A ZClass object is passed into the Delegator constructor
as the external object. ZClass does not implement MethodA. Delegator.MethodA is called in
YClass.UseDelegator. A TypeException is thrown because the ZClass does not implement the appropriate
interface:
using System;
using System.Runtime.InteropServices;
using Donis.CSharpBook;
class ZClass {
}
[ClassInterface(ClassInterfaceType.AutoDual)]
public class YClass{
public void UseDelegator(){
ZClass obj=new ZClass();
Delegator del=new Delegator(obj);
del.MethodA();
}
}
COM clients can access managed code through COM Callable Wrappers (CCWs). The following unmanaged
code creates an instance of YClass and invokes YClass.UseDelegator. As expected, an exception occurs,
which translates to E_NOTIMPL in unmanaged code. The COM client checks for this exception and displays
the appropriate message.
#include "objbase.h"
void main() {
CoInitialize(NULL);
_YClassPtr obj(CLSID_YClass);
HRESULT hResult=obj->UseDelegator();
if(hResult==E_NOTIMPL) {
MessageBox(NULL,"Required interface not implemented",
"In Managed Component", MB_OK);
}
else {
MessageBox(NULL, "Managed Component", "No error",
MB_OK);
}
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Remote Exceptions
Exceptions sometime occur in remote code. An exception that is raised in a different application domain is a
remote exception. Remote exceptions include exceptions thrown in a .NET Remoting application or a Web
service application. Exceptions that cross application domains must be serialized to maintain the state.
System exceptions are serializable. However, you need to make application exceptions serializable.
CustomException is an application exception that supports remoting. There is one property, prop_Time,
which is serialized in GetObjectData and deserialized in the two-argument constructor:
using System;
using System.Reflection;
using System.Runtime.Serialization;
[assembly:AssemblyVersion("1.1.0.0")]
[assembly:AssemblyCultureAttribute("")]
namespace Donis.CSharpBook{
[Serializable]
public class CustomException: Exception{
public CustomException()
: base("custom exception", null) {
prop_Time=DateTime.Now.ToLongDateString()+" "+
DateTime.Now.ToShortTimeString();
}
StreamingContext context ){
info.AddValue("Time", prop_Time, typeof(string));
base.GetObjectData(info,context);
}
In Microsoft Visual Studio, the assembly attributes, such as AssemblyVersion, are found in the
AssemblyInfo.cs file.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Unhandled Exceptions
Unhandled exceptions are not handled directly in application code; they are handled in a global handler. The
global handler reports the exception in an error box that offers the Debug, Send Error Report, and Don't Send
buttons. For applications within the realm of a debugger, a potential unhandled exception is handled in the
debugger.
What is the life cycle of an exception? Exceptions are initially categorized as first chance exceptions. If the
application is attached to a debugger, the debugger is first consulted about the exception. Debuggers
typically ignore first chance exceptions, and the exception is forwarded to the application next. When no
debugger is present, the first chance exception is immediately sent to the application. If the application does
not handle the first chance exception, the exception becomes a high-priority second chance exception. If a
debugger is attached, the second chance exception is handled by the debugger. Upon finding a second
chance exception, the Visual Studio debugger transfers the user to the location in the source code where
the exception happened. If no debugger is present, execution is transferred to a global exception handler,
which displays an error dialog box and then terminates the application. Figure 9-2 shows the life cycle of an
exception.
Applications can trap unhandled exceptions. The mechanism is different for Windows Forms and for Console
applications. For Windows Forms, add a handler to the Application.ThreadException event. For Console
applications, the handler is added to the AppDomain.UnhandledException event. Methods added to the
Application.ThreadException event chain catch and handle the exception. This is an advantage when
compared with AppDomain.UnhandledException. Methods added to the AppDomain.UnhandledException
event can respond to an unhandled exception, but the exception is not caught. Therefore, the exception will
resurface after the handlers have completed.
Do not use the unhandled exception handler to catch all exceptions. Proper application design identifies
specific exceptions that an application should anticipate. Those exceptions should be caught and handled
within the confines of structured exception handling. Reserve the unhandled exception method for
unanticipated exceptions.
Application.ThreadException
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
In a Windows Forms application, the windows procedure raises the Application.ThreadException event upon
an unhandled exception. Subscribe to the ThreadException event to handle the unhandled exception. The
subscriber is an exception handler, which prevents the application from being terminated. Do not propagate
an exception caught in this manner in the unhandled exception handler. The new exception is unprotected
and will likely terminate the application. After the unhandled exception handler completes, execution
continues at the next message in the message pump.
Subscribe to the ThreadException event with a ThreadExceptionEventHandler delegate, which has two
parameters. The object parameter is the thread object of the thread that raised the exception. The
ThreadExceptionEventArg parameter of the System.Threading namespace contains the exception that was
unhandled. This is the signature of the ThreadExceptionEventHandler:
ThreadExceptionEventHandler syntax:
In the following code, the OnThreadException handler is added to the ThreadException event. The
bthException_Click method raises an unhandled divide by zero exception. The unhandled exception is then
handled in the OnThreadException method, which displays an informative message. Run the application in
release mode for the expected results. Otherwise, the Visual Studio debugger intercedes the exception.
AppDomain.UnhandledException
UnhandledExceptionEventHandler syntax:
void UnhandledExceptionEventHandler(object sender, UnhandledExceptionEventArgs e)
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
AppDomain.CurrentDomain.UnhandledException+=
new UnhandledExceptionEventHandler(
OnUnhandledException);
Exception Assistant
The Exception Assistant appears in Visual Studio when an unhandled exception occurs. It displays a
translucent frame that partially obfuscates the application code. The source code that prompted the
exception is highlighted and tethered to the Exception Assistant window. The Exception Assistant is shown
in Figure 9-3.
The Exception Assistant header identifies the unhandled exception and offers a brief explanation. The
Troubleshooting Tips section offers hints to diagnose the exception. Each hint is also a link to further
information. The Actions pane specifies two actions:
New Detail displays the properties of the exception object.
Copy Exceptional Detail To The Clipboard copies basic information on the exception onto the
Clipboard.
The Exception Assistant can be disabled or otherwise configured from the Tools menu. Select the Options
menu item. Configure the Exception Assistant in the Debugging pane.
In the Exceptions dialog box, the Visual Studio debugger can be instructed to interrupt on first chance
exceptions. Open the Exceptions dialog box from the Debug menu. Choose the Exceptions menu item.
Figure 9-4 shows the Exceptions dialog box.
The Thrown and User-unhandled columns have a series of option boxes that are organized into categories
and specific exceptions. Visual Studio debugger interrupts on first chance exceptions for exceptions
selected in the Thrown column. For protected code, the debugger intercedes before the exception is caught.
In the development phase of an application, it can be instructive to be notified of exceptions that would
otherwise be consumed in an exception handler. The second column selects specific user-handled
exceptions to break on. The Add button appends application exceptions to the list of available exceptions
that are selectable in the dialog box. These exceptions can later be deleted.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Metadata is important to anyone programming in the managed environment. Assembly inspection, late
binding, and advanced concepts such as self-generating code require a nontrivial understanding of metadata.
You can interrogate metadata by using reflection. Reflection facilitates late binding and other means of
leveraging metadata. Most important, mastery of metadata promotes a better understanding of the managed
world, which (one hopes) translates into better-written code.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Metadata
Metadata about the overall assembly and modules is called the manifest. Some of the macro information
assembled in the manifest includes the simple name, version number, external references, module name,
and public key of the assembly. A portion of the manifest is created from the assembly attributes found in
the AssemblyInfo.cs file of a Microsoft Visual Studio .NET C# project. This is a partial listing of a typical
AssemblyInfo.cs file:
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
Metadata also chronicles the microdata of the assembly, such as types, methods, and attributes. Metadata
paints a portrait of each type, including the type name, methods of the type, parameters of each method of
the type, each field of the type, and further details related to the loading and executing of that type at run
time. Types are probably the most important construct in a .NET application, and metadata about types is
used throughout the life cycle of a managed application. Here are a couple of examples. At startup,
metadata is used to identify the entry point method where the program starts executing. During program
execution, when a class is first touched, an EECLASS is built ostensibly from metadata to represent that
type to the just-in-time compiler. The EECLASS is an important component of the just-in-time process. The
EECLASS is further described in Chapter 13, "Advanced Debugging."
To extend either manifest or type-related metadata, employ attributes. Attributes are the adjectives of a
managed application and extend the description of an assembly, class, method, field, or other target.
Attributes are recorded as metadata and extend the axiomatic metadata of an assembly. In addition, the
Microsoft .NET Framework class library (FCL) offers predefined-custom and pseudo-custom attributes.
Obsolete and StructLayout attributes are examples of predefined attributes. Serializable is an example of a
pseudo-custom attribute. The Obsolete attribute marks an entity as deprecated, whereas the StructLayout
attribute stipulates the memory layout of fields in the context of unmanaged memory. The latter attribute is
essential when passing a managed type to an unmanaged function or application programming interface
(API). You can augment the predefined attributes with programmer-defined custom attributes, where the limit
is only your imagination. Applying a version number to a class, assigning the name of the responsible
developer or team to a class, and identifying design documents used to architect an application are some
ways to exploit custom attributes.
there would be six records or rows in the TypeDef table. Methods of all types are stored in the MethodDef
table. Each row of the MethodDef table describes a method. The TypeDef table references the MethodDef
table to link types to member methods. The MethodList column of the TypeDef table has record indexes
(RIDs) into the MethodDef table. Extending this model, the MethodDef table has a ParamList column, which
is index to the method's parameters found in the Param table.
Metadata tables are assigned unique table identifiers, which are 1-byte unsigned integers. For example, the
table identifier for the TypeDef table is 2, whereas 6 identifies the MethodDef table. Metadata tables reserved
for the run time are not published and not assigned an external table identifier for the RID. Table 10-1 lists
some of the popular metadata tables.
Table 10-1: Metadata Tables
Metadata tables are collections of records and columns. A metadata table contains a certain type of data,
and each record is an instance of that type. Columns represent specific data on each instance, and each
column contains a constant or index. The index references another table or heap where the metadata token
is an example of an index. Metadata tokens are used as metadata pointers, allowing tables to
cross-reference each other. Metadata tables can be optimized (compressed) or not optimized. For the
purpose of this book, it is assumed that metadata is optimized. Metadata that is not optimized requires
intermediate tables for ordered access between tables.
Tokens
Metadata tokens cross-reference other metadata tables and heaps. Tokens are 4-byte unsigned integers
and a combination of the table identifier and RID. As shown in Figure 10-1, the high byte is the table
identifier, and the lower 3 bytes are the RID. A token into the Field table might be 04000002. The token
refers to the second row of the Field table. The RID is one-based, not zero-based. Because tokens are
padded with zeros, the run time might optimize them. Metadata tokens are probably the most public
manifestation of metadata. You will repeatedly see metadata tokens over the next few chapters.
In addition to other tables, metadata tables reference metadata heaps. Records of metadata tables hold
fixed-length metadata information. Variable-length data is stored in one of the metadata heaps. Methods
signatures are variable length and typical of content found on the String heap.
Metadata Heaps
The four metadata heaps are as follows: String, Userstring, Blob, and GUID.
The String heap is an array of null-terminated strings. Namespace, type, field, and method
names, as well as other identifiers, are stored on the String heap.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
User-defined strings are not placed on the String heap but instead reside on the Userstring heap,
which is also an array of null-terminated strings. String literals from your program are cached on
this heap.
The Blob heap is a binary heap and a composite of length prefix data, such as default values,
method signatures, and field signatures.
The GUID heap is an array of globally unique identifiers (GUIDs). Yes, this is obvious. You might
remember GUIDs from COM as 16-byte unique identifiers assigned to almost everything—most
notably, class identifiers (CLSIDs) are assigned to class factories. What kind of GUID is stored on
the GUID heap? The GUID heap contains module version identifiers (MVIDs).
Streams
Physically, metadata tables and heaps are persisted in streams as part of an assembly. Six possible
streams, including streams for each metadata heap, are available in .NET. There are also two mutually
exclusive streams, optimized and nonoptimized, which are reservoirs of metadata tables. Metadata tables
are optimized or not optimized. There is no concept of partially optimized metadata tables. If the metadata
tables are optimized, the optimized stream is present. Otherwise, the nonoptimized stream is available.
Therefore, a managed application has at most five streams. Table 10-2 provides a complete list of the
metadata streams.
Table 10-2:
Metadata
Streams
Na De
me scr
ipti
on
#~ Op
tim
ize
d
or
co
mp
res
se
d
me
tad
ata
tab
les
#- No
no
pti
mi
ze
d
me
tad
ata
tab
les
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-2:
Metadata
Streams
Na De
me scr
ipti
on
#Bl Ph
ob ysi
cal
rep
osi
tor
y
of
the
blo
b
he
ap
#G Ph
UI ysi
D cal
rep
osi
tor
y
of
the
GU
ID
he
ap
#St Ph
rin ysi
g cal
rep
osi
tor
y
the
Stri
ng
he
ap
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-2:
Metadata
Streams
Na De
me scr
ipti
on
#U Ph
S ysi
cal
rep
osi
tor
y
of
the
Us
ers
trin
g
he
ap
Metadata Validation
Managed execution is largely dependent on metadata. Improperly formed metadata could cause a managed
application to fail unceremoniously. An assembly with bad metadata is like a house built on quicksand.
Loading a class, just-in-time compilation, code access security, and other run-time operations depend on
robust data. Metadata validation tests the correctness of metadata and is enacted preemptively, preventing
applications with inferior metadata from being executed. Preventing application crashes manifested by
improper metadata enforces code isolation.
Developers can request metadata validation on demand with the PEVerify and Intermediate Language
Disassembler (ILDASM) tools. Both tools are included in the .NET Framework software development kit
(SDK).
PEVerify submits an assembly for metadata validation and Microsoft intermediate language (MSIL)
verification and then reports the results. (MSIL verification is discussed in Chapter 11, "MSIL Programming.")
This is the basic syntax for PEVerify:
PEVerify assemblyname
PEVerify validates the metadata of assemblyname. If metadata validation is successful, MSIL verification is
applied next. MSIL verification is skipped if the metadata validation fails. If validation fails, execution is not
viable. This removes a compelling reason to conduct MSIL verification. PEVerify offers a variety of optional
arguments, including the capability to force MSIL verification even when the metadata validation fails.
Table 10-3:
PEVerify
Options
Ar De
gu scr
me ipti
nt on
/br Ab
ea ort
k= s
err veri
orc fica
ou tio
nt n
wh
en
err
ors
ex
ce
ed
err
orc
ou
nt.
/cl Col
oc lec
k ts
dat
a
an
d
rep
ort
s
dur
ati
on
of
veri
fica
tio
n
an
d
vali
dat
ion
tes
ts.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-3:
PEVerify
Options
Ar De
gu scr
me ipti
nt on
/he Hel
lp p
info
rm
ati
on
on
par
am
ete
rs.
/ig Ign
nor ore
e= s
err list
orc ed
od err
e1, or
err co
orc de
od s.
e2,
err
oro
co
de
n
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-3:
PEVerify
Options
Ar De
gu scr
me ipti
nt on
/il Co
nd
uct
s
MS
IL
veri
fica
tio
n.
Wh
en
yo
u
us
e
thi
s
co
m
ma
nd,
if
me
tad
ata
vali
dat
ion
is
als
o
req
uir
ed
it
mu
st
be
req
ue
ste
d
ex
plic
itly
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-3:
PEVerify
Options
Ar De
gu scr
me ipti
nt on
/m Co
d nd
uct
s
me
tad
ata
vali
dat
ion
. If
MS
IL
veri
fica
tio
n
an
d
me
tad
ata
vali
dat
ion
are
join
tly
de
sir
ed,
MS
IL
veri
fica
tio
n
sh
oul
d
be
req
ue
ste
d
ex
plic
itly
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-3:
PEVerify
Options
Ar De
gu scr
me ipti
nt on
/? Sa
me
as
the
/he
lp
arg
um
ent
.
The following is a simple Hello World application, which is compiled to hello.exe. It is a minimal application,
in which not much can go wrong. PEVerify will confirm this.
using System;
class Starter {
static void Main() {
Console.WriteLine("Hello, World!");
}
}
The following code shows the result of running PEVerify on Hello.exe with the /il and /clock options. Since
the md command is omitted, metadata verification is skipped.
The elapsed cycle for validation and pure times is listed. Pure time is the duration of the actual metadata
validation, whereas cycle encapsulates the startup and shutdown processes.
ILDASM is a .NET tool that performs validation and can browse and display the metadata of an assembly.
ILDASM inspects an assembly using reflection and presents the results in a window, console, or file.
ILDASM Tool
ILDASM, which is a .NET disassembler and metadata browser, is a popular tool for developers. It proffers an
internal representation of an assembly, which includes the metadata and MSIL code of an assembly in a
variety of formats. ILDASM exercises reflection to inspect an assembly. The core syntax of ILDASM requires
only an assembly name, which opens ILDASM and displays the metadata of the assembly:
ildasm assemblyname
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The following simple application is a basic .NET application that references a library. The simple application
has a ZClass and ZStruct type, whereas the dynamic-link library (DLL) publishes the YClass type.
using System;
namespace Donis.CSharpBook{
interface IA {
}
struct ZStruct {
}
class Starter {
class ZClass: IA {
Figure 10-2 is a view of Simple.exe from ILDASM. ILDASM displays a hierarchal object graph with an icon for
each element of the application.
Some icons are collapsible and expandable, as indicated by a + or - symbol. The Assembly icon expands to
show the details of the loaded assembly, the Namespace icon expands to show the members of the
namespace, and so on. You can drill down the object graph from the assembly down to the class members.
An icon depicts each item category of the graph. Table 10-4 describes each icon for which the action is
double-clicking the icon.
Table 10-4:
Elements of
ILDASM
Ico Act
n ion
De
scr
ipti
on
s
As Sh
se ow
mb s
ly ele
me
nts
of
the
as
se
mb
ly
Cla Sh
ss ow
s
me
mb
ers
of
a
cla
ss
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-4:
Elements of
ILDASM
Ico Act
n ion
De
scr
ipti
on
s
En Sh
um ow
s
me
mb
ers
of
en
um
typ
e
Ev Vie
ent ws
me
tad
ata
an
d
MS
IL
co
de
of
eve
nt
Fie Vie
ld ws
me
tad
ata
of
fiel
d
Int Sh
erf ow
ac s
e me
mb
ers
of
int
erf
ac
e
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-4:
Elements of
ILDASM
Ico Act
n ion
De
scr
ipti
on
s
Ma Vie
nife ws
st attr
ibu
tes
of
an
as
se
mb
ly
Me Vie
tho ws
d me
tad
ata
an
d
MS
IL
co
de
of
me
tho
d
Na Sh
me ow
sp s
ac me
e mb
ers
of
the
na
me
sp
ac
e
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-4:
Elements of
ILDASM
Ico Act
n ion
De
scr
ipti
on
s
Pro Vie
per ws
ty me
tad
ata
an
d
MS
IL
co
de
of
pro
per
ty
Sta Vie
tic ws
Fie me
ld tad
ata
of
sta
tic
fiel
d
Sta Vie
tic ws
Me me
tho tad
d ata
an
d
MS
IL
co
de
of
sta
tic
me
tho
d
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-4:
Elements of
ILDASM
Ico Act
n ion
De
scr
ipti
on
s
Val Sh
ue ow
Ty s
pe me
mb
ers
of
a
val
ue
typ
e
Some elements are displayed twice. For example, a property is presented as itself and separately as
accessor and mutator methods.
ILDASM has a variety of command-line options. Table 10-5 lists these parameters.
Table 10-5:
ILDASM
Options
IL De
DA scr
SM ipti
Op on
tio
n
Ou Re
t nd
ers
me
tad
ata
an
d
MS
IL
to
a
tex
t
file.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-5:
ILDASM
Options
IL De
DA scr
SM ipti
Op on
tio
n
Te Re
xt nd
ers
me
tad
ata
an
d
rel
ate
d
MS
IL
to
co
ns
ole
.
HT Co
ML mb
ine
s
wit
h
the
out
opt
ion
to
dis
pla
y
me
tad
ata
an
d
MS
IL
in
an
HT
ML
for
ma
t.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-5:
ILDASM
Options
IL De
DA scr
SM ipti
Op on
tio
n
RT Re
F nd
ers
me
tad
ata
an
d
MS
IL
in
Ric
h
Te
xt
For
ma
t.
Byt Sh
es ow
s
MS
IL
co
de
wit
h
op
co
de
s
an
d
rel
ate
d
byt
es.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-5:
ILDASM
Options
IL De
DA scr
SM ipti
Op on
tio
n
Ra Sh
we ow
h s
lab
el
for
m
of
try
an
d
cat
ch
dir
ect
ive
s
in
raw
for
m.
To Sh
ke ow
ns s
me
tad
ata
tok
en
s.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-5:
ILDASM
Options
IL De
DA scr
SM ipti
Op on
tio
n
So Sh
urc ow
e s
MS
IL
int
erl
ac
ed
wit
h
co
m
me
nte
d
so
urc
e
co
de;
for
thi
s
co
m
ma
nd,
the
so
urc
e
co
de
an
d
de
bu
g
file
mu
st
be
ac
ce
ssi
ble
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-5:
ILDASM
Options
IL De
DA scr
SM ipti
Op on
tio
n
Lin Ins
en ert
um s
line
dir
ect
ive
s
int
o
an
out
put
str
ea
m
tha
t
ma
tch
es
so
urc
e
co
de
to
MS
IL.
Thi
s
co
m
ma
nd
req
uir
es
the
de
bu
g
file.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-5:
ILDASM
Options
IL De
DA scr
SM ipti
Op on
tio
n
Vis Dis
ibili as
ty se
mb
les
onl
y
me
mb
ers
wit
h
the
sta
ted
visi
bilit
y:
pu
b
(pu
blic
),
pri
(pri
vat
e),
fa
m
(fa
mil
y),
as
m
(as
se
mb
ly),
FA
A
(fa
mil
y
an
d
as
se
mb
ly),
foa
(fa
mil
y
an
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-5:
ILDASM
Options
IL De
DA scr
SM ipti
Op on
tio
n
Pu Dis
bo as
nly se
mb
les
onl
y
pu
blic
ele
me
nts
;
sh
ort
not
ati
on
for
visi
bili
ty=
pu
b.
Qu Bra
ote ck
All ets
Na all
me ide
s ntifi
ers
in
sin
gle
qu
ote
s.
NO Ex
CA clu
de
s
cu
sto
m
attr
ibu
tes
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-5:
ILDASM
Options
IL De
DA scr
SM ipti
Op on
tio
n
CA Dis
Ver pla
bal ys
blo
b
info
rm
ati
on
of
cu
sto
m
attr
ibu
tes
in
sy
mb
olic
for
m
an
d
not
bin
ary
.
NO Do
BA not
R dis
pla
y
pro
gre
ss
bar
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-5:
ILDASM
Options
IL De
DA scr
SM ipti
Op on
tio
n
UT Re
F8 nd
ers
out
put
file
in
UT
F8
(de
faul
t
AN
SI).
UN Re
IC nd
OD ers
E out
put
file
in
UN
IC
OD
E.
NO Do
IL not
dis
as
se
mb
le
lan
gu
ag
e
so
urc
e
co
de.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-5:
ILDASM
Options
IL De
DA scr
SM ipti
Op on
tio
n
For Ge
war ner
d ate
s
for
war
d
ref
ere
nc
es
an
d
as
se
mb
le
in
the
Cla
ss
Str
uct
ure
De
cla
rati
on
se
cti
on.
Ty Dis
pe pla
Lis ys
t list
of
typ
es.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-5:
ILDASM
Options
IL De
DA scr
SM ipti
Op on
tio
n
He Incl
ad ud
ers es
DO
S,
PE
,
CO
FF,
CL
R,
an
d
me
tad
ata
he
ad
er
info
rm
ati
on.
Ite Dis
m as
se
mb
les
a
par
tic
ula
r
cla
ss
or
me
tho
d.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-5:
ILDASM
Options
IL De
DA scr
SM ipti
Op on
tio
n
Sta Dis
ts pla
ys
sta
tist
ical
info
rm
ati
on
on
file,
PE
He
ad
er,
CL
R
He
ad
er,
an
d
me
tad
ata
.
Cla Pro
ss vid
Lis es
t a
co
m
me
nte
d
list
of
cla
ss
es
wit
h
attr
ibu
tes
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-5:
ILDASM
Options
IL De
DA scr
SM ipti
Op on
tio
n
All Co
mb
ina
tio
n
of
the
He
ad
er,
Byt
es,
Sta
ts,
Cla
ss
Lis
ts,
an
d
To
ke
ns
co
m
ma
nd
s.
Me Dis
tad pla
ata ys
sp
ecif
ic
info
rm
ati
on
rel
ate
d
to
me
tad
ata
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-5:
ILDASM
Options
IL De
DA scr
SM ipti
Op on
tio
n
Ob Sh
jec ow
tfil s
e me
tad
ata
of
a
libr
ary
file.
The user interface of ILDASM presents the same choices as the command-line options. The following
command line is typical. It disassembles simple.exe and outputs the resulting metadata, MSIL, metadata
tokens, and source code in the simple.il file.
The source option of the preceding command interlaces source code in between MSIL code. The source
code is commented. Associating MSIL to source code is invaluable when debugging.
The tokens generated per the tokens option are also commented. The disassembly created by ILDASM is a
valid MSIL program that can be recompiled (which is the reason for the il extension, as in client.il). The
assembly can be reassembled with the ILASM compiler, which compiles MSIL code. The newly assembled
assembly is identical to the original assembly.
Some ILDASM options impede the creation of a full disassembly. When a partial disassembly is requested,
ILDASM issues a warning, which prevents you from attempting to use a partial assembly as a full assembly.
One limitation is that partial assemblies cannot be reassembled using ILASM. The following command
creates a partial assembly:
The preceding command targets only the ZClass of the simple.exe assemblies. Because other types are
omitted from the disassembly, it is not complete. For this reason, a warning is added to the output file.
Following is a partial listing of the output file with the embedded warning:
implements Donis.CSharpBook.IA
{
.field private class [mscorlib]System.EventHandler AEvent
.field private string m_Time
.method public hidebysig specialname instance void
This is the final example of ILDASM and command-line options. This command profiles the metadata yields
counts, validates the metadata, and persists the results to the simple.txt file:
Reflection
An assembly is a piñata stuffed with goodies such as type information, MSIL code, and custom attributes.
You use reflection to break open the assembly piñata to examine the contents. Reflection adds important
features to .NET, such as metadata inspection, run-time creation of types, late binding, MSIL extraction,
self-generating code, and so much more. These features are crucial to solving complex real-world problems
that developers face every day.
The Reflection namespace is the container of all things related to reflection. Assembly, Module,
LocalVariableInfo, MemberInfo, MethodInfo, FieldInfo, and Binder are some of the important members of the
Reflection namespace. Reflection exposes several predefined customer attributes, such as
AssemblyVersionAttribute, AssemblyKeyFileAttribute, and AssemblyDelaySignAttribute. The Reflection
namespace contains other reflection-related namespaces; most notably the Reflection.Emit nested
namespace. Reflection.Emit is a toolbox filled with tools for building assemblies, classes, and methods at
run time, including the ability to emit metadata and MSIL code. Reflection.Emit is reviewed in Chapter 11.
The central component of reflection is the Type object. Its interface can be used to interrogate a reference or
value type. This includes browsing methods, fields, parameters, and custom attributes. General information
pertaining to the type is also available via reflection, including identifying the hosting assembly. Beyond
browsing, Type objects support more intrusive operations. You can create instances of classes at run time
and perform late binding of methods.
The Object.GetType method, the typeof operator, and various methods of the Assembly object return a Type
object. GetType is a member of the Object class, which is the ubiquitous base class. Therefore, GetType is
inherited by .NET types and available to all managed instances. Each instance can call GetType to return
the related Type object. The typeof operator extracts a Type object directly from a reference or value type.
Assembly objects have several members that return one or more Type objects. For example, the
Assembly.GetTypes method enumerates and returns the Types of the target assembly.
As a member base class object, GetType is accessible to all instances of reference and values types.
GetType returns the Type object of the instance. This is the syntax of the GetType method:
The following code creates instances of a value and a reference type, which are passed individually as object
parameters to successive calls to the DisplayType method, which homogenizes the objects, where each
object loses its distinction. The function extracts the type of the instance and displays the Type name.
Finally, if the Type represents a ZClass, the ZClass.Display method is called:
using System;
namespace Donis.CSharpBook{
class Starter{
class ZClass {
The typeof operator returns a Type object from a type. The typeof operator is evaluated at compile time,
whereas the GetType method is invoked at run time. For this reason, the typeof operator is quicker but less
flexible than the GetType method:
typeof operator:
typeof( type )
An assembly is typically the host of multiple types. Assembly.GetTypes enumerates the types defined in an
assembly. Assembly.GetType is overloaded four times. The zero-argument method returns the Type object
of the Assembly and essentially the GetType method derived from the Object class. The one-argument
version, in which the parameter is a string, returns a specific type defined in the assembly. The final two
versions of GetType are an extension of the one-argument overloaded method. The two-argument method
also has a Boolean parameter. When true, the method throws an exception if the type is not located. The
three-argument version has a second Boolean parameter that stipulates case sensitivity. If this parameter is
false, the case of the type name is significant.
Assembly.GetTypes method:
Type [] GetTypes()
Assembly.GetType method:
Type GetType()
Type GetType(string typename)
Type GetType(string typename, bool throwError)
Type GetType(string typename, bool throwError, bool ignoreCase)
An assembly can be diagrammed through reflection. The result is called the Reflection tree of that assembly.
Each element of reflection, such as an AssemblyInfo, Type, MethodInfo, and ParameterInfo component, is
placed on the tree. AppDomain is the root of the tree; GetAssemblies expands the tree from the root. The
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Reflection tree is a logical, not a physical representation. Assembly, Type, and ParameterInfo are some of
the branches on the Reflection tree. You can explore the Reflection tree while inspecting the metadata of the
application by using enumeration. For example, Assembly.GetCurrentAssembly returns the current
assembly. Assembly.GetTypes will enumerate the types defined in the assembly. Type.GetMethods will
enumerate the methods of each type. This process can continue until the application is fully reflected.
Loading Assemblies
Assemblies near the root of the reflection tree can be loaded at run time using Assembly.Load and
Assembly.LoadFrom. Assembly.Load uses the assembly loader to locate and bind to the correct assembly.
Assembly.LoadFrom consults the assembly resolver to locate an assembly, which uses a combination of
the strong name identifier and probing to bind and then load an assembly. The strong name includes the
simple name, version, culture, and public key token of the assembly. Probing is the algorithm for locating an
assembly. Both Assembly.Load and Assembly.LoadFrom are static methods that are overloaded several
times. This is the core syntax:
Assembly.Load method:
static Assembly Assembly Load(string assemblyName)
Assembly.LoadFrom method:
static Assembly Assembly.LoadFrom(string AssemblyFileName)
When the chosen assembly isn't found, Assembly.Load and Assembly.LoadFrom will fail. The Assembly
Binding Log Viewer Tool (fuslogvw.exe), included in the .NET Framework SDK, is a useful tool for diagnosing
probing failures because it displays information on the binding error.
The following sample code accentuates the difference between Assembly.Load and Assembly.LoadFrom:
using System;
using System.Reflection;
namespace Donis.CSharpBook{
class Starter{
static void Main(){
Assembly library=Assembly.Load("library, Version=2.0.0.0, "+
"Culture=Neutral, PublicKeyToken=9b184fc90fb9648d");
Console.WriteLine("Assembly.Load: {0}", library.FullName);
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
library=Assembly.LoadFrom("library.dll");
Console.WriteLine("Assembly.LoadFrom {0}", library.FullName);
}
}
}
Assembly.Load and Assembly.LoadFrom reference external assemblies. How about referencing the
currently executing assembly? Assembly.GetExecutingAssembly is a static method and returns a reference
to the currently executing assembly. This is valuable for interrogating the metadata or MSIL of the running
assembly.
Assembly.GetExecutingAssembly method:
static Assembly Assembly.GetExecutingAssembly()
Both Assembly.Load and Assembly.LoadFrom return a reference to an assembly. That assembly can be
reflected, code loaded, and then executed. Assembly.ReflectionOnlyLoad and
Assembly.ReflectionOnlyLoadFrom load an assembly only for reflection, so the code cannot be executed
later. Although you could reflect a type and iterate all the methods, you could not invoke a method of that
type. To confirm the way an Assembly is loaded, use the ReflectionOnly property. ReflectionOnly is a
Boolean attribute of an Assembly and is true if an Assembly is loaded for reflection only.
Assembly.ReflectionOnlyLoad and Assembly.ReflectionOnlyLoadFrom are equivalent to Assembly.Load and
Assembly.LoadFrom, respectively, without the execute capability. ReflectionOnly yields performance
benefits.
using System;
using System.Reflection;
using System.Diagnostics;
namespace Donis.CSharpBook{
class OnlyLoad{
static void Main() {
Stopwatch duration=new Stopwatch();
duration.Reset();
duration.Start();
Assembly a=Assembly.Load("library");
duration.Stop();
Console.WriteLine(duration.ElapsedTicks.ToString());
duration.Reset();
duration.Start();
a=Assembly.ReflectionOnlyLoad("library");
duration.Stop();
Console.WriteLine(duration.ElapsedTicks.ToString());
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
}
}
}
Type.ReflectionOnlyType method:
static Type ReflectionOnlyType(string typeName, bool notFoundException,
bool ignoreCase)
The typeName parameter is a combination of the Assembly and Type names. The Assembly and Type
names are comma-delimited. To raise an exception if the type is not found, set the notFoundException
parameter to true. When false, the ignoreCase parameter indicates that the Type name is case sensitive:
using System;
using System.Reflection;
namespace Donis.CSharpBook{
class ReflectOnlyType{
Inspecting a type begins with the Type object. Reflection has a straightforward interface for mining the
metadata of a type. Inspecting the metadata of a type essentially consists of spanning a series of
collections. The Type object interface publishes several methods and properties related to reflection.
Type.GetMembers returns the ultimate collection: the collection of all members. All members, whether the
member is a method, field, event, or property, are included in the collection. GetMembers returns a
MemberInfo array that contains an item for each member. GetMember returns a single MemberInfo object
for the named member. MemberInfo.MemberType is a property of the MemberInfo.MemberTypes type, which
is a bitwise enumeration distinguishing a member as a method, field, property, event, constructor, or
something else. (See Table 10-6.) MemberInfo has relatively few properties and operations. Here are some of
the more useful. The MemberInfo.Name offers the name of the type member. MemberInfo.MetadataToken,
another property, returns the metadata token of the member. MemberInfo.ReflectedType provides the Type
object from which the MemberInfo object was extracted.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-6:
MemberTypes
Enumeration
Me Va
mb lue
er
Ty
pe
Me 0x
mb 01
erT
ype
s.C
on
str
uct
or
Me 0x
rm 40
ber
Ty
pe
s.C
ust
om
Me 0x
mb 02
erT
ype
s.E
ven
t
Me 0x
mb 04
erT
ype
s.F
ield
Me 0x
mb 08
erT
ype
s.
Me
tho
d
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-6:
MemberTypes
Enumeration
Me Va
mb lue
er
Ty
pe
Me 0x
mb 80
erT
ype
s.N
est
ed
Ty
pe
Me 0x
mb 10
erT
ype
s.P
rop
ert
y
Me 0x
mb 20
erT
ype
s.T
ype
Inf
o
Me 0x
mb BF
erT
ype
s.A
ll
Type.GetMembers creates a basket that contains all the members of the reflected type. You can be
somewhat more granular. Type.GetMethods or Type.GetMethod returns a collection of methods or a specific
method. Type.GetFields or Type.GetField similarly returns a collection of fields or a specific field. Table 10-7
lists the methods that return specific collections where the nonplural method returns a specific member of
that collection.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Me Re Ty
tho tur pe
d ns of
Me
mb
er
Ge Co Co
tCo nst nst
nst ruc ruc
ruc torI tor
tor nfo
s []
Ge Ob Cu
tCu jec sto
sto t [] m
mA attr
ttri ibu
but te
es
Ge Me Def
tDe mb aul
fau erI t
ltM nfo me
em [] mb
ber er
s
Ge Ev Ev
tEv ent ent
ent Inf
s o []
Ge Fie Fie
tFi ldI ld
eld nfo
s []
Ge Ty Im
tInt pe ple
erf [] me
ac nte
es d
int
erf
ac
e
Ge Me All
tM mb me
em erI mb
ber nfo ers
s []
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Me Re Ty
tho tur pe
d ns of
Me
mb
er
Ge Me Me
tM tho tho
eth dIn d
od fo
s []
Ge Ty Ne
tNe pe ste
ste [] d
dT Ty
ype pe
s
Ge Pro Pro
tPr per per
op tyI ty
erti nfo
es []
The Type.GetMethods that return MethodInfo arrays are overloaded to be called with no parameters or with a
single parameter, which is BindingFlags:
BindingFlags is a bitwise enumeration that filters the results of a collection. For example, to include private
members in a collection stipulates the BindingFlags.NonPublic flag. Some BindingFlags, such as
InvokeMember, are not applicable in this context. When stipulating BindingFlags, there are no default flags.
If you specify one flag, you must specify every required flag. Each inclusion must be specified explicitly. The
zero-argument version of GetMethods grants default bindings, which vary depending on the method. At a
minimum, most of the methods default to BindingFlags.Public and BindingFlags.Instance. Notably, the
BindingFlags.Static flag is not always defaulted and static members are often excluded from a collection.
The following code first iterates private instance (nonpublic) members. Afterward, static public members are
iterated.
using System;
using System.Reflection;
namespace Donis.CSharpBook{
class DumpType{
public static void Main() {
ZClass zObj=new ZClass();
Type tObj=zObj.GetType();
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
MemberInfo [] members=tObj.GetMembers(
BindingFlags.Instance|
BindingFlags.NonPublic);
foreach(MemberInfo member in members) {
Console.WriteLine(member.Name);
}
members=tObj.GetMembers(
BindingFlags.Public|BindingFlags.Static);
Console.WriteLine(" ");
foreach(MemberInfo member in members) {
Console.WriteLine(member.Name);
}
}
}
class ZClass {
private int vara=5;
public int PropA {
get {
return vara;
}
}
static public void MethodA() {
Console.WriteLine("ZClass::MethodA called.");
}
}
}
The following application calls DumpMethods to dump the public methods of a class. This code
demonstrates various aspects of Reflection.
using System;
using System.Reflection;
namespace Donis.CSharpBook{
class DumpType{
static void Main(string [] argv) {
targetType=LoadAssembly(argv[0], argv[1]);
DumpReportHeader();
DumpMethods();
}
In the preceding code, a type name and an assembly name are read from the command line. The type to be
dumped is argv[0], while the assembly hosting the type is argv[1]. With this information, the LoadAssembly
method employs Type.ReflectionOnlyGetType and loads the type for inspection only. The DumpMethods
function iterates the methods of the target type, and then iterates the parameters of each method. The name
of each method and parameter is displayed. This dumps the members of the Console class:
Dynamic Invocation
Methods can be dynamically invoked at run time using reflection. The benefits and perils of early binding
versus late binding were reviewed in the discussion on delegates earlier in the book.
In dynamic binding, you build a method signature at run time and then invoke the method. This is somewhat
later than late binding with delegates. When compared with delegates, dynamic binding is more flexible, but
is regrettably slower. At compile time, the method signature must match the delegate at the site of the
run-time binding. Dynamic binding removes this limitation, and any method can be invoked at the call site,
regardless of the signature. This is more flexible and extensible than run-time binding.
In reflection, there are two approaches to invoking a method dynamically: MethodInfo.Invoke and
Type.InvokeMember, where MethodInfo.Invoke is the simplest solution. However, Type.InvokeMember is
more malleable. The basic syntax of MethodInvoke requires only two parameters: an instance of a type and
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
an array of parameters. The method is bound to the instance provided. If the method is static, the instance
parameter is null. To avoid an exception at run time, which is never fun, care must be taken to ensure that
the instance and parameters given to MethodInfo.Invoke match the signature of the function.
The second Invoke method has several additional parameters. The obj parameter is the instance that
method is bound. If invoking a static method, the obj parameter should be null but is otherwise ignored.
BindingFlags are next and further describe the Invoke operation, such as Binding.InvokeMethod. When no
binding flags are specified, the default is BindingFlags.DefaultBinding. Binderobj is used to select the
appropriate candidate among overloaded methods. Arguments is the array of method arguments as defined
by the method signature. The culture argument sets the culture, which defaults to the culture of the system.
Return is the method return or null for a void return.
Alternatively, you can invoke a method dynamically at run time using Type.InvokeMember, which is
overloaded several times.
InvokeMember 1 is the core overloaded method, and it has several parameters. The methodName parameter
is the name of the method to invoke. The next parameter is BindingFlags. Binderobj is the binder used to
discriminate between overloaded methods. The object to bind the method again is typeInstance. arguments
is the array of method parameters. InvokeMember 2 adds an additional parameter. To set the culture, use the
culture parameter. InvokeMember 3 is the final overload with one additional parameter: namedParameters,
which is used to specify named parameters.
In the following code, dynamic invocation is demonstrated with MethodInfo.Invoke and Type.InvokeMember:
using System;
using System.Reflection;
namespace Donis.CSharpBook{
class Starter{
Type tObj=obj.GetType();
MethodInfo method=tObj.GetMethod("MethodA");
method.Invoke(obj, null);
tObj.InvokeMember("MethodA", BindingFlags.InvokeMethod,
null, obj, null);
}
}
class ZClass {
Binders
Members such as methods can be overloaded. In reflection, binders determine the specific method to invoke
from a list of possible candidates. The default binder selects the best match based on the number and type
of arguments. You can provide a custom binder and choose a specific overloaded member. Both
MethodInfo.Invoke and Type.InvokeMember offer a binder argument for this reason.
The Binder class is an abstract class; as such, it is implemented through a derived concrete class. Binder
has abstracted methods to select a field, property, and method from available overloaded candidates. Binder
is a member of the Reflection namespace. Table 10-8 lists the public members of the Binder class. Each
method included in the table is abstract and must be overridden in any derivation.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-8:
Abstract
Methods of
the Binder
Class
Bi De
nd scr
er ipti
Me on
tho
d
Bin Sel
dT ect
oFi s
eld a
fiel
d
fro
m
a
set
of
ove
rlo
ad
ed
fiel
ds
Bin Sel
dT ect
oM s
eth a
od me
tho
d
fro
m
a
set
of
ove
rlo
ad
ed
me
tho
ds
Ch Co
an erc
ge es
Ty the
pe typ
e
of
an
obj
ect
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-8:
Abstract
Methods of
the Binder
Class
Bi De
nd scr
er ipti
Me on
tho
d
Re Re
ord set
erA s
rgu the
me arg
ntA um
rra ent
y arr
ay;
as
so
cia
ted
wit
h
the
sta
te
par
am
ete
r
of
Bin
dT
oM
eth
od
me
mb
er
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-8:
Abstract
Methods of
the Binder
Class
Bi De
nd scr
er ipti
Me on
tho
d
Sel Sel
ect ect
Me s
tho a
d me
tho
d
fro
m
ca
ndi
dat
e
me
tho
ds
Sel Sel
ect ect
Pro s
per a
ty pro
per
ty
fro
m
ca
ndi
dat
e
pro
per
tie
s
Binder.BindToMethod is called when a method is invoked dynamically. To override the default selection
criteria of overloaded methods, create a custom binder class. Inherit the binder class and at least minimally
override and implement each abstract method of the base class. How the binder is used determines the
methods to fully implement. If the MethodInfo.Invoke and Type.InvokeMember methods are called with the
BindingFlags.InvokeMethod flag set, BindToMethod will be invoked and should be completely implemented.
The first parameter of BindToMethod is BindingFlags, which is the usual assortment of binding flags. match
is a MethodBase array with an element for each possible candidate. If there are three candidates, match has
three elements. At present, MethodBase-derived classes are limited to ConstructorInfo; MethodInfo.args are
the values of method parameters. Modifiers is an array of ParameterModifier used with parameter signatures
of modified types. Culture sets the culture. Names are the identifiers of methods included as candidates. The
modifiers, culture, and names parameters can be null. The final parameter, state, is used with parameter
reordering. If this parameter is non-null, Binder.ReorderArgumentArray is called after BindToMethod and
returns the parameters to the original order.
There is no prescription for selecting a method from a set of candidates. You are free to be creative and
employ whatever logic seems reasonable. Here is a partially implemented but workable custom Binder
class. BindToMethod is implemented but the other methods are essentially stubbed. This code is somewhat
circumscribed and not written for general-purpose application.
using System;
using System.Reflection;
using System.Globalization;
class CustomBinder:Binder {
public override FieldInfo BindToField(BindingFlags bindingAttr,
FieldInfo[] match, object value, CultureInfo culture) {
return null;
}
class ZClass {
public void MethodA(long argument) {
Console.WriteLine("Long version: "+argument.ToString());
}
class Starter {
public static void Main() {
ZClass obj=new ZClass();
Type tObj=obj.GetType();
CustomBinder theBinder=new CustomBinder();
tObj.InvokeMember("MethodA", BindingFlags.InvokeMethod,
theBinder, obj, new Object[] {int.MinValue});
Console.WriteLine();
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
In the preceding code, CustomBinder inherits from the Binder class. As mentioned, BindToMethod is the
sole method implemented in the code. This function lists the signatures of each candidate. The appropriate
method is then chosen. The first foreach loop iterates the candidates while listing the method names. The
inner foreach loop iterates and lists the parameters of each MethodInfo object. This version of BindToMethod
is written specifically for the one-argument methods of the ZClass BindToMethod. The argument value is
tested. If the value is within the range of a long, the first method is returned. This method has a long
parameter. Otherwise, the second candidate, which has an integer parameter, is returned.
In Main, an instance of the ZClass and custom binder is created. ZClass is a simple class with an
overloaded method. ZClass.MethodA is overloaded three times. The type object is then extracted from the
ZClass instance, and Type.InvokeMember is called twice with the custom binder. InvokeMember is called to
dynamically invoke "MethodA", first with an integer parameter and then with a long parameter. This is the
output from the application:
Overloaded Method:
MethodA ( System.Int64 )
MethodA ( System.Int32 )
Overloaded Method:
MethodA ( System.Int64 )
MethodA ( System.Int32 )
Type Creation
Until now, the emphasis of this chapter has been on reflecting existing objects. Object instances can also
be created dynamically at run time. You can reflect, bind methods, and otherwise treat the dynamic object
as a static object. As often with Reflection, the primary benefit is added flexibility. What if the type of an
instance is not determinable at compile time? With Reflection, that decision can be delayed until run time,
when the particular class can be chosen by the user, stipulated in a configuration file, or otherwise selected
by some intermediary.
The Activator class is a member of the Reflection namespace and facilitates the creation of objects at run
time. Activator consists of four static member methods: CreateInstance creates an instance of a type;
CreateInstanceFrom leverages Assembly.LoadFrom to reference an assembly and then create an instance
of a type found in the assembly; and CreateComInstanceFrom and GetObject instantiate a COM object and
a proxy to a Remote object, respectively. To simply create an instance of a .NET type, call CreateInstance
or CreateInstanceFrom. Both CreateInstance and CreateInstanceFrom are overloaded several times. This is
the list of CreateInstance methods:
Activator.CreateInstance syntax:
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
static T CreateInstance<T>()
static public ObjectHandle CreateInstance(ActivationContext context)
static object CreateInstance(Type type)
static public ObjectHandle CreateInstance(ActivationContext context, string
[] customData)
static ObjectHandle CreateInstance(string assemblyName, string typeName)
static object CreateInstance(Type type, bool ctorPublic)
static object CreateInstance(Type type, object [] ctorArgs)
static ObjectHandle CreateInstance<T, U> (T, U)
static ObjectHandle CreateInstance(string assemblyName, string typeName,
object [] activationAttributes)
static object CreateInstance(string assemblyName, string TypeName,
object [] activationAttributes)
static object CreateInstance(Type type, BindingFlags bindingAttr,
Binder binder, object [] args, CultureInfo culture)
static object CreateInstance(Type type, object [] args, object []
activationAttributes)
static Object CreateInstance(Type type, BindingFlags bindingAttr, Binder
binder,
object [] args, CultureInfo culture, object [] activationAttributes)
static ObjectHandle CreateInstance(string assemblyName, string typeName,
bool ignoreCase, BindingFlags bindingAttr, Binder Binder,
object [] args, CultureInfo culture, object [] activationAttributes,
Evidence securityInfo)
The following code creates three instances of the same type. A local and two remote proxies to object
instances are constructed:
using System;
using System.Reflection;
using System.Runtime.Remoting;
namespace Donis.CSharpBook{
class ZClass {
public void MethodA(DateTime dt) {
Console.WriteLine("MethodA invoked at "+
dt.ToLongTimeString());
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
class Starter{
MethodInfo method=obj.GetType().GetMethod("MethodA");
method.Invoke(obj, new object [1] {DateTime.Now});
}
}
}
The preceding code presents three vehicles for creating an instance of a type, binding a method to the type,
and finally invoking that method dynamically. Dynamically invoking a method through casting is a
mechanism not previously demonstrated. (Method.Invoke and Type.InvokeMember were reviewed earlier.)
The code for engaging a method through casting is listed as follows. Calling a method dynamically through
casting has substantial performance gains when compared with MethodInfo.Invoke or Type.InvokeMember. (
MethodInfo.Invoke and Type.InvokeMember were reviewed earlier in this chapter.)
((ZClass) obj).MethodA(DateTime.Now);
Either directly or indirectly, the Activator.CreateInstance and CreateInstanceFrom methods return an object.
As the preceding code demonstrates, you can cast the generic object to a specific type and invoke the
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
chosen method. This combines late and early binding, which has significant performance benefits when
compared with late binding the type and method.
A delegate is a repository of type-safe function pointers. A single-cast delegate holds one function pointer,
whereas a multicast delegate is a basket of one or more delegates. Delegates are type-safe because the
signatures of the delegate and function pointer must match. Normally a compile error is generated if there is
a mismatch. Unlike MethodInfo, a function pointer is discriminatory and bound to a specific object.
MethodInfo is nondiscriminatory and can be associated with any affiliated object. This is the reason why
MethodInfo.Invoke and Type.InvokeMember methods have an object parameter to associate an instance
with the target method. This section assumes a fundamental understanding of delegates. If you would like a
review, take a look at Chapter 8, "Delegates and Events." The following code is typical of delegates:
using System;
using System.Reflection;
namespace Donis.CSharpBook{
delegate void XDelegate(int arga, int argb);
class ZClass {
public void MethodA(int arga, int argb) {
Console.WriteLine("ZClass.MethodA called: {0} {1}", arga, argb);
}
}
class Starter{
static void Main(){
ZClass obj=new ZClass();
XDelegate delObj=new XDelegate(obj.MethodA);
delObj.Invoke(1,2);
delObj(3,4);
}
}
}
In this code, XDelegate is the delegate type. MethodA is then added to the delegate and invoked. First,
invoke using the Delegate.Invoke method. Second, invoke the function through the delegate using the C#
syntax. At compile time, XDelegate expands into a class derived from a Delegate type. The Invoke method
is the most important member implemented and added to the class interface of the derived type. The
signature of Invoke matches the signature of the delegate. Therefore, XDelegate.Invoke has two integer
parameters and enforces type safeness of related function pointers.
The preceding code assumes the delegate signature is known at compile time. What if the signature of the
delegate is not known at compile time? Because a delegate is an implied class that is similar to any class,
you can create an instance of a delegate at run time, bind a method to the delegate, and invoke the method.
Instead of invoking a member method, a function pointer is bound and executed against the delegate. The
Delegate.CreateDelegate and Delegate.DynamicInvoke methods provide this behavior. Late binding is not as
type-safe as compile-time type checking. (This also pertains to late binding of delegates.) Care must be
taken to avoid run-time exceptions. As always, the seminal benefit of late binding is additional flexibility, but
performance might suffer.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
CreateDelegate constructs a new delegate at run time and then assigns a function pointer to the newly
invented delegate. CreateDelegate is an overloaded method where the essential parameters are the delegate
type and method identity. The delegateType is the type of delegate being created. Method is the initial
function pointer being assigned to the delegate. The signature of the method represented by a MethodInfo
object should match that of the delegate type. These are the overloaded methods:
After creating a delegate at run time, call DynamicInvoke to invoke any function pointers assigned to the
delegate. The dynamically created delegate is deprived of the Invoke method and language-specific
operations. Subsequently, you cannot call the Invoke method on a delegate returned from CreateDelegate.
This is the major difference between compile-time and run-time instances of delegates. An array of function
arguments is the only parameter of DynamicInvoke.
using System;
using System.Reflection;
namespace Donis.CSharpBook{
delegate void theDelegate(int arga, int argb);
class ZClass {
public void MethodA(int arga, int argb) {
Console.WriteLine("ZClass.MethodA called: {0} {1}", arga, argb);
}
}
class Starter{
static void Main(){
Type tObj=typeof(System.MulticastDelegate);
ZClass obj=new ZClass();
Delegate del=Delegate.CreateDelegate(typeof(theDelegate), obj,
"MethodA");
del.DynamicInvoke(new object [] {1,2});
}
}
}
Several ways to invoke a method have been presented in this chapter—from a simple method call to the
more complex dynamic invocation. Performance is an important criterion when evaluating competing
methodologies. For example, a simple call bound at compile time should be quicker than a method bound at
run time. Depending on the application, the differentiation might be material. Losing a few nanoseconds
occasionally might not be conspicuous in a user interface—driven application. However, a few lost
nanoseconds in a server application multiplied by thousands of users can pose a real problem.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
With Reflection, you can query the state of a generic type of method. Is this a generic type or method? If
confirmed, is the generic type or method open or closed? Type.IsGeneric is a Boolean property that confirms
the presence of a generic type; Type.IsGenericTypeDefinition, another Boolean property, indicates whether
the generic is open or closed. For methods, the MethodInfo.IsGenericMethod property confirms that a
method is a generic method, whereas the MethodInfo.IsGenericTypeDefinition property indicates whether the
generic method is open or closed. This program demonstrates the four properties:
using System;
using System.Reflection;
namespace Donis.CSharpBook{
class Starter{
Type tObj=typeof(XClass);
MethodInfo method=tObj.GetMethod("MethodA");
bool [] bMethod={method.IsGenericMethod,
method.IsGenericMethodDefinition};
Console.WriteLine("Is XClass.MethodA<T> a generic method? "
+bMethod[0]);
Console.WriteLine("Is XClass.MethodA<T> open? "+bMethod[1]);
}
}
}
typeof
The typeof operator is used in the preceding code to extract the Type object of an open or constructed type.
The Type object has a single parameter, which is a string and identifies the generic type. Connote an open
type using empty parameters. For example, ZClass<T> would be ZClass<>. Multiple generic type
parameters are indicated with n-1 commas. For the typeof operator, ZClass<,> is compatible with
ZClass<K,V>. Indicate a closed type by including the actual parameter types, such as ZClass<int, int>. In
addition to the typeof operator, Type.GetType and Type.GetGenericTypeDefinition methods return Type
objects representing generic types.
GetType
Type.GetType is available in two flavors: an instance and a static method. Type.GetType is an instance
method and returns the open type used to construct the object. The method has no parameters. The
Type.GetType static method is overloaded to return a Type object for either an open or a closed type. The
pivotal parameter of the static GetType method is a string naming the generic type. To specify an open type,
the string is the name of the generic with the number of parameters affixed. The suffix is preceded with a "`".
For example, "NamespaceA .XClass`2" would represent XClass<K,V>. XClass has two type parameters.
For a closed type, you need to add the bound type parameters. After the number of type parameters, list the
bound type parameters. The bound type parameters are contained in square brackets.
The following sample code demonstrates both the instance and static GetType methods:
using System;
namespace Donis.CSharpBook {
class ZClass<K,V> {
public void FunctionA(K argk, V argv) {
}
}
class XClass<T> {
public void FunctionB(T argt) {
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
}
}
class Starter {
Type typeOpen=Type.GetType("Donis.CSharpBook.XClass`1");
Console.WriteLine(typeOpen.ToString());
Type typeClosed2=Type.GetType(
"Donis.CSharpBook.ZClass`2[System.Int32, System.Decimal]");
Console.WriteLine(typeClosed2.ToString());
}
}
}
GetGenericTypeDefinition
Closed types are constructed from open types. Type.GetGenericTypeDefinition returns the Type object of
the open type used to construct a closed type.
Type GetGenericTypeDefinition()
using System;
namespace Donis.CSharpBook {
class ZClass<K,V> {
public void FunctionA(K argk, V argv) {
}
}
class Starter {
Type closedType=obj.GetType();
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Type openType=closedType.GetGenericTypeDefinition();
Type closedType2=obj2.GetType();
Type openType2=closedType2.GetGenericTypeDefinition();
Console.WriteLine(openType.ToString());
Console.WriteLine(openType2.ToString());
}
}
}
The preceding code displays identical strings. Why? The open type of ZClass<int, decimal> and
ZClass<string, float> is the same, which is ZClass<K, V>.
GetGenericArguments
You can now extract a Type object for a generic type and a MethodInfo object for a generic method.
Determining the number of unbound or bound parameters is a natural next step. If bound, the type of each
parameter would be useful. GetGenericArguments is the universal method for enumerating parameters of
generic type or method. GetGenericArguments enumerates unbound and bound parameters.
Type [] GetGenericArguments()
MethodInfo.GetGenericArguments syntax:
Type [] GetGenericArguments()
using System;
namespace Donis.CSharpBook {
class ZClass<K,V> {
public void FunctionA(K argk, V argv) {
}
}
class Starter {
Type closedType=obj.GetType();
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Type openType=closedType.GetGenericTypeDefinition();
Type closedType2=obj2.GetType();
Type openType2=closedType2.GetGenericTypeDefinition();
Console.WriteLine(openType.ToString());
Console.WriteLine(openType2.ToString());
}
}
}
Generic types can be created at run time using Reflection. First, extract the open type of the generic. This
chapter has shown several ways to accomplish this, including using GetType and GetGenericTypeDefinition.
Next, bind the type arguments of the open type. The result will be a bound (closed) type. Finally, create an
instance of the bound type in the customary manner. (Activator.CreateInstance works well.)
The Type.MakeGenericType method binds type parameters to an open type of a generic. MakeGenericType
has a single parameter, which is an array of Type objects. Each element of the array matches a specific
type to a generic parameter. If the generic has three parameters, the array passed to MakeGenericType will
have three elements—each representing a bound parameter.
Type.MakeGenericType syntax:
void MakeGenericType(Type [] genericArguments)
Generic methods, like nongeneric methods, can be invoked dynamically at run time.
MethodInfo.MakeGenericType binds parameters to generic types, similar to Type.MakeGenericType. The
benefits and pitfalls of dynamic invocation are similar to nongeneric methods. After binding parameters to a
generic method, the method can be invoked using Reflection. The following code creates a generic type and
then invokes a generic method at run time:
using System;
using System.Reflection;
namespace Donis.CSharpBook{
class Starter{
Reflection Security
Some reflection operations, such as accessing protected and private members, require ReflectionPermission
security permission. ReflectionPermission is typically granted to local and intranet applications, but not to
Internet applications. Set the appropriate ReflectionPermission flag to grant or deny specific reflection
operations.
Table 10-9:
ReflectionPer
mission Flags
Fla De
g scr
ipti
on
Me Ref
mb lec
erA tio
cc n
es of
s no
nvi
sibl
e
me
mb
ers
gra
nte
d.
No Ref
Fla lec
gs tio
n
de
nie
d
to
no
nvi
sibl
e
typ
es.
Ref Sy
lec ste
tio m.
nE Ref
mit lec
tio
n.E
mit
op
era
tio
ns
gra
nte
d.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 10-9:
ReflectionPer
mission Flags
Fla De
g scr
ipti
on
Ty Thi
peI s
nfo flag
rm is
ati no
on w
de
pre
cat
ed.
All Co
Fla mb
gs ine
s
Ty
peI
nfo
rm
ati
on,
Me
mb
erA
cc
es
s,
an
d
Ref
lec
tio
nE
mit
flag
s.
Attributes
Attributes extend the description of some elements of an assembly. Attributes fulfill the role of adjectives in
.NET and annotate assemblies, classes, methods, parameters, and other elements of an assembly.
Attributes are commonplace in .NET and fulfill many roles: They delineate serialization, stipulate import
linkage, set class layout, indicate conditional compilation, mark a method as deprecated, and much more.
Attributes extend the metadata of the targeted element. An instance of the attribute is stored alongside the
metadata and sometimes the MSIL code of the constituent. This is demonstrated in the following code and
in Figure 10-4. The Conditional attribute marks a method for conditional compilation. Use this attribute to flag
one or more symbols. If the symbol is defined, the target method and call sites are included in the compiled
applications. If not defined, the method and any invocation are ignored.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
#define LOG
using System;
using System.IO;
using System.Diagnostics;
namespace Donis.CSharpBook{
class Starter{
static void Main(){
LogInfo(new StreamWriter(@"c:\logfile.txt"));
}
[Conditional("LOG")]
private static void LogInfo(StreamWriter sr) {
// write information to log file
}
}
}
This is the MSIL code of the LogInfo method as displayed in ILDASM. Notice the custom directive that
defines the Conditional attribute. It is integrated into the MSIL code of the method (see Figure 10-4).
Attributes are available in different flavors: predefined custom attributes, programmer-defined custom
attributes, and pseudo-custom attributes.
Predefined Attributes
Predefined custom attributes, which are defined in the .NET FCL, are the most prevalent custom attributes.
There is an encyclopedic list of predefined custom attributes fulfilling a variety of responsibilities in .NET. A
short list of predefined custom attributes includes AssemblyVersion, Debuggable, FileIOPermission, Flags,
Obsolete, and Serializable. The following code uses the Obsolete attribute to flag a method as deprecated:
Combining Attributes
An entity can be assigned multiple attributes. In certain circumstances, even the same attribute can be
applied multiple times. There are two ways to combine attributes: They can be grouped together or listed
separately. Here, the attributes are applied separately:
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
In the following code, two attributes are combined and applied to a method:
Pseudo-Custom Attributes
Pseudo-custom attributes are interpreted by the run time and modify the metadata of the assembly. Unlike
predefined or custom attributes, pseudo-custom attributes do not extend metadata. Examples of
pseudo-custom attributes include DllImport, MarshalAs, and Serializable.
Anatomy of an Attribute
What exactly is an attribute? Attributes are derived from the System.Attribute class, which is the common
template of all attributes. System.Attribute is an abstract class defining the intrinsic services of an attribute.
Manage compilers and the run time often accord special treatment to attributes. For example, the
ObsoleteAttribute causes the compiler to generate error or warning messages when a deprecated member
is used.
The target type designates the type of the target and must be included in the defined attribute usages.
Attribute target is optional and usually inferred correctly if not omitted.
The attribute name is the class name of the attribute. By convention, attribute names have an Attribute
suffix. OneWayAttribute is representative of an attribute name. You can also use the alias of an attribute,
which omits the Attribute suffix. The alias for OneWayAttribute is OneWay.
Attributes accept zero or more positional parameters. If present, position parameters are not optional and
must be listed in a declared sequence. In addition, an attribute can offer any number of named parameters,
which must follow positional parameters and are optional. Named parameters are not ordered and can be
presented in any sequence.
[type: UIPermissionAttribute(SecurityAction.Demand,
Clipboard=UIPermissionClipboard.OwnClipboard)]
class Starter{
static void Main(){
}
}
[UIPermission(SecurityAction.Demand)]
class Starter{
static void Main(){
}
}
You can create custom attributes for personal consumption or to be published in a library for others. There
are definitive procedures to creating a programmer-defined custom attribute. Here are the steps to follow:
1. Select an appropriate name for the custom attribute. As mentioned, attribute names should
conclude with the Attribute suffix.
2. Define an attribute class that derives from System.Attribute.
3. Set potential targets with the AttributeUsage attribute.
4. Implement class constructors, which determine the positional parameters.
5. Implement write-accessible properties, which define the named parameters.
6. Implement other members of the attribute class.
ClassVersionAttribute is a programmer-defined custom attribute that can apply a version number to types. It
illuminates the steps to creating a custom attribute. The attribute assigns a target and current version
number to a type. The target version is the version number of the type that the attribute adorns. The current
version number is the version applied to the most recent version of the type. When the target type of the
attribute is the most recent version, the target and current version are identical. In addition, the
ClassVersionAttribute can request that future instances of the target type be replaced with the current type.
The first and most important task of creating a programmer-defined attribute is selecting a fabulous name.
Alas, ClassVersionAttribute is a mundane but descriptive name. As an attribute, ClassVersionAttribute must
inherit System.Attribute. AttributeUsageAttribute sets the potential target of the attributes. AttributeTargets
is the only positional parameter of the AttributeUsageAttribute and is a bitwise enumeration.
AttributeUsageAttribute has three named parameters: AllowMultiple, Inherited, and ValidOn. AllowMultiple is
a Boolean flag. When true, the attribute can be applied multiple times to the same target. The default is
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
false. Inherited is another Boolean flag. If true, which is the default, the attribute is inheritable from base
classes. ValidOn is an AttributeTargets enumeration. This is an alternate method of setting the potential
target of the attribute.
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Struct,
Inherited=false)]
public class ClassVersionAttribute: System.Attribute {
}
Instance constructors of an attribute provide the positional parameters for the attribute. Attributes are created
at compile time, upon reflection, and when applied at run time. When attributes are created, the selected
constructor runs. The positional arguments should match the signature of a constructor in the attribute.
Overloading the constructor allows different sets of positional parameters. Positional and named parameters
are restricted to certain types. The following is a list of available attribute parameter types.
bool
byte
char
double
float
int
long
sbyte
short
string
uint
ulong
ushort
ClassVersionAttribute has two overloaded constructors. The first constructor accepts the target version; the
second constructor accepts the target and current version number:
string current) {
m_TargetVersion=target;
m_CurrentVersion=current;
}
Define named parameters as write-only or read-write instance properties of the derived attribute class. You
can duplicate positional parameters as named parameters for additional flexibility. ClassVersionAttributes
offers a UseCurrentVersion and CurrentName named parameter. UseCurrentVersion is a Boolean value. If
true, instances of the current type should be substituted for the target type. CurrentName names the type of
the current version. This is how the named parameters are described in the ClassVersionAttribute class:
get {
return m_CurrentName;
}
}
For completeness, read-only properties are added to the ClassVersionAttribute class for the target and
current version. Here is the completed ClassVersionAttribute class:
using System;
namespace Donis.CSharpBook{
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Struct,
Inherited=false)]
public class ClassVersionAttribute: System.Attribute {
The ClassVersionAttribute class is compiled and published in a DLL. The following application minimally
uses the ClassVersionAttribute:
using System;
namespace Donis.CSharpBook{
class Starter{
static void Main(){
}
}
[ClassVersion("1.1.2.1", UseCurrentVersion=false)]
class ZClass {
}
}
Programmer-defined custom attributes are valuable as information. However, the real fun and power is in
associating a behavior with an attribute. You can read custom attributes with Reflection using the
Attribute.GetCustomAttribute and Type.GetCustomAttributes methods. Both methods return an instance or
instances of an attribute. You can downcast the attribute instance to a specific attribute type and leverage
the intricacies of the attribute to implement the appropriate behavior.
using System;
using System.Reflection;
namespace Donis.CSharpBook{
class Starter{
The following is sample code of GetCustomAttributes. The application inspects the ClassVersionAttribute
attribute with the GetCustomAttributes method and acts upon the results. The application contains two
versions of ZClass and both are applied to the ClassVersionAttribute. Each version of ZClass shares a
common interface: IZClass. The ClassVersionAttribute of Donis.CSharpBook.ZClass lists
ANamespace.ZClass as the current version or most recent version. Also, UseCurrentVersion is assigned true
to indicate that the newer version should replace instances of Donis.CSharpBook.ZClass. The function
CreateZClass accepts a ZClass as defined by the IZClass interface. Within the foreach loop,
GetCustomAttributes enumerates the attributes of the ZClass. If the attribute is a ClassVersionAttribute, the
attribute is saved and the foreach loop is exited. Next, the properties of the attribute are examined. If
UseCurrentVersion is true and a current version is named, an instance of the new version is created and
returned from the method. Otherwise, the target version is returned.
using System;
using System.Reflection;
interface IZClass {
void AMethod();
}
namespace Donis.CSharpBook{
class Starter{
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
namespace ANamespace {
[ClassVersion("2.0.0.0", UseCurrentVersion=false)]
public class ZClass: IZClass {
public void AMethod() {
Console.WriteLine("AMethod: new version");
}
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
}
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
MSIL
Metadata and the ability to inspect metadata using Reflection were the topics of this chapter. .NET
assemblies consist of metadata and MSIL, in which understanding MSIL is equally important to a developer.
(MSIL is the topic of Chapter 11.) Understanding MSIL is important for writing, maintaining, and, later,
debugging a C# application. Reflection can be used with MSIL code. You can inspect MSIL code at run
time, create self-generating code, and otherwise manipulate MSIL using Reflection.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
MSIL promotes the concept of compile-once-and-run-anywhere in the .NET environment. Just-in-time (JIT)
compilers, otherwise known as jitters, compile assemblies into native binary that targets a specific platform.
You can write an application or component once and then deploy the application to Microsoft Windows,
Linux, and other environments in which a compliant .NET platform is available. Prior to .NET, vendors
maintained different versions of their product, which is costly and time-consuming. Another advantage to
compile-once-and-run anywhere is the ability to assemble applications from components deployed on
disparate hardware and platforms. This was one of the objectives of component technologies such as
Component Object Model (COM) and Common Object Request Broker Architecture (CORBA), but it was
never truly realized. .NET makes this a reality. If platform-agnostic code is a design goal for an application,
best practices must be adopted to insulate the program from platform-specific code. This includes avoiding
or isolating interoperability and calls to native application programming interfaces (APIs).
MSIL is a full-featured, object-oriented programming (OOP) language. However, there are some differences
when compared with C# programming. For example, global functions are allowed in MSIL, but not supported
in the C# language. Despite being a lower-level language, MSIL has expanded language elements. It
encompasses the constituents common to most object-oriented languages: classes, structures, inheritance,
transfer-of-control statements, an assortment of arithmetic operators, and much more. Indeed, you can write
.NET applications directly in MSIL.
This is a book on C# programming. In that context, why is understanding MSIL important? An understanding
of MSIL code advances a deeper comprehension of C# programming and .NET in general. It isn't just magic.
MSIL removes much of the mystery and helps C# developers better maintain, debug, and write efficient code.
Managed applications, particularly production applications purchased from third-party vendors, are
sometimes deployed without the original source code. How is an application maintained without the source
code? For a managed application, as part of the assembly, the MSIL code is always available. The
exception is when the assembly is obfuscated. Several tools, including Intermediate Language Disassembler
(ILDASM), can disassemble an assembly and provide the MSIL code. With the MSIL code, a developer can
essentially read the application. You can even modify the code as MSIL and reassemble the application.
This is called roundtripping. Of course, this assumes that the developer is erudite in MSIL programming.
In a native application, debugging without a debug file (.pdb) is a challenge (which is an understatement).
Debugging a native application without symbol files invariably meant interpreting assembly code. That was
the challenge. More than a visceral understanding of assembly programming is needed to debug without
symbol files. MSIL can be viewed as the assembly code of the CLR. Debugging a managed application
without the germane symbol files requires more than a superficial understanding of MSIL. However, when
compared to the tedious task of reading assembly, MSIL is a leisure cruise.
MSIL is instructive in managed programming. Learning MSIL programming is learning C# programming. What
algorithms are truly efficient? When has boxing occurred? Which source code routines expand the footprint
of the application? These secrets can be found in understanding MSIL code.
Inline MSIL is mentioned in numerous programming blogs, but is not currently available. I am an advocate of
inline MSIL for C#. MSIL is more than an abstraction of higher-level source code. There are unique features in
MSIL code that are not exposed in C#. In addition, C# is a code generator that emits MSIL code. In rare
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
circumstances, the MSIL may not be as ideal for your specific application. For these reasons, I favor inline
MSIL. However, the problem with inline MSIL is maintaining safe code. Inline MSIL is inherently unsafe and
could lead to abuse. If a safe implementation of inline MSIL is not possible, it should not be added to the
language. As mentioned, managed applications incur two compilations. First the language compiler and then
the run time (jitter) compiles the application. You can compile MSIL code directly into an assembly with the
MSIL compiler Intermediate Language Assembler (ILASM). Conversely, you can disassemble an assembly
by engaging the MSIL disassembler (ILDASM).
This chapter is an overview of MSIL programming, not a comprehensive narrative on MSIL. The intention is to
convey enough information on the language to aid in the interpretation, maintenance, and debugging of C#
applications. For an authoritative explanation of MSIL, I recommend Inside Microsoft .NET IL Assembler,
written by Serge Lidin (Microsoft Press, 2002). Serge is one of the original architects of the ILASM compiler,
the ILDASM disassembler, and other tools included in the .NET Framework. Alternatively, consult the
European Computer Manufacturers Association (ECMA) documents pertaining to CIL, which are available
online at http://www.ecma-international.org/publications/standards/Ecma-335.htm.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
.namespace Donis.CSharpBook {
.class Starter {
.method static public void Main() cil managed {
.maxstack 2
.entrypoint
.locals init (string name)
ldstr "Donis"
stloc.0
ldstr "Hello, {0}!"
ldloc name
call void [mscorlib] System.Console::WriteLine(
string, object)
ret
}
}
}
This is the command line that compiles the MSIL code to create a hello executable:
The exe option indicates that the target is a console application, which is also the default. The dll option
specifies a library target. The debug option asks the compiler to generate a debug file (pdb) for the
application. A debug file is useful for a variety of reasons, including viewing source code in a debugger or
disassembler.
.namespace Donis.CSharpBook {
MSIL supports C++ style comments—both single and multiline comments. The first declarative is an
external reference to the mscorlib library. Mscorlib.dll contains the core of the .NET Framework class library
(FCL), which includes the System.Console class. The assembly directive describes the executing assembly,
in which the simple name of the assembly is hello. Notice that the simple name does not include the
extension. The third directive defines a namespace.
The next two lines define a class and a method. This class directive introduces a public class named Starter,
which implicitly inherits the System.Object class. The method directive defines Main as a member method.
Main is a managed, public, and static function:
.class Starter {
.method static public void Main() cil managed {
The Main method starts with three directives. The .maxstack directive sets the size of the evaluation stack to
two slots. The .entrypoint directive designates Main as the entry point of the application. By convention, Main
is always the entry point of a C# executable. In MSIL, the entry point method is whatever method contains
the .entrypoint directive, which could be a method other than Main. Finally, the .local directive declares a
local string variable called name.
.maxstack 2
.entrypoint
.locals init (string name)
Ins De
tru scr
cti ipti
on on
lds Lo
tr ad
"D the
oni stri
s" ng
"D
oni
s"
ont
o
the
eva
lua
tio
n
sta
ck.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 11-1:
Hello World
MSIL Code
Open table as
spreadsheet
Ins De
tru scr
cti ipti
on on
stl Sto
oc. re
0 "D
oni
s"
fro
m
the
eva
lua
tio
n
sta
ck
int
o
the
firs
t
loc
al
vari
abl
e,
whi
ch
is
call
ed
na
me
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 11-1:
Hello World
MSIL Code
Open table as
spreadsheet
Ins De
tru scr
cti ipti
on on
lds Lo
tr ad
"H the
ello "H
, ello
{0}! "
" stri
ng
pre
fix
ont
o
the
eva
lua
tio
n
sta
ck.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 11-1:
Hello World
MSIL Code
Open table as
spreadsheet
Ins De
tru scr
cti ipti
on on
ldlo Lo
c ad
na the
me loc
al
vari
abl
e
ont
o
the
eva
lua
tio
n
sta
ck.
(Lo
cal
vari
abl
es
are
ref
ere
nc
ed
by
ind
ex
or
by
na
me
.)
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 11-1:
Hello World
MSIL Code
Open table as
spreadsheet
Ins De
tru scr
cti ipti
on on
call Cal
voi l
d the
[m Co
sc ns
orli ole
b] ::
Sy Wri
ste teL
m. ine
Co me
ns tho
ole d,
:: whi
Wri ch
teL co
ine ns
(str um
ing es
, the
obj two
ect ite
) ms
on
the
eva
lua
tio
n
sta
ck
as
par
am
ete
rs
fro
m
rig
ht
to
left
.
"H
ello
na
me
"
is
dis
pla
ye
d.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 11-1:
Hello World
MSIL Code
Open table as
spreadsheet
Ins De
tru scr
cti ipti
on on
ret Ret
urn
fro
m
the
me
tho
d.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Evaluation Stack
The evaluation stack, which is mentioned often in the description of the Hello World application, is the pivotal
structure of MSIL applications. It is the bridge between your application and memory locations. It is similar to
the conventional stack frame, but there are salient differences. The evaluation stack is the viewer of the
application, and you can use it to view function parameters, local variables, temporary objects, and much
more. Traditionally, function parameters and local variables are placed on the stack. In .NET, this information
is stored in separate repositories, in which memory is reserved for function parameters and local variables.
You cannot access these repositories directly. Accessing parameters or local variables requires moving the
data from memory to slots on the evaluation stack using a load command. Conversely, you update a local
variable or parameter with content on the evaluation stack using a store command. Slots on the evaluation
stack are either 4 or 8 bytes.
Figure 11-1 shows the relationship between the evaluation stack and the repositories for function parameters
and local variables.
Figure 11-1: A depiction of the evaluation stack, function parameters, and local variables
The evaluation stack is a stack and thereby a last in/first out (LIFO) instrument. When a function starts, the
evaluation stack is empty. As the function runs, items are pushed and popped from the evaluation stack.
Before the function exits, except for a return value, the evaluation stack must once again be empty. The jmp
and tail instructions are exceptions to this rule. If the evaluation stack is not empty at exit, the run time
raises an InvalidProgramException exception.
The .maxstack directive limits the number of items permitted simultaneously on the stack. The directive is
optional. If the directive is not present, eight slots are reserved on the evaluation stack. The .maxstack
directive is a confirmation that an application is performing as expected. Extra items on the evaluation stack
are an indication of potential logic problems in an application or a security violation. In either circumstance,
this is a violation worthy of a notification.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
MSIL in Depth
Here are some basic facts about MSIL programming. The content of an MSIL program is case sensitive.
MSIL is also a freeform language. Statements can span multiple lines of code, in which lines can be broken
at the white space. Statements are not terminated with a semicolon. Comments are the same as in the C#
language. Double slashes (//) are used for single-line comments, and "/* comment */" is used for multiline
comments. Code labels are colonterminated and reference the next instruction. Code labels must be unique
within the scope in which it is defined.
In addition to the evaluation stack, the other important elements of a MSIL application are directives and the
actual MSIL source code. Directives are dot-prefixed and are the declarations of the MSIL program. Source
code is the executable content and control flow of the application.
Directives
There are several categories of directives. Assembly, class, and method directives are the most prominent.
Assembly directives contain information that the compiler emits to the manifest, which is metadata
pertaining to the overall assembly. Class directives define classes and the members of the class. This
information is also emitted as standard metadata, which is data about types. Method directives define the
particulars of a method, such as any local variables and the size of the evaluation stack.
Assembly Directives
Table 11-2 lists common assembly directives.
Table 11-2: Assembly Directives
Directive Description
.assembly The .assembly directive defines the simple name of the assembly. The simple name
does not include the extension. Assembly probing will uncover the correct extension.
Adding the extension will cause normal probing to fail and manifest a binding exception
when the assembly is referenced.
This is the syntax of the .assembly directive:
.assembly name {block}
The assembly block contains additional directives that further describe the assembly.
These directives are optional. You need to provide only enough directives to uniquely
identify the assembly. This is an assembly block with additional details:
.assembly Hello {
.ver 1:0:0:0
.locale "en.US"
}
These are some of the directives available in the assembly block:
.ver—The four-part version number of the assembly
.publickey—The 8-byte public key token of the public/private key pair used to
encrypt the hash of the assembly
.locale—The language and culture of the assembly
.custom—Custom attributes of the assembly
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Directive Description
.assembly The .assembly extern directive references an external assembly. The public types and
extern methods of the referenced assembly are available to the current assembly.
This is the syntax of the .assembly extern directive:
.assembly extern name as aliasname {block}
The as clause is optional and for referencing assemblies that are similarly named but a
different version, public key, or culture.
Add the .ver, .publickey, .locale, and .custom directives to the assembly extern block to
refine the identification of that assembly.
Because of the importance of mscorlib.dll, the ILASM compiler automatically adds an
external reference to that library. Therefore, assembly extern mscorlib is purely
informative.
.file The .file directive adds a file to the manifest of the assembly. This is useful for
associating documents, such as a readme file, with an assembly.
This is the syntax of the .file directive:
.file nometadata file name .hash = (bytes) .entrypoint
The file name is the sole required element of the declaration. Nometadata is the primary
option and stipulates that the file is unmanaged.
.subsystem The .subsystem directive indicates the subsystem used by the application, such as the
graphical user interface (GUI) or console subsystem. This is distinct from the target type
of the application, which is an executable, library, module, or so on. The ILASM compiler
inserts this directive based on options specified when the application is compiled. You
can also explicitly add this directive.
This is the syntax of the .subsystem directive:
.subsystem number
Number is a 32-bit integer in which:
2 is a GUI application.
3 is a console application.
.corflags The .corflags directive sets the run-time flag in the CLI header. This defaults to 1, which
stipulates an IL-only assembly. The corflags tool, introduced in .NET 2.0, allows the
configuration of this flag.
This is the syntax of the .corflags directive:
.corflags flag
The flag is a 32-bit integer.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Directive Description
.stackreserve The .stackreserve directive sets the stack size. The default size is 0x00100000. The
following code calls MethodA iteratively. Without the .stackreserve directive, which
defaults to 0x00100000, the MethodA method is called iteratively more than 110,000
times before exhausting the stack. Set the stack size to 0x0001000 using the
.stackreserve directive. Now MethodA is called only about 21,000 times before quitting.
Although the results may vary on your actual computer, the relative values are consistent.
.assembly iterative {}
.imagebase 0x00800000
.stackreserve 0x00001000
.namespace Donis.CSharpBook {
.class Starter {
.method static public void Main() il managed {
.entrypoint
ldc.i4.0
call void Donis.CSharpBook.Starter::
MethodA(int32)
ret
}
.imagebase The .imagebase directive sets the base address where the application is loaded. The
default is 0x00400000. The load address of the application image and stack size is
confirmable using the dumpbin tool. For example:
dumpbin /headers iterative.exe >iterative.txt
Class Directives
Table 11-3 describes the important class directives.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Dir Description
ect
ive
Dir Description
ect
ive
Dir Description
ect
ive
.m The .method
eth directive
od defines a
method. C#
does not
support global
methods.
Therefore, the
.method
directive is
always
included
within a type.
This is the
syntax of the .
method
directive:
.metho
d
attribut
es
callingc
onv
return
method
name
argume
nts
implattr
ibutes
{
method
body }
The method
attributes are
varied,
including the
accessibility
attributes:
public, private,
family, and
others. The
default is
private. Static
methods have
the static
attribute,
whereas
instance
methods
possess the
instance
attribute. The
default is an
instance
method.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Dir Description
ect
ive
Dir Description
ect
ive
Dir Description
ect
ive
public event
EventHandler
EventA;
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Method Directives
The .method directive adds a method to a class. MSIL allows for global methods. Global methods break the
rules of encapsulation and other tenets of OOP. For this reason, C# does not support global methods. MSIL
generated from the C# compiler (csc) uses the .method directive solely to define member methods. The
method block contains further directives and the implementation code (MSIL).
Table 11-4 lists the directives that are frequently included in the method block.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Dir Description
ect
ive
Dir Description
ect
ive
.m The .maxstack
axs directive sets
tac the number of
k slots available
on the
evaluation
stack.
Without this
directive, the
default is
eight slots,
which is the
number of
items that can
be placed on
the evaluation
stack
simultaneously
.
This is the
syntax of the .
maxstack
directive:
.maxst
ack
slots
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Dir Description
ect
ive
The following program defines MSILFunc as the entry point method. The .entrypoint directive is found at the
end of this method. The .locals directive defines two locals and assigns explicit indexes. Essentially, the
normal indexes are reversed. The instruction stloc.0 will update the second local variable. MSILFunc refers to
the local variables both as symbolic names and indexes. The MSILFunc method returns void. In MSIL code,
the ret instruction is required even when a function returns nothing. In C#, the return is optional for methods
returning void. The method displays the values of 10 and then 5.
.namespace Donis.CSharpBook {
.class Starter {
stloc.1
ldloc locala
call void [mscorlib] System.Console::WriteLine(int32)
ldloc localb
call void [mscorlib] System.Console::WriteLine(int32)
.entrypoint
ret
}
}
}
MSIL Instructions
MSIL includes a full complement of instructions, many of which were demonstrated in previous examples.
Each instruction is also assigned an opcode, which is commonly 1 or 2 bytes. 2-byte opcodes are always
padded with a 0xFE byte in the high-order byte. Opcodes are often followed with operands. Opcodes, which
provide an alternate means of defining MSIL instructions, are used primarily when emitting code dynamically
at run time. The ILGenerator .Emit method records instructions using opcodes, which is in the
System.Reflection.Emit namespace.
The byte option of ILDASM adds opcodes to the disassembly. The following is a partial listing of the
hello.exe disassembly, which includes just the Main method. As ascertained from the disassembly, the
opcode for ldstr is 72, the opcode for stloc is 0A, and the opcode for call is 28.
Short Form Some MSIL instructions have normal and short-form syntax. The short forms of the instruction
have a .s suffix. The short form of the ldloc instruction is ldloc.s. The short form of the br instruction is br.s.
Normal instructions have 4-byte operands, and short-form instructions are limited to 1-byte operands.
When used injudiciously, the short-form syntax can cause unexpected results:
.namespace Donis.CSharpBook {
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
.class Starter {
.method static public void Main() il managed {
.entrypoint
ldc.i4.s 50000
call void [mscorlib] System.Console::WriteLine(int32)
ret
}
}
}
In the preceding application, a constant of 50000 is placed on the evaluation stack. However, the ldc
instruction is in the short form. It is difficult to fit 50000 into a single byte, so the constant overflows the byte.
For this reason, the application incorrectly displays 80.
The next section of the book reviews the categories of MSIL instructions, such as branch, arithmetic, call,
and array groups of instructions. Because of the prevalence of the evaluation stack, load and store
instructions are the most frequently used of all MSIL instructions. That is a good place to start.
Load and Store Methods Load and store instructions transfer data between the evaluation stack and
memory. Load commands push memory, such as a local variable, to the evaluation stack. Store commands
move data from the evaluation stack to memory. Information placed on the evaluation stack is then
consumed by method parameters, arithmetic operations, and other MSIL instructions. Data not otherwise
consumed should be removed from the evaluation stack before the current method returns. The pop
instruction is the best command to remove extraneous data from the evaluation stack. Data needed for an
instruction should be placed on the evaluation stack immediately prior to the execution of that instruction. If
not, an InvalidProgramException is triggered. Method returns are also placed on the evaluation stack.
Ins Description
tru
cti
on
ldc 3
.i4.s
number
The ldc 1
instruction
loads a
constant of
the specified
type onto the
evaluation
stack.
The ldc 2
instruction is
more efficient
and transfers
an integral
value of -1 and
between 0
and 8 to the
evaluation
stack. The
special format
for -1 is
ldc.i4.m1.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ins Description
tru
cti
on
identify the
local variable
with the
symbolic
name. The
short form of
ldloc
efficiently
loads local
variables from
4 to 255. The
ldloc 5
instruction is
optimized to
load local
variables from
0 to 3.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ins Description
tru
cti
on
Ins Description
tru
cti
on
Ins Description
tru
cti
on
Complex Tasks
Until now, the focus has been on individual instructions. Most programs consist of complex tasks, such as
creating a new class, creating an array, or executing a for loop. Complex tasks consist of multiple
instructions.
Managing Types
Classes contain static and instance members. The static members are accessible through the class name,
whereas instance members are bound to an object. The WriteLine method is a static method. As
demonstrated, WriteLine is called directly on the Console class (for example, System.Console::WriteLine).
Instance members require an object. The newobj instruction creates an instance of a class and then invokes
the constructor to initialize the object. It also deposits a reference to the object onto the evaluation stack.
This reference can be used to call a member method or access a field. Such an action consumes the
reference and removes it from the evaluation stack. Several actions require several references. The dup
instruction is convenient for duplicating a reference or whatever is on the top of the evaluation stack.
Constructors are specially named methods. The name of a constructor is ctor, and a constructor is declared
with the .ctor directive. By convention in C#, constructors return void, which is enforced by the C# compiler.
In MSIL code, a constructor can return a value. Static constructors are named cctor and are declared with
the identically named directive. The static constructor is called when the class or instance is first accessed.
The .field directive adds a field to the class. The ldfld and ldsfld instructions load an instance and static field
onto the evaluation stack, respectively. Conversely, the stfld and stsfld instructions store data on the
evaluation stack into a field. The stfld and stsfld instructions consume a reference to the related object. The
load and store instructions have a single operand, which is the field signature.
The following program creates a class that contains an instance and static field in which the instance and
static constructors initialize the fields. The AddField and SubtractField methods return the total and
difference of the fields. An instance of the object is created in the Main method. The resulting reference is
duplicated with the dup instruction. Why? Both the AddField and SubtractField methods are called, which
require two references:
.namespace Donis.CSharpBook {
.class Starter {
ldloc.0
box int32
call void [mscorlib] System.Console::WriteLine(string, object)
call instance int32 Donis.CSharpBook.ZClass::SubtractFields()
stloc.0
ldstr "The difference is {0}"
ldloc.0
box int32
call void [mscorlib] System.Console::WriteLine(string, object)
ret
}
}
.class ZClass {
ldc.i4.s 5
stfld int32 Donis.CSharpBook.ZClass::fieldb
ret
}
}
}
The box instruction prevents an exception. The Console::WriteLine method has two parameters, which are
both reference types. However, the top of the evaluation stack has a value type and a reference type. The
assignment of a value to a reference type is the problem. The memory models are inconsistent. The box
instruction removes the value type from the evaluation stack, creates an object on the managed heap that
boxes the value type, and places a reference to the newly created object back on the evaluation stack. Now
that the value type has been replaced with a reference type, Console::WriteLine is called successfully. The
unbox instruction works in reverse. It unboxes a reference that is on the evaluation stack. This instruction
unboxes the reference type to the specified value type. The reference is replaced on the evaluation stack
with the unboxed value.
Inheritance In previous examples in this chapter, no class has directly inherited from another class. The
classes implicitly inherited System.Object. Most classes can be inherited explicitly, including System.Object
, by using the extends keyword. The child class will inherit most members of the base class, except
constructors. In the base class, methods that are expected to be overridden in the child should be prefixed
with the keyword virtual. The callvirt instruction calls a function overridden in a child. A child instance should
be on the evaluation stack. Here is sample code of inheritance in MSIL code:
.namespace Donis.CSharpBook {
.class Starter {
.class XClass {
This statement is interesting. Before this instruction is executed, there is a reference to an XClass object on
the evaluation stack. XClass has inherited MethodA from the ZClass. XClass does not override or hide the
interface of MethodA in the base class. Therefore, the implementation of MethodA resides solely in the base
class. Therefore, you must call the base function on the derived reference to successfully invoke the method.
Interfaces Classes are permitted to inherit from one class but can implement multiple interfaces. There is
no interface directive; instead, add the interface keyword to the details of the class directive. The interface
keyword enforces the semantics of an interface type on the class. Member methods must be public,
abstract, and virtual. Fields are not allowed in an interface class. In addition, constructors and destructors
are not permitted. The ILASM compiler enforces these and other rules. A class identifies interfaces to
implement with the implements keyword. The class must implement all the members of the interface to
prevent run-time errors. This code demonstrates an interface and the implementation of an interface:
.namespace Donis.CSharpBook {
.class Starter{
.method static public void Main() il managed {
.entrypoint
newobj instance void Donis.CSharpBook.ZClass::.ctor()
callvirt instance void Donis.CSharpBook.ZClass::MethodA()
ret
}
}
}
}
}
MethodA is defined as part of interface IA and is an abstract function. In C#, abstract functions do not have a
function body. In MSIL code, however, abstract methods have a body, but the body cannot contain an
implementation. An abstract method has a body but no code.
Structures As discussed, interfaces are defined with the class directive. Structures are similarly defined. To
define a structure, declare a type with the class directive and add the value keyword to the class detail. The
semantics of a structure are then enforced by the MSIL compiler. For example, a structure does not have a
default constructor or destructor, a structure is sealed, and so on. The value keyword is implemented by the
ILASM compiler as an explicit inheritance of System.ValueType. You could drop the value keyword and
inherit the System. ValueType directly. The compiler also adds keywords required for a structure, such as
the sealed keyword.
As a value type, structures are defined as local variables with the .locals directive. Value types are stored on
the stack, not on the managed heap. Accessing a member, such as calling a member method, requires
binding to the address of the structure. This entails loading the address of the structure onto the evaluation
stack before accessing a member. The ldloc instruction would load the structure on the evaluation stack. We
need the address of the structure. To load the address of a local variable, use the ldloca instruction. (The "a"
variation of an MSIL instruction refers to an address.)
Call the constructor of a structure explicitly with the call instruction, not the newobj instruction. Structures
are not created on the heap. When the constructor is called directly, the type is simply initialized.
.namespace Donis.CSharpBook {
.class Starter {
.method static public void Main() il managed {
.entrypoint
.locals init (valuetype Donis.CSharpBook.ZStruct obj)
ldloca.s obj
ldc.i4.s 10
call instance void Donis.CSharpBook.ZStruct::.ctor(int32)
ldloca.s obj
Branching
The branch instruction is available in various permutations, but in all circumstances it is essentially a goto
statement. The target of a branch instruction is a label. Most branch instructions are conditional and based
on a comparative expression or a Boolean condition. For an unconditional goto, use the br instruction. Loop
and other transfer-of-control statements in C# are implemented as some combination of branch instructions.
A conditional branch can be made with the brtrue and brfalse statements. The brtrue instruction branches
on a true condition, whereas the brfalse branches on a false condition. These instructions consume a
Boolean value, which should be on the evaluation stack.
Compare instructions perform a comparison on the top two values of the evaluation stack. The two values are
replaced on the evaluation stack with the result of the comparison, which is either true or false. The
comparison should be between related types.
Table 11-7 lists the compare instructions. In the table, assume that t2 is top value on the evaluation stack,
and t1 is the second value. t2 is the last item placed on the evaluation stack and the next to be removed.
Table 11-7: Compare Instructions
Instruction Comparison
ceq t1 equal to t2
This sample code shows unconditional and conditional branching. It also contains an example of a
comparison instruction:
.namespace Donis.CSharpBook {
.class Starter {
box int32
call void [mscorlib] System.Console::WriteLine(
string, object, object)
ret
}
}
}
As a convenience, branch and compare instructions are combinable. The combined instruction compares the
top two values of the evaluation stack and branches on the result. These are called comparative branching
instructions. Instead of requiring two instructions to perform the test, only one is needed.
Ins De
tru scr
cti ipti
on on
Be Bra
q nc
h
on
eq
ual
Bn Bra
e nc
h
on
not
eq
ual
Bg Bra
e nc
h
on
gre
ate
r
tha
n
or
eq
ual
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 11-8:
Comparative
Branching
Ins De
tru scr
cti ipti
on on
Bgt Bra
nc
h
on
gre
ate
r
tha
n
Ble Bra
nc
h
on
les
s
tha
n
or
eq
ual
Blt Bra
nc
h
on
les
s
tha
n
bgt Th
.un e
, un
blt. sig
un, ne
an d
d ver
bn sio
e.u n
n of
the
se
ins
tru
cti
on
s
Here is an example of a for loop in MSIL code. The loop increments the count from zero to five. The current
count is displayed in iterations of the loop:
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
.namespace Donis.CSharpBook {
.class Starter {
The sample code uses the short form of the branch instruction. Be careful when branching in the short form.
As with all short-form instructions, the operand is limited to a single byte. The short form of branch
instructions cannot jump to a label further than 1 byte away. The distance from the branch site to the label
must be describable in a single byte. If not, the application might branch to the wrong location.
Calling Methods
There are a variety of ways to call methods. So far, only the call and callvirt methods have been shown in
sample code. Some instructions or actions, such as newobj, call a method implicitly. The newobj instruction
calls a constructor, whereas the static constructor is called implicitly on the first access to the class or
object.
The returntype is placed on the evaluation stack; assembly is the location of the method. If in the current
assembly, the assembly element can be ignored. The complete signature of the method is represented by
signature.
Ins Description
tru
cti
on
Ins Description
tru
cti
on
call Calls a
i function
indirectly
through a
function
pointer. Place
each function
parameter and
then the
function
pointer on the
evaluation
stack. The
calli syntax is
slightly
different from
other call
instructions. It
does not
include the
target
assembly or
method name
in the syntax.
Use the ldftn
instruction to
place a
function
pointer on the
stack for a
particular
method.
This sample
code includes
both the calli
and ldftn
instructions:
.assembly
extern
mscorlib {}
.assembly
application
{}
.namespace
Donis.CSharpB
ook {
.class
Starter {
.method
static
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ins Description
tru
cti
on
jm Jumps from
p the current
method to the
target method
and transfers
the
arguments.
The caller and
callee must
have matching
signatures.
Code after the
jmp site in the
calling
function is
abandoned.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ins Description
tru
cti
on
Here is
sample code
of the jmp
instruction:
.assembly
extern
mscorlib {}
.assembly
application
{}
.namespace
Donis.CSharpB
ook {
.class
Starter {
.method
static
public void
Main() il
managed {
.entrypoint
ldstr
"Aloha!"
call
void
Donis.CSharpB
ook.Starter::
MethodA(strin
g)
ret
}
.method
static
public void
MethodA(strin
g) il
managed {
ldstr
"Before jump"
call
void
[mscorlib]
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ins Description
tru
cti
on
Arrays
An array is a collection of related types, and there are several ways to declare an array in MSIL. Regardless,
the underlying type of any array is System.Array. Exploiting the array reference, you can call the methods
and access the properties of System.Array.
type [m,n] arrayname Declares an array in which the size is m-columns and n-rows.
The newarr instruction initializes a one-dimensional array. In addition, the instruction pushes a reference to
the array onto the evaluation stack. You might want to use the array for some time. Move the array reference
from the evaluation stack to memory, such as a local variable, to extend to the array. The newarr instruction
requires the number of elements, which should be stored on the evaluation stack.
Elements of an array can be accessed with the ldelem or stelem instructions. The ldelem instruction loads
an element of an array onto the evaluation stack; the stelem instruction stores a value from the evaluation
stack into an element of an array. Both instructions have variations that depend on the type of data being
manipulated. The variations of ldelem are ldelem.i1, ldelem.i2, ldelem.i4, ldelem.i8, ldelem.u1, ldelem.u2,
ldelem.u4, ldelem.u8, ldelem.r4, ldelem.r8, ldelem.i, and ldelem.ref. The permutations of the stelem
instruction are stelem.i1, stelemi2, stelem.i4, stelem.i8, stelem.r4, stelem.r8, stelem.i, and stelem.ref . The
ldelem instruction requires, in order, the array reference and the element index. The stelem instruction
requires the array reference, element index, and the new value.
In the following code, an array of three strings is created. Each element is initialized to a different color. The
array elements are then displayed in a loop.
.namespace Donis.CSharpBook {
.class starter {
stloc names
ldloc names
ldc.i4.0
ldstr "Aqua"
stelem.ref
ldloc names
ldc.i4.1
ldstr "Violet"
stelem.ref
ldloc names
ldc.i4.2
ldstr "Orange"
stelem.ref
ldc.i4.0
stloc count
loop: ldloc names
ldloc count
ldelem.ref
call void [mscorlib] System.Console::WriteLine(string)
ldloc count
ldc.i4.1
add
dup
stloc count
ldc.i4.3
blt loop
ret
}
}
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Arithmetic Instructions
MSIL supports the standard arithmetic instructions. The operands of the arithmetic operation are retrieved
from the evaluation stack and replaced with the result. For example, the add instruction has two operands,
which are removed for the evaluation stack and replaced with the summation.
Table 11-11 lists the common arithmetic instructions. The final column is the number of operands consumed
from the evaluation stack during the instruction.
Table 11-11: Arithmetic Instructions
add Addition 2
sub Subtraction 2
mul Multiplication 2
div Division 2
rem Remainder 2
neg Negate 1
Conversion Operations
It is often necessary to convert between types. The conv instruction casts between primitive types. The
instruction has a single operand, which is the target of the cast. Conversions can cause overflows when the
memory size of the source is larger than the target. For example, this could happen when casting from a
64-bit integer to a 32-bit integer. The high-order bits are trimmed when an overflow occurs, which changes
the normal results. It's important to note that the overflow is ignored, and subtle bugs are possible. The
conv.ovf instruction prevents an overflow from going undetected. When an overflow occurs, the modified
instruction raises an overflow exception.
The following code contains an ostentatious overflow condition. The conversion raises the overflow exception.
This code is not protected from exceptions, and the application is terminated.
.namespace Donis.CSharpBook {
.class Starter {
Exception Handling
Exception handling is implemented in the CLR. Each managed language exposes exception handling in a
language-specific manner. In C#, there are the try, catch, throw, and finally keywords. The language
compiler, such as csc, compiles code for exception handling into language-agnostic MSIL code. For the
details of exception handling in C#, read Chapter 9, "Exception Handling."
There are two strategies for implementing exception handling in MSIL code: as exception clauses or as
scoped exceptions.
Exception clauses are implemented after the core MSIL code of a method in the exceptionhandling section.
An exception clause consists of a protected block, an exception identification, and an exception handler.
Execution must fall into a protected block naturally. Also, you cannot jump into a protected block. To exit
the protected block or handler, use the leave instruction. Of course, when an exception is raised, execution
is transferred away from the protected block. Do not attempt to exit a protected block or exception handler
with a branch statement.
Exception clauses have a handler. There are four types of handlers, and each is assigned a unique code.
Table 11-12 lists the four handlers.
Table 11-12:
Exception
Clauses
Co De
de scr
ipti
on
0 Thi
s
cla
us
e
defi
ne
s
an
ex
ce
pti
on
ha
ndl
er.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 11-12:
Exception
Clauses
Co De
de scr
ipti
on
1 Thi
s
cla
us
e
defi
ne
s
an
ex
ce
pti
on
filte
r.
Th
e
filte
r
det
er
mi
ne
s
wh
en
the
ha
ndl
er
sh
oul
d
be
run
.
Th
e
ex
ce
pti
on
filte
r
en
ds
at
an
en
dfil
ter
ins
tru
cti
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 11-12:
Exception
Clauses
Co De
de scr
ipti
on
2 Thi
s
cla
us
e
defi
ne
s
a
fina
lly
ha
ndl
er.
Th
e
fina
lly
ha
ndl
er
is
call
ed
wh
eth
er
an
ex
ce
pti
on
is
rai
se
d
or
not
.
Cle
an
up
co
de
tha
t
mu
st
be
ex
ec
ute
d
reg
ard
les
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 11-12:
Exception
Clauses
Co De
de scr
ipti
on
4 Thi
s
cla
us
e
defi
ne
s
a
faul
t
ha
ndl
er.
Thi
s
is
si
mil
ar
to
a
fina
lly
ha
ndl
er,
ex
ce
pt
tha
t
thi
s
cla
us
e
is
call
ed
onl
y
wh
en
an
ex
ce
pti
on
is
rai
se
d.
Th
e
faul
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
label 1 and label 2 define the protected code, whereas label 3 and label 4 set the scope of the handler. The
exception type specifies the type of handler as catch, filter, finally, or fault handler.
In the previous example, an overflow exception is thrown and not caught. The following code catches the
overflow exception:
.namespace Donis.CSharpBook {
.class Starter {
Scoped exceptions are similar to exception handling in C#, in which there is a try and catch block. Scoped
exceptions do not employ exception clauses at the end of the method.
.namespace Donis.CSharpBook {
.class Starter {
.entrypoint
.try {
ldc.i8 4000000000
conv.ovf.i4
pop
leave done
}
catch [mscorlib] System.Exception {
callvirt instance string
[mscorlib] System.Exception::get_Message()
call void
[mscorlib] System.Console::WriteLine(string)
leave done
}
done: ret
}
}
}
Miscellaneous Operations
This chapter includes several tables that explain categories of MSIL instructions. However, some MSIL
instructions appearing in the sample code of this chapter have not been included in any table. Other
instructions have not appeared in the body of code or in a table but are valuable nonetheless. Table 11-13
details some of these miscellaneous MSIL instructions.
Table 11-13:
Miscellaneou
s MSIL
Instructions
Ins De
tru scr
cti ipti
on on
an Bit
d wis
e
an
d.
bre Ins
ak ert
a
de
bu
gg
er
bre
ak
poi
nt.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 11-13:
Miscellaneou
s MSIL
Instructions
Ins De
tru scr
cti ipti
on on
ca Ca
stc st
las an
s ins
tan
ce
to
a
diff
ere
nt
typ
e.
du Du
p plic
ate
the
top
slo
t
of
the
eva
lua
tio
n
sta
ck.
do No
p no
per
ati
on
al
ins
tru
cti
on
(a
bla
nk
ins
tru
cti
on)
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 11-13:
Miscellaneou
s MSIL
Instructions
Ins De
tru scr
cti ipti
on on
or Bit
wis
e
or.
po Re
p mo
ve
the
top
ele
me
nt
of
the
eva
lua
tio
n
sta
ck.
ret Pro
hro pa
w gat
e
an
ex
ce
pti
on.
thr Thr
ow ow
an
ap
plic
ati
on
ex
ce
pti
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Process Execution
How and when is MSIL code compiled to native binary? The MSIL code is compiled into binary at run time.
Only methods that are called are compiled into binary. This is part of a larger procedure called process
execution. Most of the information in this section is obtained from the Shared Source CLI, which is an open
source implementation of the Common Language Infrastructure (CLI). The Shared Source CLI is often
referred to as Rotor. For more information on Rotor, visit this page: msdn.microsoft.com/net/sscli. This
section explains method compilation in the examination of how the entry point method is executed.
Process execution begins when a managed application is launched. At that time, the CLR is bootstrapped
into the application. The CLR is bootstrapped as the mscoree.dll library. This library starts the process of
loading the CLR into the memory of the application. _CorExeMain is the starting point in mscoree.dll. Every
managed application includes a reference to mscoree.dll and _CorExeMain. You can confirm this with the
dumpbin.exe tool. Execute the following command line on any managed application for confirmation:
Managed applications have an embedded stub. The stub fools the Windows environment into loading a
managed application. The stub temporarily masks the managed application as a native Windows application.
The stub calls _CorExeMain in moscoree.dll. _CorExeMain then delegates to _CorExeMain2 in mscorwks.
The method table contains an entry for each function in the class. The entries are called method descriptors.
The method descriptor is subdivided into parts. The first part is m_CodeOrIL. Before a method is jitted,
m_CodeOrIL contains the relative virtual address to the MSIL code of the method. The second part is a stub
containing a thunk to the JIT compiler. The first time the method is called, the jitter is invoked through the
stub. The jitter used the IL RVA part to locate and then compile the implementation of the method into
binary. The resulting native binary is cached in memory. In addition, the stub and m_CodeOrIL parts are
updated to reference the virtual address of the native binary. This is an optimization and prevents additional
jitting of the same method. Future calls to the function simply invoke the native binary.
Roundtripping
Roundtripping is disassembling an application, modifying the code through the disassembly, and then
reassembling the application. This provides a mechanism for maintaining or otherwise updating an
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The following C# application simply totals two numbers, where the project name is "Add". The result is
displayed in the console window.
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(string [] args){
if(args.Length<2) {
Console.WriteLine("Not enough parameters.");
Console.WriteLine("Program exiting...");
return;
}
try {
byte value1=byte.Parse(args[0]);
byte value2=byte.Parse(args[1]);
byte total=(byte) (value1+value2);
Console.WriteLine("{0} + {1} = {2}",
value1, value2, total);
}
catch(Exception e) {
Console.WriteLine(e.Message);
}
}
}
}
Let us assume that the above program was purchased from a third-party vendor for quintillion dollars. Of
course, the source code was not included with the application—even for quintillion dollars. Almost
immediately, a bug is discovered in the application. When the program is executed, the total is sometimes
incorrect. Look at the following example:
The result should not be 350, not 94. How is this problem fixed without the source code? Roundtripping is
the answer. This begins by disassembling the application. For convenience, the ILDASM disassembler is
used:
Open newall.il in a text editor. In examining the MSIL code, we can easily find the culprit. The addition
instruction adds two byte variables. The result is cached in another byte variable. This is an unsafe action
that occasionally causes an overflow in the total. Instead of notifying the application of the overflow event, the
value is cycled. This is the reason for the errant value. Add the ovf suffix to the conv instruction to correct the
problem. The exception is now raised when the overflow occurs.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
You can use roundtripping to add features not otherwise available directly in C#. C# supports general
exception handling but not exception filters. When an exception is raised, the exception filter determines
whether the exception handler executes. If the exception filter evaluates to one, the handler runs. If zero, the
handler is skipped.
Here is a partial listing of the disassembled program. It is modified to throw an exception when the additional
overflows the total. An exception filter has also been added to the exception handling. Changes in the code
are highlighted:
IL_0035: ldelem.ref
IL_0036: call uint8 [mscorlib]System.Byte::Parse(string)
IL_003b: stloc.1
IL_003c: ldloc.0
IL_003d: ldloc.1
IL_003e: add
IL_003f: conv.ovf.u1
IL_0040: stloc.2
IL_0041: ldstr "{0} + {1} = {2}"
IL_0046: ldloc.0
IL_0047: box [mscorlib]System.Byte
IL_004c: ldloc.1
IL_004d: box [mscorlib]System.Byte
IL_0052: ldloc.2
IL_0053: box [mscorlib]System.Byte
IL_0058: call void [mscorlib]System.Console::WriteLine(string,
object,
object,
object)
IL_005d: nop
IL_005e: nop
IL_005f: leave.s IL_0072
} // end .try
filter
{
pop
ldc.i4.1
endfilter
}
// catch [mscorlib]System.Exception
{
IL_0061: stloc.3
IL_0062: nop
IL_0063: ldloc.3
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Because the filter returns one, the exception is always handled. The ILASM compiler can reassemble the
application, which completes the roundtrip. Here is the command:
ilasm newadd.il
Run and test the newadd application. The changed program is more robust. Roundtripping has been a
success!
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The Visual Studio 2005 debugger has two primary advantages. First, Visual Studio uses a familiar graphic
user interface. Second, the debugger is integrated with other aspects of the programming environment. You
can develop, test, debug, and return to developing an application within a single product. This is very
convenient.
The debugger in Visual Studio 2005 has a plethora of rapid application development (RAD) tools for
debugging, including a multitude of debug windows, a specialized toolbar, the ability to open dumps, and
much more. The next chapter explores these tools and how to debug an application with Visual Studio 2005.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Visual Studio debugging is extensible. Developers can customize many aspects of the debugger, including
extending many of the types related to debugging. For example, developers can create user-defined
visualizers and trace listeners. Visual Studio debugging is adaptable to specific problems, objectives, and
goals.
In a debugging session, developers control how an application executes. A developer can start, interrupt, or
stop a debugging session. These are the controls explained in Table 12-1.
Table 12-1: Control of a Debugging Session
Control Description
Start From the Debug menu, there are various commands to start a debugging session. If you use
Execution the Start Debugging, Step Into, or Step Over menu command, the application is in running
mode. Alternatively, the Start Without Debugging menu command starts an application
without attaching the Visual Studio debugger.
You can also start a debugging session with the Run To Cursor command. In the source
editor, position the cursor on the target source code. Open the shortcut menu and select
the Run To Cursor menu command. This command starts an application in debug mode, but
breaks when a breakpoint or the cursor is reached. If neither occurs, the application runs
uninterrupted.
Break You can break into an application running in a debug session, which is most commonly
Execution accomplished by setting breakpoints. Developers can forcibly break into an application
using the Break All menu command from the Debug menu. The command interrupts all
applications currently being debugged by the Visual Studio debugger. After breaking into the
application, the user is transferred to the source code being executed when the break
occurred. If source code is not available, the user is transferred to the disassembly. When
execution is interrupted, the application is in break mode.
Stop Developers can choose to end a debugging session. From the Debug menu, select the Stop
Execution Debugging command to end a debugging session.
You can terminate an attached process in the Processes window. In that window, select the
process and choose the Terminate Process menu command from the context menu.
A debug session can be stopped and restarted again. On the Debug menu, the Restart
menu command restarts a debugging session.
Applications in a debugging session are either in a running mode or a break mode. In running mode, an
application is executing. In break mode, an application is interrupted, which is when a developer commonly
diagnoses an application. Certain features are applicable only in break mode, such as the Threads and Call
Stack windows. While debugging, an application can transition between running and break modes. The
following actions can transfer an application from running to break mode:
A breakpoint is hit.
A tracepoint is hit.
The cursor is hit with a Run To Cursor command.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
You can transition from break mode to running mode by starting execution again, which is defined in Table
12-1.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Debugging Overview
Debugging techniques vary between project types. For example, the strategy for debugging a Microsoft
Windows application is different from debugging a class library. This section provides an overview of
debugging strategies for basic project types.
Of the project types, managing a debugging session for a Windows Forms project is probably the easiest.
Initiate a debugging session to begin debugging a Windows Forms application.
Start a debugging session of a Windows Forms project with a start execution action, such as the Start
Debugging menu command. When the execution starts, the Visual Studio debugger is attached to the
application.
Set breakpoints before or during the debugging session. Windows Forms applications are event driven, and
breakpoints can be placed in event handlers. Breakpoints can also be added to functions called from event
handlers. When the breakpoint is hit, the debugging session is transferred to break mode. To run between
breakpoints, use the Start Debugging menu command.
Open the Attach To Process window to attach to a process. (From the Debug menu, choose the Attach To
Process menu command.) Figure 12-1 shows the Attach To Process window. The Debug menu is not
available when no solution is open. In that circumstance, choose the Attach To Process menu command
from the Tools menu.
A list of processes is presented in the Attach To Process window. To attach to a specific process, select a
process from the Available Processes list and then click the Attach button. If not a debug build, an error
message box is displayed. The debugger can be attached to multiple processes. All the attached processes
are dimmed in the process list. However, you can actively debug only a single process, which defaults to the
last process attached. You can change a current process by using the Debug Location toolbar and the
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Process drop-down list, which is shown in Figure 12-2. You can also select the Debug Location toolbar from
the Toolbars submenu on the View menu.
The Attach To Process window has several options. Transport is the transport used to connect to a target
device or remote machine. Default connects to a local machine or remote machine using the Microsoft
Visual Studio Remote Debugging Monitor (msvsmon.exe). The Qualifier window is the name of the target
machine. The Attach To choice selects the target type as managed, native, script, or T-SQL. The default is
managed. The Show Processes For All Users option lists processes from all users of the current logon
session. For Terminal Service clients remotely connected to a machine, Show Processes In All Sessions
displays the processes of the remote machine session. The process list is not static. As new processes
start up and existing processes finish, the list of running processes changes. The Refresh button updates
the list box to reflect any changes in the process list.
After attaching to an application, interrupt the application with a breakpoint. You can then use debugging
techniques and the various debugging windows to debug the application. To set a breakpoint, open the
source code of the application and insert a breakpoint. When execution hits the breakpoint, the application
is interrupted. Alternatively, use the BreakAll menu command on the Debug menu.
Terminate the debugging session to detach the debugger from the running process. For example, the Stop
Debugging menu command on the Debug menu terminates a debugging session and detaches from a
running process. The running process will continue to execute.
You can specify the host application in the debug settings for the project. In the Debug pane of the Project
properties window, select the Start External Program option as the Start action and enter the name of the
hosting application. At the start of the debugging session, the specified host is executed and the DLL is
loaded as needed. You can then debug the class library project.
If the host application and class library projects are in the same solution, setting the external program is not
necessary. Simply set the host application to the startup project. Start a debugging session normally and
debug both the host and class library projects.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
When the host application is already running, attach the debugger to that process in the Attach To Process
window. This process will initiate a debug session in which the class library project can then be debugged.
Debugging Setup
The Visual Studio debugger is fully configurable. Debugging sessions can be tailored to a developer or to the
specific requirements of an application. For convenience, debug settings are saved in a configuration. Debug
and Release are the default configurations and represent the most commonly used settings. The Debug
configuration contains the project options for creating debug builds of an application. The Release
configuration contains the options for release builds. Prior to changing project settings, it is good policy to
confirm the active configuration. This avoids inadvertently changing the wrong configuration.
Configuration Manager
Use the Configuration Manager to view, edit, and create configurations of project settings. To open the
Configuration Manager, choose the Configuration Manager menu command from the Build Menu. Figure 12-3
shows the Configuration Manager. The Configuration Manager lists the current configuration of each project
in the solution.
In the Configuration Manager, the Active Solution Configuration drop-down list selects a common
configuration for every project. You can also create a new configuration from the drop-down list. In addition,
you can rename or remove an existing configuration. The Active Solution Platform drop-down list selects the
current platform.
Each row of the Configuration Manager represents a project. The project list has several columns of
information:
Build This column is an option box, which includes or excludes projects in builds of the solution.
Debug Settings
The Visual Studio environment, solution, and project maintain separate debug settings:
Debug settings of the Visual Studio environment are set in the Options dialog box. (From the
Tools menu, choose the Options menu command.)
Debug settings of a project are set in the Project properties dialog box, which is opened by using
the Project properties menu command on the Project menu.
Debug settings of a solution are set in the Solution properties window. Open this window from the
context menu of the solution in Solution Explorer.
General window The general debugging options are an assortment of miscellaneous options. Each option
can be enabled or disabled. Figure 12-4 shows the General window for setting these options.
Figure 12-4: The General window for setting general debugging options
Op De De
tio scr fau
n ipti lt
on
As Thi En
k s abl
Bef opt ed
ore ion
Del req
eti ue
ng sts
All co
Bre nfir
ak ma
poi tio
nts n
wit
h
the
Del
ete
All
Bre
ak
poi
nts
co
m
ma
nd.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Op De De
tio scr fau
n ipti lt
on
Bre Thi En
ak s abl
All opt ed
Pro ion
ce int
ss err
es upt
Wh s
en all
On pro
e ce
Pro ss
ce es
ss bei
Bre ng
ak de
s bu
gg
ed
wh
en
a
bre
ak
oc
cur
s.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Op De De
tio scr fau
n ipti lt
on
Op De De
tio scr fau
n ipti lt
on
En Thi En
abl s abl
e opt ed
Ad ion
dre allo
ss- ws
Lev cer
el tai
De n
bu op
ggi era
ng tio
ns
at
the
ad
dre
ss
lev
el,
su
ch
as
set
tin
g
bre
ak
poi
nts
on
ins
tru
cti
on
ad
dre
ss
es.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Op De De
tio scr fau
n ipti lt
on
Sh Thi Dis
ow s abl
Dis opt ed
as ion
se req
mb ue
ly sts
If tha
So t
urc the
e dis
Is as
Not se
Av mb
aila ly
ble win
do
w
be
dis
pla
ye
d
wh
en
us
er
so
urc
e
co
de
is
not
ava
ilab
le.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Op De De
tio scr fau
n ipti lt
on
En Thi En
abl s abl
e opt ed
Bre ion
ak let
poi s
nt dev
Filt elo
ers per
s
set
filte
rs
on
bre
ak
poi
nts
,
whi
ch
ca
n
limi
t
the
sc
op
e
of
a
bre
ak
poi
nt
to
a
thr
ea
d,
pro
ce
ss,
or
ma
chi
ne.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Op De De
tio scr fau
n ipti lt
on
En Thi En
abl s abl
e opt ed
Th ion
e aut
Ex om
ce ati
pti call
on y
As dis
sis pla
tan ys
t the
Ex
ce
pti
on
As
sis
tan
t
wh
en
an
ex
ce
pti
on
is
rai
se
d
whi
le
de
bu
ggi
ng.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Op De De
tio scr fau
n ipti lt
on
Un Thi En
win s abl
d opt ed
Th ion
e allo
Cal ws
l the
Sta us
ck er
On to
Un un
ha win
ndl d
ed the
Ex call
ce sta
pti ck
on on
s a
firs
t
ch
an
ce
ex
ce
pti
on,
in
whi
ch
co
de
ch
an
ge
s
ca
n
be
ma
de
incl
udi
ng
rep
airi
ng
the
so
urc
e
of
the
ex
ce
pti
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Op De De
tio scr fau
n ipti lt
on
En Thi En
abl s abl
e opt ed
Jus ion
t allo
My ws
Co the
de de
(M bu
an gg
ag er
ed to
Onl ste
y) p
int
o
us
er
co
de.
Ot
her
co
de,
su
ch
as
sy
ste
m
co
de,
is
ski
pp
ed.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Op De De
tio scr fau
n ipti lt
on
Wa Thi En
rn s abl
If opt ed
No ion
Us dis
er pla
Co ys
de a
On war
La nin
un g
ch wh
en
de
bu
ggi
ng
is
initi
ate
d
if
no
us
er
co
de
is
ava
ilab
le.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Op De De
tio scr fau
n ipti lt
on
En Thi En
abl s abl
e opt ed
Pro ion
per eva
ty lua
Ev tes
alu the
ati res
on ult
An of
d pro
Ot per
her tie
Im s
plic an
it d
Fu oth
nct er
ion im
Cal plic
ls it
fun
cti
on
call
s
in
Wa
tch
an
d
Var
iabl
es
win
do
ws.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Op De De
tio scr fau
n ipti lt
on
Cal Thi En
l s abl
To opt ed
Stri ion
ng( call
) s
On To
Obj Stri
ect ng
s im
In plic
Var itly
iabl on
es obj
Wi ect
nd s
ow dis
s pla
(C# ye
Onl d
y) in
vari
abl
es
win
do
ws.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Op De De
tio scr fau
n ipti lt
on
En Thi Dis
abl s abl
e opt ed
So ion
urc req
e ue
Ser sts
ver tha
Su t
pp Vis
ort ual
Stu
dio
get
so
urc
e
co
urs
e
fro
m
the
so
urc
e
co
de
ser
ver
(sr
csr
v.dl
l).
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Op De De
tio scr fau
n ipti lt
on
Op De De
tio scr fau
n ipti lt
on
Op De De
tio scr fau
n ipti lt
on
Re Thi En
qui s abl
re opt ed
So ion
urc as
e ks
Fil the
es Vis
To ual
Ex Stu
act dio
ly de
Ma bu
tch gg
Th er
e to
Ori veri
gin fy
al tha
Ver t
sio the
n cur
ren
t
so
urc
e
file
ma
tch
es
the
ver
sio
n
buil
t
wit
h
the
ap
plic
ati
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Op De De
tio scr fau
n ipti lt
on
Re Thi Dis
dir s abl
ect opt ed
All ion
Ou red
tpu ire
t cts
Wi me
nd ss
ow ag
Te es
xt fro
To m
Th the
e Ou
Im tpu
me t
dia win
te do
Wi w
nd to
ow the
Im
me
dia
te
win
do
w.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Op De De
tio scr fau
n ipti lt
on
Sh Thi Dis
ow s abl
Ra opt ed
w ion
Str dis
uct abl
ure es
Of de
Obj bu
ect gg
s er
In cu
Var sto
iabl mi
es zat
Wi ion
nd s,
ow su
s ch
as
the
De
bu
gg
erD
ispl
ay
attr
ibu
te.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Op De De
tio scr fau
n ipti lt
on
Su Thi En
ppr s abl
es opt ed
s ion
JIT dis
opt abl
imi es
zat JIT
ion opt
s imi
On zat
Mo ion
dul s,
e whi
Lo ch
ad ca
(M n
an ma
ag ke
ed co
Onl de
y) ea
sie
r
to
de
bu
g.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Op De De
tio scr fau
n ipti lt
on
Wa Thi En
rn s abl
If opt ed
No ion
Sy dis
mb pla
ols ys
On a
La war
un nin
ch g
(Na wh
tive en
Onl no
y) sy
mb
ol
info
rm
ati
on
is
ava
ilab
le.
Edit And Continue window Edit And Continue permits changes to the source code while debugging. The
changes are applied immediately without having to rebuild and restart the application. Edit And Continue is
automatically enabled when breaking and then stepping through an application.
Most changes within a method are supported, such as deleting a line of code. However, Edit And Continue
supports few changes that are made outside of a method. For example, deleting class members is not
allowed. The following changes are not supported in Edit And Continue:
Modifying an active statement.
Surrounding an active statement with a foreach, using, or lock block.
If the active statement is in the current stack frame, you cannot surround the active statement
with a catch or finally block. You also cannot add a nested exception handler beneath six levels.
If the active statement is not in the current stack frame, you cannot surround the active statement
with a try, catch, or finally block. In addition, you cannot add a nested exception handler beneath
six levels. Finally, you cannot change the code in the try block that contains the active statement.
Adding a new member.
Adding or changing a global symbol.
Changing the signature of a member.
Editing an anonymous method or a function containing a anonymous method.
Changing, adding, or deleting an attribute.
Changing or deleting a local variable.
Modifying a method that contains a yield return or yield break statement.
Changing, adding, or deleting a using directive.
Modifying a constructor that contains a local variable that is initialized in an anonymous method.
SQL debugging
Unhandled exceptions, unless the Unwind The Call Stack On Unhandled Exceptions general
debugging option is enabled
Attached application debugging
64-bit application debugging
After a failed rebuild of the current application
In the Edit And Continue window, the Edit And Continue feature can be enabled or disabled. Figure 12-5
shows the Edit And Continue window. The remaining settings pertain to native code.
Just-In-Time Debugging window The JIT debugger is attached to a running application when the program
fails. Register Visual Studio as the JIT debugger in the Just-In-Time Debugging window. (See Figure 12-6.)
Visual Studio can be the JIT debugger for Managed code, Native code, and Scripting. JIT debugging is
discussed in the next chapter, "Advanced Debugging."
Native window The Native window has two debugging options. Enable the Load DLL Exports option to load
the export tables for DLLs. This is beneficial when the debug symbols are not available. For example, you
can set breakpoints on functions exported from the library even though symbols are not available. The
Enable RPC Debugging option enables stepping into Component Object Model (COM) remote procedure
calls. Both options are disabled by default.
Symbols window Visual Studio can be configured to consult a symbol server and download the correct
debugging symbols for the environment. Developers can use the Microsoft public symbol server or a local
symbol server. Identify symbol servers and downstream paths in the Symbols window, which is shown in
Figure 12-7. In the Symbol File Locations list box, symbol server paths can be entered or removed. Enter
downstream servers in the Cache Symbols From Symbol Servers To This Directory Value.
Symbols can be loaded manually in the Modules window, as shown in Figure 12-8. Open the Modules
window from the Debug menu and the Windows submenu. The Modules window lists the modules loaded in
the current application. This window is applicable in break mode, not running mode. Each row of the window
has the name of the module, path to the module, whether the module is optimized, source code availability,
symbol status, and fully qualified path to the symbol file. The Symbol Status column indicates whether the
symbols are loaded or skipped. Symbols are automatically skipped for optimized code. For skipped
symbols, you can manually load the symbols for a specific module using the context menu. The Load
Symbols menu command loads the symbols for the selected module. The Load Symbols and Symbol File
columns are then updated. The Symbol Status column shows whether the symbols are successfully loaded
for the module. The Symbol File column displays the path to the loaded symbol file. If the symbol file cannot
be found for the specified module, you are prompted with the Find Symbols dialog box, as shown in Figure
12-9. From there, you can browse to the relevant symbol file.
From a module's context menu, the Symbol Load Information menu command displays general information
about the symbols for the module. There is also a Symbol Setting menu command, which opens the Symbol
debug window. You can configure symbols in this window.
The Start Action option box indicates the startup action for debugging, which determines the application to
launch when starting a debugging session. Here are the options:
The Start Project option, which is the default, starts the application of the startup project.
The Start External Program option is typically selected to debug class library projects. Specify
the hosting application that loads the DLL of the class library project. You can then debug into the
class library.
The Start Browser With URL option is useful for debugging Web applications. Browse to a URL
that is controlled by the Web application. The Web application is then loaded via the Web server,
which provides an opportunity to debug the Web application.
The Start Options option box contains the following miscellaneous settings:
Enter command-line arguments for the application in the Command Line Arguments text box.
The Working Directory text box sets the working directory of the application. This is the default
path for directory- and file-related tasks.
The Use Remote Machine option names a remote machine, where the target application resides.
The Enable Debuggers option box indicates the kind of code that can be debugged:
The Enable Unmanaged Code Debugging option enables mixed-mode debugging. Developers can
debug between managed and unmanaged code.
The Enable SQL Server Debugging option allows developers to debug CLR assemblies running
within Microsoft SQL Server 2005.
The Enable The Visual Studio Hosting Process option activates the Visual Studio hosting
process, which is a new feature in Visual Studio 2005.
The user interface in Visual Studio 2005 has new debugging features, which include data tips and
visualizers. These enhancements improve developers' debugging experience. Other features, such as
tracepoints, represent improvements of existing features. Tracepoints are a new style of breakpoints.
Data Tips
While you are debugging, a data tip is displayed when the cursor is paused over a variable. Previously, data
tips were helpful when viewing simple types, such as integers and other value types. However, the details of
a complex type were not visible in a data tip. In Visual Studio 2005, data tips are improved and can display
the fields of a complex type. The complex type is at first collapsed in the data tip. Expansion displays the
type members and values in the data tip.
Visualizers
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Visualizers display the underlying HTML, XML, or text associated with a data tip. For example, the improved
data tip can display the composition of a dataset, which is a complex type. A dataset is more than the
composition of all its members. Datasets are an abstraction of an XML data source. The ability to view the
underlying data while debugging is invaluable. Imagine programmatically parsing XML data and receiving
incorrect results. You need to know if the problem is in the XML or the program logic. The XML Visualizer
provides a convenient way to view the source XML data.
Data tips now have an optional menu that displays available visualizers. When a data tip detects values that
are compatible with a visualizer, the visualizer menu is automatically displayed.
In the following code, myxml is a field that contains XML. The XML defines an array of books.
string newbook="<book><title>" +
"Donis's Great Adventure</title>" +
"<price>9.95</price>" +
"</book>";
myxml=myxml.Insert(5, newbook);
A string that contains XML, which describes another book, is added to the myxml string. A data tip can
easily display the modified myxml string. Viewing myxml as XML provides additional clarification and
important information. Move the cursor over the myxml variable, and a data tip appears. The modified string is
displayed. From the data tip menu, select XML Visualizer. The XML Visualizer opens, as shown in Figure
12-12. The Visualizer uncovers a problem, which is that the underlying XML is not well-formed. Why not? The
additional book was inserted at the wrong location in the myxml string, which is not detected from a simple
data tip. However, the problem is caught by the Visualizer.
The following code is modified to insert the book at the correct location in the myxml string. Now the XML
Visualizer correctly displays the XML of the string. (See Figure 12-13.) You have just found and successfully
resolved a bug!
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
myxml=myxml.Insert(7, newbook);
Visualizers are extensible; developers can create user-defined visualizers. The DataSet Visualizer is an
example of a custom visualizer. It will display the data behind a dataset, which is very helpful. The following
code displays rows in a dataset. A breakpoint is placed on the highlighted line.
The cursor is placed over the ds variable, which is an instance of a dataset. A data tip appears, which has
several rows. There is a Visualizer menu on the row of the DataSet type. Choose the DataSet Visualizer
from the menu. The dataset is then displayed in DataSet Visualizer window, with the results shown in Figure
12-14. From here, you can choose the data table to view, sort the data columns, and make basic changes to
the fields.
Breakpoints
Breakpoints are stop signs in code where execution is interrupted. When a breakpoint is hit, the application
enters the break mode. You can then debug the application with a variety of Visual Studio debugger
commands. There are different kinds of breakpoints. A normal breakpoint appears as a red circle to the left of
the target.
The F9 function key is the breakpoint command that sets a simple breakpoint on the selected line of source
code. F9 is a toggle, which sets or clears a breakpoint. In addition, clicking in the leftmost dimmed column
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
sets or clears a breakpoint. The Continue menu command (F5) on the Debug menu resumes an application.
The application runs until another breakpoint is hit, the application is otherwise interrupted, or the debug
session ends. Other commands, such as Run To Cursor, also resume execution.
You can add new breakpoints from the Debug menu and the New Breakpoint submenu. Break At Function
adds a function breakpoint. New Data Breakpoint sets a breakpoint that breaks when a variable has
changed. This option is not available in C# or Microsoft Visual Basic .NET.
Function Breakpoints
Function breakpoints break on the first line of a function and can be set at compile time or run time.
Set a function breakpoint from the New Breakpoint submenu. Choose the Break at Function command.
Figure 12-15 shows the New Breakpoint dialog box. The shortcut key is Ctrl+B. In this example, a
breakpoint is set on the first line of the WClass.MethodA function. As a shortcut, select the name of the
target function first and open the New Breakpoint dialog box. The function name will automatically appear in
the New Breakpoint dialog box. If you're breaking on an ambiguous or overloaded name, the Choose
Breakpoints dialog box opens. For example, in the sample code, MethodA is ambiguous. There are several
instances of MethodA in the application. In Figure 12-16, the New Breakpoint dialog box sets a breakpoint on
MethodA. Figure 12-17 displays the Choose Breakpoints dialog box that subsequently appears, in which the
user can select a specific location to set the breakpoint. To avoid ambiguity, you can enter the class name (
Classname.Methodname) or the method signature.
The Use IntelliSense To Verify The Function Name option of the New Breakpoint dialog box requests the
Choose Breakpoint dialog box whenever a user enters an ambiguous or overloaded function name. With this
option, users are notified of invalid function names. With the option disabled, there is no notification, and the
New Breakpoint dialog box simply closes without setting the breakpoint.
You can also set breakpoints in the Call Stack window, which is available on the Debug menu within the
Windows submenu. A function breakpoint on the call stack breaks upon re-entering that method as the
stack unwinds. For example, MethodA calls MethodB. MethodB then calls MethodC. A breakpoint is then
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
set on MethodA in the Call Stack window. When MethodB returns and the related stack frame is removed,
the application is interrupted on re-entering MethodA. The Call Stack window is available in break mode. Set
a breakpoint for a specific method using the context menu or the F9 breakpoint keyboard shortcut. A
breakpoint set on MethodA in the call stack is exhibited in Figure 12-18.
Breakpoints Window
Developers can manage breakpoints in the Breakpoints window (opened from the Debug menu and the
Windows submenu). Figure 12-19 shows the Breakpoints window.
In the Breakpoints window, breakpoints are shown on separate rows. The first column of each row is the
enabled/disabled options box. If a breakpoint is enabled, the option box is checked. Uncheck the column to
disable the breakpoint. The next column provides the description and location of the breakpoint. The
Condition column shows any conditions set on the breakpoint. The final column shows the hit count. In that
column, Break Always means that the program is interrupted every time the breakpoint is hit.
The context menu has several valuable options that affect the selected breakpoint. The first menu command
is Delete, which removes the breakpoint. The Go To Source Code menu command redirects to the
breakpoint in the source code. The Go To Disassembly menu command redirects to the disassembly at the
breakpoint. For this command, the application must be in break mode. The remaining commands of the
context menu customize the selected breakpoint and are explained in the following sections.
Location This command changes the location of a breakpoint. You are presented with the Address
Breakpoint or File Breakpoint dialog box (shown in Figures 12-20 and 12-21). The Address Breakpoint dialog
box is displayed for memory breakpoints, such as a breakpoint set on the call stack. The File breakpoint
dialog box is displayed for line breakpoints in source code.
Condition This command sets additional criteria for honoring a breakpoint. The condition can be a Boolean
expression. If true, the breakpoint is honored. Otherwise, the breakpoint is ignored. The condition can also
be based on changes to a value. If the value is changed, the breakpoint is honored.
Look at the following sample code. The breakpoint is set on the dimmed line. We want to honor the
breakpoint only when ivalue contains an even value.
The Breakpoint Condition dialog box, shown in Figure 12-22, sets the condition to break on even values.
Hit Count This command honors a breakpoint based on the hit count. Table 12-3 lists the hit count options.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-3:
Hit Count
Options
Op De
tio scr
n ipti
on
Bre Thi
ak s
Al opt
wa ion
ys alw
ay
s
ho
nor
s
the
bre
ak
poi
nt.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-3:
Hit Count
Options
Op De
tio scr
n ipti
on
Bre Thi
ak s
Wh opt
en ion
Th ho
e nor
Hit s
Co the
unt bre
Is ak
Eq poi
ual nt
To wh
en
tha
t
ins
tan
ce
of
the
bre
ak
poi
nt
oc
cur
s.
For
ex
am
ple
,
int
err
upt
on
the
fou
rth
oc
ca
sio
n
of
a
sp
ecif
ic
bre
ak
poi
nt.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-3:
Hit Count
Options
Op De
tio scr
n ipti
on
Bre Thi
ak s
Wh opt
en ion
Th ho
e nor
Hit s
Co the
unt bre
Is ak
A poi
Mu nt
ltipl as
e a
Of mu
ltipl
e
of
a
val
ue,
for
ex
am
ple
,
eve
ry
thir
d
ins
tan
ce
of
the
bre
ak
poi
nt.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-3:
Hit Count
Options
Op De
tio scr
n ipti
on
Bre Thi
ak s
Wh opt
en ion
Th ho
e nor
Hit s
Co the
unt bre
Is ak
Gr poi
eat nt
er sta
Th rtin
an g
Or at
Eq a
ual cer
To tai
n
ins
tan
ce;
for
ex
am
ple
,
ho
nor
the
bre
ak
poi
nt
at
the
thir
d
an
d
eve
ry
su
cc
es
siv
e
ins
tan
ce.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The Breakpoint Hit Count dialog box, shown in Figure 12-23, honors every third instance of the selected
breakpoint.
Filter This command sets the affinity of a breakpoint to a machine, thread, or process. The breakpoint
interrupts only in the context of the stated filter. Table 12-4 lists the available contexts.
Table 12-4:
Filter
Contexts
Co De
nte scr
xt ipti
on
Ma Thi
chi s
ne co
Na nte
me xt
is
the
na
me
of
the
ma
chi
ne.
Bre
ak
poi
nt
is
ho
nor
ed
if
the
pro
ce
ss
is
run
nin
g
on
tha
t
ma
chi
ne.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-4:
Filter
Contexts
Co De
nte scr
xt ipti
on
Pro Thi
ce s
ssI co
d nte
xt
is
the
pro
ce
ss
ide
ntifi
er.
Bre
ak
poi
nt
is
ho
nor
ed
if
co
de
is
ex
ec
uti
ng
in
the
sp
ecif
ied
pro
ce
ss.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-4:
Filter
Contexts
Co De
nte scr
xt ipti
on
Pro Thi
ce s
ss co
Na nte
me xt
is
the
pro
ce
ss
na
me
.
Fu
nct
ion
ally
eq
uiv
ale
nt
to
the
Pro
ce
ssI
d
co
nte
xt.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-4:
Filter
Contexts
Co De
nte scr
xt ipti
on
Thr Thi
ea s
dId co
nte
xt
is
the
thr
ea
d
ide
ntifi
er.
Bre
ak
poi
nt
is
ho
nor
ed
if
co
de
is
ex
ec
uti
ng
on
a
sp
ecif
ic
thr
ea
d.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-4:
Filter
Contexts
Co De
nte scr
xt ipti
on
Thr Thi
ea s
dN co
am nte
e xt
is
the
thr
ea
d
na
me
.
Fu
nct
ion
ally
eq
uiv
ale
nt
to
the
Thr
ea
dId
co
nte
xt.
The following code creates two threads that asynchronously execute the same method (MethodA). The
threads are named FirstThread and SecondThread. A breakpoint is set on the highlighted line in MethodA.
As a standard breakpoint, both threads are interrupted—probably alternating between the threads.
To break on the first thread alone, open the Breakpoint Filter dialog box, shown in Figure 12-24. Specify
FirstThread as the thread name. Future instances of the breakpoint interrupt the first thread, but not the
second thread. You can confirm the results in the Threads window.
When Hit This command creates a tracepoint (discussed in the next section).
The Breakpoints window toolbar, shown in Figure 12-25, offers several shortcuts. The controls on the toolbar
are New, Delete, Delete All Breakpoints, Disable All Breakpoints, Go To Source Code, Go To Disassembly,
and Columns. The New drop-down list inserts a new function or data breakpoint. Data breakpoints are not
available in managed code. The Columns button customizes the content of the Breakpoints window, in which
developers can add or remove columns of information.
Tracepoints
Tracepoints assign an operation to a breakpoint. You can assign source code or a macro to a breakpoint.
When the breakpoint is hit, the source code or macro is executed. Before tracepoints, this process required
two steps: setting a breakpoint and inserting temporary code into source file. When the breakpoint was no
longer needed, the developer had to remember to remove the temporary code.
There are two methods for setting a tracepoint. You can open the context menu on the source line in which
the tracepoint should be inserted. Select the Breakpoint submenu and the Insert Tracepoint menu command.
Alternatively, you can open the context menu on a source line that already contains a breakpoint. From the
Breakpoint submenu, choose the When Hit menu command. Either approach opens the When Breakpoint Is
Hit dialog box, in which a tracepoint is defined. (See Figure 12-26.)
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
In the Print A Message edit box, enter a display string for the output window. Expressions are entered within
curly braces: {expression}. Special keywords can be used, such as $ADDRESS, $TID, and $FUNCTION. In
the Run A Macro edit box, enter the name of a macro. This macro is run when the breakpoint is hit. The
Continue Execution option sets a soft breakpoint. When the breakpoint is hit, soft breakpoints do not
interrupt the application; they execute the designated code or macro. Soft breakpoints appear as diamonds
in the source code editor.
The statement in the following When Breakpoint Is Hit dialog box (shown in Figure 12-27) adds two local
variables and displays the results in the output window.
Figure 12-27: Print a message selected in the When Breakpoint Is Hit dialog box
From the preceding tracepoint, the following code is displayed in the Output window:
Table 12-5 lists the special keywords that can be used in the Print a message statement.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-5:
Tracepoint
Keywords
Ke De
yw scr
or ipti
d on
$A Thi
DD s
RE ke
SS yw
ord
ret
urn
s
the
ad
dre
ss
of
the
cur
ren
t
ins
tru
cti
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-5:
Tracepoint
Keywords
Ke De
yw scr
or ipti
d on
$C Thi
AL s
LE ke
R yw
ord
ret
urn
s
the
na
me
of
the
pre
vio
us
fun
cti
on
on
the
call
sta
ck,
whi
ch
is
the
call
ing
fun
cti
on.
$C Thi
AL s
LS ke
TA yw
CK ord
ret
urn
s
the
call
sta
ck.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-5:
Tracepoint
Keywords
Ke De
yw scr
or ipti
d on
$F Thi
UN s
CT ke
IO yw
N ord
ret
urn
s
the
na
me
of
the
cur
ren
t
fun
cti
on.
$PI Thi
D s
ke
yw
ord
ret
urn
s
the
pro
ce
ss
ide
ntifi
er
of
the
cur
ren
t
pro
ce
ss.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-5:
Tracepoint
Keywords
Ke De
yw scr
or ipti
d on
$P Thi
NA s
ME ke
yw
ord
ret
urn
s
the
na
me
of
the
cur
ren
t
pro
ce
ss.
$TI Thi
D s
ke
yw
ord
ret
urn
s
the
thr
ea
d
ide
ntifi
er
of
the
cur
ren
t
thr
ea
d.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-5:
Tracepoint
Keywords
Ke De
yw scr
or ipti
d on
$T Thi
NA s
ME ke
yw
ord
ret
urn
s
the
na
me
of
the
cur
ren
t
thr
ea
d.
Breakpoint Symbols
Breakpoints are annotated with icons in both the source code and the Breakpoints window. Each category of
breakpoint has a different symbol, as described in Table 12-6.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-6:
Breakpoint
Symbols
Sy De
mb scr
ol ipti
on
Fill Thi
ed s
cir ico
cle n
sig
nifi
es
a
nor
ma
l
bre
ak
poi
nt,
su
ch
as
a
fun
cti
on
or
loc
ati
on
bre
ak
poi
nt.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-6:
Breakpoint
Symbols
Sy De
mb scr
ol ipti
on
Dia Thi
mo s
nd ico
n
sig
nifi
es
a
tra
ce
poi
nt
tha
t
ha
s
the
Co
nti
nu
e
exe
cut
ion
opt
ion
en
abl
ed.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-6:
Breakpoint
Symbols
Sy De
mb scr
ol ipti
on
Fill Thi
ed s
cir ico
cle n
wit sig
h nifi
a es
plu a
s+ filte
r
bre
ak
poi
nt.
Filt
er
bre
ak
poi
nts
incl
ud
e
the
Co
ndi
tio
n,
Hit
Co
unt
,
an
d
Filt
er
bre
ak
poi
nts
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-6:
Breakpoint
Symbols
Sy De
mb scr
ol ipti
on
Hol Thi
low s
ed ico
cir n
cle sig
nifi
es
a
dis
abl
ed
bre
ak
poi
nt.
Code Stepping
Stepping through source code is the most common action in a debugging session. Step commands execute
an application in source line increments. With each step, execution continues incrementally. Between
steps, expressions can be evaluated, variables updated, functions called, and scope changed. Debug
windows are updated at each step to reflect any changes that occurred during the partial execution. This
sometimes requires refreshing. Extensive monitoring and expressions in debug windows can precipitously
hurt performance.
Step Commands
There are several step commands, described as follows:
Step Into This command steps to the next source line. If that is a function call, the debugger
steps into the function. You can then step through that function. For nested function calls, the
Step Into command steps into the innermost functions first.
Step Over This command steps to the next source line. However, it will not step into a function
call. The function call is treated as a single line of source code.
Step Out This command executes the remainder of the current function. Execution is then
interrupted at the first source line after the call site.
Set The Next Statement This command lets developers change the next statement, which is
useful for skipping one or more lines of source code. In the source editor, the current line is
highlighted with a yellow arrow. This is the next statement to execute. When the cursor hovers
over the yellow arrow, it changes into an arrow itself. You can then drag the current source line up
or down. The next step selected must be within the scope of the current source line. For example,
dragging the next step to another function is illegal. This command is not available during JIT
debugging. It is also not available when debugging a StackOverflowException or
ThreadAbortException exception.
swap the values of the local variables. The parameters are passed by reference. After the method call, locala
is 11 and localb is 6. This is the code:
These are the steps of the walkthrough that uses the previous code:
1. Set a breakpoint on the source line where locala is incremented. When the breakpoint is hit, the
current line is marked with a yellow arrow. The breakpoint and the current source line are initially
at the same location, as shown in Figure 12-28.
2. Drag the yellow highlight for the current line down to the first MessageBox.Show statement, which
jumps past the statements that increment the local variables and the SwitchValues method call.
Therefore, the values are neither incremented nor swapped. Figure 12-29 shows the repositioned
current line.
3. Continue execution, and the values for locala and localb are displayed. The locala variable is 5,
whereas localb is 10. Otherwise, the values would have been 11 and 6, respectively.
Debug Toolbar
The Debug toolbar contains shortcuts to several debugging commands, including the step commands. (See
Figure 12-30.) Some of the buttons and commands are enabled only in break mode.
The first set of buttons on the toolbar includes the Start Debugging, Break All, Stop Debugging, and Restart
buttons. The next set of buttons includes the Show Next Statement, Step Into, Step Over, and Step Out
buttons. The Show Next Statement button redirects to the next statement in the code editor. This statement
is highlighted with a yellow arrow. Next is the Hexadecimal button, which changes the values in the debug
windows from decimal to hexadecimal. The final button is a drop-down list that displays a menu of debug
commands.
Debug Windows
To assist in debugging, Visual Studio 2005 offers a myriad of debug windows, described in the following
sections.
Breakpoints Window
Manage breakpoints in the Breakpoints window. In this window, you can insert, delete, and disable
breakpoints. (The Breakpoints window was shown and discussed in detail earlier in this chapter.)
Output Window
This window contains messages from various sources in Visual Studio. The Output window is also available
from the View menu. The Output window is displayed in Figure 12-31.
The Output window has a toolbar, which has several buttons. Output From is the first button on the toolbar. It
filters the message sources, such a Build and Debug messages. The next three buttons locate build errors
in the code editor: Find Message in Code, Go to Previous Message, and Go to Next Message. The next
button, Clear All, erases the content of the Output window. The Toggle Word Wrap button toggles word wrap
in the Output window.
12-32. In the sample Script Explorer window, the scripts of the default.aspx page are listed. The anonymous
scripts are usually scripts associated with for server-side controls, such as validation controls.
To use the Script Explorer window, you must enable Script debugging in Microsoft Internet Explorer. The
Disable Script Debugging (Internet Explorer) option is enabled by default in Internet Explorer. This option
must be disabled to allow script debugging. Open the Tools menu and select the Internet Options menu
command. On the Advanced tab, disable the Disable Script Debugging (Internet Explorer) option.
Watch Window
You can watch variables and expressions in a Watch window. The Watch, Locals, Auto, and QuickWatch
windows are considered variables windows—they have the same general user interface and functionality. The
variables windows are disabled in running mode; you must be in break mode to use them. A Watch window,
like other variables windows, has three columns. The Name column has the variable name or expression to
evaluate. The Value column displays the variable value or result of the expression. The Type column is the
data type of the variable or expression. There are four Watch windows for grouping related values.
Variable values can be modified directly in a Watch window. Changed values are highlighted in red. This is
an exceptional way to test applications with values that stress the program. You can also test how the
application handles errant conditions.
You can add variables and expressions in a Watch window directly. The QuickWatch dialog box is an
expedited means of inspecting a value or expression and optionally adding that item to a permanent Watch
window. The QuickWatch dialog box has an Add Watch button. If desired, you can enter new expressions in
the Expression text box. The QuickWatch window is available on the Debug menu. Shortcuts for
QuickWatch are Ctrl+Alt+Q or Shift+F9. Before opening the QuickWatch window, select the target variable
in the source editor.
Expressions Debugger expressions are expressions entered in the Watch, Quickwatch, Breakpoints, or
Immediate window. These expressions are evaluated by the Managed Expression Evaluator of the Visual
Studio debugger. Use debugger expressions to calculate values or to call methods. IntelliSense is available
when entering debugger expressions in a debug window or visualizer. The added flexibility of doing more than
examining static data is invaluable.
Debugger expressions are evaluated similarly to regular expressions. There are some unique idiosyncrasies
of the Managed Expression Evaluation.
It ignores access modifiers.
All members, regardless of accessibility, are available.
Expressions are evaluated in an implicitly unsecure context.
Checked blocks are ignored and evaluated as unchecked.
Anonymous methods are not supported in debugger expressions.
Expressions can contain constants, function calls, and identifiers within scope, such as locals, parameters,
and fields. Most operators, such as +, -, ==, !=, ++, --, and / are available. You can even use the typeof and
sizeof operators. The this reference is supported. In addition, simple casts are allowed.
Expressions are evaluated between every step command. Refresh the expression to view any updates.
Beware of side effects, which can alter the state or execution of the programming unknowingly. For example,
calling a function in an expression that changes the state or outcome of the application may cause adverse
side effects.
Expression walkthrough The following steps list a walkthrough of expressions. A breakpoint is set on the
Form_Load event in the Expressions application. Run the application until the breakpoint is hit.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
1. Open a Watch window from the Debug menu. The Form_Load method has a sender and an e
parameter. (You can view their values in the Watch window.) This Watch window, including the
two parameters, is shown in Figure 12-33.
2. The Sample application has a ZClass, which is relatively simple:
3.
4. class ZClass
5. {
6. private int fielda = 5;
7. private int fieldb = 10;
8.
9. public int MethodA()
10. {
11. int locala = 5;
12. locala = 12;
13. return fielda + fieldb;
14. }
15. }
16. A breakpoint is set on the highlighted line in MethodA. When the breakpoint is hit, add fielda,
fieldb, locala, and localb to the Watch window. Click the Test button to hit the breakpoint. The
localb variable is not declared in the function. For that reason, it is displayed with an exclamation
symbol. Enter the ++fielda expression. Figure 12-34 shows the new Watch window.
17. Step two individual statements. (Press F10 twice.) Click the Refresh button to update the value of
the ++fielda expression. (The Refresh button is the green swirling button to the right of the field.)
18. The DumpType class has a static method, which dumps the methods and parameters of a type.
The DumpType.GetReflection method returns a string that contains the methods of a type. This
function is not called elsewhere in the program. It is created purely for debugging reasons. Enter
the DumpType.GetReflection in the expression. Pass the type of the current object as the sole
parameter. (See Figure 12-35.) You view the returning value of GetReflection with the Text
Visualizer. The results are shown in Figure 12-36.
Figure 12-34: Watch window with locala, localb, fielda, and fieldb values
Autos Window
The Autos window lists the values from the current and preceding line of code, which includes this reference.
The items of the Autos window are populated by the Visual Studio debugger, not the developer. You can
change the values in the window, which are then highlighted in red. Figure 12-37 shows the Autos window.
Locals Window
The Locals window lists the local variables that are currently in scope. Otherwise, the functionality is
identical to the Autos window.
Immediate Window
The Immediate window is the command-line version of the Visual Studio debugger. You can enter print
values, evaluate expressions, perform menu commands, execute statements, and more. This can be done
without having to change the application. Command windows are either in immediate or command mode.
Command mode is preferred for executing one or more Visual Studio commands. These are menu and other
commands.
For evaluating expressions, inspecting values, and otherwise debugging an application, immediate mode is
preferred. The default is the command mode. When in immediate mode, switch to command mode
temporarily by prefixing commands with a greater than (>) symbol. From the Immediate window, the cmd
command switches to command mode in the Command window. Switch back to immediate mode and the
Immediate window with the immed command.
You can navigate the Immediate window with arrows. Table 12-7 shows the various ways to navigate the
window.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-7:
Navigating
the
Immediate
Window
Ke De
yst scr
rok ipti
e on
Up Pre
arr vio
ow us
on co
co m
m ma
ma nd.
nd
line
Do Ne
wn xt
arr co
ow m
on ma
co nd.
m
ma
nd
line
Ctrl Up
+U win
p do
Arr w.
ow
or
Up
arr
ow
in
Co
m
ma
nd
win
do
w
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-7:
Navigating
the
Immediate
Window
Ke De
yst scr
rok ipti
e on
Ctrl Do
+D wn
ow win
n do
Arr w.
ow
or
Do
wn
arr
ow
in
Co
m
ma
nd
win
do
w
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-7:
Navigating
the
Immediate
Window
Ke De
yst scr
rok ipti
e on
Es Tra
c nsf
er
foc
us
to
co
de
edi
tor.
If
not
at
the
las
t
co
m
ma
nd
in
the
Im
me
dia
te
win
do
w,
Es
c
is
req
uir
ed.
There are limitations to the tasks that can be performed in the Immediate window. For example, you cannot
declare new variables, define a label, or create an instance of an object. Special commands such as the K
command are not available in managed-only code. Therefore, special commands for the Immediate window
are not explored here. In addition, the Immediate window does not accept goto, return, and loop statements,
or any statement requiring transfer of control.
Commands can have arguments and options. For example, the FindinFiles command has both. IntelliSense
will present the options, which are preceded with a forward slash. You must type a forward slash for
IntelliSense to display the available options. The following code is an example of the Edit.FindinFiles
command. If the command is represented by a window in the user interface, consult that window for an
understanding of options and arguments. In the window, edit text boxes are usually interpreted as
arguments, and everything else becomes an option. Figure 12-38 shows the Find In Files dialog box, which
has one text box and several options. The text box is the single argument of the FindinFiles command. You
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
can use as many options as needed. The defaults are defined in the window. In the example, the FindinFiles
command is used with two options: The first option requests a case-sensitive search, and the second option
directs the command to search all files in the root directory. This command has a single argument, which is
the search text.
Aliases are short forms of debugging commands. They are a convenience and never required. Table 12-8
lists some useful aliases.
Table 12-8:
Command
Aliases
Ali Co
as m
ma
nd
? De
bu
g.P
rint
bl De
bu
g.B
rea
kp
oin
ts
call De
sta bu
ck g.C
all
Sta
ck
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-8:
Command
Aliases
Ali Co
as m
ma
nd
cls De
bu
g.C
lea
rAll
cm Vie
d w.
Co
m
ma
nd
Wi
nd
ow
du Du
mp
Uni
co
de
g De
bu
g.S
tart
im To
me ols
d .Im
me
dia
te
Mo
de
K De
bu
g.L
ist
Cal
lSt
ac
k
loc De
als bu
g.L
oc
als
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-8:
Command
Aliases
Ali Co
as m
ma
nd
me De
mo bu
ry1 g.
Me
mo
ry1
me De
mo bu
ry2 g.
Me
mo
ry2
P De
bu
g.S
tep
Ov
er
pr De
bu
g.S
tep
Ou
t
pri Fil
nt e.P
rint
q De
bu
g.S
top
De
bu
ggi
ng
rtc De
bu
g.R
un
To
Cur
sor
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-8:
Command
Aliases
Ali Co
as m
ma
nd
sav Fil
eall e.S
ave
All
T Ste
p.I
nto
thr De
ea bu
ds g.T
hre
ad
s
~ De
bu
g.L
ist
Thr
ea
ds
~* De
k bu
g.L
ist
Cal
lSt
ac
k
/All
Thr
ea
ds
Developers might want to create user-defined aliases for frequently performed tasks. The alias command
defines a new alias. The following command defines two new aliases. The pripro alias replaces all instances
of the private with the protected keyword in the current source file. The propri alias performs the reverse
operation.
number, and byte offset. Parameter information includes parameter type, parameter name, and parameter
values. Use the context menu to customize the display. Except for the function name, everything is optional.
Figure 12-39 shows the Call Stack window, which is available only in break mode.
The call stack does not include external code. In some circumstances, information on external functions in
the call stack can be helpful. To view external code, disable the Enable Just My Code (Managed Only)
option in the Options dialog box of the Tools menu under the Debugging settings in the General node. Figure
12-40 shows the Call Stack window with this option disabled. For clarity, extraneous information is removed
from the Name column. You can now trace the call stack into System functions to obtain what may be
invaluable information.
Figure 12-40: Call Stack window with Enable Just My Code disabled
Each row of the call stack represents a function and a stack frame. Some variables windows are based on
the current stack frame. For example, the Autos and Locals windows are from the current stack frame.
However, you might want to view similar information about the other stack frame. To switch stack frames,
position the cursor on the target frame. Open the context menu in the call stack and choose the Switch To
Frame menu command. A curved green arrow appears next to the updated current stack frame. Variable
windows are updated according to the change.
Another option on the Call Stack context menu is the Include Calls From/To Other Threads menu command.
This command follows the call stack through thread calls. For example, this is helpful when tracing the call
stack from an XML Web Service into an ASP.NET application.
Function breakpoints and tracepoints are settable in the Call Stack window. You can use shortcut keys,
such as F9, or you can use the context menu.
Threads Window
The Threads window presents the active threads of the current process. This window is available only in
break mode. A thread is a single path of execution in a process. Threads own assets, such as local
variables, thread context, and thread local storage.
The Thread application is provided to create threads. You can assign a name and priority to each new
thread. Each thread executes the sample MethodA function. When you run the Thread application in a
debugging session, thread T1, T2, and T3 are created of varying priorities. Then the BreakAll command is
issued from the Debug menu. Figure 12-41 shows the Threads window for the application at that moment.
The columns of the Threads window are Thread Identifier, Thread Name, Method Location, Thread Priority,
and Suspend Count. From the context menu, suspend a thread with the Freeze menu command.
Suspended threads are assigned a double-bar icon. Choose the Thaw menu command to resume the thread.
Forcibly suspending a thread can affect the normal operation of an application. The Switch To Thread menu
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
command of the context menu changes the current thread. You can also double-click the target thread to
switch the current thread. Some windows are based on the current thread, such as the Call Stack. These
windows are updated when the current thread is changed.
Modules Window
The Modules window lists the modules, executables, and DLLs, which are loaded into the application. There
is considerable information presented on each module, as described in Table 12-9.
Table 12-9:
Modules
Window
Columns
Co De
lu scr
mn ipti
on
Na Thi
me s
col
um
n
is
the
na
me
of
the
mo
dul
e.
Pat Thi
h s
col
um
n
pre
se
nts
the
full
y
qu
alifi
ed
pat
h
to
the
mo
dul
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-9:
Modules
Window
Columns
Co De
lu scr
mn ipti
on
Op Thi
tim s
ize col
d um
n
indi
cat
es
wh
eth
er
the
mo
dul
e
is
opt
imi
ze
d
dur
ing
JIT
co
mp
ilati
on.
Th
e
cur
ren
t
mo
dul
e
bei
ng
de
bu
gg
ed
is
like
ly
not
opt
imi
ze
d.
De
bu
g
ver
sio
ns
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-9:
Modules
Window
Columns
Co De
lu scr
mn ipti
on
Us Thi
er s
Co col
de um
n
indi
cat
es
wh
eth
er
us
er
co
de
is
ava
ilab
le
for
a
par
tic
ula
r
mo
dul
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-9:
Modules
Window
Columns
Co De
lu scr
mn ipti
on
Sy Thi
mb s
ol col
Sta um
tus n
indi
cat
es
wh
eth
er
sy
mb
ols
are
loa
de
d
for
thi
s
mo
dul
e.
It
als
o
indi
cat
es
if
ex
por
ts
are
onl
y
loa
de
d.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-9:
Modules
Window
Columns
Co De
lu scr
mn ipti
on
Sy Wh
mb en
ol loa
Fil de
e d,
thi
s
col
um
n
dis
pla
ys
the
full
y
qu
alifi
ed
pat
h
an
d
na
me
of
the
sy
mb
ol
file.
Or Thi
der s
col
um
n
list
s
the
loa
d
ord
er
of
mo
dul
es.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-9:
Modules
Window
Columns
Co De
lu scr
mn ipti
on
Ver Thi
sio s
n col
um
n
pro
vid
es
the
ver
sio
n
nu
mb
er
of
the
mo
dul
e.
Ti Thi
me s
sta col
mp um
n
is
the
tim
est
am
p
wh
en
the
mo
dul
e
wa
s
cre
ate
d.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-9:
Modules
Window
Columns
Co De
lu scr
mn ipti
on
Ad Thi
dre s
ss col
um
n
is
the
sta
rt
an
d
en
d
loa
d
ad
dre
ss
of
the
mo
dul
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-9:
Modules
Window
Columns
Co De
lu scr
mn ipti
on
Pro Thi
ce s
ss col
um
n
is
the
pro
ce
ss
ide
ntifi
er
of
the
ap
plic
ati
on
tha
t
ho
sts
the
mo
dul
e.
From the context menu, you can request additional information on the symbol file. You can also visit Symbol
settings in Project options, where symbols are managed.
Processes Window
The Processes window enumerates the processes being debugged by the Visual Studio debugger, including
attached processes. Table 12-10 describes each column of the window.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-10:
Processes
Window
Columns
Co De
lu scr
mn ipti
on
Na Thi
me s
col
um
n
is
the
na
me
of
the
pro
ce
ss.
ID Thi
s
col
um
n
is
the
pro
ce
ss
ide
ntifi
er
of
the
pro
ce
ss.
Pat Thi
h s
col
um
n
is
the
pat
h
to
the
ex
ec
uta
ble
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-10:
Processes
Window
Columns
Co De
lu scr
mn ipti
on
Titl Thi
e s
col
um
n
co
nta
ins
the
titl
e
of
ap
plic
ati
on.
For
a
Wi
nd
ow
s
For
ms
ap
plic
ati
on,
thi
s
is
the
co
nte
nt
of
the
titl
e
bar
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-10:
Processes
Window
Columns
Co De
lu scr
mn ipti
on
Sta Thi
te s
col
um
n
sh
ow
s
the
sta
te
of
the
ap
plic
ati
on,
su
ch
as
the
bre
ak
or
run
nin
g
sta
te.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-10:
Processes
Window
Columns
Co De
lu scr
mn ipti
on
De Thi
bu s
ggi col
ng um
n
indi
cat
es
the
typ
e
of
de
bu
ggi
ng,
su
ch
as
ma
na
ge
d
or
nat
ive
de
bu
ggi
ng.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-10:
Processes
Window
Columns
Co De
lu scr
mn ipti
on
Tra Thi
ns s
por col
t um
n
is
the
tra
ns
por
t
to
the
ap
plic
ati
on.
Th
e
def
aul
t
is
no
aut
he
nti
cat
ion
.
Tra Thi
ns s
por col
t um
Qu n
alifi is
er the
ma
chi
ne
na
me
.
Memory Window
The Memory window dumps the memory of the current process. (See Figure 12-42.) Other windows format
memory for specific purposes. For example, the Locals window displays the memory of local variables alone.
The Memory window provides an unfiltered and raw presentation of process memory. Four memory windows
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
provide the opportunity to maintain different views of the process. Memory windows are available in break
mode and only if the Enable Address-Level Debugging Option is enabled. Set this option within Debug
settings in the General node. Open from the Tools menu and the Options command.
Enter a memory address in the Address edit box. You can enter a memory address directly or by using a
drag-and-drop operation. The Memory window displays rows of data at memory addresses. Column 1 of the
Memory window is the starting address of a row of data. The final column is the text translation. The memory
dump is in the intervening columns.
The Memory window can be formatted using the context menu. You can change the size of the data
columns to 1-, 2-, 4-, or 8-byte columns. The display can also be changed to 32- or 64-bit data presentation.
The text translation can be formatted as ANSI, Unicode, or hidden.
Disassembly Window
The Disassembly window shows the native assembly of the application, which is the native code generated
by JIT compilation. By default, if available, the source code is displayed also. Each assembly instruction is
displayed in several columns: the instruction address, mnemonic, and parameters. (See Figure 12-43.) Use
the context menu to change the format of the Disassembly window.
Registers Window
The Registers window displays the state of the registers. Assembly-level programming typically relies heavily
on registers. For this reason, the Disassembly and Registers windows are often used together. The
Registers window is shown in Figure 12-44. Use the context menu to change the format of the display.
Table 12-11:
Registers
Re De
gis scr
ter ipti
on
EA Thi
X s
reg
ist
er
is
a
ge
ner
al-
pur
po
se
reg
ist
er.
It
is
co
m
mo
nly
us
ed
as
the
de
sti
nat
ion
of
a
ma
th
op
era
tio
n.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-11:
Registers
Re De
gis scr
ter ipti
on
EB Thi
X s
reg
ist
er
is
a
ge
ner
al-
pur
po
se
reg
ist
er.
EC Thi
X s
reg
ist
er
is
a
ge
ner
al-
pur
po
se
reg
ist
er
an
d
is
co
m
mo
nly
us
ed
for
co
unt
ing
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-11:
Registers
Re De
gis scr
ter ipti
on
ED Thi
X s
reg
ist
er
is
a
ge
ner
al-
pur
po
se
reg
ist
er.
EI Thi
P s
reg
ist
er
co
nta
ins
the
ne
xt
ins
tru
cti
on
poi
nte
r.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-11:
Registers
Re De
gis scr
ter ipti
on
ES Thi
P s
reg
ist
er
co
nta
ins
the
poi
nte
r
to
the
top
of
the
sta
ck.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-11:
Registers
Re De
gis scr
ter ipti
on
ES Thi
I s
reg
ist
er
is
the
so
urc
e
ind
ex.
Th
e
ES
I
an
d
ED
I
reg
ist
ers
are
fre
qu
ent
ly
us
ed
in
stri
ng
op
era
tio
ns.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-11:
Registers
Re De
gis scr
ter ipti
on
ED Thi
I s
reg
ist
er
is
the
de
sti
nat
ion
ind
ex.
Th
e
ES
I
an
d
ED
I
reg
ist
ers
are
fre
qu
ent
ly
us
ed
in
stri
ng
op
era
tio
ns.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-11:
Registers
Re De
gis scr
ter ipti
on
EB Thi
P s
reg
ist
er
co
nta
ins
the
ba
se
poi
nte
r
of
the
cur
ren
t
sta
ck
fra
me
.
Tracing
Tracing instruments an application through trace messages sent during program execution. Trace messages
are used for a variety of reasons. They can confirm the execution sequence of a program, which is useful for
assuring that program flow is correct (often the cause of bugs). Trace messages can display the state of the
application at different stages. You can track the state of objects, local variables, or other data throughout
the lifetime of an application. Finally, trace messages are used to track events, such as start, stop, and
user-defined events. Tracing replaces the old technique of monitoring an application via Console.WriteLine
statements.
In the Microsoft .NET Framework 2.0, TraceSource is the preferred type for tracing. It replaces the Debug
and Trace classes, which remain available as legacy types. The TraceSource class is in the
System.Diagnostics namespace. To enable tracing, the TRACE symbol must be defined in the application.
In the source code, this is accomplished with the #define statement. Alternatively, define the symbol while
compiling the program. The /d compiler option defines a symbol:
Table 12-12:
TraceSource
Methods
Le De
vel scr
ipti
on
Co Tra
nst ce
ruc So
tor urc
s e(s
Bot trin
h g
co na
nst me
ruc )
tor Tra
s ce
as So
sig urc
n e(s
a trin
na g
me na
to me
the ,
Tra So
ce urc
So eL
urc eve
e ls
obj def
ect aul
. tLe
Th vel)
e
two
-ar
gu
me
nt
co
nst
ruc
tor
als
o
set
s
a
def
aul
t
sev
erit
y
lev
el
for
all
tra
ce
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-12:
TraceSource
Methods
Le De
vel scr
ipti
on
Clo voi
se d
Thi Clo
s se(
me )
tho
d
clo
se
s
all
the
tra
ce
list
en
ers
in
the
Lis
ten
ers
coll
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-12:
TraceSource
Methods
Le De
vel scr
ipti
on
Flu voi
sh d
Thi Flu
s sh(
me )
tho
d
flus
he
s
the
tra
ce
list
en
ers
in
the
Lis
ten
ers
coll
ect
ion
.
Thi
s
en
sur
es
tha
t
ca
ch
ed
me
ss
ag
es
are
rep
ort
ed.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-12:
TraceSource
Methods
Le De
vel scr
ipti
on
Tra
ce [Condit
Dat ionalAt
a tribute(
Thi "TRAC
s E")]
me public
tho void
d Trace
cre Data(
ate
s
a Trace
tra EventT
ce ype
me eventT
ss ype,
ag
e int id,
co
nsi
Object
sti
data)
ng
of [Condit
eve ionalAt
nt tribute(
typ "TRAC
e, E")]
a public
tra void
ce Trace
ide Data(
ntifi
er,
an Trace
d EventT
an ype
y eventT
tra ype,
ce
dat int id,
a.
Th Object
e [] data)
eve
nt
typ
e
is
the
sev
erit
y
lev
el.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-12:
TraceSource
Methods
Le De
vel scr
ipti
on
Tra
ce [Condit
Ev ionalAt
ent tribute(
"TRAC
Thi E")]
s public
me void
tho Trace
d Event(
cre
ate
s Trace
a EventT
tra ype
ce eventT
me ype,
ss
ag int id)
e
[Condit
co
ionalAt
nsi
tribute(
sti
"TRAC
ng
E")]
of
an public
eve void
nt Trace
typ Event(
e
an Trace
d EventT
me ype
ss eventT
ag ype,
e,
whi
ch int id,
is
writ string
ten messa
to ge)
the [Condit
Lis ionalAt
ten tribute(
ers "TRAC
coll E")]
ect
ion public
. void
Th Trace
e Event(
firs
t Trace
ove EventT
rlo ype
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-12:
TraceSource
Methods
Le De
vel scr
ipti
on
Tra
ceI [Condit
nfo ionalAt
rm tribute(
ati "TRAC
on E")]
Thi public
s void
me TraceI
tho nforma
d tion(
cre
ate
s string
an messa
info ge)
rm [Condit
ati ionalAt
on tribute(
al "TRAC
me E")]
ss
public
ag
void
e,
TraceI
whi
nforma
ch
tion(
is
writ
ten string
to format,
the
Lis param
ten s
ers Object
coll [] args)
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-12:
TraceSource
Methods
Le De
vel scr
ipti
on
Tra
ce [Condit
Tra ionalAt
nsf tribute(
er "TRAC
Thi E")]
s public
me void
tho TraceT
d ransfer
cre (
ate
s
tra int id,
nsf
er string
me messa
ss ge,
ag
es,
Guid
whi
related
ch
Activit
are
yID)
writ
ten
to
the
Lis
ten
ers
coll
ect
ion
.
The TraceSource type also has several valuable properties. Table 12-13 lists the properties.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-13:
TraceSource
Properties
Pr Ty
op pe
ert
y
Att Stri
rib ng
ute Dic
s tio
Thi nar
s y
pro
per
ty
get
s
the
cu
sto
m
swi
tch
attr
ibu
tes
tha
t
are
defi
ne
d
in
the
ap
plic
ati
on
co
nfig
ura
tio
n
file.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-13:
TraceSource
Properties
Pr Ty
op pe
ert
y
Lis Tra
ten ce
ers Lis
ten
Thi erC
s olle
pro cti
per on
ty
get
s
an
arr
ay
of
list
en
ers
.
Lis
ten
ers
are
the
tar
get
s
of
tra
ce
me
ss
ag
es.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-13:
TraceSource
Properties
Pr Ty
op pe
ert
y
Na stri
me ng
Thi
s
pro
per
ty
get
s
the
na
me
of
the
Tra
ce
So
urc
e
obj
ect
.
Thi
s
na
me
is
mo
st
like
ly
us
ed
wit
h
the
So
urc
eFi
lter
pro
per
ty
of
a
list
en
er.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-13:
TraceSource
Properties
Pr Ty
op pe
ert
y
Swi So
tch urc
eS
Thi wit
s ch
pro
per
ty
get
s
or
set
s
the
tra
ce
swi
tch
as
so
cia
ted
wit
h
thi
s
Tra
ce
So
urc
e
obj
ect
.
Thi
s
filte
rs
the
tra
ce
me
ss
ag
es
se
nt
fro
m
the
Tra
ce
So
urc
e
obj
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Tracing is based on criteria including severity levels, switches, listeners, and listener filters. This determines
when, where, and how trace messages are reported. TraceSource instances create trace messages.
Severity levels are assigned to trace messages, which set the message importance. Switches filter trace
messages based on their severity levels. Listeners receive trace messages. Finally, listener filters control
the trace messages reported from a listener. Tracing is configurable either programmatically or using an
application configuration file. The application configuration file is recommended. With the application
configuration file, tracing is controlled, enabled, or disabled without recompiling the program. For a
production application, this is essential. To introduce tracing concepts, the programmatic approach is
presented first.
Trace messages are set to severity levels. The trace levels determine the importance or urgency of a
message. Some trace messages are activity related, such as the start and stop time of an application.
Informational trace messages are used to stub methods and check program flow. Tracing the value of an
invalid parameter, which results in an exception, should be higher priority. TraceEventType enumeration
defines the trace levels. Table 12-14 enumerates the trace levels. The levels are listed in order of highest to
lowest priority.
Table 12-14:
Trace Levels
Le De
vel scr
ipti
on
Tra Thi
ce s
Ev err
ent or
Ty lev
pe. el
Crit is
ical res
erv
ed
for
co
ndi
tio
ns
tha
t
de
sta
bili
ze
an
ap
plic
ati
on
or
are
fat
al.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-14:
Trace Levels
Le De
vel scr
ipti
on
Tra Thi
ce s
Ev err
ent or
Ty lev
pe. el
Err is
or res
erv
ed
for
co
ndi
tio
ns
tha
t
de
sta
bili
ze
an
ap
plic
ati
on
but
are
rec
ove
rab
le.
Tra Thi
ce s
Ev err
ent or
Ty lev
pe. el
Wa is
rni res
ng erv
ed
for
no
ncr
itic
al
co
ndi
tio
ns.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-14:
Trace Levels
Le De
vel scr
ipti
on
Tra Thi
ce s
Ev err
ent or
Ty lev
pe. el
Inf is
or res
ma erv
tio ed
n for
ge
ner
al
info
rm
ati
on
tha
t
mi
ght
aid
in
the
dia
gn
osi
ng
of
an
err
or
co
ndi
tio
n.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-14:
Trace Levels
Le De
vel scr
ipti
on
Tra Thi
ce s
Ev err
ent or
Ty lev
pe. el
Ver is
bo res
se erv
ed
for
ge
ner
al
info
rm
ati
on
not
ne
ce
ss
aril
y
as
so
cia
ted
wit
h
an
err
or
co
ndi
tio
n.
Tra Se
ce e
Ev Ta
ent ble
Ty 12-
pe 14.
act
iviti
es
Some trace levels are related to activities, which are linked to events. The activity traces are grouped at the
same trace level, which is the lowest trace level, beneath the Verbose level. Table 12-15 shows the list of
activity traces.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-15:
Activity
Traces
Act De
ivit scr
y ipti
on
Tra Thi
ce s
Ev act
ent ivit
Ty y
pe. indi
Sta cat
rt es
tha
t
an
op
era
tio
n
is
sta
rtin
g.
Tra Thi
ce s
Ev act
ent ivit
Ty y
pe. indi
Sto cat
p es
tha
t
an
op
era
tio
n
is
sto
ppi
ng.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-15:
Activity
Traces
Act De
ivit scr
y ipti
on
Tra Thi
ce s
Ev act
ent ivit
Ty y
pe. indi
Su cat
sp es
en tha
d t
an
op
era
tio
n
is
su
sp
en
din
g.
Tra Thi
ce s
Ev act
ent ivit
Ty y
pe. indi
Re cat
su es
me tha
t
an
op
era
tio
n
is
res
um
ing
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-15:
Activity
Traces
Act De
ivit scr
y ipti
on
Tra Thi
ce s
Ev act
ent ivit
Ty y
pe. indi
Tra cat
nsf es
er a
ch
an
ge
of
cor
rel
ati
on
ide
ntit
y.
Switches filter trace messages based on trace levels, which determine which messages a TraceSource
sends. With switches, developers can selectively enable or disable certain trace messages. If an application
is crashing, you might decide to enable critical and error trace messages. When algorithms are performing
incorrectly, you might want to enable general and activity traces. Avoiding unnecessary tracing improves
performance. Tracing can generate a voluminous amount of information. A filter reduces the volume of trace
messages, which conserves resources. Switch filters are defined in the SourceSwitch class. The
SourceSwitch class has the Level property, which is a SourceLevels type. SourceLevels is the enumeration
that defines the trace messages that are filtered. This is a bitwise flag, and the various SourceLevels can be
combined. The source levels are detailed in Table 12-16.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-16:
Source Level
Filt De
er scr
ipti
on
So Thi
urc s
eL lev
eve el
ls. for
Act war
ivit ds
yTr act
aci ivit
ng y
tra
ce
me
ss
ag
es.
Ot
her
me
ss
ag
es
are
filte
red
.
So Thi
urc s
eL lev
eve el
ls. for
All war
ds
all
tra
ce
me
ss
ag
es.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-16:
Source Level
Filt De
er scr
ipti
on
So Thi
urc s
eL lev
eve el
ls. for
Crit war
ical ds
onl
y
crit
ical
tra
ce
me
ss
ag
es.
So Thi
urc s
eL lev
eve el
ls. se
Err nd
or s
err
or
an
d
crit
ical
tra
ce
me
ss
ag
es.
Ot
her
tra
ce
lev
els
are
filte
red
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-16:
Source Level
Filt De
er scr
ipti
on
So Thi
urc s
eL lev
eve el
ls.I filte
nfo rs
rm ver
ati bo
on se
an
d
act
ivit
y
tra
ce
me
ss
ag
es.
Ot
her
tra
ce
me
ss
ag
es
are
se
nt.
So Thi
urc s
eL lev
eve el
ls. filte
Off rs
all
tra
ce
me
ss
ag
es.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-16:
Source Level
Filt De
er scr
ipti
on
So Thi
urc s
eL lev
eve el
ls. filte
Ver rs
bo act
se ivit
y
tra
ce
me
ss
ag
es.
All
oth
er
me
ss
ag
es
are
se
nt.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-16:
Source Level
Filt De
er scr
ipti
on
So Thi
urc s
eL lev
eve el
ls. filte
Wa rs
rni all
ng tra
ce
me
ss
ag
es
ex
ce
pt
for
Crit
ical
,
Err
or,
an
d
Wa
rni
ng
eve
nts
.
To set the filter, assign an instance of SourceSwitch to the TraceSource.Switch property. The SourceSwitch
has two constructors:
public SourceSwitch(string name)
public SourceSwitch(string name, string defaultLevel)
SourceSwitch also has several useful properties, which are listed in Table 12-17.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-17:
SourceSwitch
Properties
Pr Ty
op pe
ert
y
Att Stri
rib ng
ute Dic
s tio
Thi nar
s y
pro
per
ty
get
s
the
cu
sto
m
attr
ibu
tes
tha
t
are
defi
ne
d
in
the
ap
plic
ati
on
co
nfig
ura
tio
n
file.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-17:
SourceSwitch
Properties
Pr Ty
op pe
ert
y
De stri
scr ng
ipti
on
Thi
s
pro
per
ty
get
s
a
ge
ner
al
de
scr
ipti
on
of
the
swi
tch
. It
def
aul
ts
to
an
em
pty
stri
ng.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-17:
SourceSwitch
Properties
Pr Ty
op pe
ert
y
Dis stri
pla ng
yN
am
e
Thi
s
pro
per
ty
get
s
the
na
me
of
the
swi
tch
.
Lev So
el urc
Thi eL
s eve
pro ls
per
ty
get
s
or
set
s
the
lev
el
of
the
swi
tch
.
The following code creates an instance of TraceSource and TraceSwitch. The switch is then assigned to the
TraceSource.
The event type of trace messages and the switch source level combine to determine which messages are
sent. The following code sends trace messages in each event type: critical, error, information, and so on.
The switch level, which is case sensitive, is read from the command line and determines which messages
are recognized. A console listener is used in the application, which displays the unfiltered trace messages in
a console window. This is an excellent program for understanding the combination of trace event types and
switch levels.
#define TRACE
using System;
using System.Diagnostics;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(string [] argv){
TraceSource ts=new TraceSource("sample");
SourceSwitch sw=new SourceSwitch("switch");
sw.Level=(SourceLevels)Enum.Parse(
typeof(SourceLevels), argv[0]);
ts.Switch=sw;
ConsoleTraceListener console=
new ConsoleTraceListener();
ts.Listeners.Add(console);
ts.TraceEvent(TraceEventType.Start, 0,
"Activity trace messages on");
ts.TraceEvent(TraceEventType.Verbose, 0,
"Verbose trace messages on");
ts.TraceEvent(TraceEventType.Information, 0,
"Information trace messages on");
ts.TraceEvent(TraceEventType.Warning, 0,
"Warning trace messages on");
ts.TraceEvent(TraceEventType.Error, 0,
"Error trace messages on");
ts.TraceEvent(TraceEventType.Critical, 0,
"Critical trace messages on");
ts.Flush();
ts.Close();
}
}
}
Trace messages can be sent to a variety of targets, including the console window, a text file, and a XML file.
Trace targets are called listeners. A TraceSource type maintains an array of listeners called the Listeners
collection. By default, the Listeners collection has a single element, the DefaultTraceListener, which
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
displays trace messages in the Output window of Visual Studio. When the Listeners collection contains
more than one listener, trace messages are sent to multiple targets. If the collection contains a
ConsoleTraceListener and the XMLWriterTraceListener, trace messages are displayed in the console
window and saved into an XML file. Trace listeners receive trace messages sent from a trace source, which
is filtered by the trace switch.
Lis De
ten scr
ers ipti
on
Te Thi
xt s
Wri list
ter en
Tra er
ce for
Lis war
ten ds
er tra
ce
me
ss
ag
es
to
ins
tan
ce
s
of
str
ea
m-r
ela
ted
cla
ss
es,
su
ch
as
a
Te
xt
Wri
ter
cla
ss.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-18:
Trace
Listeners
Lis De
ten scr
ers ipti
on
Ev Thi
ent s
Lo list
gTr en
ac er
eLi for
ste war
ner ds
tra
ce
me
ss
ag
es
to
an
eve
nt
log
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-18:
Trace
Listeners
Lis De
ten scr
ers ipti
on
Def Thi
aul s
tTr list
ac en
eLi er
ste for
ner war
ds
tra
ce
me
ss
ag
es
to
the
Ou
tpu
t
win
do
w.
Thi
s
is
the
def
aul
t
list
en
er
an
d
initi
ally
the
onl
y
me
mb
er
incl
ud
ed
in
the
Lis
ten
ers
coll
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-18:
Trace
Listeners
Lis De
ten scr
ers ipti
on
Co Thi
ns s
ole list
Tra en
ce er
Lis for
ten war
er ds
tra
ce
me
ss
ag
es
to
a
sta
nd
ard
out
put
or
err
or
str
ea
m.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-18:
Trace
Listeners
Lis De
ten scr
ers ipti
on
Del Si
imi mil
ted ar
Lis to
tTr the
ac Te
eLi xt
ste Wri
ner ter
Tra
ce
Lis
ten
er
cla
ss,
thi
s
list
en
er
for
war
ds
me
ss
ag
es
to
an
ins
tan
ce
of
a
str
ea
m-r
ela
ted
cla
ss.
Ho
we
ver,
the
me
ss
ag
es
are
se
par
ate
d
wit
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-18:
Trace
Listeners
Lis De
ten scr
ers ipti
on
Xm Thi
lWr s
iter list
Tra en
ce er
Lis sav
ten es
er tra
ce
s
me
ss
ag
es
as
XM
L-e
nc
od
ed
tex
t.
Trace listeners inherit the TraceListener base class, which provides the core functionality of a listener. Most
of the methods of the TraceListener mirror those of the TraceSource, Trace, and Debug types. TraceSource,
Trace, and Debug forward messages to listeners by invoking identically named trace operations in listeners.
All the listeners have a default constructor. Some listeners have multiargument constructors, in which the
listener is assigned a name or a destination is defined. For example, the following code creates a new
TextWriterTraceListener. It is named samplelistener and writes to the test.txt file.
TextWriterTraceListener file=new
TextWriterTraceListener("samplelistener", "test.txt");
Table 12-19:
TraceListener
Properties
Pr Ty
op pe
ert
y
Att Stri
rib ng
ute Dic
s tio
Thi nar
s y
pro
per
ty
get
s
the
cu
sto
m
attr
ibu
tes
tha
t
are
defi
ne
d
in
the
ap
plic
ati
on
co
nfig
ura
tio
n
file.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-19:
TraceListener
Properties
Pr Ty
op pe
ert
y
Filt Tra
er ce
Thi Filt
s er
pro
per
ty
get
s
an
d
set
s
the
filte
r
of
the
list
en
er.
Ind int
ent
Lev
el
Thi
s
pro
per
ty
get
s
an
d
set
s
the
lev
el
of
ind
ent
ati
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-19:
TraceListener
Properties
Pr Ty
op pe
ert
y
Ind int
ent
Siz
e
Thi
s
pro
per
ty
get
s
an
d
set
s
the
am
ou
nt
of
ind
ent
ati
on
per
ind
ent
ati
on
lev
el.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-19:
TraceListener
Properties
Pr Ty
op pe
ert
y
IsT bo
hre ol
ad
Saf
e
Thi
s
pro
per
ty
indi
cat
es
wh
eth
er
the
list
en
er
is
thr
ea
d-s
afe
.
Na stri
me ng
Thi
s
pro
per
ty
get
s
an
d
set
s
the
na
me
of
the
list
en
er.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-19:
TraceListener
Properties
Pr Ty
op pe
ert
y
Ne bo
edI ol
nd
ent
Thi
s
is
a
pro
tec
ted
pro
per
ty,
whi
ch
en
abl
es
or
dis
abl
es
ind
ent
ati
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-19:
TraceListener
Properties
Pr Ty
op pe
ert
y
Tra Tra
ce ce
Ou Op
tpu tio
tO ns
pti
on
s
Thi
s
pro
per
ty
get
s
an
d
set
s
an
en
um
era
tio
n
tha
t
co
ntr
ols
the
out
put
opt
ion
s
of
the
list
en
er.
The values of the TraceOption enumeration are described in Table 12-20. These values are bitwise, which
allows multiple options to be combined.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-20:
TraceOptions
Values
Va De
lue scr
ipti
on
Tra Thi
ce s
Op opt
tio ion
ns. incl
Cal ud
lSt es
ac the
k call
sta
ck
in
ea
ch
tra
ce
me
ss
ag
e.
Thi
s
info
rm
ati
on
is
fou
nd
at
the
En
viro
nm
ent
.St
ac
kTr
ac
e
pro
per
ty.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-20:
TraceOptions
Values
Va De
lue scr
ipti
on
Tra Thi
ce s
Op opt
tio ion
ns. incl
Dat ud
eTi es
me the
dat
e
an
d
tim
e
in
ea
ch
tra
ce
me
ss
ag
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-20:
TraceOptions
Values
Va De
lue scr
ipti
on
Tra Thi
ce s
Op opt
tio ion
ns. incl
Lo ud
gic es
alO the
per logi
ati cal
on op
Sta era
ck tio
n
sta
ck
in
ea
ch
tra
ce
me
ss
ag
e.
Thi
s
info
rm
ati
on
is
fou
nd
at
the
Cor
rel
ati
on
Ma
na
ger
.Lo
gic
alO
per
ati
on
Sta
ck
pro
per
ty.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-20:
TraceOptions
Values
Va De
lue scr
ipti
on
Tra Thi
ce s
Op opt
tio ion
ns. ex
No clu
ne de
s
all
opt
ion
s.
Th
e
tra
ce
me
ss
ag
e
will
co
nta
in
no
opt
ion
al
dat
a.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-20:
TraceOptions
Values
Va De
lue scr
ipti
on
Tra Thi
ce s
Op opt
tio ion
ns. incl
Pro ud
ce es
ssI the
d cur
ren
t
pro
ce
ss
ide
ntifi
er
in
ea
ch
tra
ce
me
ss
ag
e.
Thi
s
info
rm
ati
on
is
fou
nd
at
the
Pro
ce
ss.
Id
pro
per
ty.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-20:
TraceOptions
Values
Va De
lue scr
ipti
on
Tra Thi
ce s
Op opt
tio ion
ns. incl
Thr ud
ea es
dId the
cur
ren
t
thr
ea
d
ide
ntifi
er
in
ea
ch
tra
ce
me
ss
ag
e.
Thi
s
info
rm
ati
on
is
fou
nd
at
the
Thr
ea
d.
Ma
ng
ed
Thr
ea
dId
pro
per
ty.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-20:
TraceOptions
Values
Va De
lue scr
ipti
on
Tra Thi
ce s
Op opt
tio ion
ns. incl
Ti ud
me es
Sta a
mp tim
est
am
p
in
the
ea
ch
tra
ce
me
ss
ag
e.
Thi
s
info
rm
ati
on
is
ret
urn
ed
fro
m
the
Sto
pw
atc
h.
Ge
tTi
me
Sta
mp
me
tho
d.
The listeners of a TraceSource type are managed at the TraceSource.Listeners property. The Listeners
property is a TraceListenerCollection type, which implements the IList, ICollection, and IEnumerable
interfaces. You can add a listener with the Add method. The TraceListenerCollection type also implements
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
the AddRange method. Call this method to add multiple listeners to the Listeners collection. The AddRange
method is overloaded to accept an array of listeners or a TraceListenerCollection type as the sole parameter.
The following code adds a ConsoleTraceListener to a Listeners collection:
ConsoleTraceListener console=
new ConsoleTraceListener();
ts.Listeners.Add(console);
Trace messages can be filtered at the collection. Listener filters are perfect for identifying important
messages in a flood of trace messages. You can focus on a particular problem. Listener filters and trace
switch are both filters. The trace switch filters messages from the TraceSource object. Those messages are
forwarded to a listener, in which the listener filter further refines the set of trace messages. There are two
types of listener filters: SourceFilter and EventTypeFilter. SourceFilter focuses the listener on a specific
source. For example, when tracing messages from several classes, you use SourceFilter to limit trace
messages to a specific class. EventTypeFilter further refines trace messages based on priority. The priority
of EventTypeFilter is usually a subset of the source switch filter.
The only parameter is the name of the source. The listener will output only messages from the specific
source. If the name is invalid, the filter is ignored.
The SourceLevels parameter states the priority of allowed trace messages. Other trace messages are
ignored.
The following code demonstrates the SourceFilter type. The EventTypeFilter is included in a later example.
Three TraceSource instances are defined in the code. A ConsoleTraceListener is also defined, which
displays trace messages in the console window. The ConsoleTraceListener .Filter is then updated to display
trace messages from only the second source. Later, the SourceFilter is changed to limit trace messages to
the first source.
#define TRACE
using System;
using System.Diagnostics;
namespace Donis.CSharpBook{
public class Starter{
ts2.Switch=sw;
ts3.Switch=sw;
ConsoleTraceListener cs=new ConsoleTraceListener();
ts1.Listeners.Add(cs);
ts2.Listeners.Add(cs);
ts3.Listeners.Add(cs);
ts1.Flush();
ts2.Flush();
ts3.Flush();
ts1.Close();
ts2.Close();
ts3.Close();
}
}
}
Tracing Example
The following example code declares a ZClass and YClass; both classes contain a TraceSource instance. It
is a best practice to maintain separate TraceSource instances for individual classes, which affords
individualized management of tracing at the class level. For example, you could filter trace messages for
specific classes. Expose TraceSource as a static member of the class, which is initialized in the static
constructor. Functions of the class leverage the static TraceSource to send trace messages. If using the
disposable pattern, clean up the TraceSource object in the Dispose method.
static ZClass() {
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ts=new TraceSource("ZTrace");
ts.Switch=new SourceSwitch("sw1", "Information");
ts.Listeners.Add(new ConsoleTraceListener());
TextWriterTraceListener file=new
TextWriterTraceListener("samplelistener", "test.txt");
file.Filter = new EventTypeFilter(SourceLevels.Critical);
file.TraceOutputOptions=TraceOptions.DateTime;
ts.Listeners.Add(file);
}
// partial listing
The following code is a complete listing of the sample code used in this section. Main also has a trace
source, which sends trace messages to listeners for the Output and Console windows. Both the ZClass and
YClass have separate trace sources. The methods of the ZClass and YClass use their respective
TraceSource instances to send trace messages. To demonstrate both techniques, the TraceEvent and
TraceData methods are called. The hash codes of the objects are used as the trace identifier, which
identifies trace messages by class type and instance. This is sometimes useful. The trace source of the
ZClass sends trace messages to listeners for the Output window, Console window, and a text file. The
switch of that trace source forwards all trace messages. However, the filter for the Console listener restricts
output to error type messages. The YClass trace source sends trace messages of error priority to listeners
for the Output and Console windows. The switch limits the trace messages to informational messages.
#define TRACE
using System;
using System.Diagnostics;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
TraceSource ts=
new TraceSource("StarterTrace");
ts.Switch=new SourceSwitch("sw3");
ts.Switch.Level=SourceLevels.ActivityTracing;
ts.Listeners.Add(new ConsoleTraceListener());
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ts.TraceEvent(TraceEventType.Start,
0, "Starting");
ts.TraceEvent(TraceEventType.Stop,
0, "Stopping");
obj1.Dispose();
obj2.Dispose();
}
}
static ZClass() {
ts=new TraceSource("ZTrace");
ts.Switch=new SourceSwitch("sw1", "All");
ts.Listeners.Add(new ConsoleTraceListener());
TextWriterTraceListener file=new
TextWriterTraceListener("samplelistener", "test.txt");
file.Filter = new EventTypeFilter(SourceLevels.Critical);
file.TraceOutputOptions=TraceOptions.DateTime;
ts.Listeners.Add(file);
}
static YClass() {
ts=new TraceSource("YTrace");
ts.Switch=new SourceSwitch("sw2", "Information");
ts.Listeners.Add(new ConsoleTraceListener());
ts.Listeners[1].IndentSize=4;
ts.Listeners[1].IndentLevel=2;
}
}
}
Configuration File
Trace switches, listeners, and listener filters are configurable in an application configuration file. Actually,
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
this is the best practice and is preferable to programmatic configuration. Developers can update the specifics
of tracing without recompiling the application. The application configuration file has the same name as the
target assembly plus a .config extension. For example, the application configuration file for hello.exe is
hello.exe.config. The application configuration file should be in the same directory as the assembly. For a
software system, you can configure multiple applications with a publisher policy file, which is deployed in the
global assembly cache. Visit this link for a "how to" on publisher policy files:
msdn2.microsoft.com/en-us/library/dz32563a. You can configure tracing in both the configuration file and
programmatically. Where there is overlap, programmatic configuration takes precedent.
In the configuration file, the tracing is placed within the system.diagnostics element. For the co mplete
explanation of the system.diagnostics element, open this link: msdn2.microsoft.com/en-us/library/1txedc80.
Sources element Define the trace sources within the sources element. A specific trace source is declared
in a source element. The important attributes of the source element are name and switchName. The name
attribute is the name of the trace source, and switchName names the switch assigned to the trace source.
Here is an example:
<sources>
<source name="ZTrace" switchName="sw1">
</source>
</sources>
Listeners element The listeners of a particular source are listed within the source elements. The listeners
element encapsulates the listeners of a trace source. Individual listeners are added to the Listeners
collection with the add element. The key attributes of the add element are type, name, traceOutputOptions,
and initializeData. The type attribute is the kind of listener. The name attribute is the name assigned to the
listener. The initializeData attribute is additional data used to create the listener, such as the target file
name. The traceOutputOtions attribute adds optional data to trace messages of the listener, such as a
timestamp. Here are example elements:
<sources>
<source name="ZTrace" switchName="sw1">
<listeners>
<add initializeData="data.txt"
type="System.Diagnostics.TextWriterTraceListener"
name="tListener" />
<add name="cListener" />
</listeners>
</source>
</sources>
sharedListeners element The listeners element assigns a listener to a specific trace source. You can also
share listeners. Listeners are shared between trace sources in the sharedListeners element. A shared
listener is added with an add element. Shared listeners are added to a specific trace source as a regular
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
listener. However, the add element need only have the name attribute, which identifies the shared listener:
<system.diagnostics>
<sharedListeners>
<add type="System.Diagnostics.ConsoleTraceListener"
name="cListener"
traceOutputOptions="None" />
</sharedListeners>
</switches>
Switches element Switches are defined within the switches elements. An individual switch is added with an
add element. The basic attributes are name and value . The name attribute is the name of the switch, and
the value attribute is the filter for the trace message. Here is an example:
<system.diagnostics>
<switches>
<add name="sw1" value="Critical" />
<add name="sw2" value="Information" />
</switches>
</system.diagnostics>
</configuration>
#define TRACE
using System;
using System.Diagnostics;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
TraceSource ts=
new TraceSource("StarterTrace");
ts.TraceEvent(TraceEventType.Start,
0, "Starting");
ts.TraceEvent(TraceEventType.Stop,
0, "Stopping");
obj1.Dispose();
obj2.Dispose();
}
}
static ZClass() {
ts=new TraceSource("ZTrace");
}
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
static YClass() {
ts=new TraceSource("YTrace");
}
}
}
The following application configuration file sets up tracing for the application. There are some differences from
the first example. For example, the three trace sources share the same Console listener. The YTrace trace
source also has an XML listener. Otherwise, the results are virtually the same as the earlier code.
DebuggerDisplayAttribute
The DebuggerDisplayAttribute type controls how values are displayed in a debugger window. This attribute is
valid for assembly, class, struct, enum, indexer, property, field, and delegate constructs. You cannot use
DebuggerDisplayAttribute on methods. When this attribute is used as an assembly-level attribute, the Target
property must be assigned the name of the applicable type. The DebuggerDisplayAttribute type is also found
in the System.Diagnostics namespace.
The DebuggerDisplayAttribute type has a one-argument constructor. The single argument, which is a string,
is the display value of the type in the debug window. The value can contain an expression. Expressions must
be enclosed in curly braces: {expression}. Constants, static members, and instance members are valid in
the expression. Prefix static members with the class name. The expression cannot contain pointers,
aliases, or local variables.
The DebuggerDisplayAttribute type is inheritable. A derived class inherits the attribute from the base class.
However, the derived class can redefined DebuggerDisplayAttribute as desired.
Figure 12-45 shows the values in a debug window for the ZClass and YClass instances without
DebuggerDisplayAttribute.
Figure 12-45: View of the ZClass and YClass instances before applying DebuggerDisplayAttribute
The following code decorates the ZClass and YClass with the DebuggerDisplayAttribute type. The ZClass is
assigned the value NewName, which is overridden in the derived class. In addition, ZClass.fielda is adorned
with the attribute, which contains an expression in this circumstance.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
[DebuggerDisplay("NewName")]
class ZClass
{
public static int test = 1;
public virtual void MethodA()
{
int vara=5, varb=10;
Console.WriteLine("{0} {1}", vara,
varb);
}
[DebuggerDisplay("fielda = {fielda}")]
private int fielda = 5;
}
[DebuggerDisplay("DerivedName")]
class YClass : ZClass
{
public override void MethodA()
{
Console.WriteLine("YClass.MethodA");
Console.WriteLine("Fieldb: {0}", fieldb);
}
Figure 12-46 shows the results of using the DebuggerDisplayAttribute type with the ZClass and YClass .
Figure 12-46: View of ZClass and YClass types in the debugger window after applying the
DebuggerDisplayAttribute type
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
DebuggerBrowsableAttribute
Another debugger attribute is the DebuggerBrowsableAttribute type, which determines how a member is
displayed in the debugger window. This attribute is valid for properties, indexers, and fields.
Va De
lue scr
ipti
on
Ne Thi
ver s
ele
me
nt
hid
es
the
me
mb
er
in
the
de
bu
gg
er
win
do
w.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-21:
DebuggableB
rowserState
Values
Va De
lue scr
ipti
on
Col Thi
lap s
se ele
d me
nt
dis
pla
ys
the
me
mb
er,
whi
ch
is
coll
ap
se
d
initi
ally
.
Thi
s
is
the
def
aul
t.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 12-21:
DebuggableB
rowserState
Values
Va De
lue scr
ipti
on
Ro If
otH the
idd ele
en me
nt
is
an
arr
ay
or
coll
ect
ion
,
thi
s
ele
me
nt
hid
es
the
roo
t
me
mb
er
but
dis
pla
ys
the
chil
d
ele
me
nts
.
For
ex
am
ple
,
wh
en
ap
plie
d
to
a
pro
per
ty
tha
t
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Figure 12-47 shows the values for the ZClass and YClass types without the DebuggerBrowsableAttribute
type.
Figure 12-47: View of ZClass and YClass types before applying the DebuggerBrowsableAttribute type
The following code shows the ZClass and YClass types decorated with DebuggerBrowsableAttribute. In the
ZClass, the array related to the propInts property is displayed. In the YClass type, fieldb is hidden.
class ZClass
{
public static int test = 1;
public virtual void MethodA()
{
int vara=5, varb=10;
Console.WriteLine("{0} {1}", vara,
varb);
}
get
{
return Ints;
}
}
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private int fieldb = 10;
}
Figure 12-48 shows the results of using the DebuggerBrowsableAttribute type on the ZClass and YClass.
Figure 12-48: View of ZClass and YClass types after applying DebuggerBrowsableAttribute
DebuggerTypeProxyAttribute
The DebuggerTypeProxyAttribute type names a type as a proxy for another type. The type proxy is
displayed instead of the type in debugger windows. The attribute type is DebuggerTypeProxyAttribute. This
attribute is valid for assembly, class, and struct constructs. When used at the assembly level, the target
name property must be assigned the name of the target type.
The proxy type must have a one-argument constructor that accepts an instance of the target type. For this
reason, it is recommended that the proxy type be nested within the target type. This provides the proxy type
constructor easy access to the instance of the surrounding object. Only public members of the proxy are
visible in the debugger window.
The DebuggerTypeProxyAttribute type is useful for hiding sensitive data within a type. In the following code,
XClass has a password field. This field should not be exposed during live debugging because passwords
represent sensitive data. The DebuggerTypeProxyAttribute type in the sample code names XClassDebug as
the proxy. XClassDebug hides the password field and displays an appropriate value, which is "Not Available".
This is the code for the XClass and the nested XClassDebug class, which is the proxy class:
[DebuggerTypeProxy(typeof(XClassDebug))]
public class XClass
{
public XClass(string _password)
{
password=_password;
}
private string password;
internal class XClassDebug
{
public XClassDebug(XClass obj)
{
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Dump Files
Visual Studio 2005 can open and interpret dump files from managed, native, or mixed-mode applications. A
dump is a snapshot of an application's memory that lets a developer debug an application at a convenient
location and time, which is particularly beneficial for debugging production applications. Dumps created on a
production machine can then be debugged on another machine, without interfering with the production
machine. Several tools are available for creating a dump, including Windbg, Dr. Watson, Autodump+
(ADPlus), and Visual Studio. Visual Studio can create dumps while debugging native applications, such as
Microsoft Visual C++ applications. This feature is not available in managed projects. Dump files and other
advanced debugging topics are discussed in the next chapter, "Advanced Debugging."
In Visual Studio, dump files are opened as projects. Choose the Open Solution/Project menu command from
the Open submenu of the File menu. Dump files have the .dmp extension. After opening the file, start a
debug session to examine the dump file. The Debug Start menu command (F5) on the Debug menu starts a
debug session. When debugging a dump, a breakpoint is hit almost immediately. You can then debug and
diagnose the dump using the various debug windows. Figures 12-49 and 12-50 show the Call Stack and
Modules debug windows, which provide different views of the dump.
The Debug windows of Visual Studio can provide a native perspective of an unmanaged application. This is
helpful with dumps of managed applications, but a managed view is also sometimes merited. Load the Son
of Strike debugger extension (SOS.DLL) for a managed perspective of a dump. Load Son of Strike with the
.load sos command in the Immediate window. You can then issue various Son of Strike commands, which
are prefixed with an exclamation point. In Figure 12-51, Son of Strike is loaded. Afterward, the DumpHeap
command is invoked. Output is shown in the Immediate window. Some Son of Strike commands are
thread-specific. Set the thread context in the Thread window before issuing one of those commands.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Advanced Debugging
A variety of tools are available for advanced debugging. Many of these tools are downloaded from the
Debugging Tools for Windows Web site. Windbg, the latest version of Son of Strike (SOS.DLL), and
Auto-Dump Plus are three of the most valuable tools received from the download.
Windbg is a native and kernel debugger, which is commonly used by software support engineers to diagnose
problems. It supports live and postmortem debugging. Live debugging is performed by attaching the debugger
to a running process. Postmortem analysis is conducted via dump files. This includes examining memory,
evaluating call stacks, setting breakpoints, viewing threads, and much more. SOS.DLL is a debugger
extension that publishes commands for managed debugging. Son of Strike can be used in Windbg and
Visual Studio 2005, as shown earlier in this chapter.
Mdg is the new managed debugger. As the dedicated managed debugger, it offers features that are found
neither in Visual Studio debugger nor in Windbg products.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
This chapter provides general techniques for debugging managed applications. Some of these techniques are
also helpful in debugging Web applications. For guidance on debugging ASP.NET applications, refer to
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/DBGch01.asp.
Advanced debugging tools and services are available from a variety of sources, including the Microsoft
Windows operating environment, the Microsoft .NET Framework, Visual Studio .NET, and the Debugging
Tools for Windows Web site at Microsoft.com. Performance Monitor, Task Manager, and Dr. Watson are
distributed with the Windows environment. The .NET Framework has the Son of Strike (SOS) debugger
extension (SOS.dll), DbgClr, CorDbg, and other debugger tools. Visual Studio offers Spy++, Dependency
Walker, OLE Viewer, and much more. Advanced debugging tools can be downloaded from the Debugging
Tools for Windows Web site at www.microsoft.com/whdc/devtools/debugging. This Web site is updated
periodically.
You should download the current tools regularly. The Windows Debugger (WinDbg) and ADPlus script are
the most frequently used tools from this Web site.
WinDbg is the primary focus of this chapter. However, it is not intended as a replacement for the Visual
Studio Debugger. The first rule of debugging is to look for simple solutions first. The Visual Studio Debugger
is ideal for basic debugging: It is convenient, it has a familiar user interface, and superior documentation is
available. Visual Studio is a pen hammer, whereas WinDbg is a sledge hammer. If a problem occurs,
diagnose the problem first with Visual Studio before attempting a complete reclamation with WinDbg. Find
uninitialized variables or parameters, errant loop counters, logic errors, and code coverage problems with the
Visual Studio Debugger. These are likely problems in an erroneous application.
The goal of debugging is to resolve abnormal error conditions. Program hangs, crashes, memory leaks, and
unhandled exceptions are possible error conditions. Some abnormal conditions are not exceptional events.
These primarily include logic errors. For example, a program that reports incorrect results has a bug. It may
not be as intrusive as an exception, but it is a bug nonetheless.
Debugging is conducted in three phases: discovery, analysis, and testing. The discovery phase is the
process of gathering data related to the problem. In this stage, you capture the state of the application or
environment when the error condition occurred. The analysis (or debugging) phase is when the abnormal
condition is diagnosed. The testing phase validates the analysis phase. This is an iterative process. Based
on the results of the testing phase, further discovery, analysis, and testing could be mandated.
Debugging is an integral part of the life cycle of an application. Debugging a production application is a
challenge when compared with debugging in the development environment. First, the constraints are
different. The priority for debugging a production application is restarting the application, not exhaustive
analysis. With a high-traffic retail Web site, the primary concern of the client might be lost revenue. Second,
the production machine might lack debugging resources, such as symbols, source code, and debugging
tools. Third, re-creating the abnormal condition could be problematic. Load factors, memory stress, and
other conditions are hard to replicate on a developer machine. Finally, the production application may be
offsite—in a locked server closet or another otherwise inconvenient location. This might necessitate remote
debugging, which could entail setup and possible trust issues.
Debugging can be invasive or noninvasive. Noninvasive debugging is the preference for debugging production
applications that are running. The advantages of noninvasive debugging are that the debuggee is not altered
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
or shut down during the debugging process. Invasive debugging provides additional data and flexibility, but
should be limited to the development environment.
You can debug live applications or perform postmortem analysis. Live debugging is debugging an active and
running application, and it often involves breakpoints. Breakpoints are set where the live application should
be interrupted. Starting at the breakpoint, the developer can step through the application to verify program
logic, monitor local variables, inspect the heap, watch the call stack, and more. Live debugging usually
entails user interaction, which is not always possible. The antithesis to live debugging is postmortem
analysis. Postmortem analysis is performed against static data, such as a dump or log file. You can create
a dump or log file once and then debug as often as needed. Using a static file is often simpler than live
debugging, where the state is constantly evolving. Postmortem analysis also has disadvantages. It is harder
to pose future-tense questions with postmortem analysis. What is the call stack after a future operation is
performed? What is the effect of further executing a for loop? What is the impact of changing the value of a
local variable? Deriving answers to these questions from a dump or log file is indeed difficult.
Production and beta applications are typically release builds, whereas alpha and proof of concepts are often
debug builds. Release builds are optimized but harder to debug. Conversely, debug builds are not optimized
and run slightly slower. In .NET 1.1, release builds might have incomplete tracking information from the
inlining of methods. The missing stack frames could lead to unreliable stack traces in debuggers. This
optimization is not performed on .NET 2.0 applications. In addition, there is code optimization, which is a
compilation of several performance-improving techniques, including inserting jumps, reducing the lifetime of
local references, reordering instructions efficiently, and so on.
This chapter presents different versions of the Store application. Each version demonstrates a different
aspect of advanced debugging. The Store applications can be found in the companion Web content.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
DebuggableAttribute Attribute
Just-in-time (JIT) optimizations are defined in the DebuggableAttribute custom attribute. When compiled in
debug mode, this attribute is added to the metadata of the application with any optimizations disabled. The
DebuggableAttribute attribute contains the IsJITOptimizerDisabled and IsJITTrackingEnabled properties,
which indicate the status of JIT optimizations.
These optimizations can make debugging a production application difficult. Fortunately, you can create an
application initialization file that disables the optimization of a release product. The initialization file must be
in the same directory as the application, have the same root name of the application, and have the .ini
extension. For myapp.exe, the filename would be myapp.ini. The initialization file has two entries. The
GenerateTrackingInfo entry enables or disables generating complete tracking information. This entry is
redundant in .NET 2.0, where tracking information is always generated. The AllowOptimize entry controls
code optimization. In the initialization file, 1 is true and 0 is false. The following is an initialization file that
disables both JIT optimizations:
This is a partial listing of assembly code generated by the JIT compiler with optimizations enabled:
The following assembly originated from the same Microsoft intermediate language (MSIL) code as the
preceding assembly code. For this assembly, the JIT optimizations are disabled. I will not torment you with
the intricacies of assembly code. However, compare the before and after code—and note the differences.
Obviously, disabling optimizations made a considerable difference in the assembly code generated.
01050aba 90 nop
01050abb eb17 jmp 01050ad4
01050abd 8b45c0 mov eax,[ebp-0x40]
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Other differences can exist between release and debug products. Debug methods are likely to be omitted
from the release version. Trace methods are probably included in the release build, but that is not
guaranteed. These and other dissimilarities sometimes amount to relevant differences between a release and
debug version of a product. For that reason, an abnormal condition in a production application may
mysteriously disappear in a debug version. The opposite also applies. Errors in a debug version might
disappear in the release product. Make sure that Debug and Trace methods cause no side effects. This will
eliminate most cross-build problems. It's important to always extensively test both the debug and release
versions of products. After exhaustive unit testing of the debug build, submit the release build to identical
tests.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Debuggers
There are several useful tools and debuggers available to managed developers. The following list is a
compilation of some of these tools. (This list does not include third-party tools.)
Visual Studio Debugger Most Windows developers have some experience with this tool.
Consult this debugger first to diagnose a problem. It is included with the installation of Visual
Studio .NET.
Managed Debugger (MDbg) This tool is a console debugger that is dedicated to the advanced
debugging of managed code, so it has a wide variety of commands and options specific to
managed code that are not available in other debuggers. You download the code for MDbg at the
Microsoft downloads site: www.microsoft.com/downloads. It is described as the CLR Managed
Debugger (mdbg) Sample. There is a GUI extension available for MDbg that can also be
downloaded. It provides a user-friendly veneer to MDbg.
CLR Debugger (DbgClr) This tool is a managed debugger and a scaled-down version of the
Visual Studio Debugger. It has the familiar Visual Studio interface, but with only debugging
capabilities. This tool provides some debugging preparedness for production machines in which
Visual Studio might not be installed. DbgClr does not support remote or mixed-mode debugging. It
is distributed with the .NET Framework.
Performance Monitor Performance Monitor instruments a live application. It can plot a host of
data points onto a variety of graphs and reports. Alternatively, results can be logged in text files
for later examination. Performance Monitor is included with the Windows operating system.
Windows Console Debugger (CDB) CDB is both a kernel- and user-mode debugger. To debug
managed applications, load the SOS debugger extension (SOS.dll). Download CDB from the
Debugging Tools for Windows Web site at Microsoft.com.
Windows Debugger (WinDbg) WinDbg is also a kernel- and user-mode debugger. It offers a
user interface to many of the command-line instructions available in CDB and NTSD. The
command-line instructions of WinDbg and CDB are similar. Load SOS to debug managed
applications. WinDbg is probably the most popular of the advanced debugging tools. Download
WinDbg from the Debugging Tools for Windows Web site at Microsoft.com.
Dr. Watson Dr. Watson is a JIT debugger that creates logs or dump files when an abnormal
condition occurs in an application. Dr. Watson is available on production machines on which other
debuggers may not be available. It is convenient and is the one debugging tool everyone should
have. This tool is distributed with the Windows operating environment.
ADPlus (ADPlus.vbs) This service is a Visual Basic script that automates common CDB tasks,
such as creating dumps. This is another tool downloaded from the Debugging Tools for Windows
Web site at Microsoft.com.
Son of Strike (SOS.dll) This tool is a debugger extension that exposes commands for debugging
managed applications. This tool is distributed with the .NET Framework and downloadable from
the Debugging Tools for Windows Web site at Microsoft.com. Several versions of the SOS.dll are
available. The version of the SOS.dll included in the clr10 subdirectory of Debugging Tools For
Windows is not compatible with some examples in this chapter.
Some of these tools are available from multiple sources. For example, SOS is found in the .NET Framework,
distributed with Visual Studio .NET, and downloaded at the Debugging Tools for Windows Web site at
Microsoft.com. When a tool is available from various sources, the most recent version is downloadable from
the Debugging Tools for Windows Web site at Microsoft.com.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The DbgJITDebugLaunchSetting value at this key sets the behavior of the just-in-time dialog box. There are
three possible values. A value of zero requests that the Microsoft Error Reporting dialog box is displayed for
JIT debugging. In the Microsoft Error Reporting dialog box, click the Don't Send button to launch the JIT
debugging dialog box. Both the Microsoft Error Reporting and the JIT debugging dialog boxes are shown in
Figures 13-1 and 13-2. A value of 1 causes the Microsoft Error Reporting dialog box to be suppressed. A
value of 2 forces the JIT debugging dialog box to be displayed when JIT debugging occurs. However, the
Microsoft Error Reporting dialog box is not shown. The default value for DbgJITDebugLaunchSetting is zero.
In addition to the DbgJITDebugLaunchSetting value, you can name the managed JIT debugger in the
DbgManagedDebugger value. Figure 13-3 has the managed debugging key and values.
For unmanaged code, you configure JIT debugging at the following registry key:
HKLM\Software\Microsoft\Windows NT\CurrentVersion\AeDebug
The Auto value of this key controls the Just-in-Time Debugging dialog box. Zero causes the Microsoft Error
Reporting dialog box to be displayed. If the Debug button is clicked on the Microsoft Error Reporting dialog
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
box, the Just-in-Time Debugging dialog box is displayed next. A value of 1 displays the Just-in-Time
Debugging dialog box directly. The Debugger value of the AeDebug registry key holds the name of the JIT
unmanaged debugger.
Visual Studio .NET is named as the default managed and unmanaged debugger when the product is
installed. If that changes, you can reappoint Visual Studio .NET as the default debugger from the Tools menu
and the Options dialog box. Choose the Just-In-Time section in the Debugging pane. From that window,
choose the types of code for which Visual Studio .NET should be the JIT debugger.
You can install other debuggers as the default JIT debugger. The I startup option of the WinDbg or Dr.
Watson tools installs either as the default unmanaged debugger. You can also manually change the registry
to make these tools the default managed or unmanaged JIT debuggers:
WinDbg -I
Drwtsn32 -I
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Managed Debugger
MDbg supports user-mode debugging of managed applications and does not support mixed-mode
debugging. Because MDbg is dedicated to managed code, it has an expanded repertoire of debugging
commands specific to managed code. As mentioned previously, it is a console debugger. MDbg supersedes
the Runtime Debugger (CorDbg), which is the previous managed debugger. CorDbg remains available and
continues to be distributed with Visual Studio .NET and the .NET Framework.
Conveniently, MDbg and CorDbg share many of the same commands. However, MDbg commands are
different from those of WinDbg and SOS. For developers who switch between these tools frequently,
leveraging the benefits of each, the nonstandardization of the public interface of these tools is sometimes
frustrating. For example, here are three distinct commands to display a call stack. In WinDbg, you have the
k commands, such as k, kb , and kp. SOS offers the !clrstack command. MDbg has the most original name
for a stack trace command, which is the w (w for where) command.
The following walkthrough demonstrates and highlights many of the unique features of MDbg. As mentioned,
Store.exe is the demonstration application provided for walkthroughs in this chapter. In this application, you
record sales as transactions. To add a transaction, enter the number of transactions in the # of Transactions
text box and then click the Add Transactions button. The default number of transactions is 1. The
Transaction dialog box appears next, in which you select the items sold in this transaction. After accepting,
the new transaction is added to the list of transactions, and the display is updated. The Store application is
modified minimally to facilitate some of the walkthroughs. Each walkthrough has its own version of the Store
application. The generic Store application is shown in Figure 13-5.
MDbg Walkthrough
The ability to create instances of new objects or invoke managed functions is one of the coolest features
included in MDbg. You can seed an application with classes and functions that are used during debugging
sessions exclusively. They are never actually called in the application during normal operation, but are
instead called only from the debugger. A Debug class has been added to the Store application for this
purpose. Debug.Reset is a static method and the sole member of the Debug class. The Reset method
resets the Store application. The Debug class and Reset method are a normal class and method. There are
no hooks in them especially for the debugger. This is the Debug class:
Here is a second demonstration of the MDbg debugger. This walkthrough highlights exception management.
When a second-chance exception is raised in an attached application, MDbg does not intercede. Interesting,
this is contrary to the behavior of most debuggers. Second-chance exceptions are catastrophic. The
exception will terminate the application or prompt JIT debugging. If you want the debugger to intercede, you
can request that MDbg catch second-chance exceptions.
1. Restart the Store application.
2. Attach MDbg to the application and resume the program.
3. Click the Bad Action button. As advertised, a bad action occurs, which is an unhandled
exception. The Just-in-Time Debugging dialog box will most likely appear where the application
can be terminated. Terminate the application. The Bad Action button is not particularly creative in
manifesting an exception. The method has filler code but eventually raises a divide-by-zero
exception:
4.
5. private void btnBad_Click(object sender, EventArgs e)
6. {
7. int a = 5, b = 0;
8. ++a;
9. a /= 2;
10. a /= b;
11. }
12. Restart the Store application.
13. Attach the MDbg debugger.
14. Ask the MDbg debugger to catch all second-chance exceptions with the ca(tch) ex(ception)
command:
15.
16. [p#:0, t#:0] mdbg> ca ex
17. Resume the application and then click the Bad Action button. This time, the exception is trapped
in MDbg, and execution transfers to the debugger. In the debugger, the exception type and the
properties of the current exception object are dumped, providing important information on the
exception. The source code at the infraction is also displayed and shows the divide-by-zero
operation. You now have plenty of information to debug the problem.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
18.
19. [p#:0, t#:0] mdbg> g
20. STOP: Exception thrown
21. Exception=System.DivideByZeroException
22. _className=<null>
23. _exceptionMethod=<null>
24. _exceptionMethodString=<null>
25. _message="Attempted to divide by zero."
26. _data=<null>
27. _innerException=<null>
28. _helpURL=<null>
29. _stackTrace=array [24]
30. _stackTraceString=<null>
31.
32. _remoteStackTraceString=<null>
33. _remoteStackIndex=0
34. _dynamicMethods=<null>
35. _HResult=-2147352558
36. _source=<null>
37. _xptrs=1240048
38. _xcode=-1073741676
39. 134: a /= b;
MDbg Commands
MDbg has a full complement of commands for debugging managed applications. Some of the commands
were demonstrated in the previous walkthroughs. Table 13-1 lists the MDbg commands.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-1:
MDbg
Commands
Co De
m scr
ma ipti
nd on
? Th
an es
dh e
( are
elp the
) hel
p
co
m
ma
nd
s
an
d
dis
pla
y
the
MD
bg
co
m
ma
nd
s
wit
h
a
bri
ef
de
scr
ipti
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-1:
MDbg
Commands
Co De
m scr
ma ipti
nd on
ap( Thi
pro s
ce co
ss) m
ma
nd
swi
tch
es
to
an
oth
er
ma
na
ge
d
pro
ce
ss,
whi
ch
is
cur
ren
tly
bei
ng
de
bu
gg
ed.
Wit
ho
ut
par
am
ete
rs,
the
co
m
ma
nd
dis
pla
ys
the
att
ac
he
d
pro
ce
ss
es.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-1:
MDbg
Commands
Co De
m scr
ma ipti
nd on
a( Thi
tta s
ch) co
m
ma
nd
att
ac
he
s
the
MD
bg
de
bu
gg
er
to
a
ma
na
ge
d
pro
ce
ss.
Wit
ho
ut
par
am
ete
rs,
the
co
m
ma
nd
list
s
the
ava
ilab
le
ma
na
ge
d
pro
ce
ss
es.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-1:
MDbg
Commands
Co De
m scr
ma ipti
nd on
b( Thi
rea s
kp co
oin m
t) ma
nd
set
s
a
sp
ecif
ic
bre
ak
poi
nt
or
dis
pla
ys
all
the
bre
ak
poi
nts
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-1:
MDbg
Commands
Co De
m scr
ma ipti
nd on
ca( Thi
tch s
) co
m
ma
nd
sti
pul
ate
s
whi
ch
eve
nts
to
cat
ch.
It
ca
n
als
o
dis
pla
y
the
eve
nts
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-1:
MDbg
Commands
Co De
m scr
ma ipti
nd on
co Thi
nf( s
ig) co
m
ma
nd
set
s
a
par
tic
ula
r
co
nfig
ura
tio
n
or
dis
pla
ys
the
co
nfig
ura
tio
n
opt
ion
s.
del Thi
( s
ete co
) m
ma
nd
del
ete
s
a
bre
ak
poi
nt.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-1:
MDbg
Commands
Co De
m scr
ma ipti
nd on
de( Thi
tac s
h) co
m
ma
nd
det
ac
he
s
the
de
bu
gg
er
fro
m
the
cur
ren
t
de
bu
gg
ed
ap
plic
ati
on.
d( Thi
ow s
n) co
m
ma
nd
mo
ves
the
sta
ck
fra
me
do
wn.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-1:
MDbg
Commands
Co De
m scr
ma ipti
nd on
ec Thi
ho s
co
m
ma
nd
ec
ho
es
tex
t
to
the
dis
pla
y.
ex( Th
it) es
an e
dq co
(uit m
) ma
nd
s
exi
t
the
de
bu
gg
er.
fo( Thi
rea s
ch) co
m
ma
nd
ex
ec
ute
s
an
act
ion
on
all
thr
ea
ds.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-1:
MDbg
Commands
Co De
m scr
ma ipti
nd on
f( Thi
un s
cev co
al) m
ma
nd
call
s
a
me
tho
d.
g(o Thi
) s
co
m
ma
nd
res
um
es
ex
ec
uti
on
of
a
de
bu
gg
ed
ap
plic
ati
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-1:
MDbg
Commands
Co De
m scr
ma ipti
nd on
ig( Thi
nor s
e) co
m
ma
nd
dis
pla
ys
or
sti
pul
ate
s
whi
ch
eve
nts
to
ign
ore
.
Thi
s
co
m
ma
nd
co
mp
le
me
nts
the
ca(
tch
)
co
m
ma
nd.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-1:
MDbg
Commands
Co De
m scr
ma ipti
nd on
int( Thi
erc s
ept co
) m
ma
nd
int
erc
ept
s
ex
ce
pti
on
at
the
sp
ecif
ied
sta
ck
fra
me
.
k( Thi
ill) s
co
m
ma
nd
kill
s
an
act
ive
pro
ce
ss.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-1:
MDbg
Commands
Co De
m scr
ma ipti
nd on
l( Thi
ist) s
co
m
ma
nd
list
s
loa
de
d
mo
dul
es,
Ap
pD
om
ain
s,
or
as
se
mb
lies
.
lo( Thi
ad) s
co
m
ma
nd
loa
ds
an
MD
bg
ext
en
sio
n.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-1:
MDbg
Commands
Co De
m scr
ma ipti
nd on
mo Thi
(de s
) co
m
ma
nd
set
s
a
sp
ecif
ic
MD
bg
opt
ion
or
dis
pla
ys
all
the
opt
ion
s.
ne Thi
wo( s
bj) co
m
ma
nd
cre
ate
s
an
ins
tan
ce
of
a
typ
e.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-1:
MDbg
Commands
Co De
m scr
ma ipti
nd on
n( Thi
ext s
) co
m
ma
nd
s
ste
ps
ove
r
the
ne
xt
ins
tru
cti
on.
o( Thi
ut) s
co
m
ma
nd
ste
ps
out
of
a
fun
cti
on.
pa( Thi
th) s
co
m
ma
nd
set
s
or
dis
pla
ys
the
so
urc
e
pat
h.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-1:
MDbg
Commands
Co De
m scr
ma ipti
nd on
p( Thi
rint s
) co
m
ma
nd
dis
pla
ys
the
val
ue
s
of
the
loc
al
or
de
bu
g
vari
abl
es.
pro Thi
c( s
es co
se m
nu ma
m) nd
en
um
era
tes
the
ava
ilab
le
ma
na
ge
d
pro
ce
ss
es.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-1:
MDbg
Commands
Co De
m scr
ma ipti
nd on
re( Thi
su s
me co
) m
ma
nd
res
um
es
a
su
sp
en
de
d
thr
ea
d.
r( Thi
un) s
co
m
ma
nd
run
s
a
pro
gra
m
wit
h
the
MD
bg
de
bu
gg
er
im
me
dia
tel
y
att
ac
he
d.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-1:
MDbg
Commands
Co De
m scr
ma ipti
nd on
set Thi
s
co
m
ma
nd
set
s
a
loc
al
or
de
bu
g
vari
abl
e
to
a
ne
w
val
ue.
set Thi
ip s
co
m
ma
nd
mo
ves
the
ins
tru
cti
on
poi
nte
r
wit
hin
the
cur
ren
t
fun
cti
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-1:
MDbg
Commands
Co De
m scr
ma ipti
nd on
sh( Thi
ow) s
co
m
ma
nd
sh
ow
s
the
so
urc
e
co
de
at
the
cur
ren
t
ins
tru
cti
on.
s( Thi
tep s
) co
m
ma
nd
ste
ps
int
o
a
fun
cti
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-1:
MDbg
Commands
Co De
m scr
ma ipti
nd on
su( Thi
sp s
en co
d) m
ma
nd
su
sp
en
ds
a
run
nin
g
thr
ea
d.
sy( Thi
mb s
ol) co
m
ma
nd
set
s
or
dis
pla
ys
the
sy
mb
ol
pat
h.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-1:
MDbg
Commands
Co De
m scr
ma ipti
nd on
t( Thi
hre s
ad) co
m
ma
nd
swi
tch
es
to
a
sp
ecif
ied
thr
ea
d
or
dis
pla
y
all
thr
ea
ds.
U(p Thi
) s
co
m
ma
nd
mo
ves
up
the
sta
ck
fra
me
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-1:
MDbg
Commands
Co De
m scr
ma ipti
nd on
uw Thi
gc( s
ha co
ndl m
e) ma
nd
dis
pla
ys
the
obj
ect
sp
ecif
ied
by
the
GC
ha
ndl
e.
wh Thi
en s
co
m
ma
nd
ex
ec
ute
s
a
co
m
ma
nd
ba
se
d
on
a
de
bu
gg
er
eve
nt.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-1:
MDbg
Commands
Co De
m scr
ma ipti
nd on
w( Thi
her s
e) co
m
ma
nd
dis
pla
ys
a
sta
ck
tra
ce.
x Thi
s
co
m
ma
nd
dis
pla
ys
the
fun
cti
on
s
in
a
mo
dul
e
or
list
s
all
the
mo
dul
es.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-1:
MDbg
Commands
Co De
m scr
ma ipti
nd on
Ctrl Thi
+C s
ke
yst
rok
e
int
err
upt
s
a
run
nin
g
ap
plic
ati
on.
Ctrl Thi
+B s
rea ke
k yst
rok
e
ter
mi
nat
es
the
run
nin
g
ap
plic
ati
on
an
d
exi
ts
the
de
bu
gg
er.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
WinDbg
WinDbg is both a kernel- and user-mode debugger. It is pronounced Windbag, Win"d-b-g," or, more
intuitively, WinDebug. For many developers, WinDbg is the center of the advanced debugging universe. It has
been available for some time and has evolved to encompass an impressive array of commands. Some of
these commands are admittedly bewildering but always interesting. I have taught the .NET Advanced
Debugging Workshop at Microsoft for years, which includes coverage of WinDbg. I still learn something new
and amazing about WinDbg almost every month.
The focus of this book is C# and managed code. This is not the ideal place to troll the depths of WinDbg. It
would be fun, but probably not entirely relevant. However, some basic WinDbg commands are helpful even
when debugging managed applications. Although WinDbg offers a basic user interface, most developers
operate from the command line. For this reason, we will use the user interface in a limited manner.
When WinDbg is launched, the debugger can be attached to an application using commandline arguments.
This requires the process identifier of the debuggee. Tlist is a utility installed with Debugging Tools for
Windows to lists the process identifier of active processes.
The following is sample output from the Tlist utility. Applications are listed in execution sequence. The first
column is the process identifier, the second column is the program, and the final column contains a
description, if available.
C:\store>tlist
0 System Process
4 System
784 smss.exe
844 csrss.exe
872 winlogon.exe
916 services.exe
928 lsass.exe
736 KHALMNPR.exe KHALHPP_MainWindow
1052 gcasDtServ.exe GIANT AntiSpyware Data Service
1444 DVDRAMSV.exe
1488 inetinfo.exe
1524 mdm.exe
1748 sqlservr.exe
1828 nvsvc32.exe NVSVCPMMWindowClass
1440 wscntfy.exe
2380 iPodService.exe
2620 alg.exe
3384 iTunes.exe iTunes
3648 WINWORD.EXE MarshallChap14_0830 - Microsoft Word
2892 cmd.exe Visual Studio .NET Whidbey Command Prompt - tlist
2572 Store.exe Store
2696 tlist.exe
windbg -p 2572
You can also attach to a running process simply with the application name:
Alternatively, WinDbg can be started without being attached to anything. You can later attach to an
application using the File menu of the WinDbg user interface. From the File menu, select the Attach to a
Process command. Choose the debuggee from the list of available processes. The Open Executable
command on the same menu starts an application and immediately attaches the debugger. This is
convenient if the application is not already running.
When using WinDbg, some essential commands are helpful. Most of the WinDbg commands are case
insensitive, but there are exceptions. Table 13-2 lists the basic commands.
Table 13-2:
Basic
WinDbg
Commands
Co De
m scr
ma ipti
nd on
g(o Thi
) s
co
m
ma
nd
res
um
es
ex
ec
uti
on
of
the
de
bu
gg
ed
ap
plic
ati
on.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-2:
Basic
WinDbg
Commands
Co De
m scr
ma ipti
nd on
Ctrl Thi
+B s
rea ke
k yst
rok
e
int
err
upt
s
the
run
nin
g
ap
plic
ati
on.
q( Thi
uit) s
co
m
ma
nd
qui
ts
the
de
bu
gg
er.
? Thi
s
co
m
ma
nd
dis
pla
ys
hel
p
do
cu
me
nta
tio
n.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Now that the basic commands have been presented, we can discuss the more interesting commands.
Displaying the active threads and changing thread context are frequent requests for any debugger. In
WinDbg, the tilde (~) command is for thread manipulation.
The ~ command without parameters lists the threads. Thread identifier, status, and address of the
thread environment block include some of the information reported. If the thread context is
elsewhere, the current thread is highlighted with the pound (#) prefix.
The ~n command displays information on the specified thread, where n is the thread number.
Thread priority, status, priority class, and other thread-relevant information is presented.
The ~ns command changes the thread context to the thread that is indicated. The WinDbg
prompt is updated to reflect the new thread context. When the context is changed, the context
record of the new thread is displayed, which includes the register values.
The following demonstration lists the threads, displays information pertaining to Thread 2, and then selects
Thread 3 as the current thread:
0:001> ~
0 Id: a1c.9c0 Suspend: 1 Teb: 7ffde000 Unfrozen
. 1 Id: a1c.804 Suspend: 1 Teb: 7ffdd000 Unfrozen
2 Id: a1c.ea0 Suspend: 1 Teb: 7ffdc000 Unfrozen
3 Id: a1c.de0 Suspend: 1 Teb: 7ffdb000 Unfrozen
# 4 Id: a1c.c04 Suspend: 1 Teb: 7ffda000 Unfrozen
0:001> ~2
2 Id: a1c.ea0 Suspend: 1 Teb: 7ffdc000 Unfrozen
Start: mscorwks!Thread::intermediateThreadProc (79ee80cf)
Priority: 2 Priority class: 32
0:001> ~3s
eax=4ec62ef0 ebx=0103fe7c ecx=0000d9c7 edx=7c90eb94 esi=00000000 edi=7ffdf000
eip=7c90eb94 esp=0103fe54 ebp=0103fef0 iopl=0 nv up ei pl zr na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!KiFastSystemCallRet:
7c90eb94 c3 ret
In WinDbg, variations of the k command present varied permutations of a stack trace. Optionally, you can
follow a stack trace command with a number, which indicates the depth of the call stack.
Table 13-3:
Stack Trace
Commands
Co De
m scr
ma ipti
nd on
k Thi
s
co
m
ma
nd
list
s
the
me
tho
ds
on
the
call
sta
ck.
In
ad
diti
on,
the
chil
d
fra
me
poi
nte
r
an
d
the
ret
urn
ad
dre
ss
of
the
call
ing
me
tho
d
are
list
ed
in
col
um
ns.
No
par
am
ete
rs
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-3:
Stack Trace
Commands
Co De
m scr
ma ipti
nd on
kb Thi
s
co
m
ma
nd
list
s
the
call
sta
ck
an
d
the
firs
t
thr
ee
par
am
ete
rs
of
the
me
tho
ds.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-3:
Stack Trace
Commands
Co De
m scr
ma ipti
nd on
kp Thi
s
co
m
ma
nd
list
s
the
call
sta
ck
an
d
the
ent
ire
par
am
ete
r
list
of
the
me
tho
ds.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-3:
Stack Trace
Commands
Co De
m scr
ma ipti
nd on
kn Thi
s
co
m
ma
nd
list
s
the
call
sta
ck
wit
h
the
fra
me
nu
mb
er
for
ea
ch
me
tho
d.
Yo
u
ca
n
us
e
the
fra
me
info
rm
ati
on
to
mo
ve
bet
we
en
fra
me
s
on
the
call
sta
ck
usi
ng
the
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The following is the call stack from a thread in the Store application. Three parameters are displayed
because the kb command is used.
0:003> kb
ChildEBP RetAddr Args to Child
0103fe50 7c90e9ab 7c8094f2 00000002 0103fe7c ntdll!KiFastSystemCallRet
0103fe54 7c8094f2 00000002 0103fe7c 00000001
ntdll!ZwWaitForMultipleObjects+0xc
0103fef0 77d495f9 00000002 0103ff18 00000000
KERNEL32!WaitForMultipleObjectsEx+0x12c
0103ff4c 77d496a8 00000001 0103ffac ffffffff
USER32!RealMsgWaitForMultipleObjectsEx+0x13e
0103ff68 4ec95846 00000001 0103ffac 00000000
USER32!MsgWaitForMultipleObjects+0x1f
0103ffb4 7c80b50b 00000000 00000000 0012e0d0 gdiplus!BackgroundThreadProc+0x59
0103ffec 00000000 4ec957ed 00000000 00000000 KERNEL32!BaseThreadStart+0x37
What if you want the stack trace of every thread? Each thread could be selected and a k command
submitted. However, that approach becomes tedious if there are dozens of threads. The solution is '~*'.
Commands prefixed with '~*' are applied to all threads of the application. This command performs a stack
trace on every thread:
~* k
0:003> d 0103fe50
0103fe50 cc 33 66 00 ab e9 90 7c-f2 94 80 7c 02 00 00 00 .3f....|...|....
0103fe60 7c fe 03 01 01 00 00 00-00 00 00 00 00 00 00 00 |...............
The second letter (L) of the display command varies based on the actual display command, such as dc, dd,
and du. The second letter is also case sensitive. address1 is the beginning address, whereas address2 is
the ending address. Memory is displayed from address1 to address2. Omit the ending address, and a
default number of bytes are displayed. If neither the beginning nor the ending memory address is provided,
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Co De
m scr
ma ipti
nd on
d Thi
s
co
m
ma
nd
rep
eat
s
the
pre
vio
us
dis
pla
y
co
m
ma
nd
an
d
def
aul
ts
to
the
db
co
m
ma
nd.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-4:
Common
Display
Memory
Commands
Co De
m scr
ma ipti
nd on
da Thi
s
co
m
ma
nd
dis
pla
ys
the
AS
CII
int
erp
ret
ati
on
of
the
me
mo
ry.
dc Thi
s
co
m
ma
nd
dis
pla
ys
the
dat
a
in
fou
r-b
yte
col
um
ns.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-4:
Common
Display
Memory
Commands
Co De
m scr
ma ipti
nd on
dd Thi
s
co
m
ma
nd
is
the
sa
me
as
dc,
ex
ce
pt
the
byt
e
tra
nsl
ati
on
is
om
itte
d
at
the
en
d
of
ea
ch
row
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-4:
Common
Display
Memory
Commands
Co De
m scr
ma ipti
nd on
du Thi
s
co
m
ma
nd
dis
pla
ys
the
Uni
co
de
int
erp
ret
ati
on
of
the
me
mo
ry.
The following command has four-byte columns of memory, three columns per row, and a starting address.
The /c option controls the number of columns displayed:
0:003> dd /c 3 0103fe50
0103fe50 006633cc 7c90e9ab 7c8094f2
0103fe5c 00000002 0103fe7c 00000001
0103fe68 00000000 00000000 00000000
0103fe74 00000002 00000000 00000694
0103fe80 0000068c 009999cc 00cc99cc
0103fe8c 00ff99cc 0000cccc 0033cccc
0103fe98 0066cccc 00000014 00000001
0103fea4 00000000 00000000 00000010
0103feb0 0066ffcc 0099ffcc 00ccffcc
0103febc 7ffdb000 7ffdc000 003300ff
0103fec8 00000000 0103fe7c
Co Description
m
ma
nd
bp This is the
standard
breakpoint
command.
The program
is interrupted
when
execution
reaches the
specified
location.
This is an
abbreviated
syntax for the
bp command:
bp
locatio
n
options
The /1 option
is useful for
defining
one-off
breakpoints.
This type of
breakpoint is
automatically
removed after
being hit.
Therefore, the
breakpoint is
reached only
once.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Co Description
m
ma
nd
ba This
command is
break on
access to a
memory
address.
This is the
abbreviated
syntax for the
ba command:
ba
options
size
addres
s
options
specifies the
action to
break on:
e—
execut
e
r—
read/wr
ite
w—
write
i-
input/o
utput
size is the
width of the
memory
address.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Co Description
m
ma
nd
bc This
command
clears a
breakpoint.
You can clear
multiple
breakpoints in
a space- or
comma-delimit
ed list.
Alternatively,
specify a
range with a
hyphen.
bl This
command
lists the
available
breakpoints.
Multiple
breakpoints
can be
specified as
described for
the bc
command.
The following is a demonstration of the breakpoint command. The sxe command is the set exception
command. This command requests that the debugger interrupt on an exception or other event. In this
example, the sxe command asks the debugger to interrupt when the mscorwks module is loaded, which is
where the Common Language Runtime (CLR) is found. For a compound statement, use a semicolon. In the
following command, the sxe and g commands are combined into a compound statement.
The following command breaks when the mscorwks module is loaded (the application is then resumed):
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll
0:000> bp mscorwks!SystemDomain::ExecuteMainMethod;g
ModLoad: 78130000 781ca000
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\MSVCR80.dll
ModLoad: 7c9c0000 7d1d4000 C:\WINDOWS\system32\shell32.dll
ModLoad: 773d0000 774d2000 C:\WINDOWS\WinSxS\x86_Microsoft.Windows.Common-
Controls_6595b64144ccf1df_6.0.2600.2180_x-ww_a84f1ff9\comctl32.dll
ModLoad: 5d090000 5d127000 C:\WINDOWS\system32\comctl32.dll
ModLoad: 60340000 60348000
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\culture.dll
ModLoad: 790c0000 79baa000
C:\WINDOWS\assembly\NativeImages_v2.0.50727_32\mscorlib\cee6ddb
471db1c489d9b4c39549861b5\mscorlib.ni.dll
Breakpoint 0 hit
eax=0012ff38 ebx=00000002 ecx=00000000 edx=ffffffff esi=00000000 edi=00000000
eip=79efb428 esp=0012ff1c ebp=0012ff68 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
mscorwks!SystemDomain::ExecuteMainMethod:
79efb428 55 push ebp
After the breakpoint is hit, the kb command performs a stack trace. You see ExecuteMainMethod in the call
stack:
0:000> kb
ChildEBP RetAddr Args to Child
0012ff18 79efb3cb 00400000 00000000 b4ebfe93
mscorwks!SystemDomain::ExecuteMainMethod
0012ff68 79ef8bc8 00400000 b4ebfe4b 00080000 mscorwks!ExecuteEXE+0x59
0012ffb0 790122f6 00d9fa9c 79e70000 0012fff0 mscorwks!_CorExeMain+0x11b
0012ffc0 7c816d4f 00080000 00d9fa9c 7ffd8000 mscoree!_CorExeMain+0x2c
0012fff0 00000000 790122c2 00000000 78746341 KERNEL32!BaseProcessStart+0x23
Step Commands
After reaching a breakpoint, it is common to step through an application and evaluate the results. There are
also plenty of other reasons and opportunities to step through an application. The best means of stepping
through an application is by using the Debugging toolbar. (Choose the View menu and the Toolbar option to
display the Debugging toolbar.)
Figure 13-6 shows the Step buttons on the Debugging toolbar: the Step In, Step Over, Step Out, and Run to
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Cursor buttons.
WinDbg Directives WinDbg commands affect the application currently being debugged. The commands
display, modify, or otherwise affect or inspect the debuggee. Conversely, WinDbg directives alter the
debugging session. For example, the .load directive loads a debugging extension dynamic-link library (DLL).
The .logopen directive opens a log file and echoes any subsequent activity to this file.
There are several WinDbg directions. Table 13-6 includes some of the available directives. Directives are
prefixed with a dot (.).
Table 13-6: WinDbg
Directives
Co Description
m
ma
nd
.lo This
ad command
dlln loads a
am debugger
e extension
DLL.
Extension
commands
are exposed
as the
exported
functions of
the DLL.
Developers of
managed
code routinely
load the SOS
(SOS.dll)
debugger
extension.
Commands
from debugger
extensions
are prefixed
with an
exclamation
point (!).
.un This
loa command
d unloads a
dlln debugger
am extension.
e
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Co Description
m
ma
nd
.ch This
ain command
lists the
debugger
extensions
that are
presently
available.
.rel This
oa command
d reloads
symbols and
is usually
requested
after the
symbol path
has been
updated.
Normally,
symbols are
retrieved as
needed. The /f
option forces
the immediate
load of all
symbols.
.lo This
go command
pe opens a log
n file and
file echoes any
na subsequent
me activity to this
file.
.lo This
gcl command
os closes the log
e file.
.kil This
l command
terminates the
current
debuggee and
ends the
debugging
session.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Co Description
m
ma
nd
.fra This
me command
n shifts the
stack to the
designated
frame number
or displays
the current
local context.
.sr This
cp command
ath sets or
displays the
source code
path.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Co Description
m
ma
nd
.du This
mp command
opt creates a
ion user- or
s kernel-mode
file dump, which
na is used for
me postmortem
analysis.
.dump
filename
creates a
minidump.
The following
command
creates a
minidump with
full memory
and handle
information:
.dump
/mfh
filenam
e
This
command
creates a full
dump. The full
dump
command is
available only
in
kernel-mode
operations of
WinDbg
(which is not
discussed in
this chapter):
.dump
/f
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Any of the following statements loads the SOS extension DLL. To load a particular version of SOS, provide a
fully qualified directory path. When live debugging, the current version of WinDbg automatically loads SOS.
However, it is not automatically loaded for postmortem analysis.
.load sos
.load sos.dll
.load c:\path\sos.dll
This walkthrough is an introduction to SOS. Details from previous walkthroughs and demonstration are
omitted for brevity. Some of the listings have also been abbreviated for clarity.
1. Start the Store application. Open the Transaction dialog box, as shown in Figure 13-7. For this
debugging session, do not complete or close this dialog box.
2. Launch the WinDbg debugger and then attach it to the Store application.
3. SOS has a !threads command, which lists the managed threads of the application. Use the
!threads command. This is the abbreviated result:
4.
5. 0:004> !threads
6. ThreadCount: 2
7. UnstartedThread: 0
8. BackgroundThread: 1
9. PendingThread: 0
10. DeadThread: 0
11. Hosted Runtime: no
12.
13. PreEmptive GC Alloc
Lock
14. ID OSID ThreadOBJ State GC Context Domain
Count
15. 0 1 424 001501f8 6020 Enabled 013da5dc:013dadb8 001483a8
0
16. 2 2 c24 00153e40 b220 Enabled 00000000:00000000 001483a8
0
17. Change to Thread 0, which is a managed thread.
18.
19. 0:004> ~0s
20. eax=790ff90c ebx=01392b50 ecx=013a7704 edx=0000ce7d esi=00000000
edi=013da5b8
21. eip=7c90eb94 esp=0012edb4 ebp=0012ee4c iopl=0 nv up ei pl zr
na po nc
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
SOS Commands
Table 13-7 is an overview of some of the SOS commands. For a complete listing, see the help command.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Co Description
m
ma
nd
!Clr This
Sta command
ck displays the
call stack of
the current
thread.
Here are
some of the
options:
The -l
option
include
s the
local
variable
s in
the
output.
The -p
option
include
s the
parame
ters in
the
output.
The -a
option
combin
es
both
the -l
and -p
options
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Co Description
m
ma
nd
!Du This
mp command first
He displays the
ap objects that
are on the
managed
heap. It then
displays
statistics
about the type
of objects on
the managed
heap.
Here are
some of the
options:
The
-stat
option
display
s the
type of
objects
alone.
The
-min
option
exclud
es
objects
less
than
the
minimu
m
addres
s from
the
report.
The
-max
option
exclud
es
objects
greater
than
the
maxim
um
addres
s from
the
report.
The -mt
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Co Description
m
ma
nd
!Du This
mp command
IL dumps the
md MSIL of the
ad method
dre associated
ss with the
method
descriptor.
!Du This
mp command
MT dumps
mt information
ad about the
dre method table.
ss The -md option
lists
information on
the method
descriptors in
the method
table, such as
the function
names and
method
descriptor
addresses.
!Du This
mp command
Ob dumps
j information on
obj the specific
ad object.
dre
ss
!Du This
mp command
Sta lists value
ck types and
Ob references
jec that are on
ts stack of the
opt current
ion thread. The
only option is
-verify.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Co Description
m
ma
nd
!E This
EH command
ea dumps
p information on
opt Generation 0,
ion 1, 2, and the
s managed
heap. The
-loader option
lists the
private heaps
of each
AppDomain,
module, and
heap
associated
with the JIT
compiler.
!E This
EV command
ers displays
ion information of
the run-time
environment,
such as the
version
number.
!Fi This
nali command
ze lists the
Qu objects on the
eu finalization
e queue. For
opt additional
ion information on
SyncBlocks
and
RuntimeCallab
leWrappers,
use the -detail
option.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Co Description
m
ma
nd
!G This
Cro command
ot lists
-op instances that
tio hold
ns references to
obj the specified
ad object. The
dre -nostacks
ss option
excludes
references
held on the
stack.
!IP This
2M command
D displays the
jita method
ddr descriptor of a
es jitted method.
s
!Na If target is a
me type, the
2E command
E dumps the
pro method table
gra of that type. If
m target is a
tar method name,
get the method
descriptor of
that method is
dumped.
!Sy This
nc command
blk lists the
indexes of the
sync block
table.
!Th This
rea command
ds lists the
managed
threads.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Co Description
m
ma
nd
!He This
lp command
co displays
m detailed help
ma on SOS
nd commands.
Without the
command
option, an
overview of all
the
commands is
displayed.
Now that a few more commands have been introduced, an additional walkthrough is helpful. The following
walkthrough dumps the source code, MSIL, and assembly code of the btnTransaction_Click button handler,
which is informative.
1. Start the walkthrough by running the Store application and attach WinDbg to the application.
2. Dump information about the btnTransaction_Click method with the !name2ee command. Notice
that the method has not been jitted yet, which means that the method has not been invoked
before. A function is jitted when it is first invoked.
3.
4. 0:004> !name2ee store.exe Store.Form1.btnTransaction_Click
5. Module: 00d40c14 (Store.exe)
6. Token: 0x06000004
7. MethodDesc: 00d43968
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Dumps
Dumps are used for postmortem analysis, which is sometimes the most effective means of debugging an
application. When a program intermittently crashes, postmortem analysis may be the sole option to resolve
the problem. You can capture a dump when the crash occurs, which is then used to diagnose the problem.
Production applications are not ideal for live debugging. How do you perform live debugging on an active Web
server? Convenience is another advantage to postmortem analysis. You remove the dump to a developer
machine that hosts an assortment of debugging tools and resources. In this environment, diagnosing the
problem is much easier.
Remember that memory dumps are static. The debugging paradigm is different from performing live
debugging. You cannot step through the application, threads cannot be restarted, and so on.
As documented earlier, the .dump directive in WinDbg creates dump files. Dump files can also be created
with ADPlus and Dr. Watson tools.
ADPlus
The ADPlus tool is downloaded from the Debugging Tools for Windows Web site at Microsoft.com. It is a
Microsoft VBScript that automates the CDB debugger. Use ADPlus to debug applications that hang or
crash. The utility creates dump and log files for postmortem analysis. Unique names are assigned to these
files to avoid overriding previously generated files. ADPlus operates in either the crash or hang mode.
In crash mode, ADPlus attaches cdb to the target processes. It is attached invasively. You run ADPlus
before the application crashes. At that time, a minidump and log file are created. ADPlus will also write an
entry into the Event Log. A crash is interpreted as the application ending from an unhandled exception or
other abnormal condition. To detach prematurely before a crash, open the minimized CDB console window
and press Ctrl+C. This is the syntax of running ADPlus in the crash mode. Multiple processes can be
attached to simultaneously. All instances of the named processes are attached.
Alternatively, the process identifier can be used to identify the target processes:
adplus -crash -p pid 1 -p pid 2 -p pid n
In hang mode, run ADPlus after the hang occurs, which attaches CDB to the target application noninvasively.
The debuggee is resumed after the dump is created. This is the syntax to start ADPlus in the hang mode:
adplus -hang -pn processname 1 -pn processname 2 -pn processname n
adplus -hang -p pid 1 -p pid 2 -p pid n
O(utput) option This option sets the output directory for dump and log files.
N(otify) option This option notifies a user that an application has crashed.
ADPlus Walkthrough The Store application for this walkthrough has an error and unexpectedly crashes.
ADPlus is used to create a dump when the application crashes. The dump is then opened in WinDbg, and
the problem is isolated:
1. Start the Store application.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
2. Click the Hang button. As expected, this procedure hangs the application.
3. Run ADPlus in hang mode to attach the CDB debugger to the Store application. Get the correct
process identifier from the tlist utility:
4.
5. C:\store>adplus -hang -p 3520 -o c:\dumps
6. Attaching the debugger to: STORE.EXE
7. (Process ID: 3520)
8. Start WinDbg and open the dump. From the File menu, choose the Open Crash Dump command.
Find and open the dump.
9. Load the Son of Strike extension. Change to Thread 0 and request a managed stack trace. The
following is a partial listing of the call stack. It shows correctly that the btnHang_Click handler
was the last method entered. This provides a starting point in uncovering the culprit that caused
the hang.
10.
11. 0:000> .load sos
12. 0:000> !clrstack
13. OS Thread Id: 0xae8 (0)
14. ESP EIP
15.
16. 0012f030 00de0906 Store.Form1.btnHang_Click(System.Object,
System.EventArgs)
17. 0012f044 7b070a8b
System.Windows.Forms.Control.OnClick(System.EventArgs)
18. 0012f054 7b114cd9 System.Windows.Forms.Button.OnClick(System.EventArgs)
19. 0012f060 7b114ddf
System.Windows.Forms.Button.OnMouseUp(System.Windows.Forms.MouseEventAr
gs)
20. 0012f084 7b0dfeea
System.Windows.Forms.Control.WmMouseUp(System.Windows.Forms.Message
21. ByRef, System.Windows.Forms.MouseButtons, Int32)
22. 0012f0d0 7b082bbf
System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message
ByRef)
23. 0012f0d4 7b09149e [InlinedCallFrame: 0012f0d4]
24. 0012f170 7b0913bb
System.Windows.Forms.Button.WndProc(System.Windows.Forms.Message ByRef)
25. 0012f178 7b08a70d
System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Wi
26. ndows.Forms.Message ByRef)
27. 0012f17c 7b08a6e6
System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows
28. .Forms.Message ByRef)
29. 0012f190 7b08a535 System.Windows.Forms.NativeWindow.Callback(IntPtr,
Int32, IntPtr, IntPtr)
30. 0012f324 003420d4 [NDirectMethodFrameStandalone: 0012f324]
System.Windows.Forms.Unsafe
31. NativeMethods.DispatchMessageW(MSG ByRef)
32. 0012f334 7b094682
System.Windows.Forms.Application+ComponentManager.System.Windows.For
33. ms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32,
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Int32, Int32)
34. 0012f3d4 7b094249
System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(I
35. nt32, System.Windows.Forms.ApplicationContext)
36. 0012f440 7b094087
System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32,
37. System.Windows.Forms.ApplicationContext)
38. 0012f470 7b0d66ea
System.Windows.Forms.Application.Run(System.Windows.Forms.Form)
39. 0012f480 00de00a8 Store.Program.Main()
40. 0012f69c 79e80b8b [GCFrame: 0012f69c]
Dr. Watson
Dr. Watson (drwtsn32.exe) also creates dumps. This product is installed with the operating system. On
nondeveloper machines, Dr. Watson is the only JIT debugger present. Dr. Watson attaches to a failing
application to create a log file and optionally a dump file.
Figure 13-8 shows the main window of Dr. Watson. In this window, you configure Dr. Watson for JIT
debugging.
The following list describes the configurable items in the Dr. Watson window:
Log File Path The path for the log files created by the Dr. Watson.
Wave File The path to whatever musical accompaniment you deem appropriate for an application
failure. Flight of the Valkyrie might be appropriate.
Number of Instructions The number of instructions to disassemble around the instruction pointer.
Number of Errors to Save The maximum number of errors to save in the log file.
Crash Dump Type The type of dump request: full, mini, or NT-compatible.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Dump Symbol Table Dump the name and address of every symbol into the log file.
Dump All Thread Contexts Include the thread context, register values, and other context data in
the log file.
Append To Existing Log File Add the next log entry from a program failure to the current log
file. If not selected, a new log file is created for each failure.
Visual Notification Display a dialog box to notify of program failure and just-in time debugging.
Sound Notification Play the Flight of the Valkyrie or whatever WAV file is specified.
Create Crash Dump File When a program fails, create a dump file.
Dr. Watson Walkthrough Following is a walkthrough of Dr. Watson, which is the only walkthrough that
does not use the Store application. The walkthrough uses the Crash application. The Crash application fails
immediately after the application is launched.
1. Make sure that Dr. Watson is installed as the native debugger.
2.
3. C:\>drwtsn32 -i
4. Configure Dr. Watson to create a minidump file, set the dump file path and name, and then
request notification. Accept all other defaults.
5. Run the Crash program. This program should immediately hang, which will prompt notification
from Dr. Watson.
6. Confirm that the dump and log file are created. Inspect the log file, which includes general
information, system information, task list, module list, thread context, stack trace, symbol table,
and a variety of raw dumps:
7.
8. Microsoft (R) DrWtsn32
9. Copyright (C) 1985-2001 Microsoft Corp. All rights reserved.
10.
11. Application exception occurred:
12. App: C:\codew\Crash\Debug\Crash.exe (pid=4004)
13.
14. When: 9/5/2005 @ 08:19:29.796
15. Exception number: 80000007
16. ()
17.
18. *----> System Information <----*
19. Computer Name: DONISMLAPTOP
20. User Name: Donis
21. Terminal Session Id: 0
22. Number of Processors: 2
23. Processor Type: x86 Family 15 Model 2 Stepping 9
24. Windows Version: 5.1
25. Current Build: 2600
26. Service Pack: 2
27. Current Type: Multiprocessor Free
28. Registered Organization:
29. Registered Owner: Donis
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
30.
31. *----> Task List <----*
32. 0 System Process
33. 4 System
34. 788 smss.exe
35. 908 csrss.exe
36. 932 winlogon.exe
37. 976 services.exe
38. 988 lsass.exe
39. 1152 svchost.exe
40. 1220 svchost.exe
41.
42. *----> Module List <----*
43. (0000000000400000 - 000000000041a000: C:\codew\Crash\Debug\Crash.exe
44. (0000000010200000 -
45. 0000000010320000:
C:\WINDOWS\WinSxS\x86_Microsoft.VC80.DebugCRT_1fc8b3b9a1e18e3b_8.0.
46. 50727.7_x-ww_ec5d0b23\MSVCR80D.dll
47. (00000000629c0000 - 00000000629c9000: C:\WINDOWS\system32\LPK.DLL
48. (0000000074d90000 - 0000000074dfb000: C:\WINDOWS\system32\USP10.dll
49. (0000000076390000 - 00000000763ad000: C:\WINDOWS\system32\IMM32.DLL
50. (0000000077b40000 - 0000000077b62000: C:\WINDOWS\system32\Apphelp.dll
51. (0000000077c00000 - 0000000077c08000: C:\WINDOWS\system32\VERSION.dll
52. (0000000077c10000 - 0000000077c68000: C:\WINDOWS\system32\msvcrt.dll
53. (0000000077d40000 - 0000000077dd0000: C:\WINDOWS\system32\USER32.dll
54.
55. *----> State Dump for Thread Id 0xb5c <----*
56.
57. eax=00390000 ebx=0012f6fc ecx=00001000 edx=7c90eb94 esi=0000007c
edi=00000000
58. eip=7c90eb94 esp=0012f6e0 ebp=0012f9e8 iopl=0 nv up ei pl nz
na pe nc
59. cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000
efl=00000202
60.
61. *----> Stack Back Trace <----*
62. ChildEBP RetAddr Args to Child
63. 0012f9e8 102116d1 0012fa5c 00000002 00000000 ntdll!KiFastSystemCallRet
64. 0012fa0c 004119a2 c0000005 0012fa5c 1021c2b4 MSVCR80D!XcptFilter+0x61
65. 0012fa18 1021c2b4 00000000 00000000 00000000
Crash!__tmainCRTStartup+0x1f2
66. 0012ffb8 0041179d 0012fff0 7c816d4f 023dd680
MSVCR80D!seh_longjmp_unwind4+0x2e
67. 0012ffc0 7c816d4f 023dd680 7c90e1fe 7ffdd000 Crash!wmainCRTStartup+0xd
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Memory Management
Traditionally, memory-related issues have been the impetus to a vast majority of bugs. Win32 processes,
including managed applications that run in the Windows environment, own several assets. Of those assets,
virtual memory is one of the most important. Win32 processes normally own four gigabytes of virtual
memory, where the operating system resides in the upper two gigabytes. The upper two gigabytes are
shared and protected. There is no reason to load the operating system for each instance of a Win32
process. The lower two gigabytes are private memory in which the application code, heaps, static data area,
stack, and other aspects of the application are loaded. This memory is private and protected from access by
other processes. Virtual Memory Manager (VMM), which is the kernel-level component of the NT Executive,
guards private memory from incidental or deliberate external modifications.
The managed heap is created in the private memory of a managed application. There are several families of
APIs that allocate memory from the available virtual memory, including the Heap APIs such as HeapCreate,
HeapAlloc, and HeapFree. The Memory Mapped family of APIs include CreateFileMapping, MapViewOfFile,
UnmapViewOfFile, and related functions. The Virtual APIs include VirtualAlloc, VirtualFree, and others.
Internally, the Heap and Memory Mapped File APIs decompose to Virtual APIs.
Initially, the garbage collector (GC) calls VirtualAlloc with the MEM_RESERVE flag and reserves a block of
memory. It then requests memory from the reserved area with successive calls to VirtualAlloc, but with the
MEM_COMMIT flag. When committing memory, the first parameter of VirtualAlloc is the base address to a
block of committed memory. The GC keeps the ending address of the previous memory allocation. With that
information, it can calculate the base address of the next block of committed memory, which is stacked
upon the previous allocation. This is a quick and efficient method of allocating memory. VirtualAlloc can be
called with a null first parameter, which requires the VMM search for an appropriate location to commit
memory, which is expensive. This is the syntax of VirtualAlloc:
LPVOID VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize,
DWORD flAllocationType, DWORD flProtect)
The following is a stack trace and shows VirtualAlloc being called in a managed program. The first argument
(0x00b54000) is the location of the allocation. This is where the memory is being committed.
0:000> kb
ChildEBP RetAddr Args to Child
0012e674 79e74391 00b54000 00001000 00001000 KERNEL32!VirtualAlloc
0012e6b4 79e74360 00b54000 00001000 00001000 mscorwks!EEVirtualAlloc+0x104
0012e6c8 79e74348 7a38b1b0 00b54000 00001000
mscorwks!CExecutionEngine::ClrVirtualAlloc+0x15
0012e6e0 79e8b7a4 00b54000 00001000 00001000 mscorwks!ClrVirtualAlloc+0x1b
0012e718 79e9f940 000000a8 00000001 00010000
mscorwks!UnlockedLoaderHeap::GetMoreCommittedPa
ges+0x90
Reference Tree
The GC does not perform reference counting. Some memory models maintain a reference count on each
memory object. When the count drops to zero, the object is immediately removed from memory. Overhead
attributed to reference counting, especially for objects that are never reclaimable, is considerable. There are
two benefits to the reference counting model. First, the cost of garbage collection is distributed across the
life of the application. Second, it is proactive. Memory is reclaimed prior to being needed.
In managed code, a reference tree is erected when garbage collection is initiated, which avoids expensive
reference counting. References no longer in the tree are assumed collectable and the memory for those
objects is reclaimed. Memory is then consolidated and outstanding references are updated. This phase of
memory management prevents fragmentation of the managed heap. The model described ignores finalization
for the moment. The tree is not cached between garbage collection cycles. Rebuilding the tree is one reason
why garbage collection is expensive. However, garbage collection is performed only when needed, which is a
considerable efficiency.
An object is rooted when another object holds a reference to it. Conversely, root objects are not referenced
by another object, including static, global, and local objects. C# does not support global objects. The root
objects are the base of the object tree. The branches of the tree emerge from the root objects, as shown in
Figure 13-8.
Memory Walkthrough
In this tutorial, the Store application is explored again. Three transactions are added and the root reference of
each transaction is displayed.
1. Start the Store application and add three transactions.
2. Attach to the Store application with WinDbg.
3. Transactions are instances of the Item class. Display information on the Item class using the
!name2ee command:
4.
5. 0:004> !name2ee store.exe Store.Item
6. Module: 00d40c14 (Store.exe)
7. Token: 0x02000002
8. MethodTable: 00d442dc
9. EEClass: 00db21c4
10. Name: Store.Item
11.
12. With the MethodTable address, you can list the address of each transaction item. This is
information is obtained with the dumpheap command:
13.
14. 0:004> !dumpheap -mt 00d442dc
15. Address MT Size
16. 013a03b8 00d442dc 20
17. 013b409c 00d442dc 20
18. 013bca10 00d442dc 20
19. total 3 objects
20. Statistics:
21. MT Count TotalSize Class Name
22. 00d442dc 3 60 Store.Item
23. Total 3 objects
24. Check the root of each object using the !gcroot command. This is a partial listing from the first
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Item object:
25.
26. 0:004> !gcroot 013a03b8
27. Note: Roots found on stacks may be false positives. Run "!help gcroot"
for
28. more info.
29. ebx:Root:01392b60(System.Windows.Forms.Application+ThreadContext)->
30. 01392214(Store.Form1)->
31. 01392454(System.Collections.Generic.List`1[[Store.Item, Store]])->
32. 013d5f2c(System.Object[])->
33. 013a03b8(Store.Item)
34. Scan Thread 0 OSTHread ee4
35. Scan Thread 2 OSTHread c48
36. DOMAIN(001483A8):HANDLE(WeakLn):9f1088:Root:013a074c(System.Windows.For
ms.NativeMethod
37. s+WndProc)->
38. 0139ec94(System.Windows.Forms.Control+ControlNativeWindow)->
39. 0139ebc4(System.Windows.Forms.CheckBox)->
40. 0139d6f8(Store.Transaction)->
41. 013a03b8(Store.Item)
42. Are you curious about composition of the Item object? The items objects are shown as 20 bytes.
What do those 20 bytes contain? Here is the source code for the Item class:
43.
44. public class Item: IDisposable
45. {
46. public Item()
47. {
48. ++nextPropId;
49. propItemId = nextPropId;
50. }
51.
52. public enum eProducts
53. {
54. Computer = 1,
55. Laptop = 2,
56. Printer = 4,
57. Software = 8
58. };
59.
60. private eProducts propProducts=0;
61.
62. public eProducts Products
63. {
64. get
65. {
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
That was easy because the source code was available. What if the source code is not available? This is the
normal case on a production machine. The !dumpclass command dumps the class. It uses the EEClass
address, which is provided with !name2ee command. Actually, !dumpclass provides information that may be
more valuable than the source code. You also receive state information. In the following listing, we are told
that the static count is 3, which is the correct value at this moment.
Generations
The managed heap is organized into three generations and a large object heap. Generations are numbered
0, 1, and 2. New objects are placed in a generation or large object heap. Younger and smaller objects are
found in the earlier generations, whereas older and larger objects are found in the later generations and the
large object heap. This is efficiency by proximity. Objects that are apt to message other objects are kept
close together in memory. This decreases page faults, which are costly, and the amount of physical memory
required at any time.
Garbage collection in .NET is often described as nondeterministic, which means that memory recovery
cannot be predicted. Garbage collection occurs when memory commits exceed the memory reserved for a
particular generation. Because only a portion of the managed heap is being collected, this is more efficient.
When an application starts, objects are allocated on Generation 0 first. Eventually, the memory available to
Generation 0 is exceeded, which triggers garbage collection. If enough memory is reclaimed during garbage
collection, the pending allocation is performed on Generation 0. If enough memory cannot be reclaimed,
Generation 0 objects are promoted to Generation 1. This continues until Generation 0 and 1 are replete with
objects. At that time, Generation 0 and 1 objects are promoted to Generation 1 and 2, respectively. By
design, the older and larger objects tend to migrate toward the higher generations, whereas younger and
smaller object are found in lower generations.
Memory on the managed heap is allocated top-down. The new objects are at the higher addresses.
Generation 0 is at a higher address than Generation 1. 256 kilobytes, 2 megabytes, and 10 megabytes are
reserved for Generation 0, 1, and 2, respectively. These thresholds may be adjusted. The GC changes these
thresholds based on the pattern of allocations in the managed application.
As the name implies, the large object heap hosts large objects. Objects greater than 85 kilobytes (KB) in
size are considered large objects. Instead of promoting these objects from one generation to another, which
is costly, large objects are immediately placed on the large object heap at allocation.
Generations Walkthrough
This time the Store application has an Add Transactions button and an Add Large Transactions button. The
Add Large Transactions button adds large transactions, which are instances of the LargeItem class. The
LargeItem class inherits from the Item class and adds additional fields, such as the largeStuff field. The
largeStuff field is greater than 85 KB and qualifies as a large object. The objective of this walkthrough is to
determine the generation of each Item, LargeItem and largeStuff instance.
1. Start the Store application. Add three regular transactions and four large transactions.
2. Launch WinDbg and attach to the Store application.
3. There should be three instances of the Item class in memory. Retrieve the method table address
of the Item class with the !name2ee command. Then dump the Items instances using the
!dumpheap -mt command:
4.
5. 0:004> !name2ee Store.exe Store.Item
6. Module: 00d40c14 (Store.exe)
7. Token: 0x02000005
8. MethodTable: 00d4431c
9. EEClass: 00db22f4
10. Name: Store.Item
11. 0:004> !dumpheap -mt 00d4431c
12. Address MT Size
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Based on the addresses of the Item, LargeItem, and largeStuff instances, Table 13-8 maps each object to
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Ite Ad
m dr
ess
No N/
obj A
ect
s
Ge 0x
ne 01
rat 3d
ion 24
0 88
St
art
s
Lar 0x0
geI 13
te ca
m4 90
0
Ite 0x0
m6 13
c1
a4
c
Lar 0x0
geI 13
te be
m3 02
0
Ite 0x0
m5 13
b5
13
c
Ge 0x
ne 01
rat 3af
ion 93
1 c
St
art
s
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-8:
Item,
LargeItem,
and
largeStuff
Instances
Ite Ad
m dr
ess
Lar 0x0
geI 13
te ae
m2 5a
4
Lar 0x0
geI 13
te ab
m1 03
c
Lar 01
geI 3a
te e5
m2 a4
Ite 0x0
m3 13
aa
dd
8
Ite 0x0
m2 13
aa
bb
0
Ite 0x0
m1 13
a8
bd
0
Ge 0x
ne 01
rat 39
ion 10
2 00
St
art
s
lar 02f
ge 08
Stu 91
ff 4 8
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-8:
Item,
LargeItem,
and
largeStuff
Instances
Ite Ad
m dr
ess
lar 02
ge b3
Stu 7fe
ff 3 8
lar 02
ge 76
Stu 76
ff 2 c8
lar 02
ge 39
Stu 6d
ff 1 a8
lar 02
ge 76
Stu 76
ff 2 c8
La 0x
rg 02
e 39
obj 10
ect 00
he
ap
It is time to diagnose the earlier bug. Actually, there is no bug. The issue is nondeterministic collection. In
the Store application, each transactions is initially an Item object. A LargeItem object is then initialized with
the Item transaction. At that point, the Item object is no longer required, but the memory is not reclaimed.
Therefore, there is a shadow item in memory for every LargeItem, which is the reason for the extra Item
instances. At the next garbage collection, those items will be removed from memory. For demonstration
purposes, there is a version of the Store application that forces garbage collection. It has a Collect Memory
button that calls GC.Collect and removes the extra Item instances. When using this Store application, dump
the Item instances before and after clicking the Collect Memory button to confirm that GC.Collect is
reclaiming the extra objects. In general, calling GC.Collect is not recommended because it is an expensive
operation.
Finalization
The finalization process has been ignored until now. It plays a vital role in garbage collection. Finalization
also affects the performance and effectiveness of garbage collection. In C#, finalization is linked to class
destructors. For C++ programmers, .NET presents an entirely different methodology for destructors.
Object.Finalize is the universal destructor in .NET. In C#, Finalize calls the class destructor. Destructors are
called deterministically in C++. However, CLR calls destructors nondeterministically during garbage
collection. Do not invoke destructors directly. Destructors are called as part of the garbage-collection
process and are not called in a guaranteed sequence. In addition, you should clean up for only unmanaged
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
resources in the destructor. For deterministic garbage collection, implement the IDisposable interface.
Destructors add processing overhead to an object. The overhead is incurred even before object is collected.
The GC adds references for objects with destructors to the Finalization queue when the object is created.
Thus, the extra overhead starts at the beginning of the object's lifetime.
Objects that have destructors but no outstanding references require at least two garbage collections to be
reclaimed. During the first garbage-collection cycle, references to reclaimable objects are transferred from
the Finalization queue to the FReachable queue, which is serviced by a dedicated thread. These objects are
added to the list of objects already waiting on the FReachable queue to have their destructors called. The
Finalization thread is responsible for invoking destructors on objects and then removing that object from the
FReachable queue. When that happens, the object can be reclaimed and removed from memory during the
next garbage collection.
The !finalizequeue command reports on objects waiting to have their destructors called. These are the
objects on the FReachable queue.
Performance Monitor
The Performance Monitor has several counters that are helpful when debugging managed applications. Table
13-9 itemizes some of the more useful memory-related counters.
Table 13-9:
Performance
Monitor
Counters
Na De
me scr
ipti
on
#G Th
C e
Ha nu
ndl mb
es er
of
GC
ha
ndl
es
to
ext
ern
al
res
our
ce
s,
su
ch
as
win
do
ws
an
d
file
s.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-9:
Performance
Monitor
Counters
Na De
me scr
ipti
on
# Tot
Byt al
es nu
in mb
All er
He byt
ap es
s allo
cat
ed
for
Ge
ner
ati
on
0,
1,
2,
an
d
the
lar
ge
obj
ect
he
ap.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-9:
Performance
Monitor
Counters
Na De
me scr
ipti
on
# Th
Ind e
uc pe
ed ak
GC nu
mb
er
of
tim
es
gar
ba
ge
coll
ect
ion
wa
s
ind
uc
ed
be
ca
us
e
of
GC
.Co
llec
t.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-9:
Performance
Monitor
Counters
Na De
me scr
ipti
on
# Th
of e
Pin nu
ne mb
d er
Obj of
ect pin
s ne
d
obj
ect
s
dis
cov
ere
d
dur
ing
the
las
t
gar
ba
ge
coll
ect
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-9:
Performance
Monitor
Counters
Na De
me scr
ipti
on
# A
of co
Sin unt
ks of
Blo sy
ck nc
s blo
in ck
Us ent
e rie
s.
(Th
e
sy
nc
blo
ck
is
dis
cu
ss
ed
in
the
se
cti
on
on
de
bu
ggi
ng
thr
ea
ds.
)
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-9:
Performance
Monitor
Counters
Na De
me scr
ipti
on
#G Th
en e
0 nu
Col mb
lec er
tio of
ns tim
es
tha
t
Ge
ner
ati
on
0
ha
s
be
en
gar
ba
ge
coll
ect
ed.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-9:
Performance
Monitor
Counters
Na De
me scr
ipti
on
#G Th
en e
1 nu
Col mb
lec er
tio of
ns tim
es
tha
t
Ge
ner
ati
on
1
ha
s
be
en
gar
ba
ge
coll
ect
ed.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-9:
Performance
Monitor
Counters
Na De
me scr
ipti
on
#G Th
en e
2 nu
Col mb
lec er
tio of
ns tim
es
tha
t
Ge
ner
ati
on
2
ha
s
be
en
gar
ba
ge
coll
ect
ed.
# Th
Tot e
al tot
Co al
m nu
mit mb
ted er
Byt of
es virt
ual
me
mo
ry
co
m
mit
ted
by
the
GC
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-9:
Performance
Monitor
Counters
Na De
me scr
ipti
on
# Th
Tot e
al tot
Re al
ser nu
ve mb
Byt er
es of
virt
ual
me
mo
ry
res
erv
ed
by
the
GC
.
Ge Ma
n xi
0 mu
He m
ap nu
Siz mb
e er
of
allo
cat
ed
byt
es
for
Ge
ner
ati
on
0.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-9:
Performance
Monitor
Counters
Na De
me scr
ipti
on
Ge Ma
n xi
1 mu
He m
ap nu
Siz mb
e er
of
allo
cat
ed
byt
es
for
Ge
ner
ati
on
1.
Ge Ma
n xi
2 mu
He m
ap nu
Siz mb
e er
of
allo
cat
ed
byt
es
for
Ge
ner
ati
on
2.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-9:
Performance
Monitor
Counters
Na De
me scr
ipti
on
Lar Nu
ge mb
Obj er
ect of
He byt
ap es
Siz cur
e ren
tly
allo
cat
ed
for
the
lar
ge
obj
ect
he
ap.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-9:
Performance
Monitor
Counters
Na De
me scr
ipti
on
%T Th
im e
e per
in ce
GC nta
ge
of
tim
e
sp
ent
in
gar
ba
ge
coll
ect
ion
,
whi
ch
is
up
dat
ed
at
ea
ch
gar
ba
ge
coll
ect
ion
cy
cle
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-9:
Performance
Monitor
Counters
Na De
me scr
ipti
on
All Th
oc e
ate nu
d mb
Byt er
es/ of
Se byt
c es
allo
cat
ed
per
se
co
nd,
whi
ch
is
up
dat
ed
at
ea
ch
gar
ba
ge
coll
ect
ion
cy
cle
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-9:
Performance
Monitor
Counters
Na De
me scr
ipti
on
Fin Th
aliz e
ati nu
on mb
Sur er
vivo of
rs obj
ect
tha
t
sur
vive
d
gar
ba
ge
coll
ect
ion
an
d
wai
tin
g
for
de
str
uct
ors
to
be
call
ed.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-9:
Performance
Monitor
Counters
Na De
me scr
ipti
on
Ge Th
n e
0 ma
He xi
ap mu
Siz m
e nu
mb
er
byt
es
allo
cat
ed
for
Ge
ner
ati
on
0.
Ge Th
n e
0 nu
Pro mb
mo er
ted of
Byt byt
es/ es
Se per
c se
co
nd
pro
mo
ted
fro
m
Ge
ner
ati
on
0
to
1.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-9:
Performance
Monitor
Counters
Na De
me scr
ipti
on
Ge Th
n e
1 cur
He ren
ap t
Siz nu
e mb
er
of
byt
es
in
Ge
ner
ati
on
1.
Ge Th
n e
1 nu
Pro mb
mo er
ted of
Byt byt
es/ es
Se per
c se
co
nd
pro
mo
ted
fro
m
Ge
ner
ati
on
1
to
2.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-9:
Performance
Monitor
Counters
Na De
me scr
ipti
on
Ge Th
n e
2 cur
He ren
ap t
Siz nu
e mb
er
of
byt
es
in
Ge
ner
ati
on
2.
Pro Th
mo e
ted nu
Fin mb
aliz er
ati of
on- byt
Me es
mo pro
ry mo
fro ted
m to
Ge Ge
n0 ner
ati
on
1
be
ca
us
e
of
pe
ndi
ng
fina
lize
rs.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-9:
Performance
Monitor
Counters
Na De
me scr
ipti
on
Pro Th
mo e
ted nu
Fin mb
aliz er
ati of
on- byt
Me es
mo pro
ry mo
fro ted
m to
Ge Ge
n1 ner
ati
on
2
be
ca
us
e
of
pe
ndi
ng
fina
lize
rs.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-9:
Performance
Monitor
Counters
Na De
me scr
ipti
on
Pro Tot
mo al
ted byt
Me es
mo for
ry obj
fro ect
m s
Ge tha
n0 t
wer
e
pro
mo
ted
to
Ge
ner
ati
on
1.
Thi
s
do
es
not
incl
ud
e
the
obj
ect
s
wai
tin
g
on
pe
ndi
ng
fina
lize
rs.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 13-9:
Performance
Monitor
Counters
Na De
me scr
ipti
on
Pro Tot
mo al
ted byt
Me es
mo for
ry obj
fro ect
m s
Ge tha
n1 t
wer
e
pro
mo
ted
to
Ge
ner
ati
on
2.
Thi
s
do
es
not
incl
ud
e
the
obj
ect
s
wai
tin
g
on
pe
ndi
ng
fina
lize
rs.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Threads
Multithread applications have multiple threads. Each thread represents a path of execution and owns
resources, such as stack, thread local storage, local variables, and thread environment block. Proper use of
threads can enhance the performance of an application, whereas poor implementation can hinder
performance.
Creating threads is not difficult. Managing threads is the real challenge. Thread synchronization, which is the
management of threads, entails several activities, including preventing race conditions and controlling access
to resources. Threads are like children that require corralling.
Improperly implemented threads can lead to a high utilization or low utilization condition. High utilization is
characterized by one or more threads consuming at or near 100-percent CPU utilization. Other threads are
starved for time, which makes the application appear to hang or behave incorrectly. Low utilization is the
reverse. The process and contained threads are receiving minimal or no CPU usage.
The previous items highlight some of the reasons for high and low utilization. There are plenty more.
However, high and low utilization are not the only issues. Multithreaded and simultaneous access to
non-thread-safe resources is another cause for application failures. Actually, the list of potential
transgressions from multithreading is almost endless. Threading is fertile ground for debugging.
For brevity, this section focuses on monitors and mutexes. Synchronization problems from semaphores,
events, and reader-writer locks are not discussed. Monitors synchronize access to a single resource. The
resource could be an object, data structure, or even an algorithm—anything that requires singular access.
Monitors are limited to synchronizing threads within the same process. A mutex also synchronizes access
to a single resource. However, mutexes have additional power and flexibility. For example, mutexes can
synchronize threads across processes.
Monitors are the most frequently used synchronization device. For that reason, the CLR tracks monitors for
efficient access. Instances of objects have an additional field called the syncblock index, which is an index
into the syncblock table where monitors are tracked. The syncblock index of an object defaults to zero.
When an object is assigned to a monitor, the syncblock index is updated to point to an entry in the
syncblock table. Otherwise, the syncblock index remains 0. In .NET 2.0, a syncblock entry is not created for
every object associated with a monitor. If the monitor is not already associated with a syncblock, a thinlock
is created instead. The command !syncblk -all lists the outstanding syncblocks, !dumpheap -thinlock
reports the thinlocks.
Markers for thread synchronization can be found on the call stack. Finding AwareLock.Enter,
WaitHandle.WaitAll, and WaitForMultipleObjects in the call stack are indications of thread synchronization
activity.
AwareLock.Enter This method is called when an object is bound to a monitor. If the following
breakpoint is hit, that thread is entering a monitor:
bp mscorwks!AwareLock::Enter
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
WaitHandle.WaitAll Except for monitors, most synchronization objects in .NET are derived from
the WaitHandle class. Look for method calls from this class on the managed call stack, including
WaitOne and WaitAll, as a sign of pending synchronization.
WaitForMultipleObjects Most waits on synchronization objects, managed or unmanaged,
dissolve in a WaitForMultipleObjects API, which is the workhorse of thread synchronization. This
is the syntax of the WaitForMultipleObjects API:
o DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE*
o lphandles, BOOL bWaitAll, DWORD dwMilliseconds)
Threads Commands
The first step for debugging threads in WinDbg and SOS is to use the thread commands. In WinDbg, the
tilde (~) is the thread command; in SOS, the command is !threads.
0:000> ~
. 0 Id: f7c.f80 Suspend: 1 Teb: 7ffdd000 Unfrozen
1 Id: f7c.f9c Suspend: 1 Teb: 7ffdc000 Unfrozen
2 Id: f7c.fa0 Suspend: 1 Teb: 7ffdb000 Unfrozen
3 Id: f7c.fa4 Suspend: 1 Teb: 7ffda000 Unfrozen
4 Id: f7c.de0 Suspend: 1 Teb: 7ffd9000 Unfrozen
Listed in order, the columns are thread number, process identifier, thread identifier, suspend count, address
of thread environment block, and status of the thread.
0:000> !threads
ThreadCount: 4
UnstartedThread: 1
BackgroundThread: 1
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
PreEmptive GC Alloc Lock
ID OSID ThreadOBJ State GC Context Domain Count
APT Exception
0 1 f80 001501f8 6020 Disabled 013c2cf0:013c32bc 001483a8 0 STA
2 2 fa0 00153e40 b220 Enabled 00000000:00000000 001483a8 0 MTA
(Finalizer)
4 3 de0 0018b898 b020 Disabled 013b4cdc:013b52bc 001483a8 2 MTA
XXXX 4 0 0018e750 9400 Enabled 00000000:00000000 001483a8 0 Ukn
Threads Walkthrough
Multithread capabilities have been added to the Store application. Two buttons have been added. The
Enumerate button writes the transactions to the forward.txt file. The Reverse Enumerate button writes the
transactions, in reverse order, to the reverse.txt file. Each button handler creates and starts a thread to
accomplish the reporting tasks.
1. Close all instances of the Store application.
2. Start the Store program and add three transactions. Click the Enumerate button to write the
transactions to the forward.txt file. Close the Store program and open the forward.txt file, which is
found to be empty. It should contain three transactions!
3. Try again. Reopen the Store application and add three transactions. However, upon clicking the
Enumerate button an unhandled exception occurs. What is the problem? You need to investigate.
4. Start the Store application yet again. Retrieve the process identifier using the tlist command.
However, notice that there are two Store applications running. Apparently a previous version is still
running in the background. This kind of problem is typical of a hung thread, which keeps an
application alive after the user closes the main window.
5. Use ADPlus to obtain a dump of the earlier Store application. This is the command:
6.
7. adplus -hang -o c:\dumps -p 2696
8. Open the resulting dump file in WinDbg.
9. Load the SOS debugger extension and list out the managed threads. For readability, some of the
columns have been removed from this listing:
10.
11. 0:000> .load sos
12. 0:000> !threads
13. ThreadCount: 3
14. UnstartedThread: 0
15. BackgroundThread: 2
16. PendingThread: 0
17. DeadThread: 0
18. Hosted Runtime: no
19.
20. PreEmptive GC Alloc
Lock
21. ID OSID ThreadOBJ State GC Context
Domain Count
22. 0 1 a90 001501f8 2016220 Enabled 013dbe2c:013dc990
001483a8 0
23. 2 2 9c8 00153e40 b220 Enabled 00000000:00000000
001483a8 0
24. 4 3 3e0 00190718 b020 Disabled 013d63b0:013d6990
001483a8 2
Thread 4 seems to be the culprit. It is the only thread with a positive lock count. What is the thread waiting
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
From the preceding listing, both thinlocks are owned by Thread 4. The address of the Thread 4 object is
0x00199a78, which could be nested locks. Dump the method tables of the thinlocks to uncover what Thread
4 is waiting for:
What is known? When the program hung, Thread 4 had outstanding locks on an Item and Object instance.
No other thread is holding a lock, which narrows the problem to Thread 4. Here is the source code for Thread
4:
while (true)
{
if (enumerator.MoveNext())
{
Item current = enumerator.Current;
string message = current.ItemId + " Product Mask: "
+ ((int)current.Products).ToString();
sw.WriteLine(message);
}
else
{
break;
}
}
sw.Close();
}
}
}
Review the code—I hope that the problem is obvious. The lock statement is a shortcut to calling the
Monitor.Enter method. After acquiring the locks, the program enters an infinite loop, which was shown
previously. Because execution never continues past the loop where the locks are released, the locks are still
being held. Remove the extraneous while(true) loop and retry the program. Before recompiling the
application, you have to kill the hung process. The forward.txt file should be created and contain transactions.
Threads Walkthrough 2
The Store application appears to be working, but not in all circumstances. Delete the forward.txt and
reverse.txt files. End any current sessions of the Store application. Start a new session of the Store
application and add a couple of transactions.
1. Click the Reverse Enumerate button followed by the Enumerate button. Close the application and
check for the forward.txt and reverse.txt files. Neither file is created. Why not?
2. Restart the Store application. Add two new transactions. Click the Reverse Enumerate button
followed by the Enumerate button.
3. Start WinDbg and attach to the Store application.
4. List the available threads with the !threads command. Threads 4 and 5 have a positive lock count.
Both threads are waiting for something:
5.
6. 0:006> !threads
7. ThreadCount: 4
8. UnstartedThread: 0
9. BackgroundThread: 1
10. PendingThread: 0
11. DeadThread: 0
12. Hosted Runtime: no
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The !syncblk command reports the syncblock entries. Thread 5 is using an Item instance as a
monitor. Thread 4 is using an Object instance as a monitor. This confirms Threads 4 and 5 as the
potential source of the problem. The other threads are not using monitors:
0:006> !syncblk
Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner
2 00174854 3 1 00190b98 ccc 5 01392440
System.Collections.Generic.List`1[[Store.Item, Store]]
21 001747f4 3 1 00191f60 2e0 4 01392468
System.Object
-----------------------------
Total 44
CCW 0
RCW 0
ComClassFactory 0
Free 0
20. Perform a stack trace on Threads 4 and 5. Notice the inclusion of the AwareLock::Enter method,
which is a marker of thread synchronization. Also note that the top of the call stack is populated
with WaitForMultipleObjects methods, which means the thread is currently blocked on something:
21.
22. 0:004> kb
23. ChildEBP RetAddr Args to Child
24. 0358f4a0 7c90e9ab 7c8094f2 00000001 0358f4cc ntdll!KiFastSystemCallRet
25. 0358f4a4 7c8094f2 00000001 0358f4cc 00000001
ntdll!ZwWaitForMultipleObjects+0xc
26. 0358f540 79f4aa60 00000001 00174868 00000000
KERNEL32!WaitForMultipleObjectsEx+0x12c
27. 0358f5a8 79f16d92 00000001 00174868 00000000
mscorwks!WaitForMultipleObjectsEx_SO_TOLE
28. RANT+0x6f
29. 0358f5c8 79f16d03 00000001 00174868 00000000
mscorwks!Thread::DoAppropriateAptStateWai
30. t+0x3c
31. 0358f64c 79f16b9e 00000001 00174868 00000000
mscorwks!Thread::DoAppropriateWaitWorker+
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
32. 0x144
33. 0358f69c 79f4a9d9 00000001 00174868 00000000
mscorwks!Thread::DoAppropriateWait+0x40
34. 0358f6f8 79ebc06e ffffffff 00000001 00000000
mscorwks!CLREvent::WaitEx+0xf7
35. 0358f708 7a0fd093 ffffffff 00000001 00000000
mscorwks!CLREvent::Wait+0x17
36. 0358f794 7a0fd28f 00191f60 ffffffff 00191f60
mscorwks!AwareLock::EnterEpilog+0x94
37. 0358f7b0 79f0fe6a 8a20e59b 0358f888 01392214
mscorwks!AwareLock::Enter+0x61
38. Look at the parameters for WaitForMultiple objects. Thread 4 is waiting for a single
synchronization object, which is the first parameter. The second parameter is an address, which
is a pointer to an array of handles. Dump that parameter to find the handle to the synchronization
object:
39.
40. 0:004> dd 00174868
41. 00174868 00000658 0000000d 00000000 00000000
42. 00174878 00000000 00000000 00000000 00000000
43. 00174888 00000000 00000000 00000000 80000023
44. The handle to the synchronization object is 0x00000658. Use the !handle command to obtain
more information on that handle. It is an event handle:
45.
46. 0:004> !handle 00000658
47. Handle 658
48. Type Event
49.
50. Repeat steps 1 through 7 for Thread 5. The result should be similar.
The problem has been isolated to Threads 4 and 5. Threads 4 and 5 are started in the Forward and
Reverse handlers. Here is the source code for these methods. Both methods have nested locks.
However, the locks in the Forward and Reverse methods are in reverse order, which is causing a
deadlock.
while (true)
{
// other code
}
sw.Close();
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
}
}
}
Exceptions
Have you ever been presented with a memory exception? See Figure 13-9. Of course, I am sure this
exception occurred while you were using an application that someone else wrote. As a kind gesture, you
volunteered to help diagnose the problem and make an unstable application robust.
Managed exceptions start life as native exceptions. To raise an exception, the CLR calls
mscorwks!RaiseTheException. RaiseTheException then calls RaiseException, which is a system-level API.
RaiseException assigns all managed exceptions the E0434F4D exception code. Because all managed
exceptions are raised with this same exception code, it is difficult to distinguish between managed
exceptions. Fortunately, the first parameter of the RaiseTheException function call is the managed exception
object. You can dump that object to ascertain the specific exception objection.
Exception Walkthrough
The Exception application is modified to raise an exception when the Add Transactions button is clicked.
The object of this walkthrough is to determine the type of exception.
1. Start the Store application. Open WinDbg and attach to that program.
2. Set a breakpoint on the RaiseException API and resume the application:
3.
4. 0:004> bp Kernel32!RaiseException; g
5. Click the Add Transaction button. The exception is raised and the breakpoint is hit in WinDbg.
Display the call stack.
6. Use the !dumpobj command to dump the first parameter of RaiseTheException, which is the
managed exception object. You now have the details of the exception, including the exception
type.
Symbols
Until now, it is has been assumed that debug symbol files have aligned perfectly. Admittedly, that is a major
assumption. However, the advent of symbol stores and servers makes me optimistic that your symbols are
aligning without the obligatory drama. Having the correct debug symbols files for the environment and
application make the debugging experience infinitely easier. With symbol files, function names, parameter
names, local variables, source code line, and data to resolve, frame pointer omissions (FPOs) are available.
Without symbol files, little of this valuable information is available, which makes your debugging quest much
more difficult.
Debug symbol files are versioned. Different versions of debug symbol files are available for separate Windows
environments. Windows 98, Windows 2000, Windows XP, and other Windows operating environments do not
have identical symbols. Service packs may also have different symbols. Because of the large assortment of
symbol combinations, obtaining the correct symbols for an environment becomes a trial-and-error process—
which is further complicated when performing postmortem analysis. The dump may have originated on
another machine that has a different operating system, which requires having the symbols for the
environment on the source machine available on your machine. Things are getting complicated very quickly.
A more direct approach is needed, and the solutions are symbol servers and symbol stores.
Symbol servers orchestrate the downloading of required symbols from a symbol store. The symbol server
assumes the responsibility of finding the correct symbols; a symbol store is a repository of related debug
symbol files. Symbol servers consult with the symbol store to download the correct versions of the debug
symbol files. Microsoft publishes a public symbol store for developers, which can be accessed here:
http://msdl.microsoft.com/download/symbols
You can download symbols directly instead of using symbol servers. However, the approaches are not
mutually exclusive. Symbol packages for different Windows environments are downloadable from this
location:
http://www.microsoft.com/whdc/devtools/debugging/symbolpkg.mspx
Symsrv.dll is the symbol server distributed with Debugging Tools for Windows, and it must be installed in the
same directory as the debugger. Symsrv contacts the listed symbol stores and downloads debug symbol
files into a downstream directory. Files can be transferred over http and https.
This is the generic syntax to download debug symbol files with a symbol server:
Symsrv*ServerDLL*DownstreamStore*SymbolStore
The following command leverages the symsrv.dll symbol server. The symbol server is instructed to contact
the Microsoft Public Symbol Store to download missing or correct versions of symbol files. The symbols are
transferred to c:\ symbols, which is the downstream store:
Symsrv*Symsrv*c:\symbols*http://msdl.microsoft.com/download/symbols
Because symsrv.dll is the most prevalent symbol server, an abbreviated syntax is available that is specific
for that server:
srv*DownstreamStore*SymbolStore
The following command uses the abbreviated syntax and is identical to the previous expanded command that
used the symsrv.dll symbol server:
srv*c:\symbols*http://msdl.microsoft.com/download/symbols
As shown in the following syntax, symbol servers can have multiple downstream servers. If the symbol file is
not in DownstreamStore 1, it is downloaded from DownstreamStore 2. Furthermore, if the file is not in
DownstreamStore 2, DownstreamStore 3 is contacted. If the file is found there, it is downloaded from
DownstreamStore 3 into DownstreamStore 2 and then DownstreamStore 2 into DownstreamStore 1. This pattern
continues until the debug symbol is found or the symbol stores are exhausted.
WinDbg and Visual Studio .NET read the _NT_SYMBOL_PATH environment variable for the default path for
the symbol server. Use the System tool in Control Panel to create or modify the _NT_SYMBOL_PATH
environment variable.
Application Symbols
The Microsoft Public Symbol Store downloads the symbols for the environment. You also need the symbols
for your application. Symbol files are not always distributed with release versions of a product, so a
production server might not have the required symbol files. It is imperative that the debug symbol files for the
release product be maintained somewhere, such as a private symbol store, for debugging if needed.
Visual Studio .NET creates a symbol file (PDB) for both debug and release versions of the product. It is
important to retain the symbol files for each version of the debug and release product. Because clients may
have older versions of your product, the debug symbol files for those versions must also be retained and
available.
WinDbg
In WinDbg, the symbol path can be set via the user interface or the command line. From the user interface,
select the Symbol File Path menu item from the File menu. From the command line, the .sympath directive
can display or set the symbol path. When the symbol path is changed, the .reload directive resynchronizes
and potentially downloads additional symbol files.
The !sym noisy command helps diagnose errors in loading debug symbol files. This command displays
detailed information on each symbol that should be downloaded, which is helpful for resolving symbol
problems. The !sym quiet command, which is the default, suppresses the extra binding data.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Memory Management
Memory management was introduced in this chapter. Concepts such as root objects, reference trees,
generations, and finalization queues were reviewed. The next chapter expands the conversation to include
the disposable pattern, weak references, unsafe pointers, and other topics related to memory management.
Understanding the managed paradigm to memory management is particularly important to C- and Cbased
programmers. C programmers are accustomed to managing their own memory with minimal assistance from
the environment. For them, memory management in .NET represents a different mindset, which includes
adopting new best practices for allocating, managing, and releasing memory. Some C developers perceive
the managed memory model as confining, when actually it is liberating.
Although the CLR provides memory management, developers have some sway in the process, which
includes pinned pointer, memory stress, forcing garbage collection, and allocation patterns. These topics are
introduced in the next chapter.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Developers of managed applications can directly affect the stack and managed heap. The other forms of
memory, such as registers and virtual memory, are largely unavailable except through interoperability. Value
type instances, local values, are created on the stack. Instances of reference types, objects, are created on
the managed heap. Lifetimes of local values are confined by scope. When a local value loses scope, it is
removed from the stack. For example, the local variables and parameters of a function are removed from
memory when the function is exited. The lifetime of an object, which resides on the managed heap, is
controlled by the garbage collector (GC), which is an element of the Common Language Runtime (CLR). The
GC periodically performs garbage collection to cleanse memory of unused objects.
The policies and best practices of the GC and garbage collection are the primary focus of this chapter.
Although garbage collection is not language-specific, the tradition of C-based developers, as related to
memory management, is somewhat different from other developers—particularly Microsoft Visual Basic and
Java developers. The memory model employed in Visual Basic and Java is cosmetically similar to the
managed environment. However, the memory model of previous C-based languages is dissimilar to this
environment. These differences make this chapter especially important.
Developers in C-based languages are accustomed to deterministic garbage collection, in which developers
explicitly set the lifetime of an object. The malloc/free and new/delete statement combinations create and
destroy objects that reside on a heap. Managing the memory of a heap required programmer discipline,
which proved insufficient for guaranteeing consistently robust code. Memory leaks and other problems were
common. These leaks could eventually destabilize the application and cause complete application failure.
Instead of each developer individually struggling with these issues, the managed environment has the GC,
which is omnipresent and controls the lifetime of objects located on the managed heap.
The GC offers nondeterministic garbage collection. Developers explicitly allocate memory for objects.
However, the GC determines when garbage collection is performed and unused objects are vanquished from
memory.
When memory for an object is allocated at run time, the GC returns a reference to that object. The new
operator requests that an instance of a type (an object) is placed on the managed heap. A reference is an
indirect pointer to that object. This indirection helps the GC transparently manage the managed heap,
including the relocation of pointers when necessary.
In .NET, unused objects are eventually removed from memory. When is an object unused? Reference
counting is not performed in the managed environment. Reference counting was common to Component
Object Model (COM) components. When the reference count became zero, the related COM component
was considered no longer relevant and removed from memory. There were many problems in this model.
First, this required careful synchronization of the AddRef and Release methods. Breakdown of
synchronization could sometimes cause memory leakage and exceptions. Second, reference counting was
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
expensive. Reference counting was applied to collectable and noncollectable components. Finally, programs
incurred the overhead of reference counting, even when there was no memory stress on the application. For
this reason, reference counting was deservedly abandoned for a more efficient model that addresses the
memory concerns of modern applications. When there is memory stress in the managed environment,
garbage collection occurs, and an object graph is built. Objects not on the graph become candidates for
collection.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Unmanaged Resources
Objects that incorporate unmanaged resources have a managed and unmanaged context, which affects
memory management and garbage collection.
For example, the unmanaged resource of a managed object might consume copious amount of unmanaged
memory. This cannot be ignored. Managed and unmanaged memories are allocated from the same pool of
virtual memory. Corrective action must be taken when the pool is drained, regardless of the source.
Developers can compensate for unmanaged resources in .NET Framework 2.0 and take corrective action to
prevent out of memory occurrences. You can no longer hide an unmanaged elephant behind a managed
object. A managed wrapper class for a bitmap is one example. The wrapper class is relatively small, and the
memory associated with the managed object is trivial. However, the bitmap is the elephant. A bitmap
requires large amounts of memory, which cannot be ignored. If ignored, creating several managed bitmaps
could unexpectedly cause your application to be trampled by a stampede of elephants.
The availability of an unmanaged resource can be discrete. When consumed in a managed application,
discrete resources must be tracked to prevent overconsumption. The overconsumption of unmanaged
resources can have an adverse affect on the managed application, including the potential of resource
contention or raising an exception. For example, a device can support three simultaneous connections.
What happens when the fourth connection is requested? In the managed environment, you should be able to
handle this scenario gracefully.
The lifetime of an unmanaged resource is a separate consideration apart from the lifetime of the managed
object that hosts that resource. Equating the lifetimes can delay the release of sensitive resources. This
delay can result in resource contention, poor user responsiveness, or application failure. For example, the
FileStream class is a wrapper for a native resource: a physical file. The FileStream instance, which is a
managed component, is collected nondeterministically. Therefore, the lapse between when the file is no
longer needed and the managed component is collected could be substantial. This could prevent access to
the file from within and outside your application. When the file is no longer required, the ability to release the
file deterministically is imperative. You need the ability to say "Close the file now." The Disposable pattern
provides this ability to managed developers and separates the lifetime of managed components and
unmanaged resources.
This section is an overview of garbage collection. A detailed explanation of the mechanics of garbage
collection is presented in Chapter 13, "Advanced Debugging," and not repeated here. Two assumptions
guide the implementation of garbage collection in the managed environment. The first assumption is that
objects are more likely to communicate with other objects of a similar size. The second assumption is that
smaller objects are short-lived objects, whereas larger objects are long-lived objects. For these reasons, the
GC attempts to organize objects based on size and age.
In the managed environment, garbage collection is nondeterministic. With the exception of the GC.Collect
method, which is discouraged, developers cannot explicitly initiate garbage collection. The managed heap is
partitioned into generations, which are Generations 0, 1, and 2. The initial sizes of the generations are about
256 kilobytes, 2 megabytes, and 10 megabytes, respectively. As the application executes, the GC
fine-tunes the thresholds based on the pattern of memory allocations. Garbage collection is prompted when
the threshold for Generation 0 is exceeded. At that time, nonrooted objects are removed from memory.
Objects that survive garbage collection are promoted from Generation 0 to 1. Generation 0 is now empty. The
generations are then compacted and references updated. If during garbage collection Generation 0 and 1
exceed their thresholds, both are collected. Surviving objects are promoted from Generation 0 to 1 and from
Generation 1 to 2. Afterward, Generation 0 is empty again, generations are compacted, and references are
updated. In fewer instances, all three generations will exceed thresholds and require collection. The later
generations contain larger objects, which live longer. Because the short-lived objects reside primarily on
Generation 0, most garbage collection is focused in this generation. Generations allow the garbage
collection to implement a partial cleanup of the managed heap at a substantial performance benefit.
Objects larger than 85 kilobytes are considered large objects and are treated differently from other objects.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Large objects are placed on the large object heap, not on a generation. Large objects are generally long-lived
components. Placing large objects on the large object heap eliminates the need to promote these objects
between generations, thereby conserving resources and reducing the number of overall collection cycles. The
large object heap is collected with Generation 2, so large objects are collected only during a full
garbage-collection cycle.
When garbage collection is performed, the GC builds an object graph to determine which objects are not
rooted and can be discarded. The object graph is a graph of live objects. First the GC populates the object
graph with root references. The root references of an application are global, static, and local references.
Local references include references that are locals and function parameters. The GC then adds to the graph
those objects reachable from a root reference. An embedded object of a local is an example of an object
reachable from a root reference. Of course, the embedded object could contain other objects. The GC
extrapolates all the reachable objects to compose the branches of the object graph. Objects can appear
once in the object graph, which avoids circular references and other problems. Objects not in the graph are
not rooted and are considered candidates for garbage collection.
Objects that are not rooted can hold outstanding references to other objects. In this circumstance, the entire
branch is disconnected from a rooted object and can be collected. (See Figure 14-1.)
The Rooted application demonstrates how nonrooted objects are collected. In the application, the Branch
class contains the Leaf class, which contains the Leaf2 class. The Branch class is used as a field in the
Form1 class. When an instance of the Branch class is created, it is rooted through the Form class. Leaf and
Leaf2 instances are rooted through the Branch instances. (See Figure 14-2.)
In the following code, a modal message box is displayed in the finalizer. This is done for demonstration
purposes only and generally considered bad practice.
{
MessageBox.Show("Branch d'tor");
}
public Leaf e= new Leaf();
}
MessageBox.Show("Leaf d'tor");
}
public Leaf2 e2= new Leaf2();
}
The user interface of the Rooted application allows users to create the Branch, which includes Leaf
instances. (See Figure 14-3.) You can then set the Branch or any specific Leaf to null, which interrupts the
path to the root object. That nullified object is collectable. Objects that are rooted through the nullified object
are also now collectable. For example, if the Leaf instance is set to null, the Branch instance is not
collectable; it is before the Leaf reference in the object graph. However, Leaf2 object is immediately a
candidate for collection. After setting the Branch or Leaf objects to null, garbage collection can occur. If it
doesn't occur, click the Collect Memory button to force garbage collection.
As mentioned, garbage collection occurs when the memory threshold of Generation 0 is exceeded. Other
events, which are described in the following list, can prompt garbage collection:
When the threshold of Generation 0 is exceeded, garbage collection is triggered. Certain
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Certain suppositions are made for garbage collection in the managed environment. For example, small
objects are generally short-lived. Coding contrary to these assumptions can be costly. Although it makes for
an interesting theoretical experiment, this is not recommended for production applications. Defining a basket
of short-lived but larger objects is an example of coding against assumptions of managed garbage collection,
which would force frequent and full collections. Full collections are particularly expensive. Defining a
collection of near-large objects—objects that are slightly less than 85 kilobytes—is another example. These
objects would apply immediate memory pressure. Because they are probably long-lived, you have the
overhead of eventually promoting the near-large objects to Generation 2. It would be more efficient to pad the
objects with a buffer, forcing them into large object status, in which the objects are directly placed onto the
large object heap. You must remain cognizant of the underlying principal of garbage collection. Implement
policies that enhance, not exasperate, garbage collection in the managed environment.
One such policy is to limit boxing. Constant boxing of value types can cause garbage collection more often.
Boxing creates a copy of the value type on the managed heap. Most value types are small. For this reason,
the resulting object placed on the managed heap could be larger than the original value. This is yet another
reason that boxing is inefficient and should be avoided when possible. This is particularly a problem with
collection types. The best solution is to use generic types.
This introduction to garbage collection omits the topic of finalization, which is discussed in complete detail
later in this chapter.
GC Flavors
There are two flavors of the garbage collection: Workstation GC is optimized for a workstation or
single-processor system, whereas Server GC is fine-tuned for a server machine that has multiple processors.
Workstation GC is the default; Server GC is never the default—even with a multiprocessor system. Server
GC can be enabled in the application configuration file. If your application is hosted by a server application,
that application might select the Server GC.
Workstation GC can have concurrent garbage collection enabled or disabled. Concurrent is a single thread
concurrently servicing the user interface and garbage collection. The thread simultaneously responds to
events of the user interface while also conducting garbage collection responsibilities. The alternative is
noncurrent. When garbage collection is performed, other responsibilities, such as servicing the user
interface, are deferred.
The following are the garbage collection steps for Workstation GC with concurrent processing:
1. Garbage collection is triggered.
2. All managed threads are paused.
3. Perform garbage collection. If garbage collection is completed, proceed to step 5.
4. Interrupt garbage collection. Resume threads for a short time to respond to user interface
requests. Return to step 3.
5. Resume threads.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Threads are suspended at a secure point. The CLR maintains a table of secure points for use during garbage
collection. If a thread is suspended at an nonsecure point, the thread is hijacked until a secure point is
reached or the current function returns.
Here are the garbage collection steps for Workstation GC without concurrent processing:
1. Garbage collection is triggered.
2. All managed threads are paused.
3. Garbage collection is conducted.
4. Managed threads resume.
Server GC
Server GC is designed for multiprocessor machines that are commonly deployed as servers, such as Web,
application, and database servers. In the server environment, emphasis is on throughput and not the user
interfaces. The Server GC is fine-tuned for scalability. For optimum scalability, garbage collection is not
handled on a single thread. The Server GC allocates a separate managed heap and thread for every
processor. Garbage collection is not conducted on the thread that requests the allocation.
Configuring GC Flavor
Workstation GC with concurrent garbage collection can be enabled or disabled in an application
configuration file. Because concurrent is the default, the configuration file is used primarily to disable
concurrent garbage collection. This is set in the gcConcurrent tag, as shown in the following code:
<configuration>
<runtime>
<gcConcurrent enabled="false"/>
</runtime>
</configuration>
Server GC is also stipulated in the application configuration file. Remember, Server GC is never the default—
even in a multiprocessor environment. Select the Server GC using the gcServer tag, which is exhibited in the
following code:
<configuration>
<runtime>
<gcServer enabled="true"/>
</runtime>
</configuration>
You can also use the .NET Framework Configuration Tool (mscorcfg.msc) to select the appropriate GC. This
tool is installed in the Administrative Tools of the Control Panel. Open the .NET Framework Configuration tool
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
and select the Applications icon, which is presented in the left pane. Click the Add An Application To
Configure link in the Applications window. From the Configure An Application dialog box, browse and select
the target application. The application window will appear. In that window, click the View The Application's
Properties link. The application's Properties dialog box will appear. From the two options, you can select the
appropriate Garbage Collection mode in this window. Figure 14-4 shows the application properties dialog box
and the garbage collection options.
Finalizers
Resources of an object are cleaned up in a Finalize method. Before an object is removed from memory, the
Finalize method is called to release collateral resources. As a group, Finalize methods are called finalizers.
Every object inherits the Finalize method from the System.Object type. This is the syntax of the Finalize
method:
Object.Finalize syntax:
o protected virtual void Finalize()
Finalizers are not called programmatically; they are called automatically during the garbage collection cycle.
Garbage collection is nondeterministic, which means that the finalizer may not be called immediately. This
means that unmanaged resources, such as files, database connections, and devices, might not be released
in a timely manner. This delay in relinquishing resources can have adverse side effects on this and possibly
other applications.
Instead of a Finalize method, C# has a destructor. Coding a Finalize method explicitly causes a compiler
error. Destructor methods are preceded with a tilde and share the name of the class.
Destructor syntax:
~Classname()
~ZClass() {
--propCount;
}
The C# compiler emits a Finalize method for the ZClass. The Finalize method supplants the destructor in
Microsoft intermediate language (MSIL) code. Figure 14-5 shows the ZClass. Notice the Finalize method and
the absence of the Destructor method.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The C# compiler generated the following MSIL code for the ZClass destructor (for clarity, extraneous code
has been removed from the listing):
}
finally
{
IL_0011: ldarg.0
IL_0012: call instance void [mscorlib]System.Object::Finalize()
IL_0018: endfinally
}
IL_0019: nop
IL_001a: ret
}
The Finalize method provided by the C# compiler contains a termination handler. The try block contains the
code of the destructor. In this circumstance, the count is decremented. The termination handler calls the
Finalize method of the base class. Because the call is in a termination handler, the finalizer of the base
class is called even if an unhandled exception is raised in the destructor. This means that calls to the
Finalize method propagate through the class hierarchy even when an exception is raised. This also means
that developers should never attempt to directly call the destructor of the base class. The manifested Finalize
method automatically assumes this responsibility.
The following code defines a class hierarchy, in which each class in the hierarchy has a destructor. This
demonstrates that the destructors are called bottom-up automatically. The destructor in the XClass throws
an unhandled exception. Despite the error condition, the base destructors are called anyway. This confirms
that the Finalize method of the base class is called in a termination handler:
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
XClass obj=new XClass();
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
}
}
~ZClass() {
Console.WriteLine("ZClass d'tor");
}
~YClass() {
Console.WriteLine("YClass d'tor");
}
~XClass() {
Console.WriteLine("XClass d'tor");
throw new Exception("D'tor exception");
}
Many have lamented the decision to associate finalizers with destructors. C++ developers are familiar with
destructors. They are an integral part of the C++ language. This familiarity creates a level of expectation,
which is only partially honored in C#. There are credible differences between C++ and C# destructors,
including the following:
C++ destructors can be called deterministically, whereas C# destructors are called
nondeterministically.
C++ destructors can be virtual or nonvirtual, whereas C# destructors are implicitly virtual.
C++ destructors execute on the same thread that created the object, whereas C# destructors
execute on a dedicated finalizer thread.
C++ destructors cannot be suppressed at run time, whereas C# destructors can be suppressed.
For the remainder of this chapter, C# destructors are referred to as finalizers to distinguish between C# and
C++ destructors.
As mentioned, finalizers are called nondeterministically. In code, you cannot call them directly. When are
finalizers called?
During the garbage collection process
When the AppDomain of an object is unloaded
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Knowing the specific reason for finalization can be helpful. For example, if finalization is part of normal
garbage collection, you might choose to recycle the object. However, if the application domain is being
unloaded, recycling a dead object would be pointless. You can confirm that finalization is initiated by an
application unload or a CLR shutdown. The AppDomain .IsFinalizingForUnload method, which is an instance
method, returns true if the application domain is unloading and finalization has begun on objects in that
domain. Otherwise, the method returns false. The Environment.HasShutdownStarted property is a static
property that returns true if the CLR is shutting down or the application domain is unloading. Otherwise, the
property returns false.
The Recycle application recycles an instance of the ZClass type. When the object is being finalized, the
Environment.HasShutdownStarted property is checked. If false, the reference to the object is re-established,
which ends finalization. This resurrects the object (more about resurrection later). If the property is true, the
object is not recycled. There is no reason to recycle an object if the current application domain is unloading
or the CLR is shutting down. This is the ZClass type from the Recycle application:
~ZClass()
{
++propCount;
++_count;
AppDomain current = AppDomain.CurrentDomain;
if (!Environment.HasShutdownStarted)
{
Form1.obj = this;
GC.ReRegisterForFinalize(this);
}
}
At application shutdown, finalizers have a limited amount of time to complete their tasks. This prevents an
indefinite shutdown. Each Finalize method has two seconds to complete. If the time allotment is exceeded,
that finalizer is terminated. The remaining finalizers then continue to execute. As a group, the Finalize
methods have 40 seconds to complete all finalization chores. When this time limit is exceeded, the
remaining finalizers are skipped. Forty seconds is nearly an eternity in processing time and should be
sufficient to complete even the most extensive housecleaning of an application.
In the following application, you can adjust the duration of the finalizer and view the results. A number
between 1 and 10 provides the best comparative results. Specify the value as a command-line argument:
using System;
using System.Threading;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(string [] args){
Shutdown.ZTime=int.Parse(args[0]);
ZClass [] obj=new ZClass[500];
for(int count=0;count<500;++count) {
obj[count]=new ZClass();
}
}
}
public ZClass() {
++globalcount;
localcount=globalcount;
}
~ZClass() {
for(int i=0; i< Shutdown.ZTime; ++i) {
Thread.Sleep(50);
}
Console.WriteLine(localcount+" "+
"ZClass d'tor");
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
}
}
Finalizer Thread
The finalizer thread calls finalizers on objects waiting in the FReachable queue. After the finalizer is called,
the object is removed from the FReachable queue and deleted from memory during the next garbage
collection cycle. The finalizer thread executes the finalizer asynchronously. There is one finalizer thread that
services all finalizers of the application, which creates a potential bottleneck. For this reason, finalizers
should be short and simple. A long finalizer can delay the finalization of the remaining objects on the
FReachable queue. In addition, this extends the lifetime of objects pending finalization and increases
memory pressure on the managed heap. This causes more garbage collection, which is always expensive.
Finalizer Considerations
When implementing finalizers, there are several considerations. These considerations should help developers
implement finalizers correctly. Some of these considerations emphasize that finalizers should be avoided
whenever possible. For example, you should never implement an empty finalizer. In the C++ language, an
empty destructor is harmless. In C#, an empty destructor (finalizer) is costly, as explained in this section.
Finalizers are expensive As mentioned, finalizers are expensive. At least two garbage collection cycles
are required to collect a finalizable object that is not rooted. During the first garbage collection, the finalizable
object is moved from the Finalizable queue to the FReachable queue. After the finalizer has been called, the
memory of the object is reclaimed in a future garbage collection. The lifetime of objects referenced by the
finalizable object are equally extended. They must wait for the finalizable object to be reclaimed before being
released themselves. Additional object retention increases memory pressure, which causes additional
garbage collection. Needless garbage collection is particularly expensive. The finalizable objects are
promoted to later generations, which make a full garbage collection more likely. Full garbage collection is
much more expensive than collecting only Generation 0. Actually, the extra expense of defining a finalizable
object starts at its allocation. When a finalizable object is created, a reference to the object must be added
to the Finalizable queue. Objects without finalizers avoid this additional expense.
Finalizers are not guaranteed Finalizers are not always called. Some of the reasons why a finalizer might
not be called have already been mentioned. One such reason is the shutdown of the CLR by a host
application. At that time, finalizers must run in an allotted amount of time. You can also programmatically
suppress a finalizer with the GC.SuppressFinalize method. An asynchronous exception, such as a thread
abort exception, can cause a finalizer not to run.
Multithreading Finalizable objects are multithreaded. Object code and the finalizer execute on different
threads. For this reason, certain activities should be avoided in the finalizer. Most notably, never access
thread-local storage associated with the object in the finalizer. Because the thread context has changed,
using thread local storage in the finalizer is inappropriate.
The TLS sample application contains the following code that uses thread local storage. This is a Windows
Forms application. A reference to a StreamWriter object is placed into the TLS table for each thread. Various
threads accessing the same TMonitor object will own a different StreamWriter reference in the TLS table.
Therefore, each thread will write status messages to a different file. In the sample application, two threads
are accessing a TMonitor instance. In the destructor of the TMonitor type, a StreamWriter is retrieved from
TLS and closed. Which StreamWriter is closed? Is this the StreamWriter of the first or second thread? The
answer is neither. The destructor is running on the finalizer thread, which is unrelated to the other threads.
This thread has no StreamWriter reference, and an error is reported. In Microsoft Visual Studio 2005,
Console.WriteLine in a Windows Forms application displays in the Output window:
class TMonitor
{
public void WriteFile()
{
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
StreamWriter sw = Thread.GetData(Form1.Slot)
as StreamWriter;
if (sw == null)
{
sw = new StreamWriter(string.Format(
@"C:\{0}File.txt",
Thread.CurrentThread.Name),
true);
Thread.SetData(Form1.Slot, sw);
}
sw.WriteLine(DateTime.Now.ToLongTimeString());
}
~TMonitor()
{
StreamWriter sw = Thread.GetData(Form1.Slot)
as StreamWriter;
if (sw != null)
{
sw.Close();
}
else
{
Console.WriteLine("Error in destructor");
}
}
}
The preceding application is for demonstration purposes only. In a finalizer, you should not reference other
managed objects.
One finalizer thread There is a single finalizer thread that services the FReachable queue. It calls pending
finalizers of finalizable objects. The finalizer thread is different from other threads that might be accessing the
object methods. Do not change the context of the finalization thread. This is not your thread. Changing the
context of the finalization thread can have an adverse effect on the finalization process.
Finalizers and virtual functions Do not call virtual functions from finalizers. This can cause unexpected
behavior such as inadvertent leaks and resources not being cleaned up. If overridden, there is no assurance
that the derived class will provide the appropriate implementation. This is especially true for classes
published in a class library, in which the developer of the derived class may have limited knowledge of the
base class.
using System;
namespace Donis.CSharpBook{
public class Starter{
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
~ZClass() {
CloseResource();
}
}
~YClass() {
CloseResource();
}
}
}
In the preceding code, the finalizer in the ZClass calls the CloseResource method, which is a virtual method.
YClass is derived from ZClass and overrides the CloseResource method. An instance of the derived class is
created in the Main method. At garbage collection, the following messages are displayed:
Yes, CloseResource of the derived class is called twice, which creates a couple of problems. First, an
exception could be raised on the second attempt to close the resource. Second, the ZClass resource is not
removed and leaks.
Order of finalization Garbage collection is not performed in a guaranteed sequence. For this reason, do
not access another managed object in the finalizer. There is no assurance that the other managed object
has not been collected and removed from memory.
In the following code, the ZClass has a StreamWriter field. In the finalizer, the StreamWriter is closed.
Normally, this is perfectly acceptable behavior, but not in a finalizer. When the application exits, an
exception is raised in the finalizer. The StreamWriter object was collected before the current object.
Therefore, an exception is raised when an instance method of the StreamWriter class is called in the
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
finalizer.
sw = new StreamWriter("test.txt");
}
~ZClass()
{
sw.Close();
}
Resurrection Zombies do exist. Yikes! Like any object, finalizable objects are created on the managed
heap. When there are no outstanding references to the object, a finalizable object is submitted for garbage
collection. At that time, the object is dead. However, the object is resurrected to be placed in the FReachable
queue. After the finalizer is called, the object is removed from the FReachable queue. It is removed from
memory during the next garbage collection. At that time, the object is dead again.
You can intentionally, or more likely inadvertently, resurrect an object permanently during the finalization
process. At that time, the object is resurrected without the finalizer attached. Therefore, the resurrected
object is not completely returned to the live status. The object is a zombie. Zombies pose a distinct problem
because of their dangling finalizers. Because the finalizer is not called, proper cleanup is not performed when
the zombie is later collected. The result could be a memory leak, a resource not being released, or other
related problems. You can manually reconnect the finalizer with the GC.ReRegisterForFinalize method.
Afterward, the object is normal and is no longer a zombie.
Another problem with resurrection is that the original finalizer has executed. This could render the object
unusable because needed resources might have been relinquished.
During finalization, a common cause of accidental resurrection is the creation of a new reference to the
current object. At that moment, the object is resurrected. Despite being resurrected, the finalizer of the
object runs to completion. In the following code, the current object is directly referenced in the finalizer. An
indirect reference is more likely. Referencing another managed object in a finalizer can have a domino effect
and should be avoided. That object might access another managed object, which might reference another
object and so on until someone references the current object. Voilà—resurrection has occurred. This is a
compelling reason to avoid referencing managed objects in a destructor.
using System;
using System.IO;
namespace Donis.CSharpBook{
obj=null;
GC.Collect();
GC.WaitForPendingFinalizers();
obj.TimeStamp();
}
public ZClass() {
sw=new StreamWriter("test.txt");
}
~ZClass() {
Starter.obj=this;
sw.Close();
sw=null;
}
The first statement in the ZClass destructor resurrects the object. It assigns the object reference to a static
field of the Starter class. Because there is again an outstanding reference, the object is resurrected. The
underlying file of the StreamWriter instance is then closed. In Main, an instance of the ZClass is created.
The instance is assigned to a static field of the Starter class. Afterward, the reference is assigned null, and
garbage collection is forced. This kills and then resurrects the object. The next call on the object raises an
exception because the underlying file has been closed in the previous finalization.
The following code corrects the problem and successfully resurrects the object. The first statement confirms
whether the CLR is shutting down. If true, the managed application is exiting. In that circumstance, there is
no reason to resurrect the object while the application domain is being unloaded. At that time, static objects
are submitted to garbage collection. For that reason, the static object (sw) is accessible only when the
application domain is not being unloaded. In the if block, the object is resurrected and the finalizer is
reattached.
~ZClass() {
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
if(Environment.HasShutdownStarted==false) {
Starter.obj=this;
sw.Close();
sw=null;
GC.ReRegisterForFinalize(this);
}
}
The TimeStamp method of the previous code must also be updated. The finalizer sets the sw object, which
is a StreamWriter instance, to null. This is checked in the revised TimeStamp method. If null, the sw
reference is reinitialized. The file resource is then available again for writing:
using System;
using System.IO;
namespace Donis.CSharpBook{
public ZClass() {
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
~ZClass() {
if(Environment.HasShutdownStarted==false) {
Starter.obj=this;
sw.Close();
sw=null;
GC.ReRegisterForFinalize(this);
}
}
}
}
Finalizers and reentrancy A finalizer is reentrant. The best example of this is complete resurrection, in
which the finalizer is reattached. In that circumstance, the finalizer is called at least twice. When a finalizer
is called more than once, resurrection is the likely culprit. Redundant calls on a finalizer should not cause a
logic error or an exception.
Deep object graph A deep object graph can make garbage collection more expensive. Roots and branches
of the object graph can be anchored with a finalizable object. As mentioned, the lifetime of a finalizable
object is extended to encompass at least two garbage collections. All objects, even nonfinalizable objects,
along the path of the finalizable object have their lives similarly extended. Therefore, one finalizable object
can keep several other objects from being garbage collected. Deeper graphs by definition have longer
branches and can extend the problem.
Finalizer race condition The finalizer can execute at the same time as other functions of the finalizable
object. Because the finalizer executes on a dedicated thread, other functions of the finalizable object could
be running when finalization starts. This can cause a race condition to occur between normal operations and
the finalization. Standard thread-synchronization techniques can protect against a finalization race condition.
In the following code, MethodA is called on a ZClass instance. MethodA has a built-in delay. Shortly
thereafter, the object is set to null and collected. The finalizer then runs simultaneously with MethodA. The
race has begun! The results are usually unpredictable.
~ZClass() {
Console.WriteLine("ZClass.~ZClass Started");
// D'tor operation
Console.WriteLine("ZClass.~ZClass Finished");
}
}
Constructors An exception in the constructor does not deter the object from being finalized. The finalizer is
called regardless of the success of the constructor, which can create an interesting dilemma in which an
object that is unsuccessfully constructed is still finalized. Cleanup of a partially constructed object can pose
risks.
The following code demonstrates the problems that can occur when an exception is raised in a constructor.
The exception is caught by the exception handler in Main. At that time, the object exists but is not correctly
initialized. Despite the earlier exception in the constructor, the finalizer is called as the application exits.
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
try {
ZClass obj=new ZClass();
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
}
catch {
}
}
}
public ZClass() {
Console.WriteLine("ZClass c'tor");
throw new Exception("Error");
}
~ZClass() {
Console.WriteLine("ZClass d'tor");
}
}
}
The following code resolves the problem of a partially constructed finalizable object. If the constructor does
not complete successfully, GC.SuppressFinalize prevents the finalizer from being called later. In addition, a
flag is maintained that indicates the state of the object. The bPartial flag is set to true if the constructor fails,
which is checked in instance methods. If the flag is true, the methods raise an exception because the object
might not be stable.
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
try {
ZClass obj=null;
obj=new ZClass();
if(obj!=null){
obj.MethodA();
}
}
catch(Exception ex) {
Console.WriteLine(ex.Message);
}
}
}
public ZClass() {
try {
Console.WriteLine("ZClass c'tor");
throw new Exception("Error");
}
catch {
GC.SuppressFinalize(this);
bPartial=true;
}
}
~ZClass() {
Console.WriteLine("ZClass d'tor");
}
}
}
Console and finalization You can safely use the Console class in a finalizer. The Console class is
exempted from the rule about not using managed classes in finalizers. It is especially written for use during
finalization.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
IDisposable.Dispose
The Dispose method is an alternative to a finalizer. Contrary to finalizers, Dispose methods are deterministic
and can freely access managed types. Finalizers are nondeterministic, which delays invocation. This can
prevent resources from being released in a timely manner and create resource contention. For example, an
unreleased file handle can block or cause errors in other threads waiting on that handle. Dispose methods
are deterministic and sometimes referred to as explicit garbage collection. You can call the Dispose method
to release an expendable resource, such as immediately closing a file handle. Accessing managed objects
in a finalizer is unadvisable. Garbage collection is undeterminable, and that object may no longer exist. This
requirement greatly limits the scope and functionality of a finalizer. The Dispose method does not
necessarily have these limitations because garbage collection may not be occurring.
The Dispose method is defined in the IDisposable interface of the System namespace. Disposable objects
should inherit and implement the IDisposable interface, in which the Dispose method is the only member.
You then call the Dispose method as a normal method to start deterministic garbage collection. Although
possible, you should not implement the Dispose method separate of the IDisposable interface. The
IDisposable interface is an important marker that confirms the presence of a disposable object. There are
statements and behaviors, such as the using statement, that require this marker.
In the preceding code, the Dispose method is not guaranteed to run. Raising an exception prior to the
Dispose method call could cause the method to be skipped and result in resource leakage. To protect
against this possibility, place the Dispose method call in a finally block. This assures that the Dispose
method is called whether or not an exception is raised in the try block. This is an updated version of the
previous code, in which the Dispose method is placed in a finally block:
try {
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
disposableobject=new ZClass();
}
finally {
disposableobject.Dispose();
disposableobject=null;
}
}
The using block is the short form of the preceding code. A new object is defined in the using statement.
When the using block exits, the Dispose method is called automatically on the declared object of the using
statement:
The C# compile substitutes a try and a finally block for the using statement and block. This is some of the
MSIL code emitted for the using block:
IL_0006: stloc.0
.try
{
IL_0007: nop
IL_0008: nop
IL_0009: leave.s IL_001b
} // end .try
finally
{
// partial listing…
Multiple objects of the same type can be declared in the using statement. Delimit the objects with commas.
All objects declared in the statement are accessible in the using block. When the using block exits, the
Dispose method is called on each of the declared objects from the using statement. In the following code,
two objects of the same type are declared in the using statement:
using System;
namespace Donis.CSharpBook{
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
obj2=new ZClass()) {
}
}
}
You can also declare objects of different types for a using block—simply stack multiple using statements. In
the following code, three objects are scoped to the using block. One object is the XClass type, whereas two
objects are ZClass types. All three are disposed at the end of the using block.
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
using(XClass obj3=new XClass())
using(ZClass obj1=new ZClass(),
obj2=new ZClass()) {
}
}
}
Class can contain both a Dispose method and a finalizer. You can relinquish both managed and unmanaged
resources in the Dispose method, whereas the finalizer can clean up only the unmanaged resources. When
a Dispose method is not called as planned, the finalizer is an effective safety net. Finalizers are called
automatically and cannot be forgotten. If not called earlier, finalizers of disposable objects are called at CLR
shutdown. Finalization should not be performed on a disposed object. A second iteration of cleanup could
have unexpected results. For this reason, developers typically suppress the finalizer in the Dispose method.
The GC.SuppressFinalize method is called in the Dispose method to suppress the finalizer. Performance is
improved because future finalization of the object is eliminated.
In the following code, the ZClass has both a Dispose and finalizer method. Note that GC.SuppressFinalize is
invoked in the Dispose method.
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
using(ZClass obj1=new ZClass()) {
}
}
}
~ZClass() {
Console.WriteLine("ZClass.ctor");
// Cleanup unmanaged resources
}
}
}
This section reviewed the simple implementation of the Dispose method, which is sufficient for sealed
classes. However, inheritable classes require the more complex Disposable pattern.
{
StreamWriter sw = Thread.GetData(Form1.Slot)
as StreamWriter;
if (sw == null)
{
sw = new StreamWriter(string.Format(
@"C:\{0}File.txt",
Thread.CurrentThread.Name),
true);
Thread.SetData(Form1.Slot, sw);
}
sw.WriteLine(DateTime.Now.ToLongTimeString());
}
Disposable Pattern
The Disposable pattern provides a template for implementing the Dispose method and finalizer in a base and
derived class. The Disposable pattern, shown in the following code, should be implemented if the base class
does not presently possess resources:
using System;
using System.Threading;
namespace Donis.CSharpBook{
~Base() {
Dispose (false);
}
}
We will focus first on the base class, which implements the IDisposable interface and contains two Dispose
methods.
The one-argument Dispose method, which is a protected method, has a single argument. The disposing
argument indicates whether the method is being called during deterministic or nondeterministic garbage
collection. If called during nondeterministic garbage collection, the disposing argument is set to false.
Otherwise, the argument is true. When the argument is false, only unmanaged releases are releasable in the
method. When true, both managed and unmanaged resources can be released.
Both the no-argument Dispose method and the finalizer delegate to the one-argument Dispose method. The
no-argument Dispose method is public and called explicitly for deterministic garbage collection. This Dispose
method delegates to the one-argument destructor with the disposing flag set to true. It also suppresses
finalization of this object in future garbage collection. The finalizer delegates to the one-argument destructor
with the disposing flag set to false. This limits cleanup to unmanaged resources, which is appropriate during
normal garbage collection.
The no-argument Dispose method is not a virtual method, whereas the one-argument Dispose method is
virtual. The no-argument Dispose method should not be overridden in the derived class. This method should
always delegate to the most derived one-argument Dispose method. Any other behavior would seriously
break the disposable pattern.
In the derived class, override the one-argument Dispose method to clean up managed and unmanaged
resources of the derived class. Do not override or hide the no-argument Dispose method of the base class.
Finally, the one-argument Dispose method in the derived class should call the same method in the base
class, affording that class the opportunity to release its resources.
In the derived class, do not implement a finalizer. The base class implementation of the finalize method will
correctly call the most derived dispose method. Disposal then propagates from the most derived class to all
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
There are several factors to consider when implementing a simple dispose or the more complex Disposable
pattern. This section lists many of the factors that should be considered when implementing a Dispose
method.
The following code demonstrates a resilient Dispose method, which can be called multiple times. The
ReverseReader type is a thin wrapper for a StreamReader. It inverts information read from the StreamReader.
ReserveReader contains a StreamReader field. It is initialized in the class constructor and closed in the
Dispose method. If the StreamReader is null, the object is presumed disposed. This is checked in the
Dispose method. If the object is already disposed, the Dispose method simply returns. When the object is
disposed, the other ReverseReader method throws the ObjectDisposedException exception. After the using
block, a second Dispose method is called, which proves to be harmless. A second call to the ReadLine
method is commented. If uncommented, an exception would be raised.
using System;
using System.IO;
namespace Donis.CSharpBook{
public class Starter{
}
}
Close Method
Instead of the Dispose method, some classes expose another method for deterministic cleanup. Although
this should not be done as a general practice, the exception is when another method is more intuitive than
the Dispose method. For example, the FileStream class exposes the Close method. (Close is the traditional
term for releasing a file.) The alternative method should delegate to the Dispose method. Do not implement
the disposable routine more than once. Both the Dispose and alternative methods are available to the clients
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
for deterministic garbage collection. The correct implementation is demonstrated with the StreamWriter.Close
method, as shown in the following code. StreamWriter.Close delegates to TextWriter.Dispose for
deterministic garbage collection. TextWriter.Dispose is inherited by the StreamWriter class. The Close
method suppresses the finalizer, which is standard behavior of a deterministic method. Both the Close and
Dispose method are available on the StreamWriter class. You should clearly document any alternate method
for deterministic garbage collection.
Reusable Objects
The Disposable pattern accommodates reusable objects. Unless the object is nondisposed, it can be
recycled. Do not recycle an object implicitly. Automatically recycling an object on the assignment operator
or a function call is not recommended. Expose a method that explicitly recycles the object. There is no
convention for naming this method. However, an Open method is always a good choice. This method should
have the dual purpose of initializing instances created with a default constructor. Recyclable objects should
also expose a property that confirms the status of the object as alive or disposed.
The following code is a revision of the ReverseReader class, which was presented earlier in this chapter. This
version is recyclable. It has a default and one-argument constructor. The one-argument constructor
delegates to the Open method. You can call the Open method to initialize or recycle a ReverseReader
instance. The Active property returns the status of the object. If true, the object is active.
public ReverseReader() {
}
A disposable class should dispose the disposable fields contained in the type. Call the Dispose method of
those fields in the Dispose method of the class. After disposing, set the disposable fields to null. Of course,
the inner objects dispose the disposable objects they contain. In this way, the Dispose method is transitive.
public ZClass() {
inner=new YClass();
}
inner=null;
}
An object should not dispose any object not fully within its control, which can cause some unwanted side
affects. In the following code, the _inner field is initialized with a property. The _inner field is a disposable
type. Two instances of the containing class are created. One is created before the using block, and the other
is created in the using statement. The _inner field of both objects is initialized to the same instance.
Therefore, neither object has full control of the object. When the using block is exited, the inner object is
disposed in the second object. However, the other object remains active. When the remaining active object
attempts to access the inner object, an exception is raised.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
ZClass obj1=new ZClass();
obj1.Inner=new YClass();
using(ZClass obj2=new ZClass()) {
obj2.Inner=obj1.Inner;
}
obj1.MethodA(); // exception
obj1.Dispose();
obj1=null;
}
}
public ZClass() {
}
}
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Weak Reference
Weak references are one of my favorite features of the .NET Framework. A weak reference accords an object
less persistence than a strong reference. A strong reference is a conventional reference and created with the
new operator. As long as there is a strong reference pointing to the object, the object persists in memory. If
not, the object becomes a candidate for removal and is collected in a future garbage collection. Conversely,
a weak reference to an object is insufficient to retain that object in memory. A weak reference can be
collected as memory is needed. You must confirm that weakly referenced objects have not been collected
before use.
Weak references are not ideal for objects that contain information that is expensive to rehydrate. Information
read from a persistent source such as a file or data store is preferred. You can simply reread the file or
request the dataset again. Rehydrating a dataset can be light or heavy based on several factors, including
the location of the dataset.
The central type for establishing weak references is, appropriately, the WeakReference type. These are the
steps for creating and using a weak reference:
Create a WeakReference type. In the constructor, initialize the weak reference with the strong reference.
Afterward, set the strong reference to null. An outstanding strong reference prevents the weak reference from
controlling the lifetime of the object:
Before using the object, request a strong reference to the object from the weak reference.
WeakReference.Target returns a strong reference to the weak object. If WeakReference.Target is null, the
object has been collected and is no longer in memory. In this circumstance, the object needs to be
rehydrated:
if (weaknames.Target == null)
{
// do something
}
class XNames
{
public XNames()
{
StreamReader sr=new StreamReader("names.txt");
string temp = sr.ReadToEnd();
_Names = temp.Split('\n');
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The preceding code is from the Weak application. It uses the XNames class to display the names from a file
in a list box. The Weak application is shown in Figure 14-6.
In the Weak application, a weak reference is created and initiated with an instance of the XNames class.
The code in the Fill List button enumerates the names of the XNames instance to populate the list box.
Before using the instance, the status of the instance must be confirmed. Is it collected or not? The Apply
Pressure button applies memory pressure to the application that eventually forces the weakly referenced
object to be collected. When this occurs, the content in the list box is removed. You must then refill the list
box using the Fill List button.
This is the code from the form class of the Weak application that pertains to weak references:
{
DialogResult result=MessageBox.Show(
"Rehydrate?",
"Names removed from memory.",
MessageBoxButtons.YesNo);
if (result == DialogResult.No)
{
return;
}
else
{
weaknames.Target = new XNames();
}
}
foreach(string name in (XNames)weaknames.Target)
{
lblNames.Items.Add(name);
}
}
Weak references are tracked in short weak reference and long weak reference tables. Weak references are
not created on the managed heap, but are entries in the short weak reference or long weak reference tables.
Both tables are initially empty.
Each entry in the short weak reference table is a reference to a managed object on the heap. When garbage
collection occurs, objects referenced in the short weak reference table that are not strongly rooted are
collectable. The related slot in the table is set to null. References to finalizable objects that are being
collected are moved from the finalization to the FReachable queue.
Entries in the long weak reference table are evaluated next. Long weak references are weak references that
track an object through finalization. This allows the resurrection of a weakly referenced object. Objects
referenced in the long weak reference table that are not strongly rooted are collectable.
Me De
mb scr
er ipti
Na on
me
We Th
ak e
Ref on
ere e-a
nc rgu
e(o me
bje nt
ct co
tar nst
get ruc
) tor
initi
aliz
es
the
we
ak
ref
ere
nc
e
wit
h
the
tar
get
obj
ect
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 14-1:
WeakReferen
ce Class
Me De
mb scr
er ipti
Na on
me
We Th
ak e
Ref two
ere -ar
nc gu
e(o me
bje nt
ct co
tar nst
get ruc
, tor
bo initi
ol aliz
tra es
ck the
Re we
sur ak
rec ref
tio ere
n) nc
e
wit
h
the
tar
get
obj
ect
. If
tra
ck
Re
sur
rec
tio
n
is
tru
e,
the
obj
ect
is
als
o
tra
ck
ed
thr
ou
gh
fina
liza
tio
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 14-1:
WeakReferen
ce Class
Me De
mb scr
er ipti
Na on
me
IsA Thi
live s
is
a
pro
per
ty
an
d
get
s
wh
eth
er
or
not
the
tar
get
obj
ect
ha
s
be
en
coll
ect
ed.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 14-1:
WeakReferen
ce Class
Me De
mb scr
er ipti
Na on
me
Tar Thi
get s
is
an
obj
ect
pro
per
ty
an
d
get
s
or
set
s
the
obj
ect
bei
ng
ref
ere
nc
ed.
Conditions can prevent a finalizer from running, and critical cleanup code might not execute. Resource
leakage and other problems can occur when critical finalization code is abandoned. .NET Framework 2.0
introduces critical finalizable objects to ensure that critical finalizers run. The CLR is more diligent about
executing finalizers in critical finalizer objects. Conditions that can prevent a normal finalizer from running,
such as a forcible thread abort or an unload of the application domain, do not affect a critical finalizer. This is
especially an issue in environments that host the CLR, such as Microsoft SQL Server 2005. A CLR host can
asynchronously interrupt a managed application, which can strand important finalizers.
During garbage collection, critical finalizers run after the normal finalizers.
Critical finalizer objects are derived from the CriticalFinalizerObject, which is located in the System.Runtime
namespace. The finalizer runs uninterrupted in a Constrained Execution Region (CER).
CER is a region of reliable code that is guaranteed to run. Even an asynchronous exception will not prevent
the region from executing to completion. Within the CER, developers are constrained to certain actions. This
is a shortlist of some of the actions that should be avoided in a CER:
Boxing
Unsafe code
Locks
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Serialization
Calling unreliable code
Actions likely to cause an asynchronous exception
In a CER, the CLR delays an asynchronous exception until the region is exited. The CLR performs a variety
of checks and does some preparation to ensure that code in the CER runs uninterrupted.
Developers must make reliability assurances for code in a constrained region. The CLR does not strictly
enforce the reliability constraints within the CER. Instead, the CLR relies on developer commitment, which is
a reliability contract. Reliability contracts state the level of compliance for a method, class, or assembly. Set
a reliability contract with the ReliabilityContractAttribute. This attribute is found in the
System.Runtime.ConstrainedExecution namespace. The Reliability-ContractAttribute constructor has two
parameters. The first parameter is the ConsistencyGuarantee property, which indicates the potential scope
of corruption if an asynchronous exception occurs during execution. For example,
Consistency.MayCorruptAppDomain means that an asynchronous exception can leave the application
domain in an unreliable state. The second parameter is the Cer property, which is a completion guarantee for
code in a CER region. Cer.Success is the highest guarantee and promises that the code will successfully
complete when running in a CER, assuming valid input.
[ReliabilityContract(Consistency.WillNotCorruptState,
Cer.Success)]
class ZClass {
void MethodA() {
RuntimeHelpers.PrepareConstrainedRegions();
try {
}
finally {
// CER Region
}
}
Stephen Toub authored a detailed and informative article on CERs called "Keep Your Code Running with the
Reliability Features of the .NET Framework." It can be found at the following link:
http://msdn.microsoft.com/msdnmag/issues/05/10/reliability/default.aspx
Safe Handles
As a convenience, partial implementation of the CriticalFinalizerObject type is found in the
System.Runtime.InteropServices namespace. The implementation creates a critical finalizer, which contains
a CER. CriticalHandle, SafeHandle, SafeHandleMinusOneIsInvalid, and
Safe-HandleZeroOrMinusOneIsInvalid are derived from CriticalFinalizerObject and are adequate for most
purposes. These classes are found in the Microsoft.Win32.SafeHandles namespace. Safe-Handle
implements reference counting, whereas CriticalHandle does not support reference counting. Both classes
are essentially safe wrappers for kernel handles and ensure that kernel handles are properly closed in the
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
future. The classes are abstract and require minimal implementation in the derived type. The .NET
Framework class library (FCL) provides a complete implementation of the CriticalHandle and SafeHandle
classes in the Microsoft.Win32.SafeHandles namespace. SafeWaitHandle is one implementation. It is a
wrapper for a wait handle and indirectly extends the SafeHandle class.
In the following code, the PipeHandle class is a safe wrapper for a pipe handle. It also exposes the
CreatePipe and the CloseHandle application programming interfaces (APIs) using interoperability. Because
the method is called in a CER, a reliability contract is placed on the CloseHandle method. PipeHandle
derives from the SafeHandleMinusOneIsInvalid class for the behavior of a critical finalizer object. The finalizer
of the base class automatically calls the ReleaseHandle method. The ReleaseHandle of the derived type,
PipeHandle, also has a reliability contract.
In the AnonymousPipe constructor, two PipeHandle instances are initialized, which are a read and write
handle. The handle values are also displayed. The PipeHandle and AnonymousPipe classes are included in
the Pipe application found on the companion CD-ROM. In the sample code, the application domain hosting
the PipeHandle instances can be forcibly unloaded. Because the instances are critical finalizer objects, their
destructors are called despite the unloading of the application domain:
private PipeHandle()
: base(true)
{
[ReliabilityContract(Consistency.WillNotCorruptState,
Cer.Success)]
protected override bool ReleaseHandle()
{
return CloseHandle(handle);
}
[DllImport("kernel32.dll")]
extern public static bool CreatePipe(
out PipeHandle hReadPipe,
out PipeHandle hWritePipe,
IntPtr securityAttributes,
int nSize);
[ReliabilityContract(Consistency.WillNotCorruptState,
Cer.Success)]
[DllImport("kernel32.dll")]
public static extern bool CloseHandle(IntPtr handle);
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
public AnonymousPipe()
{
MessageBox.Show((writeHandle.DangerousGetHandle())
.ToInt32().ToString());
}
Managed code often relies on unmanaged resources. The unmanaged resource is typically accessible
through a managed wrapper. The MyDevice program is an unmanaged application that emulates a hardware
device. DeviceWrapper is a managed wrapper for the MyDevice unmanaged resource. This is the code for the
DeviceWrapper class:
public MyDevice()
{
obj = new MyDeviceLib.DeviceClass();
++count;
}
{
obj.CloseDevice();
}
~MyDevice()
{
// resource released
--count;
}
}
Memory Pressure
The wrapper for an unhandled resource can hide the true memory cost of an object. Incorrect accounting of
unhandled memory in the managed environment can cause unexpected out of memory exceptions.
.NET 2.0 introduces memory pressure, which accounts for unmanaged memory in the managed
environment. This prevents a wrapper to an unmanaged resource from hiding an elephant in the closet.
Memory pressure forces garbage collection sooner, which collects unused instances of the wrapper class.
The wrapper releases the unmanaged resource to reduce the memory pressure on both managed and
unmanaged memory.
The GC.AddMemoryPressure method adds artificial memory pressure on the managed heap for an
unmanaged resource, whereas the GC.RemoveMemoryPressure method removes memory pressure. Both
methods should be integrated into the wrapper class of the unmanaged resource. Call AddMemoryPressure
and RemoveMemoryPressure in the constructor and Dispose method, respectively. Each instance of the
MyDevice unmanaged resource allocates 40,000 bytes of memory on the unmanaged heap. In the following
code, the constructor and destructor for the MyDevice wrapper now account for the unmanaged memory:
public MyDevice()
{
GC.AddMemoryPressure(40000);
obj = new MyDeviceLib.DeviceClass();
++count;
}
~MyDevice()
{
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
GC.RemoveMemoryPressure(40000);
// resource released
--count;
}
Handles
Some native resources are available in limited quantities. Exhausting the resource can hang the application,
crash the environment, or cause other adverse reactions. The availability of a limited resource should be
tracked. When the resource is exhausted, corrective action should occur. Some limited kernel resources,
such as a window, are assigned handles. The HandleCollector class is introduced in .NET Framework 2.0 to
manage handles to limited kernel resources. Despite the name, the HandleCollector class is not limited to
tracking kernel handles. You can use the HandleCollector class to manage any resource that has limited
availability. The HandleCollector class is found in the System.Runtime.InteropServices namespace.
The HandleCollector class has a three-argument constructor that configures the important properties of the
type. The arguments are the name: initial threshold and maximum threshold. The initial threshold sets the
minimal requirement for starting garbage collection. The maximum threshold sets the limit where garbage
collection is forced. When the availability is exceeded, garbage collection is triggered. Hopefully, this will
collect managed wrappers that are holding unmanaged resources and replenish native resources. Using the
constructor, create a static instance of the HandleCollector in the managed wrapper of the unmanaged
resource. In the constructor, call HandleCollector.Add. In the finalizer, call HandleCollector.Remove.
The following code shows the MyDevice class revised for the HandleCollector class. The MyDevice
unmanaged resource supports an initial threshold of three simultaneous connections and a maximum of five.
You can test the effectiveness of the wrapper in the UseResource application by clicking the Connect button
and monitoring the message boxes.
public MyDevice()
{
GC.AddMemoryPressure(40000);
track.Add();
obj = new MyDeviceLib.DeviceClass();
++count;
MessageBox.Show("Device count: " + count.ToString());
}
~MyDevice()
{
GC.RemoveMemoryPressure(40000);
track.Remove();
// resource released
--count;
}
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
GC Class
Developers use the GC class as an interface to the GC in .NET. You can manipulate and monitor garbage
collection with the GC class. Some GC commands affect the normal operation of the garbage collection.
Garbage collection usefully performs best without developer interference. Table 14-2 lists the static members
of the GC class.
Table 14-2: GC Class
MaxGeneration This property is an integer property and gets the maximum number of
generations.
AddMemoryPressure This method forces the GC to recognize memory allocations for unmanaged
resources.
Collect This method forces garbage collection on a specific generation. There are
two overloads of the method. The no-argument Collect performs garbage
collection for all the generations. The one-argument Collect has a parameter
that specifies the oldest generation to be collected.
CollectionCount This method returns the number of garbage collection cycles for the
specified generation.
GetTotalMemory This method returns the total number of bytes thought to be allocated for the
managed heap. You can wait on garbage collection and finalization by
setting the Boolean parameter to true.
KeepAlive This method keeps alive the specified object from the beginning of the
current routine to where the KeepAlive method is called.
RemoveMemoryPressure This method removes some of the memory pressure set aside for
unmanaged resources.
WaitForPendingFinalizers This method suspends the current thread until the Finalization queue is
empty.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Nonsecure Code
The final chapter, which is the next chapter, pertains to unsafe code, which is an ominous name. What
developer wants to purposely write unsafe code? In .NET, unsafe code really means potentially unsafe code,
which is code or memory that exists outside the normal controls of the CLR. For this reason, developers
should approach unsafe code with caution.
Developers have access to raw pointers in C#, which are called pinned pointers. They are Cstyle pointers.
You can use pointer operators, such as * and &, with these pointers. Pointers should be avoided because
they interrupt the normal operation of the GC. How-ever, not secure pointers are sometimes necessary. For
example, porting algorithms that rely heavily on pointers is one situation in which not secure pointers are
beneficial.
Managed developers must sometime call unmanaged routines. Although the breadth of the .NET FCL is
expanding quickly, there is considerable system behavior that still resides out-side its realm. In addition,
many third-party routines are not managed—particularly legacy applications. Finally, some code will never
be managed for performance or other reasons. Interoperability allows managed developers to build bridges
back to managed code. You can call unmanaged code from a managed routine. Conversely, you can call
managed code from unmanaged routines.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Pointers to unmanaged memory are available in unsafe code. Like unmanaged memory, pointers are also
outside the realm of the CLR. Pointers point to a fixed location in unmanaged memory, whereas reference
types point to a moveable location in managed memory. The CLR manages reference types, which includes
controlling the lifetime of objects and calling cleanup code. Developers do not delete memory allocated for
reference types and are not overly involved in the intricacies of memory management. In C and C++
application development, developers were preoccupied with memory management. Despite this, improper
management of pointers is a primary contributor to unsafe code in the unmanaged environment, including
memory leaks, access of invalid memory, deletion of pointers, and fence post errors. Abstracting the
nuances of pointer management and manipulation with reference types has made managed code
considerably safer. However, when needed, you can exempt yourself from secure code and access pointers
directly.
When is unsafe code appropriate? Not often. Unsafe code is provided within C# as the exception, not the
rule. There are specific circumstances in which unsafe code is recommended:
Unmanaged code often relies heavily on pointers. When porting this sort of code to C#, unsafe
code is a possible solution. Most nontrivial C and C++ programmers heavily leverage pointers.
Implementing a software algorithm, in which pointers are integral to the design, might necessitate
unsafe code.
Calling an unmanaged function might require function pointers.
Pointers might be required when working with binary memory resident data structures.
Unmanaged pointers might improve performance and efficiencies in certain circumstances.
Code in an unmanaged module is also considered unsafe. Code in an unmanaged module is shielded from
the CLR. Therefore, no code verification, stack tracing, or other checking is performed on the unmanaged
code, which makes the code less safe.
Developers sometimes need to call unmanaged code from managed applications. Although the Microsoft
.NET Framework class library (FCL) contains most of the code needed for .NET application development, the
FCL umbrella does not encompass everything. You might need to call functions (APIs) in operating system
libraries for behavior outside the FCL. In addition, proprietary and vendor software might not be available as
managed code. Alternatively, you can call managed code from an unmanaged module, such as during a
callback. Managed code might also be exposed to unmanaged clients.
Platform invocation services (PInvoke) is the bridge between managed and unmanaged execution. The bridge
is bidirectional. From managed code, PInvoke is responsible for locating, loading, and executing a function in
an unmanaged module. Marshaling is the primary concern of cross-platform calls and the responsibility of
the Interop marshaler. Marshaling converts parameters and return types between unmanaged and managed
acceptable formats. Fortunately, marshaling is not always required, which avoids unnecessary overhead.
Certain types, such as blittable types, do not require transformation and are the same in managed and
unmanaged memory.
You can also build bridges between managed code and COM components, which contain unmanaged code.
The Runtime Callable Wrapper (RCW) helps managed code call COM components. The COM Callable
Wrapper (CCW) portrays a managed component as a COM component. This makes the managed
component indirectly accessible to COM clients. COM components are also available via PInvoke. However,
the CCW and RCW are more convenient and are the recommended solutions in most scenarios. COM
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
interoperability is not a topic for this book. COM Programming with Microsoft .NET, by John Paul Mueller and
Julian Templeman (Microsoft Press, 2003), is an excellent resource for additional information on COM
interoperability and .NET.
Unsafe code is also not trusted code. Code access security does not extend to unsafe code. Type
verification, which helps prevent buffer overrun attacks, is not performed. Code verification is not performed.
Therefore, the reliability of the code is undetermined. These are some of the reasons why unsafe code is not
trusted. Because it is not trusted, elevated permissions are required to call unsafe code from managed code.
Applications that rely on unsafe code might not execute successfully in every deployment situation and
should be thoroughly tested in all potential scenarios. Managed code requires the
SecurityPermission.UnmanagedCode permission to call unsafe code. The
SuppressUnmanagedCodeSecurityAttribute attribute disables the stack walk that confirms the
SecurityPermission.UnmanagedCode permission in callers. This attribute is a free pass for other managed
code to call unsafe code. It is a convenient option, but also dangerous.
Managed applications that include unsafe code must be compiled with the unsafe option. The C# compiler
option is simply /unsafe. In Microsoft Visual Studio 2005, this option is found in the project properties. The
project properties are accessible from Solution Explorer. Open the context menu on the project name and
choose the Properties menu item. In the Build window, choose the Allow unsafe code option, as shown in
Figure 15-1.
Figure 15-1: The Build window with the unsafe compiler option
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Unsafe Keyword
The unsafe keyword sets the perimeter of an unsafe context and prevents the inadvertent placement of
unsafe code. Code within the unsafe context can be unsafe, which allows unmanaged pointers to be
declared and used in expressions. The unsafe keyword can be applied to a class, struct, interface, or
delegate. When it is applied to a type, all the members of that type are also considered unsafe. You can
also apply the unsafe keyword to specific members of a type. If applied to a function member, the entire
function operates in the unsafe context.
In the following code, the ZStruct contains two fields that are pointers. Each is annotated with the unsafe
keyword.
In this example, ZStruct is marked as unsafe. The unsafe context extends to the entire struct, which
includes the two fields. Both fields are unsafe.
You can also create an unsafe block using the unsafe statement. All code encapsulated by the block is in
the unsafe context. The following code has an unsafe block and method. Within the unsafe block in the Main
method, MethodA is called and passed an int pointer as a parameter. MethodA is an unsafe method. It
assigns the int pointer to a byte pointer, which now points to the lower byte of the int value. The value at that
lower byte is returned from MethodA. For an int value of 296, MethodA returns 40.
return *temp;
}
The unsafe status of a base class is not inherited by a derived class. Unless explicitly designated as unsafe,
a derived class is safe. In an unsafe context, the derived class can use unsafe members of the base class
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
In the following code, a compile error occurs in the derived type. The fieldb member of YClass requires an
unsafe context, which is not inherited from the ZClass base class. Add the unsafe keyword explicitly to
fieldb, and the code will compile successfully.
Pointers
Unsafe code is mostly about direct access to pointers, which point to a fixed location in memory. Because
the location is fixed, the pointer is reliable and can be used for dereferencing, pointer math, and other
traditional pointer type manipulation. Pointers are outside the control of the GC. The developer is responsible
for managing the lifetime of the pointer, if necessary.
C# does not automatically expose pointers. Exposing a pointer requires an unsafe context. In C#, pointers
are usually abstracted using references. The reference abstracts a pointer to memory on the managed heap.
That reference and related memory is managed by the GC and subject to relocation. A moveable pointer
underlies a reference, which is why references are not available for direct pointer manipulation. Pointer
manipulation on a moveable address would yield unreliable results.
You can declare multiple pointers in a single statement using comma delimiters. Notice that the syntax is
slightly different from C or C++ languages:
The unmanaged types (a subset of managed types) are sbyte, byte, short, ushort, int, uint, long, ulong, char,
float, double, decimal, bool, and enum. Some managed types, such as string, are not included in this list.
You can create pointers to user-defined structs, assuming that they contain all unmanaged types as fields.
Pointer types do not inherit from System.Object, so they cannot be cast to or from System.Object.
Void pointers are allowed but dangerous. This is a typeless pointer that can emulate any other pointer type.
Any pointer type can be implicitly cast to a void pointer. This unpredictability makes void pointers
extraordinarily unsafe. Except for void pointers, pointers are moderately type-safe. Although you cannot
implicitly cast between different pointer types, explicitly casting between most pointer types is always
allowed. As expected, the following code would cause a compiler error because of the pointer mismatch.
This assignment could be forced with an explicit cast to another pointer type. In that circumstance, the
developer assumes responsibility for the safeness of the pointer assignment.
int val=5;
float* pA=&val;
You can initialize a pointer with the address of a value or with another pointer. In the following code, both
methods of initializing a pointer are shown:
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
In the preceding code, the asterisk (*) is used to declare a pointer. The ampersand (&) is used to
dereference a value. Table 15-1 describes the various symbols that are used with pointers.
Table 15-1:
Pointer
Symbols
Sy De
mb scr
ol ipti
on
Poi For
nte poi
r nte
de rs,
cla the
rati ast
on eri
(*) sk
sy
mb
ol
ha
s
two
pur
po
se
s.
Th
e
firs
t
is
to
de
cla
re
ne
w
poi
nte
r
vari
abl
es.
int
*pA;
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-1:
Pointer
Symbols
Sy De
mb scr
ol ipti
on
Poi Th
nte e
r se
der co
efe nd
ren pur
ce po
(*) se
of
the
ast
eri
sk
is
to
der
efe
ren
ce
a
poi
nte
r.
Poi
nte
rs
poi
nt
to
an
ad
dre
ss
in
me
mo
ry.
Der
efe
ren
cin
g
a
poi
nte
r
ret
urn
s
the
val
ue
at
tha
t
ad
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-1:
Pointer
Symbols
Sy De
mb scr
ol ipti
on
Ad Th
dre e
ss am
of per
(&) sa
nd
sy
mb
ol
ret
urn
s
the
me
mo
ry
loc
ati
on
of
a
vari
abl
e,
whi
ch
is
a
fixe
d
val
ue.
Th
e
foll
owi
ng
co
de
ret
urn
s
the
me
mo
ry
ad
dre
ss
of
an
int.
It
is
us
ed
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-1:
Pointer
Symbols
Sy De
mb scr
ol ipti
on
Me Arr
mb ow
er not
ac ati
ce on
ss der
(->) efe
ren
ce
s
me
mb
ers
of
a
typ
e
fou
nd
at
a
me
mo
ry
loc
ati
on.
For
ex
am
ple
,
yo
u
ca
n
ac
ce
ss
me
mb
ers
of
a
str
uct
usi
ng
arr
ow
not
ati
on
an
d
a
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-1:
Pointer
Symbols
Sy De
mb scr
ol ipti
on
Poi A
nte poi
r nte
ele r
me ele
nt me
([]) nt
is
an
offs
et
fro
m
the
me
mo
ry
ad
dre
ss
of
a
poi
nte
r.
For
ex
am
ple
,
p[2
]
is
an
offs
et
of
two
.
Off
set
s
are
inc
re
me
nte
d
by
the
siz
e
of
the
poi
nte
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-1:
Pointer
Symbols
Sy De
mb scr
ol ipti
on
Poi A
nte poi
r nte
to r
a to
poi a
nte poi
r nte
(**) r
poi
nts
to
a
loc
ati
on
in
me
mo
ry
tha
t
co
nta
ins
the
ad
dre
ss
of
a
poi
nte
r.
Alt
ho
ug
h
rar
ely
us
eful
,
yo
u
ca
n
ext
en
d
the
ch
ain
of
poi
nte
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-1:
Pointer
Symbols
Sy De
mb scr
ol ipti
on
Poi Poi
nte nte
r r
ad ad
diti diti
on on
(+) ad
ds
the
siz
e
of
the
poi
nte
r
typ
e
to
the
me
mo
ry
loc
ati
on.
ZStru
ct
obj=n
ew
ZStru
ct(5)
;
int
*pA=&
obj.f
ielda
;
pA=pA
+2;
//
Add
eight
to
point
er
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-1:
Pointer
Symbols
Sy De
mb scr
ol ipti
on
Poi Poi
nte nte
r r
su su
btr btr
act act
ion ion
(-) su
btr
act
s
fro
m
the
poi
nte
r
the
siz
e
of
the
poi
nte
r
typ
e.
ZStru
ct
obj=n
ew
ZStru
ct(5)
;
int
*pA=&
obj.f
ielda
;
pA=pA
-3;
//
Subtr
act
twelv
e
from
point
er
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-1:
Pointer
Symbols
Sy De
mb scr
ol ipti
on
Poi Poi
nte nte
r r
inc inc
re re
me me
nt nt
(++ inc
) re
me
nts
the
poi
nte
r
by
the
siz
e
of
the
poi
nte
r
typ
e.
ZStru
ct
obj=n
ew
ZStru
ct(5)
;
int
*pA=&
obj.f
ielda
;
++pA;
//
incre
ment
point
er
by
four
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-1:
Pointer
Symbols
Sy De
mb scr
ol ipti
on
Poi Poi
nte nte
r r
de de
cre cre
me me
nt nt
(--) de
cre
me
nts
the
poi
nte
r
by
the
siz
e
of
the
poi
nte
r
typ
e.
ZStru
ct
obj=n
ew
ZStru
ct(5)
;
int
*pA=&
obj.f
ielda
;
--pA;
//
decre
ment
point
er
by
four
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-1:
Pointer
Symbols
Sy De
mb scr
ol ipti
on
Rel Th
ati e
on rel
al ati
sy on
mb al
ols op
era
tor
s,
su
ch
as
<
>
>=
<=
!=
==,
ca
n
be
us
ed
to
co
mp
are
poi
nte
rs.
Th
e
co
mp
ari
so
n
is
ba
se
d
on
me
mo
ry
loc
ati
on
an
d
not
poi
nte
r
typ
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
A pointer is a legitimate variable. As such, a pointer can be used as a variable in most circumstances,
including as a parameter or return type. When used as a return type, ensure that the lifetime of the pointer is
the same as or exceeds that of the target. For example, do not return a pointer to a local variable from a
function—the local variable loses scope outside of the function and the pointer is then invalid.
In the following code, a pointer is used as both a parameter and return type. MethodA accepts a pointer as a
parameter. It then returns the same pointer. After the method call, both pB and pA point to the same location
in memory. They are aliases. Therefore, Console.WriteLine displays the same number when the values at
the pointers are displayed.
using System;
namespace Donis.CSharpBook{
public class Starter{
public unsafe static void Main() {
int val=5;
int *pA=&val;
int *pB;
pB=MethodA(pA);
Console.WriteLine("*pA = {0} | *pB = {0}",
*pA, *pB);
}
The ref or out modifiers can be applied to pointer parameters. Without the modifiers, the memory location is
passed by pointer. The pointer itself is passed by value on the stack. In the function, you can dereference
the pointer and change values at the memory location. These changes will persist even after the function
exits. However, changes to the pointer itself are discarded when the function exists. With the ref or out
modifier, a pointer parameter is passed by reference. In the function, the pointer can be changed directly.
Those changes continue to persist even after the function exits.
In the following code, both MethodA and MethodB have a pointer as a parameter. MethodA passes the
pointer by value, whereas MethodB passes the pointer by reference. In both methods, the pointer is
changed. The change is discarded when MethodA exists. When MethodB exits, the change persists.
using System;
namespace Donis.CSharpBook{
public class Starter{
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Fixed
What is wrong with the following code?
int [] numbers={1,2,3,4,5,6};
int *pI=numbers;
The problem is that the numbers variable is an array, which is a reference type. The code will not compile.
References are moveable types and cannot be converted to pointers. Because objects are moveable, you
cannot obtain a reliable pointer. Conversely, structs are value types and are placed on the stack and outside
of the control of the GC. Struct values have a fixed address and are easily converted into pointers. In the
preceding code, if the type were changed from an array to a struct, it would compile successfully. With the
fixed statement, you pin the location of a moveable type—at least temporarily. Pinning memory for an
extended period of time can interfere with efficient garbage collection.
Here is the code revised with the fixed statement. This code compiles successfully.
int [] numbers={1,2,3,4,5,6};
fixed(int *pI=numbers) {
// do something
}
The fixed statement pins memory for the span of a block. In the block, the memory is unmovable and is
exempt from garbage collection. You can access the pinned memory using the pointer declared or initialized
in the fixed statement, which is a read-only pointer. When the fixed block exits, the memory is unpinned.
Multiple pointers can be declared in the fixed statement. The pointers are delimited with commas, and only
the first pointer is prefixed with the asterisk (*):
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
int [] n1={1,2,3,4};
int [] n2={5,6,7,8};
int [] n3={9,10,11,12};
fixed(int *p1=n1, p2=n2, p3=n3) {
}
using System;
namespace Donis.CSharpBook{
public class Starter{
In the following code, ZClass is a class and a moveable type. The fixed statement makes the ZClass object
fixed in memory. A pointer to the integer member is then obtained.
stackalloc
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The stackalloc command allocates memory dynamically on the stack instead of the heap. The lifetime of the
allocation is the duration of the current function, which provides another option for allocating memory at run
time. The stackalloc command must be used within an unsafe context. It can be used to initialize only local
variables. The CLR will detect buffer overruns caused by the stackalloc command.
The stackalloc command returns an unmanaged type. The expression should evaluate to an integral value,
which is the number of elements to be allocated. The base pointer of the memory allocation is returned. This
memory is fixed and not available for garbage collection. It is automatically released at the end of the
function. These are the particulars of the stackalloc command.
The following code allocates 26 characters on the stack. The subsequent for loop assigns alphabetic
characters to each element. The final loop displays each character.
using System;
namespace Donis.CSharpBook{
public unsafe class Starter{
public static void Main(){
char* pChar=stackalloc char[26];
char* _pChar=pChar;
for(int count=0;count<26;++count) {
(*_pChar)=(char)(((int)('A'))+count);
++_pChar;
}
for(int count=0;count<26;++count) {
Console.Write(pChar[count]);
}
}
}
}
Platform Invoke
You can call unmanaged functions from managed code using platform invoke (PInvoke). Managed and
unmanaged memory might be laid out differently, which might require marshaling of parameters or the return
type. In .NET, marshaling is the responsibility of the Interop marshaler.
Interop Marshaler
The Interop marshaler is responsible for transferring data between managed and unmanaged memory. It
automatically transfers data that is similarly represented in managed and unmanaged environments. For
example, integers are identically formatted in both environments and automatically marshaled between
managed and unmanaged environments. Types that are the same in both environments are called blittable
types. Nonblittable types, such as strings, are managed types without an equivalent unmanaged type and
must be marshaled. The Interop marshaler assigns a default unmanaged type for many nonblittable types. In
addition, developers can explicitly marshal nonblittable types to specific unmanaged types with the
MarshalAsAttribute type.
DllImport
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The DllImportAttribute imports a function exported from an unmanaged library, and the unmanaged library
must export a function. DllImportAttribute is in the System.Runtime .InteropServices namespace.
DllImportAttribute has several options that configure the managed environment to import the function. The
library is dynamically loaded, and the function pointer is initialized at run time. Because the attribute is
evaluated at run time, most configuration errors are not found at compile time; they are found later.
Options are used to configure the import. The name of the library is the only required option. The name of the
library should include the fully qualified path if it is not found in the path environment variable. Accessibility is
the visibility of the function, such as public or protected. Imported functions must be static and extern. The
remainder is the managed signature of the function.
The following code imports three functions to display the vertical and horizontal size of the screen. GetDC,
GetDeviceCaps, and ReleaseHandle are Microsoft Win32 APIs. The imported functions are configured and
exposed in the API class, which is a static class.
using System;
using System.Runtime.InteropServices;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
IntPtr hDC=API.GetDC(IntPtr.Zero);
int v=API.GetDeviceCaps(hDC, API.HORZRES);
Console.WriteLine("Vertical size of window {0}mm.", v);
int h=API.GetDeviceCaps(hDC, API.HORZRES);
Console.WriteLine("Horizontal size of window {0}mm.", h);
int resp=API.ReleaseDC(IntPtr.Zero, hDC);
if(resp!=1) {
Console.WriteLine("Error releasing hdc");
}
}
}
In the preceding code, the name of the library was the option used. There are several other options, which
are described in the following sections.
EntryPoint This option explicitly names the imported function. Without this option, the name is implied from
the managed function signature. When the imported name is ambiguous, the EntryPoint option is necessary.
You can then specify a different name for the entry point and the managed function.
In the following code, MessageBox is being imported. However, the managed name for the function is
ShowMessage.
using System;
using System.Runtime.InteropServices;
namespace Donis.CSharpBook{
public class Starter{
public static void Main() {
string caption="Visual C# 2005";
string text="Hello, world!";
API.ShowMessage(0, text, caption, 0);
}
}
CallingConvention This option sets the calling convention of the function. The default calling convention is
Winapi, which maps to the standard calling convention in the Win32 environment. The calling convention is
set with the CallingConvention enumeration. Table 15-2 lists the members of the enumeration.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-2:
CallingConve
ntion
Enumeration
Me De
mb scr
er ipti
on
Cd Th
ecl e
call
er
re
mo
ves
the
par
am
ete
rs
fro
m
the
sta
ck,
whi
ch
is
the
call
ing
co
nve
nti
on
for
fun
cti
on
s
tha
t
hav
e
a
vari
abl
e-l
en
gth
arg
um
ent
list
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-2:
CallingConve
ntion
Enumeration
Me De
mb scr
er ipti
on
Fa Thi
stC s
all call
ing
co
nve
nti
on
is
not
su
pp
ort
ed.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-2:
CallingConve
ntion
Enumeration
Me De
mb scr
er ipti
on
Std Th
Cal e
l call
ed
me
tho
d
re
mo
ves
the
par
am
ete
rs
fro
m
the
sta
ck.
Thi
s
call
ing
co
nve
nti
on
is
co
m
mo
nly
us
ed
for
AP
Is
an
d
is
the
def
aul
t
for
call
ing
un
ma
na
ge
d
fun
cti
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-2:
CallingConve
ntion
Enumeration
Me De
mb scr
er ipti
on
Thi Th
sC e
all firs
t
par
am
ete
r
of
the
fun
cti
on
is
the
thi
s
poi
nte
r
foll
ow
ed
by
the
co
nve
nti
on
al
par
am
ete
rs.
Th
e
thi
s
poi
nte
r
is
ca
ch
ed
in
the
EC
X
reg
ist
er
an
d
us
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-2:
CallingConve
ntion
Enumeration
Me De
mb scr
er ipti
on
Wi Def
nA aul
pi t
call
ing
co
nve
nti
on
of
the
cur
ren
t
pla
tfor
m.
For
Wi
n3
2
env
iro
nm
ent
,
thi
s
is
the
Std
Cal
l
call
ing
co
nve
nti
on.
For
Wi
nd
ow
s
CE
.N
ET,
CD
ecl
is
the
def
aul
t.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The following code imports the printf function, which is found in the C Runtime Library. The printf function
accepts a variable number of parameters and supports the CDecl calling convention.
using System;
using System.Runtime.InteropServices;
namespace Donis.CSharpBook{
public class Starter{
public static void Main() {
int val1=5, val2=10;
API.printf("%d+%d=%d", val1, val2, val1+val2);
}
}
ExactSpelling This option stipulates that the exact spelling of the function name is used to resolve the
symbol. Names are not always what they seem. For example, the function names of many Win32 APIs are
actually macros that map to the real API, which is an A or W suffixed method. The A version is the ANSI
version, whereas the W (wide) version is the Unicode version of the function. The ANSI versus Unicode
extrapolation pertains mostly to Win32 APIs that have string parameters. For example, the supposed
CreateWindow API is a macro that maps to either the CreateWindowW or CreateWindowA API. For the
DllImportAttribute, the version selected is determined in the CharSet option. If ExactSpelling is false, the
function name is treated as the actual name and not translated. The default is false.
The following code imports the GetModuleHandleW function specifically. ExactSpelling is true to prevent
mapping to another name.
using System;
using System.Runtime.InteropServices;
namespace Donis.CSharpBook{
public class Starter{
public static void Main() {
int hProcess=API.GetModuleHandleW(null);
}
}
PreserveSig This option preserves the signature of the method when resolving the symbol. COM functions
usually return an HRESULT, which is the error status of the call. The real return is the parameter decorated
with the [out, retval] Interface Definition Language (IDL) attribute. In managed code, the HRESULT is
consumed and the [out,retval] parameter is the return. To resolve a COM function, that managed signature
cannot be preserved; it should be mapped to a COM signature. Conversely, the signature of non-COM
functions should be preserved. PreserveSig defaults to true.
The following code demonstrates the PreserveSig option with a fictitious COM function:
SetLastError This option requests that the CLR cache the error code of the named Win32 API. Most Win32
APIs return false if the function fails. False is minimally descriptive, so developers can call GetLastError for
an integer error code. GetLastError must be called immediately after the failed API; if not, the next API might
reset the error code. In managed code, call Marshal.GetLastWin32Error to retrieve the error code. The
Marshal type is in the System.Runtime .InteropServices namespace. SetLastError defaults to false.
In the following code, CreateDirectory and FormatMessage are imported in the API class. CreateDirectory
creates a file directory; FormatMessage converts a Win32 error code into a user-friendly message. For
CreateDirectory, the SetLastError option is set to true. In Main, CreateDirectory is called with an invalid path.
The "c*" drive is probably an incorrect drive on most computers. The resulting error code is stored in the resp
variable, which is then converted into a message in the FormatMessage API. FormatMessage returns the
user-friendly messages as an out parameter.
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace Donis.CSharpBook{
public class Starter{
public static void Main() {
bool resp=API.CreateDirectory(@"c*:\file.txt",
IntPtr.Zero);
if(resp==false) {
StringBuilder message;
int errorcode=Marshal.GetLastWin32Error();
API.FormatMessage(
API.FORMAT_MESSAGE_ALLOCATE_BUFFER |
API.FORMAT_MESSAGE_FROM_SYSTEM |
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
API.FORMAT_MESSAGE_IGNORE_INSERTS,
IntPtr.Zero, errorcode,
0, out message, 0, IntPtr.Zero);
Console.WriteLine(message);
}
}
}
[DllImport("kernel32.dll", SetLastError=false)]
public static extern System.Int32 FormatMessage(
System.Int32 dwFlags,
IntPtr lpSource,
System.Int32 dwMessageId,
System.Int32 dwLanguageId,
out StringBuilder lpBuffer,
System.Int32 nSize,
IntPtr va_list);
CharSet
This option indicates the proper interpretation of strings in unmanaged memory, which can affect the
ExactSpelling option. CharSet is also an enumeration with three members. The default is CharSet.Ansi. Table
15-3 lists the members of the CharSet enumeration.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-3:
CharSet
Enumeration
Va De
lue scr
ipti
on
Ch Stri
arS ng
et. s
An sh
si oul
d
be
ma
rsh
ale
d
as
AN
SI.
Ch Stri
arS ng
et. s
Uni sh
co oul
de d
be
ma
rsh
ale
d
as
Uni
co
de.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-3:
CharSet
Enumeration
Va De
lue scr
ipti
on
Ch Th
arS e
et. ap
Aut pro
o pri
ate
co
nve
rsi
on
is
de
cid
ed
at
run
tim
e
de
pe
ndi
ng
on
the
cur
ren
t
pla
tfor
m.
The following code marshals string for unmanaged memory as ANSI. The ExactSpelling option defaults to
false, and the GetModuleHandleA API is called.
using System;
using System.Runtime.InteropServices;
namespace Donis.CSharpBook{
public class Starter{
public static void Main() {
int hProcess=API.GetModuleHandle(null);
}
}
BestFitMapping This option affects the Unicode-to-ANSI mapping of text characters passed from managed
to unmanaged functions running in the Windows 98 or Windows Me environment. If true, best fit mapping is
enabled. When there is not a direct character match, the Unicode character is mapped to the closest match
in the ANSI code page. If no match is available, the Unicode character is mapped to a "?" character. The
default is true.
Blittable Types
Blittable types are identically represented in managed and unmanaged memory. Therefore, no conversion is
necessary from the Interop marshaler when marshaling between managed and unmanaged environments.
Because conversion can be expensive, blittable types are more efficient than nonblittable types. For this
reason, when possible, parameters and return types should be blittable types, which include System.Byte,
System.SByte, System.Int16, System.UInt16, System.Int32, System.UInt32, System.Int64, System.IntPtr,
System.UIntPtr, System.Single, and System.Double. Vectors of blittable types are also considered blittable.
Formatted value types that contain only blittable types are also considered blittable.
Nonblittable types have different representation in managed and unmanaged memory. Some nonblittable
types are automatically converted by the Interop marshaler, whereas others require explicit marshaling.
Strings and user-defined classes are examples of nonblittable types. A managed string can be marshaled as
a variety of unmanaged strings: LPSTR, LPTSTR, LPWSTR, and so on. Classes are nonblittable unless
formatted. A formatted class marshaled as a formatted value type is blittable.
Formatted Type
A formatted type is a user-defined type in which the memory layout of the members is explicitly specified.
Formatted types are prefixed with the StructLayoutAttribute, which sets the layout of the members as
described in the LayoutKind enumeration. Table 15-4 lists the members of the LayoutKind enumeration.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-4:
LayoutKind
Enumeration
Va De
lue scr
ipti
on
Lay Th
out e
Kin CL
d.A R
uto set
s
the
loc
ati
on
of
me
mb
ers
in
un
ma
na
ge
d
me
mo
ry.
Th
e
typ
e
ca
nn
ot
be
ex
po
se
d
to
un
ma
na
ge
d
co
de.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-4:
LayoutKind
Enumeration
Va De
lue scr
ipti
on
Lay Me
out mb
Kin ers
d.S are
eq sto
ue red
nti in
al co
nti
gu
ou
s
(se
qu
ent
ial)
an
d
tex
tua
l
ord
er
in
un
ma
na
ge
d
me
mo
ry.
If
de
sir
ed,
set
pa
cki
ng
wit
h
the
Str
uct
Lay
out
Att
rib
ute
.Pa
ck
opt
ion
.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-4:
LayoutKind
Enumeration
Va De
lue scr
ipti
on
Lay Thi
out s
Kin val
d.E ue
xpli allo
cit ws
the
dev
elo
per
to
sti
pul
ate
the
ord
er
of
the
fiel
ds
in
me
mo
ry
usi
ng
Fie
ldO
ffs
etA
ttri
but
e.
Thi
s
val
ue
is
us
eful
for
rep
res
ent
ing
a
C
or
C+
+
uni
on
typ
e
in
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
In the following code, the API class contains the GetWindowRect unmanaged API. This function returns the
location of the client area in the screen. The parameters of GetWindowRect are a window handle and a
pointer to a Rect structure, which is also defined in the API class. The Rect structure, which is initialized
inside the function, is a formatted value type and is blittable. By default, value types are passed by value. To
pass by pointer, the out modifier is assigned to the Rect parameter.
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
public int left;
public int top;
public int right;
public int bottom;
}
}
Here is the code that uses the GetWindowRect API and the Rect structure:
The following code is a version of the API class that defines a Rect class instead of a structure. Because the
Rect class has the StructLayout attribute, it is a formatted type. Classes are passed by reference by default.
The out modifier required for a structure is not necessary for a class.
class API2
{
[DllImport("user32.dll")]
public static extern bool GetWindowRect(
IntPtr hWnd,
Rect windowRect);
[StructLayout(LayoutKind.Sequential)]
public class Rect
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
{
public int left;
public int top;
public int right;
public int bottom;
}
}
This is the code to call the GetWindowRect API using the Rect class:
Unions are fairly common in C and C++ code. A union is a type in which the members share the same
memory location. This preserves memory by overlaying data in shared memory. C# does not offer a union
type. In managed code, emulate a union in unmanaged memory with the LayoutKind.Explicit option of
StructLayoutAttribute. Set each field of the union to the same offset, as shown in the following code:
[StructLayout(LayoutKind.Explicit)]
struct ZStruct {
[FieldOffset(0)] int fielda;
[FieldOffset(0)] short fieldb;
[FieldOffset(0)] bool fieldc;
}
Directional Attributes
Directional attributes explicitly control the direction of marshaling. Parameters can be assigned InAttribute,
OutAttribute, or both attributes to affect marshaling. This is equivalent to [in], [out], and [in,out] of the IDL.
InAttribute and OutAttribute are also represented by keywords in C#. Table 15-5 lists the attributes and
related keywords.
Table 15-5:
Directional
Attributes and C#
Keywords
Ke Att ID
yw rib L
or ute
d
Table 15-5:
Directional
Attributes and C#
Keywords
Ke Att ID
yw rib L
or ute
d
Ou Ou [ou
t tAt t]
trib
ute
The default directional attribute depends on the type of parameter and any modifiers.
StringBuilder
Strings are immutable and dynamically sized. An unmanaged API might require a fixed-length and modifiable
string. In addition, some unmanaged APIs initialize the string with memory allocated at run time. A string
type should not be used in these circumstances. Instead, use the StringBuilder class, which is found in the
System.Text namespace. StringBuilders are fixed-length and not immutable. Furthermore, you can initialize
the StringBuilder with memory created in the unmanaged API.
In the following code, the GetWindowText unmanaged API is imported twice. (This code, which is from the
GetWindowText application, is included on the CD-ROM for this book.) GetWindowText retrieves the text
from the specified window. For an overlapped window, this is text from the title bar. The second parameter of
GetWindowText is a string, which is initialized with the window text during the function call. The first version
of GetWindowText in the API class has a string parameter, whereas the version in the API2 class has a
StringBuilder parameter. The GetWindowText application is a Windows Forms application that has two
buttons. The first button calls API.GetWindowText. The second button calls API2.GetWindowText.
Because of the string parameter in API.GetWindowText, an exception is raised because the API attempts to
change that parameter. The second button invokes API2.GetWindowText successfully.
{
[DllImport("user32.dll")]
public static extern int GetWindowText(
IntPtr hWnd, StringBuilder lpString, int nMaxCount);
}
Unmanaged Callbacks
Some unmanaged APIs accept a callback as a parameter, which is a function pointer. The API invokes the
function pointer to call a function in the managed caller. Callbacks are typically used for iteration. For
example, the EnumWindows unmanaged API uses a callback to iterate top-level window handles.
.NET abstracts function pointers with delegates, which are type-safe and have a specific signature. In the
managed signature, substitute a delegate for the callback parameter of the unmanaged signature.
The following code imports the EnumWindows unmanaged API. The first parameter of EnumWindows is a
callback. EnumWindows enumerates top-level windows. The callback function is called at each iteration and
is given the current window handle. In this code, APICallback is a delegate and provides a managed
signature for the callback.
class API
{
[DllImport("user32.dll")]
public static extern bool EnumWindows(
APICallback lpEnumFunc,
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
System.Int32 lParam);
EnumWindows is called in the click handler of a Windows Forms application, where GetWindowHandle is
the callback function. GetWindowHandle is called for each iterated window handle. The function adds each
window handle to a list box:
Explicit Marshaling
Explicit marshaling is sometimes required to convert nonblittable parameters and fields, and returns types to
proper unmanaged types. Marshaling is invaluable for strings, which have several possible representations in
unmanaged memory. Strings default to LPSTR. Use the MarshalAsAttribute to explicitly marshal a managed
type as a specific unmanaged type. The UnmanagedType enumeration defines the unmanaged types
available to the MarshalAsAttribute. Table 15-6 lists the members of the UnmanagedType enumeration.
Table 15-6:
UnmanagedT
ype
Enumeration
Me De
mb scr
er ipti
on
An Le
siB ngt
Str h-p
refi
xe
d
AN
SI
stri
ng
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-6:
UnmanagedT
ype
Enumeration
Me De
mb scr
er ipti
on
As Dy
An na
y mi
c
typ
e
for
whi
ch
the
typ
e
of
the
val
ue
is
set
at
run
tim
e
Bo Fo
ol ur-
byt
e
Bo
ole
an
val
ue
BS Le
tr ngt
h-p
refi
xe
d
Uni
co
de
stri
ng
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-6:
UnmanagedT
ype
Enumeration
Me De
mb scr
er ipti
on
By Ma
Val rsh
Arr als
ay an
arr
ay
by
val
ue;
Siz
eC
on
st
set
s
the
nu
mb
er
of
ele
me
nts
By Inli
Val ne
TS fixe
tr d-l
en
gth
ch
ara
cte
r
arr
ay
tha
t
is
a
me
mb
er
of
a
str
uct
ure
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-6:
UnmanagedT
ype
Enumeration
Me De
mb scr
er ipti
on
Cur CO
ren M
cy cur
ren
cy
typ
e
Cu A
sto cu
m sto
Ma m
rsh ma
ale rsh
r ale
r
to
be
us
ed
wit
h
Ma
rsh
alA
sAt
trib
ute
.M
ars
hal
Ty
pe
or
Ma
rsh
alA
sAt
trib
ute
.M
ars
hal
Ty
pe
Ref
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-6:
UnmanagedT
ype
Enumeration
Me De
mb scr
er ipti
on
Err HR
or ES
UL
T
Fu C-s
nct tyl
ion e
Ptr fun
cti
on
poi
nte
r
I1 On
e-b
yte
int
eg
er
I2 Tw
o-b
yte
int
eg
er
I4 Fo
ur-
byt
e
int
eg
er
I8 Eig
ht-
byt
e
int
eg
er
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-6:
UnmanagedT
ype
Enumeration
Me De
mb scr
er ipti
on
IDi IDi
sp sp
atc atc
h h
poi
nte
r
for
CO
M
Int CO
erf M
ac int
e erf
ac
e
poi
nte
r
IUn IUn
kn kn
ow ow
n n
int
erf
ac
e
poi
nte
r
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-6:
UnmanagedT
ype
Enumeration
Me De
mb scr
er ipti
on
LP Poi
Arr nte
ay r
to
the
firs
t
ele
me
nt
of
an
un
ma
na
ge
d
arr
ay
LP Nul
Str l-te
rmi
nat
ed
AN
SI
stri
ng
LP Poi
Str nte
uct r
to
an
un
ma
na
ge
d
str
uct
ure
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-6:
UnmanagedT
ype
Enumeration
Me De
mb scr
er ipti
on
LP Pla
TS tfor
tr m-
de
pe
nd
ent
stri
ng
LP Uni
W co
Str de
stri
ng
R4 Fo
ur-
byt
e
floa
tin
g
poi
nt
nu
mb
er
R8 Eig
ht-
byt
e
floa
tin
g
poi
nt
nu
mb
er
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-6:
UnmanagedT
ype
Enumeration
Me De
mb scr
er ipti
on
Saf Saf
eAr e
ray arr
ay
in
whi
ch
the
typ
e,
ran
k,
an
d
bo
un
ds
are
defi
ne
d
Str Ma
uct rsh
als
for
ma
tte
d
val
ue
an
d
ref
ere
nc
e
typ
es
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-6:
UnmanagedT
ype
Enumeration
Me De
mb scr
er ipti
on
Sy Pla
sIn tfor
t m-
de
pe
nd
ent
int
eg
er
(32
bit
s
in
Wi
n3
2
env
iro
nm
ent
)
Sy Pla
sUI tfor
nt m-
de
pe
nd
ent
un
sig
ne
d
int
eg
er
(32
bit
s
in
Wi
n3
2
env
iro
nm
ent
)
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-6:
UnmanagedT
ype
Enumeration
Me De
mb scr
er ipti
on
TB Le
Str ngt
h-p
refi
xe
d,
pla
tfor
m-
de
pe
nd
ent
stri
ng
U1 On
e-b
yte
un
sig
ne
d
int
eg
er
U2 Tw
o-b
yte
un
sig
ne
d
int
eg
er
U4 Fo
ur-
byt
e
un
sig
ne
d
int
eg
er
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table 15-6:
UnmanagedT
ype
Enumeration
Me De
mb scr
er ipti
on
U8 Eig
ht-
byt
e
un
sig
ne
d
int
eg
er
Var Tw
ian o-b
tBo yte
ol VA
RI
AN
T_
BO
OL
typ
e
VB Mi
By cro
Ref sof
Str t
Vis
ual
Ba
sic
-sp
ecif
ic
GetVersionEx is imported in the following code. The function is called in Main to obtain information on the
current operating system. GetVersionEx has a single parameter, which is a pointer to an OSVERSIONINFO
structure. The last field in the structure is szCSDVersion, which is a universally unique identifier (UUID). A
UUID is a 128-byte array. The MarshalAs attribute marshals the field as a 128-character array. Each
character is one byte long.
using System;
using System.Runtime.InteropServices;
namespace Donis.CSharpBook{
[StructLayout(LayoutKind.Sequential)]
public struct OSVERSIONINFO {
public System.Int32 dwOSVersionInfoSize;
public System.Int32 dwMajorVersion;
public System.Int32 dwMinorVersion;
public System.Int32 dwBuildNumber;
public System.Int32 dwPlatformId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)]
public String szCSDVersion;
}
}
Fixed-Size Buffers
In the previous code, the MarshalAs attribute defined a fixed-size field of 128 characters or bytes. As an
alternative to the MarshalAs attribute, C# 2.0 introduces fixed-size buffers using the fixed keyword. The
primary purpose of this keyword is to embed aggregate types, such as an array, in a struct. Fixed-size
buffers are accepted in structs but not in classes.
The following code rewrites the OSVERSIONINFO structure that used the MarshalAs attribute. This version
uses the fixed keyword for the szCSDVersion field.
Summary
Although pointers are not normally available in C#, developers can choose to use pointers at their discretion.
Pointers are available in the context of unsafe code, which requires the unsafe keyword. You can create
pointers to unmanaged types, such as the int, float, and char types. In addition, unsafe code must be
compiled with the unsafe compiler option.
You cannot create pointers to moveable memory. Managed memory is moveable and managed by the
garbage collector (GC). The fixed statement pins managed memory within a block. Pinned memory is fixed.
While pinned, memory is not accessible to the GC.
The DllImportAttribute, which describes an unmanaged function that is exported from a library, has various
options to configure the importing of a function. The Interop marshaler marshals parameters and returns
values between managed and unmanaged memory during the function call. Blittable types do not require
conversion. Nonblittable types require conversion to unmanaged memory. Developers can explicitly marshal
nonblittable types using the MarshalAs attribute.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
User-defined types that represent numerical systems or perform mathematical operations benefit the most
from operator overloading. A fraction, complex number, summation, hexadecimal numerical system, and
other similar types that have well-established behavior are improved with operator overloading.
You can also describe type conversion of user-defined types using operator overloading. Conversion
operators can convert or cast a user-defined type to another type, which can be a built-in type or another
user-defined type. With some exceptions, you can cast from one built-in type to another built-in type. For
example, you can implicitly cast from a short to a long value. You can also cast explicitly from a long to a
short value. However, what does it mean to cast a ZClass instance to an int value? This is another situation
in which developer input is essential. Developers overload conversion operators to instruct the compiler in the
conversions of user-defined types.
Operator overloading should be intuitive and is never required. Do not stray from the underlying intent of the
operator. For example, the plus operator should perform some type of addition. System.String overloads the
operator+ to concatenate strings. Concatenation and addition are similar concepts. If the implementation of
the operator+ within the string type truncated characters, that would be nonintuitive and more confusing than
helpful. Operator overloading can be overused and is not appropriate in all circumstances. The operator+ is a
reasonable addition to a string class. However, an operator-, although available, would be nonsensical.
Remember, the overloaded operator must be intuitive to everyone, not just to you. Confirm with peers your
perception of intuitive behavior. An alternative to operator overloading is simply implementing a named
method: Add method instead of an operator+ and Subtract method instead of overloading the operator-.
There is the added benefit that named methods are called overtly, whereas operator functions are called
tacitly.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
The compound assignment operators, such as += and /=, are implicitly overloaded using the individual
operators. For example, the += and /= operators are implicitly overloaded by overloading the + and /
operators.
In addition, developers cannot use operator overloading to define new operators. For example, you could not
define a ^^ operator, which does not otherwise exist.
There are some notable differences between usage in the C++ and C# languages. In C++, you can overload
the assignment, new, and array operators. This is not allowed in C#. This may be an issue when porting
code from C++ to C#. Instead of overloading the assignment operator, implement the ICloneable interface.
The garbage collector (GC) is responsible for managing dynamic memory. For that reason, the new operator
cannot be overloaded in managed code.
The array operator is commonly overloaded in C++ to create a secure array, where fence post errors are
usually checked. Fencepost errors are automatically detected by the CLR, which eliminates one of the
primary reasons to overload the array operator.
Overloading an operator redefines the behavior of that operator in an expression. However, you cannot
change the syntax, precedence, or associativity of an operator with operator overloading. Overloading an
operator does not change the core principles of using the operator. Assume that the division operator is
overloaded in a user-defined type. The syntax for employing the division operator remains obj1/obj2.
Precedence is also preserved. In obj1+obj2/obj3-obj4, the division operation is performed before a plus or
minus operator, but after the increment or decrement operator. Multiple division operations, as in
obj1/obj2+obj3/obj4 are evaluated from left to right, which maintains the normal associativity.
Implementation
Overloaded operators are implemented as static and public functions. Other modifiers, such as virtual and
sealed, are not allowed. The unary operator has a single operand, whereas the binary operator has two
operands. Parameters of an overloaded operator cannot be ref or out.
For a unary operator method, the operand is the same type as the containing class. For a binary operator
method, at least one of the parameters must be the containing type. This allows the type to appear as lhs,
rhs, or both lhs and rhs in the expression. In the obj+5 expression, the object instance is passed as the lhs
operand to the operator+ method. The object instance is passed as the rhs operand in the 5+obj expression.
The return type is any value type but not void.
The following is an example of the implementation and use of an overloaded operator. The ZClass is a simple
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
class and wrapper for two integer fields. The operator+ method is overloaded to add two ZClass instances.
Because the operator+ method is part of the class, it has access to the private members of that class. This
is convenient when accessing parameters of that type within the operator method. In this code, the operator+
is called implicitly in the expression: obj1+obj2. You cannot call an operator function explicitly.
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
ZClass obj1=new ZClass(5,10);
ZClass obj2=new ZClass(15,20);
ZClass obj3=obj1+obj2;
Console.WriteLine(obj3.Total);
}
}
class ZClass {
Operator methods are available in a derived class. At least one operand of the operator function remains the
base type. However, you can freely substitute the derived instance with the base type operand and access
the base type members. The return type can also be a base type, which might pose problems. As a return,
assigning a base instance to a derived type causes a run-time error. In addition, the derived type can replace
operator methods of the base class. Simply implement the operator function again in the derived class. The
implementation in the derived class overloads the base operator method.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
In the following code, YClass derives from the ZClass class and inherits operator+, fielda, and fieldb. (
ZClass.operator+ was shown in the previous code.) YClass also implements an operator-function. In Main,
both the operator+ and operator-functions are used. The operator+ on the YClass returns a ZClass instance,
which is the base type. You cannot explicitly cast this result to a YClass without raising a run-time
exception.
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
YClass obj1=new YClass(5,10);
YClass obj2=new YClass(15,20);
ZClass obj3=obj1+obj2;
Console.WriteLine(obj3.Total);
YClass obj4=obj1-obj2;
Console.WriteLine(obj4.Total);
}
}
// Partial listing
You can overload an operator member function. The general rules of overloading apply, and each overloaded
operator member function must have a unique signature. Implicit conversion of operands to other types can
limit the necessity for some operator overloading. Operators are typically overloaded to use the containing
type as an lhs or rhs.
All three operator member functions are used in the following code:
obj3=obj1+obj2;
obj1=obj1+10;
obj2=20+obj2;
Operator overloading is not stipulated in the Common Language Specification (CLS) and therefore not
guaranteed in all managed languages. It is good practice to implement a parallel named method for each
operator member function. The named method should call the operator member function as demonstrated in
the following code:
fieldb=_fieldb;
}
The semantics of overloading certain mathematical and logical operators are unique. Special rules must be
followed when overloading these operators in a type.
The Increment (++) and Decrement (--) operators have special semantics. These are unary operators, where
the operand and return type must be the containing class or a derivative. To maintain the underlying behavior,
the Increment and Decrement operator should return a modified instance of the current object, which is the
operand. You can (but should not) return an entirely new instance. Remember, overloaded operators should
preserve the underlying meaning of the operator. Finally, overloading the Increment or Decrement operator
revises both the prefix and postfix usage of the operator.
Here is sample code showing an overloading of both the Increment and Decrement operators:
--curr.fieldb;
return curr;
}
The LeftShift and RightShift operators normally perform a binary shift. Both are binary operators. When
overloading these operators, the first operand must be the same type as the containing class. The second
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
operand must be an int, which is the amount of the shift. The return type can be anything except void. The
LeftShift and RightShift operators are not paired operator methods. You can implement them independently
of each other. However, I would recommend implementing both because the methods are logically related:
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
ZClass obj1=new ZClass(5,10);
ZClass obj2=obj1<<2;
Console.WriteLine(obj2.Total);
}
}
True and false are logical operators and are used in conditional expressions and sometimes in assignments.
They are paired operator methods, which require both functions of the pair to be implemented. If either the
operator true or operator false method is overloaded, both must be implemented. Both operators are unary
operators, in which the current object is the operand. The operand must be the containing class type or a
derivative, and the return type should be bool. Add operator true and operator false methods to classes that
have a false or true representation. This representation is typically interpreted from the state of the object.
When an object is used as a Boolean expression, the operator true method is called. The operator false
method is called in other circumstances, such as an && expression. Operator overloading and the &&
operator are discussed later in this appendix.
This is sample code of an operator true and operator false. The operator true is used in the Main method at
the if statement.
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
ZClass obj1=new ZClass(5,5);
if(obj1) {
Console.WriteLine("obj1 is true");
}
else {
Console.WriteLine("obj1 is false");
}
}
}
Console.WriteLine("ZClass.operator false");
return curr.fielda!=curr.fieldb;
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Paired Operators
In addition to operator true and operator false, the relational operators are paired operators. Paired operators
enforce a practical guideline—which is that related operators should be overloaded to maintain consistency.
For example, because they are logically related, you should overload both the operator== and operator!=
methods. This is the complete list of paired relational operators, which are discussed in the following
sections:
operator== and operator!=
operator<= and operator>=
operator< and operator>
operator| and operator&
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
ZClass obj1=new ZClass(5,10);
ZClass obj2=new ZClass(5,10);
ZClass obj3=new ZClass(5,15);
if(obj1==obj2) {
Console.WriteLine("obj1 and obj2 are equal.");
}
else {
Console.WriteLine("obj1 and obj2 are not equal.");
}
if(obj1==obj3) {
Console.WriteLine("obj1 and obj3 are equal.");
}
else {
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Actually, operator|| and operator&& cannot be overloaded directly. These are the logical and and or
operators, respectively. Instead, operator|, operator&, operator true, and operator false combine to overload
operator|| and operator&&. The bitwise operators are normally operator| and operator&. If a user-defined type
is used in an && or || expression, the four mentioned operator functions must be overloaded in that type.
Because of short-circuiting, an && or || expression is sometimes partially evaluated. If the lhs of an &&
expression is false, the entire expression is false. This means the rhs can be ignored. For || expressions, the
entire expression is true if the lhs is true. When that occurs, the rhs of the || expression can be ignored.
Potential short-circuiting complicates the overloading of the && and || operators. Several steps are required
to evaluate the && or || operators for a user-defined type.
Following are the steps for evaluating the && operator, where the expression is lhs && rhs. (Let us assume
that lhs and rhs are the same type as the containing class.)
1. The overloaded operator false method is called. If the method returns true, the expression
short-circuits because the first part of the && expression is confirmed to be false.
2. If short-circuiting, the expression evaluates to type.operator true(lhs).
3. If the expression does not short-circuit, the overloaded operator& is called:
result=type.operator&(lhs, rhs).
4. Finally, type.operator true(result) is called. This method returns the final result of the lhs && rhs
expression when short-circuiting does not occur.
Following are the steps for evaluating the || operator, where the expression is lhs || rhs:
1. The overloaded operator true method is called. If the method returns true, the expression
short-circuits. The expression short-circuits because the first part of the || expression is confirmed
to be true.
2. If short-circuiting, the expression evaluates to type.operator true(lhs).
3. If the expression does not short-circuit, the overloaded operator | is called:
result=type.operator|(lhs, rhs).
4. Finally, type.operator true(result) is called. This method returns the final result of the lhs || rhs
expression when short-circuiting does not occur.
For the operator& and operator| methods, the operand and return must be the same type.
In the following code, ZClass is overloaded to support the && and || expression. All four required methods are
implemented: operator true, operator false, operator&, and operator|. The overloaded methods of the ZClass
type display messages for auditing program flow. In Main, ZClass instances are used in a || expression.
Because the operator true is hard-coded to return false, the expression will never short-circuit.
using System;
namespace Donis.CSharpBook{
public class Starter{
class ZClass {
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Conversion Operators
Implementing conversion operators facilitates the implicit or explicit casting of user-defined types to built-in
types or even other user-defined types. Developers implement a conversion operator to explain to the
compiler how to interpret a user-defined type in the context of another type. Like mathematical and logical
operators, the primary reason for implementing conversion operators is convenience. It is never required. You
could as easily expose ToType methods, such as ToInt, ToFloat, or ToDecimal.
An implicit cast is considered a secure cast, whereas explicit casting is required for casting that is not
secure. For built-in types, a secure cast is available when there is no potential loss of precision or accuracy.
When there is potential loss of precision or accuracy, an explicit cast is required. For example, an int can be
assigned to long implicitly. Eight bytes are reserved for a long value and four bytes are reserved for an int
value. The int value will be promoted to a long. The promotion occurs silently—no warning or notice. The
reverse, in which a long is assigned to an int, requires an explicit cast. This is exhibited in the following code:
int a=5;
long b=10;
b=a; // implicit cast
a=(int) b; // explicit cast
C# does not support conversion constructors. A conversion constructor is a one-argument constructor used
to create an instance of a type from a different type. Conversion constructors are supported in C++ but are
not allowed in C#. Conversion constructors were convenient—too convenient. Conversion constructors were
sometimes called transparently when a compiler error for mismatched types was more appropriate.
You cannot overload the cast operator directly. Instead, overload the cast operator selectively with
conversion operator methods. This is the syntax of a conversion operator:
public static implicit operator returntype(classtype obj)
public static explicit operator returntype(type obj)
For the conversion operator syntax, there are many similarities when compared with mathematical and
relational operators. Conversion operators must be public and static. Other modifiers, such as virtual and
sealed, are syntax errors. Conversion operators that are implicit do not require casting, whereas explicit
conversion operators require casting for use. I recommend explicit casting in all circumstances. Implicit
casting allows the conversion function to be called transparently and sometimes inadvertently, which may
cause undetected side effects. With explicit casting, developers affirmatively state their intentions through
casting. Either the return or operand of the conversion operator must be the same as the containing type. If
converting to the containing type, the return type should be the containing class. Notice that the return type
is after the operator keyword, not before. When converting from the containing type, the operand should be
the same type as the containing class.
Here is sample code of implicit and explicit conversion methods. The ZClass has two conversion operators.
The first conversion operator converts ZClass to an int. The second conversion operator converts a YClass to
a ZClass.
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
ZClass obj1=new ZClass(5,10);
int ival=obj1;
YClass obj2=new YClass(5);
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Conversion operators are often overloaded to provide the illusion of a user-defined type, which can support a
variety of casts. In the following code, the conversion operator is overloaded several times to allow the
conversion of ZClass instances to a variety of types:
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
using System;
namespace Donis.CSharpBook{
// and so on
Operator String
The operator string operator is a special conversion operator that converts a user-defined type to a string.
This appears to overlap with the ToString method, which is inherited from System.Object. Actually, every
type is also automatically provided an operator string method, which simply calls the polymorphic ToString
method. Look at the following code. If a class has both a ToString and operator string method, which method
is called in the Console.WriteLine?
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
ZClass obj=new ZClass();
Console.WriteLine(obj);
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
}
}
}
}
The preceding program displays ZClass.operator string. The operator string is called for the
Console.WriteLine operand. Why? Unlike the default operator string, the custom operator string does not call
ToString. In most circumstances, calling ToString in the operator string is the best practice, which
eliminates the necessity of implementing a custom operator string. The default operator string already has
this behavior. You simply override the ToString method with the proper string representation of the type.
Inconsistencies and confusion can occur when the operator string and ToString have disparate
implementations.
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Practical Example
Until now, simple examples have been provided to demonstrate operator overloading and conversion
operators. The following code contains a more practical example. Summation notation iterates and totals an
expression in a specified range. Figure A-1 offers three examples of summation notation.
Summation is not a predefined type in C#. The following class defines the Summation type. It contains
several methods:
The Summation class has three constructors. The two- and three-argument constructors delegate
to the four-argument constructor and set unspecified parameters a default value.
The Calculate method calculates the result of the summation.
The class has two operator+ methods, which allow instances of the class to be either lhs or rhs in
the binary expression. Each operator+ method extends the iteration by adding to the stop value of
the summation. A new instance with an updated number of iterations is returned.
The operator++ method extends the number of iterations by one. The current object is then
returned.
The class has an operator int conversion constructor. This method converts a Summation object
into an int value, which is the result of the operation.
There are two string functions: ToString returns the result of the operation; ToNotationString
displays summation notation and shows the parameters of the operation.
temp=(int)Math.Pow(count, power);
temp=temp*product;
propResult+=temp;
}
}
The following code tests the Summation type. The ingredients of the summation are read from the command
line. In order, provide the start, stop, product, and power for the summation as command-line arguments. The
number of iterations is between the start and stop value. In Main, the summation notation is displayed
followed by the result. The number of iterations is then increased using the operator++. The new results are
then displayed:
Console.WriteLine();
Console.WriteLine(sum.ToNotationString());
Console.WriteLine("\n[Result = {0}]",
sum.Result);
++sum;
int isum=(int)sum;
Console.WriteLine();
Console.WriteLine(sum.ToNotationString());
Console.WriteLine("\n\n[Result = {0}]", isum);
}
}
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Table A-1 lists the replacement methods for mathematical and logical operators.
Table A-1: Replacement Methods for Mathematical and Logical Operators
Open table as spreadsheet
operator+ op_Addition
operator- op_Subtraction
operator* op_Multiply
operator++ op_Increment
operator-- op_Decrement
operator/ op_Division
operator% op_Modulus
operator& op_BitwiseAnd
operator| op_BitwiseOr
operator^ op_ExclusiveOr
operator>> op_RightShift
operator<< op_LeftShift
operator! op_LogicalNot
operator~ op_OnesComplement
operator> op_GreaterThan
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
operator< op_LessThan
operator>= op_GreaterThanOrEqual
operator<= op_LessThanOrEqual
operator== op_Equality
operator!= op_Inequality
Conversion operators are implemented as op_Explicit and op_Implicit methods. The methods are overloaded
for every combination of explicit or implicit conversion operator provided in the contained type. The following
class has two explicit operators and one implicit conversion operator:
}
public static implicit operator double(ZClass obj) {
return 0;
}
}
Figure A-3 is an internal view of the application. There are two overloaded op_Explicit methods and a single
op_Implicit method.
Index
Symbols
/* and */ (block comments), 10
: (colon punctuator), 9
, (comma punctuator), 9
- (Decrement) operator overloading, 632
/* (delimited comments), 10
*/ (delimited comments), 10
/// (documentation comments), 10–12
. (dot punctuator), 9
++ (Increment) operator overloading, 632
/* and */ (multiline comments), 10
; (semicolon punctuator), 8
// (single-line comments), 10
/// (single-line documentation comments), 10–12
~ (tilde) command, 514
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
A
abstract
classes, 106, 112
indexers, 106
keyword, 106
members, 106
methods, 106
overview, 106
properties, 106
static members, 106
access modifiers, 98–99
accessibility
classes, 46
constructors, 63
delegates, 298
destructors, 68
enumerations, 78
fields, 53
members, 49
methods, 56
nested types, 73
Activator class, 378
activity traces, 479
additional information
Constrained Execution Region (CER) information, 593
Debugging Tools for Windows, 500
Microsoft Press Technology Updates Web page, xxiii
Microsoft Public Symbol Store, 548
support for this book, xxiv
AddMemoryPressure method, 596
Address Breakpoint dialog box, 456
ADPlus
crash mode, 525
hang mode, 526
overview, 503, 525
walkthrough, 526
anonymous methods
delegate keyword, 313
generic anonymous methods, 318
limitations, 318
local variables, 316
naming, 315
nested classes, 317
outer variables, 316–318
overview, 313–316
private nested classes, 317
private static methods, 317
static methods, 317
syntax, 314
AppDomain.UnhandledException, 349–350
application exceptions
overview, 338–340
serializing, 346
applications
exceptions. See application exceptions
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
symbols, 549
thread local storage (TLS), 580
trapping unhandled exceptions, 347
Application.ThreadException, 348–349
arithmetic instructions
conversion operations, 430
exception handling, 431–432
list of, 429–430
overview, 429
array covariance, 213
Array.AsReadOnly method, 199
Array.Clone method, 200
Array.CreateInstance method, 201–202
Array.FindAll method, 202–203
ArrayList collection
IList interface, 215
members, 215–219
overview, 215
Array.Resize method, 203
arrays
accessing elements, 428
array covariance, 213
casting between, 213
conversion, 213–214
delegates, 300
elements, 188, 191
function returns, 213–214
indexers, compared to, 209
jagged arrays. See jagged arrays
multidimensional arrays. See multidimensional arrays
nested arrays, 193
overview, 186, 187, 428
parameters, 213–214
params keyword, 211–212
reference types vs. value types, 188
single-dimensional arrays. See single-dimensional arrays
syntax, 189–191, 428
System.Array. See System.Array
System.MulticastDelegate class, 301–302
value types vs. reference types, 188
Array.SyncRoot property, 204
as operator
overview, 128
syntax, 130
ASP.NET debugging, 499
assemblies
Assembly Binding Log Viewer Tool (fuslogvw.exe), 368
loading, 367–369
Reflection trees, 366
Type objects, 366
Assembly Binding Log Viewer Tool (fuslogvw.exe), 368
assembly directives
overview, 406
syntax, 407
association, 151
asynchronous delegate diagram
delegate internals, 312
overview, 311
asynchronous invocation
BeginInvoke method, 307–309
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
B
base types, 83
BeginInvoke method, 307–309
BestFitMapping, 617
binary operators
details, 22
overview, 22
Binder class
abstract methods, 375
BindingFlags parameter, 375
overview, 375
sample code, 377–378
syntax, 375
binders, 375
binding, dynamic, 373–374
BindingFlags enumeration, 371
BindingFlags parameter, 375
BitArray collection
members, 219–220
overview, 219
BitVector32
collection, 232
bitwise enumerations, 79
blittable types, 611, 618
Blob heap, 358
block comments (/* and */), 10
blocks, 16
Boolean operators
list of, 23
overview, 23
bound parameters, 243
boxing
collections, 233
garbage collection overview, 559
IEnumerator interface, 278
overview, 419
branching
calling methods, 425–427
comparative branching instructions, 424
compare instructions, 423
overview, 423
Breakpoint Condition dialog box, 457
Breakpoint Filter dialog box, 458
Breakpoint Hit Count dialog box, 457
breakpoints
breakpoints memory commands, 516–518
Breakpoints window, 455–459, 464
code stepping, 461–463
function breakpoints, 453–455
memory commands, 516–518
overview, 453
symbols, 461
tracepoints, 459–461
When Breakpoint Is Hit dialog box, 460
Breakpoints window
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
C
call instructions, list of, 426
Call Stack window, 471–472
callbacks
overview, 291
unmanaged callbacks, 621–622
calling methods, 425
CallingConvention, 613–614
casting
arrays, 213
generic types, 256–257
IEnumerator interface, 278
inheritance, 125–130
reflection, 380
catch statements, 329–330
CCW. See COM Callable Wrappers (CCWs)
<![CDATA ]> elements, 168
CDATA syntax, 168
CDB. See Windows Console Debugger (CDB)
CER. See Constrained Execution Region (CER)
change tracking, 159
characters
escape characters, 19–20
overview, 18
verbatim characters, 20–21
CharSet, 617
Class Designer, 145
class diagrams
association, 151
inheritance, 149
overview, 145–149
toolbox, 148–149
walkthrough, 151–154
class directives, 409–412
class hierarchies
association, 151
Class Designer, 145
class diagrams, 145–149, 151–154
Class View window, 144
Error List window, 156
inheritance, 149–151
Object Browser window, 144–145
overview, 143
Class View window, 144
classes
abstract, 106
accessibility, 46
Activator, 378
Binder, 375
Console, 576
declaration syntax, 46
delegates, 297–298
Employee, 96
EventArgs, 321
Exception. See System.Exception
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
GC, 598
HandleCollector, 596–597
hierarchies. See class hierarchies
inheritance. See inheritance
member access syntax, 48
members, list of, 48
members overview, 48
modifiers, 46
nested. See nested classes
overview, 44, 45, 46–47
partial, 74–76
private nested, 317
refinement, 81
sealed, 107
SourceSwitch, 479, 480
static, 50
StringBuilder, 620–621
Summation, 643
System.Exception. See System.Exception
TraceListener, 482
TraceSource. See TraceSource
types. See types
WeakReference, 591
ClickOnce deployment
Manifest Generation and Editing (MageUI) tool, 183
manifests, 183
modes of deployment, 182
overview, 135, 182
Publish Wizard, 185–186
publishing applications, 185–186
Close method, 584
closed constructed types
overview, 243
static members, 260
this references, 246
CLR Debugger (DbgClr), 503
Code Editor
change tracking, 159
color formatting, 158
font formatting, 158
IntelliSense, 156–158
overview, 156
profiles, 160
source code formatting, 159
Surround With feature, 158
Code elements, 168
code expansion, 162
code samples. See sample C# programs
Code Snippet Schema Reference, 166
code snippets
<![CDATA ]> elements, 168
Code elements, 168
code expansion, 162
Code Snippet Schema Reference, 166
Code Snippets Manager, 165–166
CodeSnippets elements, 166
copying, 161
creating, 166–171
Declarations elements, 167
default snippets, 163–164
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
single-line (//), 10
single-line documentation (///), 10–12
common exception model, 327
Common Language Runtime (CLR)
exception handling, 325
MSIL programming, 401
Common Language Specification (CLS), 631
Community menu, 137
companion Web site, xxiv
comparative branching instructions, 424
compare instructions, 423
complex iterations, 290
composition, 83–87
compound operators
list of, 22–23
overview, 22
concurrent garbage collection
overview, 559
Workstation GC, 560
condition command, 456
conditional preprocessor directives, 14
configuration files, 488–489
Configuration Manager, 442–443
consistency, 92
Console class, 576
constants, 52
Constrained Execution Region (CER)
overview, 592–593
safe handles, 593–594
Web site, 593
constraints
constructors, 255
default constructor, 255
derivation, 248–252
integer, 251
interface, 252–253
list of, 247
overview, 246–247
recursive, 250
reference type, 254
restrictions, 252
value type, 254
value types, 251
constructed types, 243
constructors
accessibility, 63
constraints, 255
default, 255
delegates, 298
EventTypeFilter, 484
finalizers, 575–576
generics, 238
instance, 393
overloading, 64
overview, 63, 108
private, 63
sheet generic type, 241
singletons, 67
SourceFilter, 484
SourceSwitch, 480
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
static, 66
syntax, 63, 109
System.Exception, 336
this references, 110
containment, 83
contravariance, 299–300
conversion operations, 430
conversion operator overloading
operator string operator, 641–642
overview, 639–641
sample code, 640–641
summation notation, 642–645
CorDbg. See Managed Debugger (MDbg)
core language features
blocks, 16
characters, 18
comments, 10
escape characters, 19–20
generic types, 17
keywords, 25–29
line terminators, 9
nullable types, 18
numeric suffixes, 18–19
operators, 21–24
overview, 7
preprocessor directives, 13–16
primitives, 29
punctuators, 8–9
symbols and tokens overview, 7
tabs, 8
Unicode characters, 18
verbatim characters, 20–21
white space, 8
counters, list of, 538–539
covariance
arrays, 213
delegates, 299–300
CreateDelegate method, 381
cross-language inheritance, 89
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
D
data members
constants, 52
fields, 52–54
overview, 51
Data menu, 141–142
Data Source Configuration Wizard, 141
data tips, 450
DataSet Visualizer, 452–453
DbgClr (CLR Debugger), 503
Debug configuration, 441
Debug Source Files window, 449
Debug toolbar, 464
debug windows
Autos window, 468
Breakpoints window, 464
Call Stack window, 471–472
Disassembly window, 474
Immediate window, 468–471
Locals window, 468
Memory window, 474
Modules window, 472–473
Output window, 464
overview, 464
Processes window, 473
Registers window, 475–476
Script Explorer window, 465
Threads window, 472
Watch window, 465–468
DebuggableAttribute, 501–503
DebuggableBrowsableState, 493
DebuggerBrowsableAttribute, 493–495
DebuggerDisplayAttribute
DebuggableBrowsableState, 493
DebuggerBrowsableAttribute, 493–495
DebuggerTypeProxyAttribute, 495–496
overview, 492–493
DebuggerTypeProxyAttribute, 495–496
debugging
ADPlus, 503, 525–526
advanced debugging overview, 498, 499–501
ASP.NET applications, 499
attaching debuggers to running processes, 439–440
class library projects, 441
CLR Debugger (DbgClr), 503
Configuration Manager, 442–443
console application projects, 440–441
Debug configuration, 441–442
DebuggableAttribute, 501–503
DebuggableBrowsableState, 493
DebuggerBrowsableAttribute, 493–495
DebuggerDisplayAttribute. See DebuggerDisplayAttribute
debuggers overview, 503
DebuggerTypeProxyAttribute, 495–496
Debugging Tools for Windows Web site, 500
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
E
Edit And Continue window, 445–446
Employee class, list of, 96
EndInvoke method, 309–310
entry points, 35–36
EntryPoint, 613
enumerable objects, 270
enumerations
accessibility, 78
BindingFlags, 371
bitwise, 79
MemberTypes, 370
overview, 46, 77–79, 269
syntax, 78
System.Enum methods, 79
enumerators
generic, 278
IEnumerator interface, 278
nested classes, 287
overview, 267, 271–272
sample code (basic), 273–275
sample code (static collections), 275–276
sample code (versioned collections), 276
states, 272–273
equivalence compared to identity, 80–81
error handling. See also exception handling
MSIL programming, 431–432
validating object states, 72
Error List window, 156
Error objects, mapping COMException class to, 342
escape characters, 19–20
evaluation stacks, 405–406
EventArgs class, 321
events
EventArgs class, 321
exception handling, 323
overriding, 101
overview, 291, 295–296, 319
publishers, 320
publishing, 319–320
raising, 321
sealed, 107
subscribe, 320–321
syntax, 319
EventTypeFilter constructors, 484
ExactSpelling, 614–615
Exception Assistant, 351
exception clauses, 431
exception handling. See also error handling
AppDomain.UnhandledException, 349–350
Application.ThreadException, 348–349
catch statements, 329–330
COM Callable Wrappers (CCWs), 345
COM interoperability exceptions, 341–345
common exception model, 327
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
F
FCL (Framework Class Library), 31
features, core language
blocks, 16
characters, 18
comments, 10
escape characters, 19–20
generic types, 17
keywords, 25–29
line terminators, 9
nullable types, 18
numeric suffixes, 18–19
operators, 21–24
overview, 7
preprocessor directives, 13–16
primitives, 29
punctuators, 8–9
symbols and tokens overview, 7
tabs, 8
Unicode characters, 18
verbatim characters, 20–21
white space, 8
feedback, questions, or comments, xxiv
fields
accessibility, 53
declaration syntax, 52
overview, 52
private fields, 53
read-only fields, 53
volatile fields, 54
File Breakpoint dialog box, 456
filter command, 458–459
filters, 483
finalization
critical finalization objects, 592
finalizers, 569–570
garbage collections, 537
overview, 537–538
Finalize method
destructors, 563
syntax, 562
termination handler, 564
finalizers
considerations, 567
Console class, 576
constructors, 575–576
critical finalization objects, 592
deep object graphs, 574
destructors, 562
Dispose method, 577
expense, 568
multithreading, 568–569
order of finalization, 569–570
overview, 562–566
race conditions, 574
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
reentrancy, 574
resurrection, 571–573
running guarantees, 568
threads, 567, 569
virtual functions, 569–570
zombies, 571
finally statements
Dispose method, 577
overview, 332
fixed-size buffers, 624–625
fixed statement, 609–610
fonts, formatting, 158
for keyword, 163
formatted types, 618–620
Framework Class Library (FCL), 31
functions
breakpoints, 453–455
call performance, 383
members, 51
operators, 261–263
overloading, 62–63
overview, 54–55
parameters, 58–61
pointers, 295
static members, 261–263
System.Exception, 336
fuslogvw.exe (Assembly Binding Log Viewer Tool), 368
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
G
garbage collections
boxing, 559
concurrent garbage collection, 559–560
finalization, 537
finalizers. See finalizers
GC class, 598
generations, 534
overview, 555–560
reference tree, 531
garbage collector (GC)
flavors, 559–560, 561–562
garbage collection overview, 555
GC class, 598
overview, 530
Server GC, 559, 560–562
Workstation GC, 559, 560
GC. See garbage collector (GC)
GC class, 598
General window debugging options, 443–445
generations
garbage collections, 534, 555
overview, 534
walkthrough, 534–537
generic enumerable objects, 279
generic enumerators
IEnumerable<T> interface, 279
IEnumerator<T> interface, 279–280
overview, 278
sample code (versioned collections), 281
generic methods
derivation constraints, 249
overloaded methods, 244–245
overview, 243
prototypical generic method, 243
suitability, 238
syntax, 244
this reference for generic types, 246
type inference, 244
generic types
casting, 256–257
closed constructed types, 243
constructed types, 243
derivation constraints, 249
inheritance, 257–260
interfaces, 266–267
MSIL view, 265
nested types, 259–260
nodes, 250
open constructed types, 243
overview, 17, 233
serialization, 263
static members, 260
suitability, 238
syntax, 242
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
H
HandleCollector class, 596–597
handles, 596–597
Hashtable collection
IDictionaryEnumerator enumerator, 225
members, 222–224
overview, 222
Header elements, 166
heaps
Blob, 358
GUID, 358
String, 358
Userstring, 358
Hello World applications
metadata validation, 360
overview, 403–405
sample C# programs, 30–31
sample MSIL programs, 403–405
hit count command, 457
HybridDictionary collection, 232
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
I
IAsyncResult object properties, 308
ICloneable interface
Object.MemberwiseClone method, 95
overview, 207
ICollection interface, 207
IDE. See integrated development environment (IDE)
IDE Navigator, 142
identifiers, 24
identity compared to equivalence, 80–81
IDictionaryEnumerator enumerator, 225
IDisposable interface
Close method, 584
Disposable pattern considerations, 582
Disposable pattern overview, 581–582
Dispose method. See Dispose method
disposing inner objects, 586–587
overview, 577–580
reusable objects, 585
IEnumerable interface, 207
IEnumerable<T> interface
generic enumerable objects, 279
Yield statements, 284
IEnumerator interface, 278
IEnumerator<T> interface, 279–280
IErrorInfo to COMException mapping, 342
ILASM. See Intermediate Language Assembler (ILASM)
ILDASM. See Intermediate Language Disassembler (ILDASM)
IList interface
ArrayList collection, 215
members, 208
overview, 208
Immediate window
aliases, 470–471
IntelliSense, 469
limitations, 469
navigating, 469
overview, 468
implements keyword, 421
Imports elements, 167
Increment (++) operator overloading, 632
indexers
abstract, 106
arrays, compared to, 209
generics, 238
overloading, 210
overview, 208
properties, compared to, 208–209
sample code, 209
sealed, 107
syntax, 209
inheritance. See also classes
abstract, 106–107
access modifiers, 98–99
attributes, 130–131
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
base types, 83
casting, 125–130
class diagrams, 149
class hierarchies, 149–151
class refinement, 81
composition, 83
constructors, 108–111
containment, 83
cross-language inheritance, 89
derived types, 83
destructors, 108–111
Employee class, 96
generic types, 257–260
implementing, 98–99
interfaces. See interfaces
MSIL programming, 420–421
nested types, 259–260
new modifier, 102–105
operators, 128–130
overload vs. override, 100
override keyword, 99
overriding events, 101
overriding methods, 258–259
overriding overview, 99–101
overview, 83
polymorphism. See polymorphism
sample code, 88–89
sealed, 107–108
syntax, 98
System.Object, 90
terminology, 87
virtual keyword, 99
Visual Studio 2005, 149–151
inheritance polymorphism, 236
instance constructors, 393
instance fields, 52
instance members, 50
instructions, MSIL
arithmetic instructions, 429–433
boxing, 419
branching, 423–427
call instructions, 426
comparative branching instructions, 424
compare instructions, 423
list of, 433
load method, 415–417
overview, 414
short forms, 414–415
store method, 415–417
integer constraints, 251
integrated development environment (IDE)
adding references, 141
AutoRecover, 143
Community menu, 137
creating projects, 137–138
Data menu, 141–142
Data Source Configuration Wizard, 141
IDE Navigator, 142
managing windows, 142
overview, 136
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
J
jagged arrays
overview, 187, 194
sample code, 194
syntax, 194
JIT. See just-in-time (JIT) compilers; just-in-time (JIT) debugging
jitters, 401
just-in-time (JIT) compilers, 401
just-in-time (JIT) debugging
DebuggableAttribute, 501–503
Microsoft Error Reporting dialog box, 504
Modules window, 447–448
Native window, 447
overview, 446, 504–506
Symbols window, 447–448
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
K
keywords
abstract, 106
delegate, 296
delegate keyword, 313
implements, 421
interface, 421–422
for keyword, 163
list of, 25
override, 99
overview, 25
params, 211–212
return, 57
struct, 254
tracepoints, 461
unsafe, 603
virtual, 99
Knowledge Base articles, xxiv
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
L
language origins, 4–6
late binding delegates, 380
LayoutKind enumeration, 618
LeftShift operator overloading, 633
line directives, 16
line terminators, 9
links
code samples for this book, xxiv
Constrained Execution Region (CER) information, 593
Debugging Tools for Windows, 500
Microsoft Knowledge Base articles, xxiv
Microsoft Press Technology Updates Web page, xxiii
Microsoft Public Symbol Store, 548
support for this book, xxiv
listeners
managing, 483
overview, 481
predefined, 481–482
TraceListenerCollection, 483
Literal elements, 167
load method, 415–417
local variables
anonymous methods, 316
overview, 36, 55
scope, 55–56
syntax, 55
Locals window, 468
location command, 456
logical operators. See mathematical and logical operator overloading
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
M
MageUI (Manifest Generation and Editing) tool, 183
main entry points, 35–36
MakeGenericType method, 387
Managed Debugger (MDbg)
commands, 511–512
overview, 503, 506–507
walkthrough, 507–510
managed heap
generations, 534
overview, 530
manifest, 355
Manifest Generation and Editing (MageUI) tool, 183
MarshalAs attribute, 624
marshaling, explicit, 622–624
mathematical and logical operator overloading
C++ vs. C# languages, 628
Decrement (-) operators, 632
implementing, 629–632
Increment (++) operators, 632
LeftShift operators, 633
operator false, 634
operator true, 634
operator|| and operator&&, 636–637
operator== and operator!=, 635
overview, 628–629
paired operators, 635–637
replacement methods, 646
RightShift operators, 633
MDbg. See Managed Debugger (MDbg)
member functions
constructors, 63
destructors, 68
function code, 54–55
function overloading, 62–63
local variables, 55–56
methods, 56
nested types, 73
overview, 54
parameters, 58–61
partial classes, 74–76
properties, 69–72
returns, 57–58
members
abstract, 106
accessibility, 49
ArrayList collection, 215–219
attributes, 49
BitArray collection, 219–220
class members, overview, 48
data members, 51
delegates, 306
function members, 51
Hashtable collection, 222–224
instance members overview, 50
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
AddMemoryPressure, 596
anonymous. See anonymous methods
BeginInvoke, 307–309
calling, 425–427
Close, 584
Combine, 302
CreateDelegate, 381–382
Dispose. See Dispose method
DynamicInvoke, 382
EndInvoke, 309–310
Finalize, 562
function parameters, 58–61
generic. See generic methods
GetCustomAttribute, 396, 397
GetCustomAttributes, 396–397
GetGenericArguments, 386
GetGenericTypeDefinition, 385
GetInvocationList, 303
GetType, 385
global, 412
inheritance, 258–259
Invoke, 300
load, 415–417
MakeGenericType, 387
MulticastDelegate type, 306
Object.Equals, 92
Object.GetHashCode, 93
Object.GetType, 93
Object.MemberwiseClone, 94–95
Object.ReferenceEquals, 95
Object.ToString, 94
overriding, 258–259
overview, 56
private static, 317
RemoveMemoryPressure, 596
return types, 57–58
sealed, 107
static, 317
store, 415–417
syntax, 56
System.Array, 199–203
System.Enum, 79
System.Exception, 336
System.Object, 91
TraceSource, 476–477
Microsoft Build Engine (MSBuild)
command-line switches, 180
ItemGroup elements, 177
items, 177
overview, 135, 176
project files, 179–180
properties, 177
reserved properties, 177
syntax, 180
Target elements, 178
tasks, 178–179
walkthrough, 180–181
Microsoft Error Reporting dialog box, 504
Microsoft intermediate language programming. See MSIL programming
Microsoft Knowledge Base articles, xxiv
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
N
namespaces
Framework Class Library (FCL), 31
global, 32
global vs. nonglobal, 33
overview, 31
Reflection, 170
Reflection namespace, 364
NameValueCollection collection, 232
nested arrays, 193
nested classes
anonymous methods, 317
enumerators, 287
iterators, 286
nested parameters, 242
nested try blocks, 333–334
nested types
accessibility, 73
generic types, 259–260
inheritance, 259–260
overview, 73
syntax, 73
new keyword, 102
new modifier
overview, 102–105
polymorphism, 124–125
Next Statement command, 462
nodes, 250
nonblittable types, 611–612, 618
nonglobal namespaces compared to global namespaces, 33
nonsecure code. See unsafe code
nullable types, 18, 37
numeric suffixes, 18–19
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
O
Object Browser window, 144–145
Object elements, 167
Object.Equals method
consistency, 92
overview, 92
reflexivity, 92
symmetry, 92
transitivity, 92
Object.Finalize, 538
Object.GetHashCode method, 93
Object.GetType method, 93
Object.MemberwiseClone method, 94–95
Object.ReferenceEquals method, 95
objects
disposing inner, 586–587
enumerable, 270
reusable, 585
System.Object. See System.Object
Object.ToString method, 94
open constructed types, 243
operator false, 634
operator overloading
Common Language Specification (CLS), 631
conversion operators. See conversion operator overloading
Decrement (-) operators, 632
implementing, 629–632
Increment (++) operators, 632
internals, 645–647
LeftShift operators, 633
mathematical and logical operators. See mathematical and logical operator overloading
operator false, 634
operator string operator, 641–642
operator true, 634
operator|| and operator&&, 636–637
operator== and operator!=, 635
overview, 627–628
paired operators, 635–637
relational operators, 646
RightShift operators, 633
Summation class, 643
summation notation, 642–645
Summation type, 643
operator string operator, 641–642
operator true, 634
operator|| and operator&&, 636–637
operator== and operator!=, 635
operators
binary, 22
Boolean, 23
compound, 22–23
conversion. See conversion operator overloading
Decrement (-) operators, 632
expressions, 38
functions, 261–263
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
generics, 251
identifiers, 24
Increment (++) operators, 632
inheritance, 128–130
is operator, 128
LeftShift, 633
logical. See mathematical and logical operator overloading
mathematical. See mathematical and logical operator overloading
as operator, 128
operator false, 634
operator string, 641–642
operator true, 634
operator|| and operator&&, 636–637
operator== and operator!=, 635
overloading. See operator overloading
overview, 21
pointer, 24
relational, 646
RightShift, 633
static members, 261–263
ternary, 24
unary, 21
OrderedDictionary collection, 232
origins of language, 4–6
outer variables, 316–318
Output window, 464
overloading
constructors, 64
functions, 62–63
indexers, 210
methods, 244–245
operators. See operator overloading
overriding, compared to, 100
override keyword, 99
overriding
events, 101
methods, 258–259
overloading, compared to, 100
overview, 99
overview, 3–4, xxi–xxii
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
P
paired operators, 635–637
parameterized templates compared to generics, 265
parameters
arrays, 213–214
bound parameters, 243
functions, 58–61
multiple parameters for generics, 241
params keyword, 211–212
pointers, 608–611
type parameters, 239
parametric polymorphism
compared to generics, 265
overview, 238
params keyword, 211–212
partial classes, 74–76
PE COFF (Portable Executable Common Object File Format), 352
performance, function call, 383
Performance Monitor, 503, 538–539
PEVerify
arguments, 359
metadata validation, 359
options, 359–360
syntax, 359
pinned pointers, 599
PInvoke. See Platform invocation services (PInvoke)
Platform invocation services (PInvoke)
BestFitMapping, 617
CharSet, 617
directional attributes, 620
DllImport, 612–616
explicit marshaling, 622–624
fixed-size buffers, 624–625
formatted types, 618–620
Interop marshaler, 611
LayoutKind enumeration, 618
MarshalAs attribute, 624
overview, 602, 611
StringBuilder class, 620–621
ThrowOnUnmappableChar, 617
unmanaged callbacks, 621–622
pointer operators
list of, 24
overview, 24
sample code, 24
pointers
exposing, 605
fixed statement, 609–610
initializing, 605
parameters, 608–611
pinned, 599
raw, 599
return types, 608–611
stackalloc command, 610–611
symbols, 606–607
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
syntax, 605
unsafe code, 604–607
void, 605
polymorphism
advantages, 120
inheritance, 236
interfaces, 123
new modifier, 124–125
overview, 119–123
parametric, 238
requirements, 121
Portable Executable Common Object File Format (PE COFF), 352
position parameters, 391
predefined attributes, 390
preprocessor directives
conditional, 14
declarative, 13
diagnostic, 15
line, 16
overview, 13
region, 15
PreserveSig, 615
Preview Changes dialog box, 172
primitives
list of, 29–30
overview, 29
private constructors, 63
private fields, 53
private nested classes, 317
private static methods, 317
process execution, 433–436
Processes window, 473
profiles, 160
program samples, 30–31
programmer-defined custom attributes, 390
propagating exceptions, 330–331
properties
abstract, 106
error handling, 72
generics, 238
IAsyncResult object, 308
indexers, compared to, 208–209
IsGeneric Boolean, 383
IsGenericMethod, 383
IsGenericTypeDefinition Boolean, 383
Microsoft Build Engine (MSBuild), 177
MulticastDelegate type, 306
overview, 69–72
read-only, 72
sealed, 107
SourceSwitch, 480
syntax, 70
System.Array, 203–204
System.Exception, 337
TraceListener class, 482
TraceSource, 478
validating object states, 72
write-only, 72
pseudo-custom attributes, 390
Public Symbol Store Web site, 548
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
Q
questions, comments, or feedback, xxiv
Queue collection
members, 226–227
overview, 226
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
R
raw pointers, 599
RCW (Runtime Callable Wrapper), 602
read-only fields, 53
read-only properties, 72
rectangular arrays. See multidimensional arrays
recursive constraints, 250
refactoring
operations, 172
overview, 171
Preview Changes dialog box, 172
Refactoring menu, 172
walkthrough, 173–176
Refactoring menu, 172
reference tree, 531–533
reference types
constraints, 254
overview, 45
user-defined types, 254
References elements, 167
refinement of classes, 81
reflection
Activator class, 378
assembly loading, 367–369
attributes, 389–392, 396–398
binders, 375–378
casting, 380
creating generic types, 387
dynamic invocation, 373–378
exception handling, 352
function call performance, 383
GetCustomAttribute method, 396, 397
GetCustomAttributes method, 396–397
GetGenericArguments method, 386
GetGenericTypeDefinition method, 385
GetType method, 384
IsGeneric Boolean property, 383
IsGenericMethod property, 383
IsGenericTypeDefinition Boolean property, 383
late binding delegates, 380–382
MakeGenericType method, 387
overview, 355, 364, 383
predefined attributes, 390
programmer-defined custom attributes, 390
pseudo-custom attributes, 390
sample code, 372, 379–380
security, 388
type creation, 378–380
Type objects, 365–367
typeof operator, 384
Reflection namespace
code snippets, 170
overview, 364
Reflection trees, 366
ReflectionPermission flags, 388
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
reflexivity, 92
region directives, 15
Registers window, 475–476
relational operators, replacement methods, 646
Release configuration
overview, 441
settings, 442
remote exceptions, 345–347
RemoveMemoryPressure method, 596
resources for this book
Constrained Execution Region (CER) information, 593
Debugging Tools for Windows, 500
links to code samples for this book, xxiv
Microsoft Knowledge Base articles, xxiv
Microsoft Press Technology Updates Web page, xxiii
Microsoft Public Symbol Store, 548
support for this book, xxiv
resurrection, 571–573
rethrowing exceptions, 330
return keyword, 57
return statements, 285
return types
methods, 57–58
pointers, 608–611
reusable objects, 585
reverse iteration, 288
RightShift operator overloading, 633
roundtripping, 434–436
Runtime Callable Wrapper (RCW), 602
Runtime Debugger (CorDbg). See Managed Debugger (MDbg)
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
S
safe handles, 593–594
sample C# programs
Binder class, 377–378
complex iterations, 290
conversion operator overloading, 640–641
Dispose method, 577
dual iteration, 287–288
dynamic binding, 374
enumerators (basic), 273–275
enumerators (static collections), 275–276
enumerators (versioned collections), 276
exception handling, 326
four-dimensional array sample code, 193
generic enumerators (versioned collections), 281
GetGenericArguments method, 386
GetGenericTypeDefinition method, 385
GetType method, 385
Hello World application, 30–31, 360
indexers, 209
inheritance, 88–89
iterator examples (complex iterations), 290
iterator examples (dual iteration), 287–288
iterator examples (overview), 287
iterator examples (reverse iteration), 288
iterator examples (temporary collections), 289
jagged arrays, 194
links to code samples for this book, xxiv
multidimensional arrays, 193
reflection, 372
reverse iteration, 288
sheet collections, 239
temporary collections, 289
tracing, 480, 485–486, 489–491
type creation, 379–380
type parameters, 239
sample MSIL programs, 403–405
scoped exceptions, 432
Script Explorer window, 465
sealed
classes, 107
events, 107
indexers, 107
members, 108
methods, 107
overview, 107
properties, 107
static members, 107
security, 388
selection statements, 38–41
semicolon punctuators, 8
serialization, 263
serializing application exceptions, 346
Server GC
configuring GC flavor, 561–562
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
tabs, 8
TRACE symbol, 476
Unicode characters, 18
verbatim characters, 20–21
white space, 8
symmetry, 92
Symsrv symbol server, 548–549
syntax
anonymous methods, 314
arrays, 189–191, 428
assembly directives, 407
attributes, 391
BeginInvoke method, 308
Binder class, 375
calling methods, 425
CDATA, 168
class declarations, 46
class member access, 48
constants, 52
constructors, 63, 109
CreateDelegate method, 381
delegates, 298
destructors, 68
DllImportAttribute, 612
dynamic binding, 373, 374
DynamicInvoke method, 382
EndInvoke method, 310
enumerations, 78
events, 319
field declarations, 52
Finalize method, 562
fixed-size buffers, 625
generic methods, 244
generic types, 242
GetGenericArguments method, 386
GetGenericTypeDefinition method, 385
GetInvocationList method, 303
indexers, 209
inheritance, 98
instance members, 50
instructions, MSIL, 414–415
interfaces, 112
is operator, 129
jagged arrays, 194
local variables, 55
methods, 56
Microsoft Build Engine (MSBuild), 180
multidimensional arrays, 192
nested types, 73
as operator, 130
PEVerify, 359
pointers, 605
properties, 70
single-dimensional arrays, 189
stackalloc command, 611
structures, 76
UnhandledExceptionEventHandler, 350
VirtualAlloc, 530
WaitForMultipleObjects, 541
system requirements, xxii–xxiii
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
T
tables, metadata, 357
tabs, 8
Target elements, 178
technology updates, xxiii
templates, parameterized, 265
temporary collections, 289
termination handlers, 332, 564
ternary operators, 24, 628
this references
closed constructed types, 246
constructors, 110
generic types, 246
overview, 50–51
XClass type, 246
thread local storage (TLS)
application, 568, 580
overview, 553
thread-safe Dispose method, 585
threads
AwareLock.Enter, 541
commands, 541–542
finalizers, 567, 569
monitors, 540
mutexes, 540
overview, 539–541
Son of Strike (SOS), 541
WaitForMultipleObjects, 541
WaitHandle.WaitAll, 541
walkthrough, 542–546
Windows Debugger (WinDbg), 541
Threads window, 472
ThrowOnUnmappableChar, 617
tilde (~) command, 514
Tlist, 513
TLS. See thread local storage (TLS)
tokens. See symbols and tokens
TRACE symbol, 476
TraceListener class properties, 482
TraceListenerCollection, 483
TraceOptions, 483
tracepoints
keywords, 461
overview, 459–461
TraceSource
members, 476
methods, 476–477
overview, 476
properties, 478
sample code, 485–486
tracing
activity traces, 479
configuration files, 488–489
EventTypeFilter, 484
filters, 483
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
levels, 478–479
listeners, 481–482
overview, 476
sample code, 480, 485–486, 489–491
source levels, 479–480
SourceFilter, 484
SourceSwitch, 479
switches, 479
TraceListener class, 482
TraceListenerCollection, 483
TraceOptions values, 483
TraceSource. See TraceSource
transitivity, 92
trapping unhandled exceptions, 347
try blocks
exception handling, 326
nested try blocks, 333–334
try statements, 327–328
type inference, 244
Type methods, 371
Type objects
assemblies, 366
browsing information, 370
MemberTypes enumeration, 370
metadata collections, 371
obtaining, 365–367
overview, 365
typeof operator, 366
type parameters
closed constructed types, 243
derivation constraints, 248, 250, 251
multiple parameters, 241
open constructed types, 243
overview, 239
sample code, 239
sheet generic type, 241
typeof operator, 366, 384
types
blittable, 611, 618
browsing information, 370
classes. See classes
collections, 215
creating, 378–380
creating generic, 387
DebuggerBrowsableAttribute, 493–495
DebuggerDisplayAttribute. See DebuggerDisplayAttribute
DebuggerTypeProxyAttribute, 495–496
EventTypeFilter, 484
formatted, 618–620
generic. See generic types
managing, 417–418
MemberTypes enumeration, 370
metadata collections, 371
nested, 73
nonblittable, 611–612, 618
nullable, 18, 37
overview, 45
reference, 45
sample code, 379–380
SourceFilter, 484
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
SourceLevels, 479
StringBuilder, 168
Summation, 643
TraceListenerCollection, 483
TraceSource. See TraceSource
user-defined, 627
value, 45
WeakReference, 588
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
U
unary operators
list of, 21
overview, 21
unboxing, 278
unhandled exceptions
AppDomain.UnhandledException, 349–350
Application.ThreadException, 348–349
Exception Assistant, 351
overview, 347–348
trapping, 347
UnhandledExceptionEventHandler, 350
Unicode characters, 18
unmanaged callbacks, 621–622
unmanaged resources
boxing, 559
garbage collection overview, 555–559
generations, 555
handles, 596–597
managing, 595–597
memory pressure, 596
overview, 554–555
UnmanagedType enumeration, 623
unsafe code
appropriate uses, 601
blittable types, 611, 618
CharSet, 617
directional attributes, 620
DllImport, 612–616
explicit marshaling, 622–624
fixed-size buffers, 624–625
fixed statement, 609–610
formatted types, 618–620
Interop marshaler, 611
LayoutKind enumeration, 618
MarshalAs attribute, 624
nonblittable types, 611–612, 618
overview, 598–599, 601–603, 625
Platform invocation services. See Platform invocation services (PInvoke)
pointer parameters and return, 608–611
pointers, 604–607
Runtime Callable Wrapper (RCW), 602
stackalloc command, 610–611
StringBuilder class, 620–621
summary, 625
unmanaged callbacks, 621–622
void pointers, 605
unsafe keyword, 603
untrusted code. See unsafe code
URLs. See Web sites
user-defined types
operator overloading, 627
reference type constraints, 254
Userstring heap, 358
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
V
validation, 359–360
value types
constraints, 251, 254
overview, 45
variables
local, 36
outer, 316–318
vectors. See single-dimensional arrays
verbatim characters, 20–21
versioned collections
sample code (enumerators), 276
sample code (generic enumerators), 281
virtual functions, 569–570
virtual keyword, 99
Virtual Memory Manager (VMM), 530
VirtualAlloc, 530
Visual Studio 2005
adding references, 141
association, 151
AutoRecover, 143
building, 176
change tracking, 159
Class Designer, 145
class diagrams, 145–149, 151–154
class hierarchies. See class hierarchies
Class View window, 144
ClickOnce deployment, 135, 182–186
Code Editor, 156–160
code snippets. See code snippets
color formatting, 158
Community menu, 137
creating projects, 137–138
Data menu, 141–142
Data Source Configuration Wizard, 141
debugging. See debugging with Visual Studio 2005
deploying, 176
Error List window, 156
Exception Assistant, 351
Exceptions dialog box, 351–352
font formatting, 158
IDE Navigator, 142
inheritance, 149–151
integrated development environment. See integrated development environment (IDE)
IntelliSense, 156–158
managing exceptions in, 351–352
managing windows, 142
Microsoft Build Engine. See Microsoft Build Engine (MSBuild)
Object Browser window, 144–145
overview, 131, 135–136
profiles, 160
project types, 140–141
refactoring, 171–176
Solution Explorer, 138–141
source code formatting, 159
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
W
WaitForMultipleObjects, 541
WaitHandle.WaitAll, 541
Watch window
expressions, 466–468
overview, 465
weak references
Constrained Execution Region (CER), 592–594
critical finalization objects, 592
internals, 591
overview, 588–590
WeakReference class, 591
WeakReference type, 588
WeakReference class, 591
WeakReference type, 588
Web sites
companion for this book, xxiv
Constrained Execution Region (CER) information, 593
Debugging Tools for Windows, 500
Microsoft Knowledge Base articles, xxiv
Microsoft Press Technology Updates Web page, xxiii
Microsoft Public Symbol Store, 548
support for this book, xxiv
When Breakpoint Is Hit dialog box, 460
when hit command, 459
white space, 8
WinDbg. See Windows Debugger (WinDbg)
Windows Console Debugger (CDB), 503
Windows Debugger (WinDbg)
application symbols, 549
breakpoints memory commands, 516–518
commands list, 513–514
commands overview, 513
directives, 518–519
display memory commands, 515–516
overview, 500, 503, 512
stack trace commands, 514–515
step commands, 518–519
thread commands, 541
tilde (~) command, 514
Tlist, 513
Windows Forms projects
attaching debuggers to running processes, 439–440
class library projects, 441
console application projects, 440–441
debugging, 438
Workstation GC
concurrent garbage collection, with, 560
concurrent garbage collection, without, 560
overview, 559
write-only properties, 72
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
Index
X–Y–Z
XClass type, 246
XML Visualizer, 451
Yield statements
IEnumerable<T> interface, 284
iterator blocks, 285
overview, 284
zombies, 571
ABC Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
List of Figures
Chapter 1: Introduction to Visual C# Programming
Figure 1-1: Evolution of programming languages from inception to C#
Chapter 2: Types
Figure 2-1: Figure diagramming the scope and visibility of a local variable
Chapter 3: Inheritance
Figure 3-1: A basket of marbles
Figure 5-2: A command line and the results from running the application
Chapter 6: Generics
Figure 6-1: MSIL view of a generic type
Chapter 7: Iterators
Figure 7-1: A view of the ZClass type, which includes the nested enumerator classes
Figure 8-4: Disassembly of nested class for an anonymous method that uses outer variables
Figure 12-4: The General window for setting general debugging options
Figure 12-27: Print a message selected in the When Breakpoint Is Hit dialog box
Figure 12-34: Watch window with locala, localb, fielda, and fieldb values
Figure 12-40: Call Stack window with Enable Just My Code disabled
Figure 12-45: View of the ZClass and YClass instances before applying DebuggerDisplayAttribute
Figure 12-46: View of ZClass and YClass types in the debugger window after applying the
DebuggerDisplayAttribute type
Figure 12-47: View of ZClass and YClass types before applying the DebuggerBrowsableAttribute type
Figure 12-48: View of ZClass and YClass types after applying DebuggerBrowsableAttribute
List of Tables
Chapter 1: Introduction to Visual C# Programming
Table 1-1: C# Symbols and Tokens
Chapter 2: Types
Table 2-1: Class Modifier Table
Chapter 3: Inheritance
Table 3-1: Inheritance Terminology
Chapter 6: Generics
Table 6-1: Inheritance Table for Generic Types
Chapter 7: Iterators
Table 7-1: Enumerator States