Discover how to: • Build, package, and deploy applications and their types • Understand how primitive, value, and reference types behave so you use them more efficiently • Use generics
Trang 1
About the Author
Jeffrey Richter is a cofounder of
Wintellect (www.wintellect.com),
a training and consulting firm dedicated to helping companies build better software faster In addition to this book’s highly regarded previous editions, he’s written several other popular titles,
including Windows via C/C++ A longtime
consultant to the Microsoft NET Framework Team, Jeff worked with Microsoft to develop
a new asynchronous programming model that’s part of NET Framework 4.5
The definitive guide to mastering CLR and NET
development—from the ground up
Dig deep and master the intricacies of the common language
runtime, C#, and NET development Led by programming expert
Jeffrey Richter, a longtime consultant to the Microsoft NET Team—
you’ll gain pragmatic insights for developing robust, reliable, and
responsive apps and components
Discover how to:
• Build, package, and deploy applications and their types
• Understand how primitive, value, and reference types behave
so you use them more efficiently
• Use generics and interfaces to define reusable algorithms
• Work effectively with special CLR types—delegates, custom
attributes, nullable types, arrays, strings
• Understand how the managed heap and the garbage
collector work
• Get a quick start with serialization and deserialization services
• Design responsive, scalable solutions using thread pools, tasks,
cancellations, timers, and asynchronous functions
• Use exception handling to assist with state management
• Construct dynamically extensible apps using CLR hosting,
AppDomains, assembly loading, and reflection
• Interoperate with Windows® Runtime (WinRT) components
Download from the author’s website:
http://wintellect.com/books
Jeffrey Richter
CLR via C# Fourth Edition
About the Fourth Edition
• Fully updated for Microsoft® NET Framework 4.5 and Visual Studio® 2012
• Focuses on core types in the Framework Class Library
• Expertly teaches multicore programming, generics, threading, and other essentials
• Shares practical advice from extensive insider and field experience
Trang 2PUBLISHED BY
Microsoft Press
A Division of Microsoft Corporation
One Microsoft Way
Redmond, Washington 98052-6399
Copyright © 2012 by Jeffrey Richter
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
Library of Congress Control Number: 2012951989
ISBN: 978-0-7356-6745-7
Printed and bound in the United States of America
First Printing
Microsoft Press books are available through booksellers and distributors worldwide If you need support related
to this book, email Microsoft Press Book Support at mspinput@microsoft.com Please tell us what you think of this book at http://www.microsoft.com/learning/booksurvey
Microsoft and the trademarks listed at http://www.microsoft.com/about/legal/en/us/IntellectualProperty/Trademarks/EN-US.aspx are trademarks of the Microsoft group of companies All other marks are property of their respective owners
The example companies, organizations, products, domain names, email addresses, logos, people, places, and events depicted herein are fictitious No association with any real company, organization, product, domain name, email 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
Acquisitions Editor: Devon Musgrave
Developmental Editor: Devon Musgrave
Project Editor: Carol Dillingham
Editorial Production: Online Training Solutions, Inc
Technical Reviewer: Christophe Nasarre; Technical Review services provided by Content Master,
a member of CM Group, Ltd
Copyeditor: Candace Sinclair
Indexer: Jan Bednarczuk
Cover: Twist Creative • Seattle
Trang 3Kristin, words cannot express how I feel about our life together I cherish our family and all our adventures I’m filled each day with love for you.
Aidan (age 9) and Grant (age 5), you both have been an tion to me and have taught me to play and have fun Watching the two of you grow up has been so rewarding and enjoyable for
inspira-me I am lucky to be able to partake in your lives I love and preciate you more than you could ever know.
Trang 5ap-Contents at a Glance
CHAPTER 2 Building, Packaging, Deploying, and
Administering Applications and Types 33
CHAPTER 3 Shared Assemblies and Strongly Named Assemblies 65
CHAPTER 5 Primitive, Reference, and Value Types 111
CHAPTER 14 Chars, Strings, and Working with Text 317
Trang 6PART IV CORE FACILITIES
CHAPTER 21 The Managed Heap and Garbage Collection 505
CHAPTER 25 Interoperating with WinRT Components 643
CHAPTER 27 Compute-Bound Asynchronous Operations 691
CHAPTER 29 Primitive Thread Synchronization Constructs 757
CHAPTER 30 Hybrid Thread Synchronization Constructs 789
Index 823
Trang 7Introduction xxiii
PART I CLR BASICS Chapter 1 The CLR’s Execution Model 3 Compiling Source Code into Managed Modules 3
Combining Managed Modules into Assemblies 6
Loading the Common Language Runtime 8
Executing Your Assembly’s Code .11
IL and Verification .16
Unsafe Code .17
The Native Code Generator Tool: NGen exe .19
The Framework Class Library .22
The Common Type System .24
The Common Language Specification .26
Interoperability with Unmanaged Code .30
Chapter 2 Building, Packaging, Deploying, and Administering Applications and Types 33 .NET Framework Deployment Goals .34
Building Types into a Module .35
Response Files 36
A Brief Look at Metadata 38
What do you think of this book? We want to hear from you!
Microsoft is interested in hearing your feedback so we can continually improve our
books and learning resources for you To participate in a brief online survey, please visit:
microsoft.com/learning/booksurvey
Trang 8Combining Modules to Form an Assembly 45
Adding Assemblies to a Project by Using the Visual Studio IDE 51
Using the Assembly Linker 52
Adding Resource Files to an Assembly 53
Assembly Version Resource Information .54
Version Numbers 58
Culture .59
Simple Application Deployment (Privately Deployed Assemblies) .60
Simple Administrative Control (Configuration) 62
Chapter 3 Shared Assemblies and Strongly Named Assemblies 65 Two Kinds of Assemblies, Two Kinds of Deployment 66
Giving an Assembly a Strong Name 67
The Global Assembly Cache 72
Building an Assembly That References a Strongly Named Assembly 74
Strongly Named Assemblies Are Tamper-Resistant 75
Delayed Signing .76
Privately Deploying Strongly Named Assemblies 79
How the Runtime Resolves Type References 80
Advanced Administrative Control (Configuration) 83
Publisher Policy Control 86
PART II DESIGNING TYPES Chapter 4 Type Fundamentals 91 All Types Are Derived from System.Object 91
Casting Between Types 93
Casting with the C# is and as Operators 95
Namespaces and Assemblies 97
How Things Relate at Run Time 101
Trang 9Chapter 5 Primitive, Reference, and Value Types 111
Programming Language Primitive Types 111
Checked and Unchecked Primitive Type Operations 115
Reference Types and Value Types .118
Boxing and Unboxing Value Types .124
Changing Fields in a Boxed Value Type by Using Interfaces (and Why You Shouldn’t Do This) 136
Object Equality and Identity .139
Object Hash Codes 142
The dynamic Primitive Type .144
Chapter 6 Type and Member Basics 151 The Different Kinds of Type Members .151
Type Visibility 154
Friend Assemblies .154
Member Accessibility 156
Static Classes 158
Partial Classes, Structures, and Interfaces 159
Components, Polymorphism, and Versioning 160
How the CLR Calls Virtual Methods, Properties, and Events 162
Using Type Visibility and Member Accessibility Intelligently 166
Dealing with Virtual Methods When Versioning Types 169
Chapter 7 Constants and Fields 175 Constants 175
Fields 177
Chapter 8 Methods 181 Instance Constructors and Classes (Reference Types) .181
Instance Constructors and Structures (Value Types) 184
Type Constructors 187
Trang 10Operator Overload Methods .191
Operators and Programming Language Interoperability 193
Conversion Operator Methods 195
Extension Methods 198
Rules and Guidelines 200
Extending Various Types with Extension Methods .201
The Extension Attribute .203
Partial Methods 204
Rules and Guidelines 207
Chapter 9 Parameters 209 Optional and Named Parameters .209
Rules and Guidelines 210
The DefaultParameterValue and Optional Attributes 212
Implicitly Typed Local Variables 212
Passing Parameters by Reference to a Method 214
Passing a Variable Number of Arguments to a Method 220
Parameter and Return Type Guidelines 223
Const-ness .224
Chapter 10 Properties 227 Parameterless Properties 227
Automatically Implemented Properties 231
Defining Properties Intelligently 232
Object and Collection Initializers .235
Anonymous Types 237
The System.Tuple Type 240
Parameterful Properties 242
The Performance of Calling Property Accessor Methods 247
Property Accessor Accessibility .248
Generic Property Accessor Methods 248
Trang 11Chapter 11 Events 249
Designing a Type That Exposes an Event 250
Step #1: Define a type that will hold any additional information that should be sent to receivers of the event notification 251
Step #2: Define the event member 252
Step #3: Define a method responsible for raising the event to notify registered objects that the event has occurred 253
Step #4: Define a method that translates the input into the desired event 256
How the Compiler Implements an Event 256
Designing a Type That Listens for an Event 258
Explicitly Implementing an Event 260
Chapter 12 Generics 265 Generics in the Framework Class Library 270
Generics Infrastructure 271
Open and Closed Types .272
Generic Types and Inheritance .274
Generic Type Identity .275
Code Explosion 277
Generic Interfaces 277
Generic Delegates 278
Delegate and Interface Contra-variant and Covariant Generic Type Arguments 279
Generic Methods 281
Generic Methods and Type Inference .283
Generics and Other Members .284
Verifiability and Constraints 284
Primary Constraints 287
Secondary Constraints .288
Constructor Constraints 289
Other Verifiability Issues 290
Trang 12Chapter 13 Interfaces 295
Class and Interface Inheritance .296
Defining an Interface 296
Inheriting an Interface 298
More About Calling Interface Methods 300
Implicit and Explicit Interface Method Implementations (What’s Happening Behind the Scenes) .301
Generic Interfaces 303
Generics and Interface Constraints 305
Implementing Multiple Interfaces That Have the Same Method Name and Signature 307
Improving Compile-Time Type Safety with Explicit Interface Method Implementations .308
Be Careful with Explicit Interface Method Implementations .310
Design: Base Class or Interface? 312
PART III ESSENTIAL TYPES Chapter 14 Chars, Strings, and Working with Text 317 Characters .317
The System.String Type 320
Constructing Strings .320
Strings Are Immutable .323
Comparing Strings 323
String Interning 329
String Pooling 332
Examining a String’s Characters and Text Elements 333
Other String Operations 335
Constructing a String Efficiently 336
Constructing a StringBuilder Object 336
StringBuilder Members .337
Trang 13Obtaining a String Representation of an Object: ToString 339
Specific Formats and Cultures 340
Formatting Multiple Objects into a Single String 344
Providing Your Own Custom Formatter 345
Parsing a String to Obtain an Object: Parse 348
Encodings: Converting Between Characters and Bytes 350
Encoding and Decoding Streams of Characters and Bytes .355
Base-64 String Encoding and Decoding 356
Secure Strings .357
Chapter 15 Enumerated Types and Bit Flags 361 Enumerated Types .361
Bit Flags 367
Adding Methods to Enumerated Types .371
Chapter 16 Arrays 373 Initializing Array Elements 376
Casting Arrays 378
All Arrays Are Implicitly Derived from System.Array 380
All Arrays Implicitly Implement IEnumerable, ICollection, and IList 381
Passing and Returning Arrays 382
Creating Non-Zero Lower Bound Arrays .383
Array Internals 384
Unsafe Array Access and Fixed-Size Array 388
Chapter 17 Delegates 391 A First Look at Delegates 391
Using Delegates to Call Back Static Methods .394
Using Delegates to Call Back Instance Methods 395
Trang 14Demystifying Delegates 396
Using Delegates to Call Back Many Methods (Chaining) 400
C#’s Support for Delegate Chains 404
Having More Control over Delegate Chain Invocation 404
Enough with the Delegate Definitions Already (Generic Delegates) 407
C#’s Syntactical Sugar for Delegates 408
Syntactical Shortcut #1: No Need to Construct a Delegate Object 409
Syntactical Shortcut #2: No Need to Define a Callback Method (Lambda Expressions) 410
Syntactical Shortcut #3: No Need to Wrap Local Variables in a Class Manually to Pass Them to a Callback Method .413
Delegates and Reflection 416
Chapter 18 Custom Attributes 421 Using Custom Attributes 421
Defining Your Own Attribute Class 425
Attribute Constructor and Field/Property Data Types 428
Detecting the Use of a Custom Attribute 430
Matching Two Attribute Instances Against Each Other 434
Detecting the Use of a Custom Attribute Without Creating Attribute-Derived Objects 437
Conditional Attribute Classes 440
Chapter 19 Nullable Value Types 441 C#’s Support for Nullable Value Types 443
C#’s Null-Coalescing Operator 446
The CLR Has Special Support for Nullable Value Types 447
Boxing Nullable Value Types 447
Unboxing Nullable Value Types 448
Calling GetType via a Nullable Value Type 448
Calling Interface Methods via a Nullable Value Type .448
Trang 15PART IV CORE FACILITIES
Chapter 20 Exceptions and State Management 451
Defining “Exception” .452
Exception-Handling Mechanics 453
The try Block 454
The catch Block 455
The finally Block 456
The System.Exception Class 460
FCL-Defined Exception Classes 463
Throwing an Exception .466
Defining Your Own Exception Class 467
Trading Reliability for Productivity .469
Guidelines and Best Practices 478
Use finally Blocks Liberally 478
Don’t Catch Everything 480
Recovering Gracefully from an Exception 481
Backing Out of a Partially Completed Operation When an Unrecoverable Exception Occurs—Maintaining State 482
Hiding an Implementation Detail to Maintain a “Contract” 483
Unhandled Exceptions 485
Debugging Exceptions 490
Exception-Handling Performance Considerations 492
Constrained Execution Regions (CERs) 494
Code Contracts 498
Chapter 21 The Managed Heap and Garbage Collection 505 Managed Heap Basics 505
Allocating Resources from the Managed Heap 506
The Garbage Collection Algorithm 507
Garbage Collections and Debugging 510
Trang 16Generations: Improving Performance 513
Garbage Collection Triggers .519
Large Objects 519
Garbage Collection Modes 520
Forcing Garbage Collections 522
Monitoring Your Application’s Memory Usage .524
Working with Types Requiring Special Cleanup 525
Using a Type That Wraps a Native Resource 532
An Interesting Dependency Issue 537
Other GC Features for Use with Native Resources 538
Finalization Internals 542
Monitoring and Controlling the Lifetime of Objects Manually .545
Chapter 22 CLR Hosting and AppDomains 553 CLR Hosting 554
AppDomains .556
Accessing Objects Across AppDomain Boundaries 559
AppDomain Unloading 570
AppDomain Monitoring .571
AppDomain First-Chance Exception Notifications 573
How Hosts Use AppDomains 574
Executable Applications 574
Microsoft Silverlight Rich Internet Applications 574
Microsoft ASP.NET and XML Web Services Applications 575
Microsoft SQL Server 575
Your Own Imagination .576
Advanced Host Control 576
Managing the CLR by Using Managed Code 576
Writing a Robust Host Application 577
How a Host Gets Its Thread Back .578
Trang 17Chapter 23 Assembly Loading and Reflection 583
Assembly Loading 584
Using Reflection to Build a Dynamically Extensible Application 588
Reflection Performance 589
Discovering Types Defined in an Assembly 590
What Exactly Is a Type Object? 591
Building a Hierarchy of Exception-Derived Types 593
Constructing an Instance of a Type .594
Designing an Application That Supports Add-Ins 596
Using Reflection to Discover a Type’s Members 599
Discovering a Type’s Members .599
Invoking a Type’s Members 603
Using Binding Handles to Reduce Your Process’s Memory Consumption 608
Chapter 24 Runtime Serialization 611 Serialization/Deserialization Quick Start 613
Making a Type Serializable 617
Controlling Serialization and Deserialization 619
How Formatters Serialize Type Instances 623
Controlling the Serialized/Deserialized Data 624
How to Define a Type That Implements ISerializable When the Base Type Doesn’t Implement This Interface 630
Streaming Contexts .631
Serializing a Type As a Different Type and Deserializing an Object As a Different Object 633
Serialization Surrogates 636
Surrogate Selector Chains .639
Overriding the Assembly and/or Type When Deserializing an Object 640
Trang 18Chapter 25 Interoperating with WinRT Components 643
CLR Projections and WinRT Component Type System Rules 645
WinRT Type System Core Concepts .645
Framework Projections .649
Calling Asynchronous WinRT APIs from NET Code .649
Interoperating Between WinRT Streams and NET Streams 654
Passing Blocks of Data Between the CLR and WinRT .656
Defining WinRT Components in C# 658
PART V THREADING Chapter 26 Thread Basics 669 Why Does Windows Support Threads? 669
Thread Overhead 670
Stop the Madness 674
CPU Trends 677
CLR Threads and Windows Threads 678
Using a Dedicated Thread to Perform an Asynchronous Compute-Bound Operation .678
Reasons to Use Threads 681
Thread Scheduling and Priorities 683
Foreground Threads vs Background Threads 688
What Now? 689
Chapter 27 Compute-Bound Asynchronous Operations 691 Introducing the CLR’s Thread Pool .692
Performing a Simple Compute-Bound Operation 693
Execution Contexts 694
Cooperative Cancellation and Timeout .696
Tasks .700
Waiting for a Task to Complete and Getting Its Result 702
Canceling a Task 704
Trang 19Starting a New Task Automatically When Another
Task Completes 705
A Task May Start Child Tasks 707
Inside a Task .707
Task Factories 709
Task Schedulers 711
Parallel’s Static For, ForEach, and Invoke Methods 713
Parallel Language Integrated Query 717
Performing a Periodic Compute-Bound Operation 720
So Many Timers, So Little Time 723
How the Thread Pool Manages Its Threads 723
Setting Thread Pool Limits 724
How Worker Threads Are Managed 724
Chapter 28 I/O-Bound Asynchronous Operations 727 How Windows Performs I/O Operations .727
C#’s Asynchronous Functions 732
How the Compiler Transforms an Async Function into a State Machine 734
Async Function Extensibility 738
Async Functions and Event Handlers .741
Async Functions in the Framework Class Library .742
Async Functions and Exception Handling .744
Other Async Function Features .745
Applications and Their Threading Models 748
Implementing a Server Asynchronously 751
Canceling I/O Operations 751
Some I/O Operations Must Be Done Synchronously 752
FileStream-Specific Issues 753
I/O Request Priorities 754
Trang 20Chapter 29 Primitive Thread Synchronization Constructs 757
Class Libraries and Thread Safety 759
Primitive User-Mode and Kernel-Mode Constructs 760
User-Mode Constructs 762
Volatile Constructs 762
Interlocked Constructs .768
Implementing a Simple Spin Lock 773
The Interlocked Anything Pattern 776
Kernel-Mode Constructs 778
Event Constructs 782
Semaphore Constructs 784
Mutex Constructs 785
Chapter 30 Hybrid Thread Synchronization Constructs 789 A Simple Hybrid Lock 790
Spinning, Thread Ownership, and Recursion 791
Hybrid Constructs in the Framework Class Library 793
The ManualResetEventSlim and SemaphoreSlim Classes 794
The Monitor Class and Sync Blocks 794
The ReaderWriterLockSlim Class 800
The OneManyLock Class 802
The CountdownEvent Class 804
The Barrier Class 805
Thread Synchronization Construct Summary 805
The Famous Double-Check Locking Technique .807
The Condition Variable Pattern 811
Asynchronous Synchronization .814
The Concurrent Collection Classes 818
Index 823
What do you think of this book? We want to hear from you!
Microsoft is interested in hearing your feedback so we can continually improve our books and learning resources for you To participate in a brief online survey, please visit:
microsoft.com/learning/booksurvey
Trang 21Well, here we are again Who would’ve thought? Oh, I know—I would’ve thought!
When you sign up for marriage, you really are living Groundhog Day If you
haven’t seen that movie, watch it, because you will suddenly see why you have to make
the same mistakes over and over again In this case, when Jeff said he wouldn’t write
another book, I knew it was the empty promise of an addict Jeff cannot not write
another book Just today, we were discussing another book he is absolutely not going
to write (except that there is already a chapter in progress) It is coded in his DNA A
thoroughbred is born to run and Jeff is born to write
Jeff is as predictable as the seasons He cannot stay away from the little 0s and 1s
locked inside his hard drive They cannot be ignored And while the rest of you are all
snug in your beds, Jeff’s internal alarm starts ringing around 3:00 a.m (coincidently,
when our four-year-old climbs into bed with us, another pattern I seem to have no
control over) some mysterious force compels Jeff’s brain to unlock little solutions, big
brainstorms, and frightening bugs that control him It forces him into his office to work
them out of his head The rest of us can roll over and go back to sleep, safe, knowing
that Jeff is out there somewhere solving these problems for us—like a cyber-super
hero, saving the thread from becoming just another loose end
But accruing this knowledge just for himself is not enough for Jeff He feels selfish
hoarding his insights in his little space in the universe So he must broadcast them; he
must write them down They're like radio waves hurling outward wondering if a listener
will pick them up This he does for you, dear reader; a testament to his passion for
Microsoft technologies
This book is actually adding a new layer of wisdom Jeff is getting older each time
he flies around the sun, and with the accumulation of years, he is starting to look back
Thinking about things in a more mature manner, he has rewritten the chapter
cover-ing Reflection Maybe you too will join him as he waxes poetic on this subject This is a
place where we can learn how to have the code ask about the code and really
encour-aging some deeper insights as to why Reflection works the way it does Put on your
smoking jackets, sink into a leather chair, and spend some time thinking about your
own code and its greater purpose in life
On a more lively note, there is stuff about async/await in here Apparently, this is the
progression of the AsyncEnumerator my love has been going on about for some time
Whew, I didn’t think we would ever move on from that! The thing is, as many times as
he has talked about his AsyncEnumerator, it hasn’t stuck in my mind at all I decided if
Trang 22I knew what enumerator meant, maybe this would help me According to Wikipedia, it
is a census taker Is this chapter about census takers working out of synchronicity? That seems like a waste of taxpayer dollars Whatever it means in the computer world must
be better than that Jeff worked with the team at The Big M to perfect async/await, and now it is here in this book laid out for your reading pleasure; I suggest you read it sequentially
Another major addition to the book is the one I’m most excited about It is my expectation of you all to read and internalize this WinRT stuff This is a nerd word that somehow means: “Make Me Really Cool Apps for Some Awesome Slate Device NOW!” That’s right; the new Windows Runtime is all about awesome touch screens My kids would like some birds that fly into pigs I would like maybe something with flowers, and definitely you could use it for some educational stuff Just let your imagination go! Come up with Wonderful Innovative Nifty Really Touchy stuff Use this chapter for my benefit, please Otherwise, I may run out of patience with Jeff and his continuous book writing and lock him in a room with knitting needles and no electricity You program-mers decide: write cool apps with WinRT or no new books from Jeff!
In summary, with your continued patronage, Jeff has delivered yet another terpiece Our family can return to a more normal state Which is what, really? I think maybe normal is the book writing state
mas-Ever patiently awaiting the siren call of yet another book, Kristin Trace (Jeff’s wife)
October 2012
Help! Please save Jeff from the knitting!
Trang 23It was October 1999 when some people at Microsoft first demonstrated the Microsoft
.NET Framework, the common language runtime (CLR), and the C# programming
language to me The moment I saw all of this, I was impressed and I knew that it was
going to change the way I wrote software in a very significant way I was asked to do
some consulting for the team and immediately agreed At first, I thought that the NET
Framework was an abstraction layer over the Win32 API and COM As I invested more
and more of my time into it, however, I realized that it was much bigger In a way, it is
its own operating system It has its own memory manager, its own security system, its
own file loader, its own error handling mechanism, its own application isolation
bound-aries (AppDomains), its own threading models, and more This book explains all these
topics (and more) so that you can effectively design and implement software
applica-tions and components for this platform
It is October 2012 as I write this text, making it 13 years now that I’ve worked with
the NET Framework and C# Over the 13 years, I have built all kinds of applications and,
as a consultant to Microsoft, have contributed quite a bit to the NET Framework itself
As a partner in my own company, Wintellect (http://Wintellect.com), I have worked with
numerous customers to help them design software, debug software, performance-tune
software, and solve issues they have with the NET Framework All these experiences
have really helped me learn the spots that people have trouble with when trying to be
productive with the NET Framework I have tried to sprinkle knowledge from these
experiences through all the topics presented in this book
Who This Book Is For
The purpose of this book is to explain how to develop applications and reusable
classes for the NET Framework Specifically, this means that I intend to explain how
the CLR works and the facilities that it offers I’ll also discuss various parts of the
Framework Class Library (FCL) No book could fully explain the FCL—it contains
liter-ally thousands of types now, and this number continues to grow at an alarming rate
Therefore, here I’m concentrating on the core types that every developer needs to be
aware of And although this book isn’t specifically about Windows Forms, Windows
Presentation Foundation (WPF), Microsoft Silverlight, XML web services, Web Forms,
Microsoft ASP.NET MVC, Windows Store Apps, and so on, the technologies presented
in the book are applicable to all these application types.
Trang 24The book addresses Microsoft Visual Studio 2012, NET Framework 4.5, and sion 5.0 of the C# programming language Because Microsoft tries to maintain a large degree of backward compatibility when releasing a new version of these technologies, many of the things I discuss in this book apply to earlier versions as well All the code samples use the C# programming language as a way to demonstrate the behavior of the various facilities But, because the CLR is usable by many programming languages, the book’s content is still quite applicable for the non-C# programmer
ver-Note You can download the code shown in the book from Wintellect’s
web-site (http://Wintellect.com/Books).
My editors and I have worked hard to bring you the most accurate, up-to-date, in-depth, easy-to-read, painless-to-understand, bug-free information Even with this fantastic team assembled, however, things inevitably slip through the cracks If you find any mistakes in this book (especially bugs) or have some constructive feedback, I would
greatly appreciate it if you would contact me at JeffreyR@Wintellect.com
Acknowledgments
I couldn’t have written this book without the help and technical assistance of many people In particular, I’d like to thank my family The amount of time and effort that goes into writing a book is hard to measure All I know is that I could not have pro-duced this book without the support of my wife, Kristin, and my two sons, Aidan and Grant There were many times when we wanted to spend time together but were un-able to due to book obligations Now that the book project is completed, I really look forward to adventures we will all share together
For this book revision, I truly had some fantastic people helping me Several people
on the NET Framework team (many of whom I consider friends) reviewed chapters and participated in stimulating conversations with me Christophe Nasarre, who I’ve worked with on several book projects, has done just a phenomenal job of verifying my work and making sure that I’d said everything the best way it could possibly be said He has truly had a significant impact on the quality of this book As always, the Microsoft Press team is a pleasure to work with I’d like to extend a special thank you to Ben Ryan, Devon Musgrave, and Carol Dillingham Also, thanks to Susie Carr and Candace Sinclair for their editing and production support
Trang 25Errata & Book Support
We’ve made every effort to ensure the accuracy of this book and its companion
con-tent Any errors that have been reported since this book was published are listed on our
Microsoft Press site at oreilly.com:
We Want to Hear from You
At Microsoft Press, your satisfaction is our top priority, and your feedback our most
valuable asset Please tell us what you think of this book at:
http://www.microsoft.com/learning/booksurvey
The survey is short, and we read every one of your comments and ideas Thanks in
advance for your input!
Stay in Touch
Let’s keep the conversation going! We’re on Twitter: http://twitter.com/MicrosoftPress.
Trang 27PART I
CLR Basics
CHAPTER 1 The CLR's Execution Model 3
CHAPTER 2 Building, Packaging, Deploying, and
Administering Applications and Types .33
CHAPTER 3 Shared Assemblies and Strongly Named
Assemblies .65
Trang 29C H A P T E R 1
The CLR’s Execution Model
In this chapter:
Compiling Source Code into Managed Modules 3
Combining Managed Modules into Assemblies 6
Loading the Common Language Runtime 8
Executing Your Assembly’s Code 11
The Native Code Generator Tool: NGen.exe 19
The Framework Class Library 22
The Common Type System 24
The Common Language Specification 26
Interoperability with Unmanaged Code 30
The Microsoft NET Framework introduces many concepts, technologies, and terms My goal in this
chapter is to give you an overview of how the NET Framework is designed, introduce you to some
of the technologies the framework includes, and define many of the terms you’ll be seeing when you
start using it I’ll also take you through the process of building your source code into an application or
a set of redistributable components (files) that contain types (classes, structures, etc.) and then explain
how your application will execute
Compiling Source Code into Managed Modules
OK, so you’ve decided to use the NET Framework as your development platform Great! Your first
step is to determine what type of application or component you intend to build Let’s just assume that
you’ve completed this minor detail; everything is designed, the specifications are written, and you’re
ready to start development
Now you must decide which programming language to use This task is usually difficult because
different languages offer different capabilities For example, in unmanaged C/C++, you have pretty
low-level control of the system You can manage memory exactly the way you want to, create threads
easily if you need to, and so on Microsoft Visual Basic 6.0, on the other hand, allows you to build UI
applications very rapidly and makes it easy for you to control COM objects and databases
The common language runtime (CLR) is just what its name says it is: a runtime that is usable by
different and varied programming languages The core features of the CLR (such as memory
man-agement, assembly loading, security, exception handling, and thread synchronization) are available
to any and all programming languages that target it—period For example, the runtime uses
Trang 30excep-tions to report errors, so all languages that target the runtime also get errors reported via excepexcep-tions Another example is that the runtime also allows you to create a thread, so any language that targets the runtime can create a thread.
In fact, at runtime, the CLR has no idea which programming language the developer used for the source code This means that you should choose whatever programming language allows you to express your intentions most easily You can develop your code in any programming language you desire as long as the compiler you use to compile your code targets the CLR
So, if what I say is true, what is the advantage of using one programming language over another? Well, I think of compilers as syntax checkers and “correct code” analyzers They examine your source code, ensure that whatever you’ve written makes some sense, and then output code that describes your intention Different programming languages allow you to develop using different syntax Don’t underestimate the value of this choice For mathematical or financial applications, expressing your intentions by using APL syntax can save many days of development time when compared to express-ing the same intention by using Perl syntax, for example
Microsoft has created several language compilers that target the runtime: C++/CLI, C# nounced “C sharp”), Visual Basic, F# (pronounced “F sharp”), Iron Python, Iron Ruby, and an Inter-mediate Language (IL) Assembler In addition to Microsoft, several other companies, colleges, and universities have created compilers that produce code to target the CLR I’m aware of compilers for Ada, APL, Caml, COBOL, Eiffel, Forth, Fortran, Haskell, Lexico, LISP, LOGO, Lua, Mercury, ML, Mon-drian, Oberon, Pascal, Perl, PHP, Prolog, RPG, Scheme, Smalltalk, and Tcl/Tk
(pro-Figure 1-1 shows the process of compiling source code files As the figure shows, you can create source code files written in any programming language that supports the CLR Then you use the cor-responding compiler to check the syntax and analyze the source code Regardless of which compiler
you use, the result is a managed module A managed module is a standard 32-bit Windows portable
executable (PE32) file or a standard 64-bit Windows portable executable (PE32+) file that requires the CLR to execute By the way, managed assemblies always take advantage of Data Execution Prevention (DEP) and Address Space Layout Randomization (ASLR) in Windows; these two features improve the security of your whole system
C#
source code
file(s)
Basicsource codefile(s)
IL source codefile(s)
Managed module(IL and metadata)
FIGURE 1-1 Compiling source code into managed modules
Trang 31Table 1-1 describes the parts of a managed module.
TABLE 1-1 Parts of a Managed Module
PE32 or PE32+ header The standard Windows PE file header, which is similar to the Common Object File Format
(COFF) header If the header uses the PE32 format, the file can run on a 32-bit or 64-bit version of Windows If the header uses the PE32+ format, the file requires a 64-bit ver- sion of Windows to run This header also indicates the type of file: GUI, CUI, or DLL, and contains a time stamp indicating when the file was built For modules that contain only IL code, the bulk of the information in the PE32(+) header is ignored For modules that con- tain native CPU code, this header contains information about the native CPU code CLR header Contains the information (interpreted by the CLR and utilities) that makes this a man-
aged module The header includes the version of the CLR required, some flags, the
MethodDef metadata token of the managed module’s entry point method (Main
method), and the location/size of the module’s metadata, resources, strong name, some flags, and other less interesting stuff.
Metadata Every managed module contains metadata tables There are two main types of tables:
tables that describe the types and members defined in your source code and tables that describe the types and members referenced by your source code.
IL code Code the compiler produced as it compiled the source code At run time, the CLR
com-piles the IL into native CPU instructions.
Native code compilers produce code targeted to a specific CPU architecture, such as x86, x64, or ARM All CLR-compliant compilers produce IL code instead (I’ll go into more detail about IL code
later in this chapter.) IL code is sometimes referred to as managed code because the CLR manages its
execution
In addition to emitting IL, every compiler targeting the CLR is required to emit full metadata into
every managed module In brief, metadata is a set of data tables that describe what is defined in the module, such as types and their members In addition, metadata also has tables indicating what the managed module references, such as imported types and their members Metadata is a superset of older technologies such as COM’s Type Libraries and Interface Definition Language (IDL) files The important thing to note is that CLR metadata is far more complete And, unlike Type Libraries and IDL, metadata is always associated with the file that contains the IL code In fact, the metadata is always embedded in the same EXE/DLL as the code, making it impossible to separate the two Because the compiler produces the metadata and the code at the same time and binds them into the resulting managed module, the metadata and the IL code it describes are never out of sync with one another.Metadata has many uses Here are some of them:
Trang 32■ The CLR’s code verification process uses metadata to ensure that your code performs only
“type-safe” operations (I’ll discuss verification shortly.)
In Chapter 2, “Building, Packaging, Deploying, and Administering Applications and Types,” I’ll scribe metadata in much more detail
de-Microsoft’s C#, Visual Basic, F#, and the IL Assembler always produce modules that contain aged code (IL) and managed data (garbage-collected data types) End users must have the CLR (presently shipping as part of the NET Framework) installed on their machine in order to execute any modules that contain managed code and/or managed data in the same way that they must have the Microsoft Foundation Class (MFC) library or Visual Basic DLLs installed to run MFC or Visual Basic 6.0 applications
man-By default, Microsoft’s C++ compiler builds EXE/DLL modules that contain unmanaged (native) code and manipulate unmanaged data (native memory) at run time These modules don’t require the CLR to execute However, by specifying the /CLR command-line switch, the C++ compiler produces modules that contain managed code, and of course, the CLR must then be installed to execute this code Of all of the Microsoft compilers mentioned, C++ is unique in that it is the only compiler that allows the developer to write both managed and unmanaged code and have it emitted into a single module It is also the only Microsoft compiler that allows developers to define both managed and unmanaged data types in their source code The flexibility provided by Microsoft’s C++ compiler is unparalleled by other compilers because it allows developers to use their existing native C/C++ code from managed code and to start integrating the use of managed types as they see fit
Combining Managed Modules into Assemblies
The CLR doesn’t actually work with modules, it works with assemblies An assembly is an abstract
concept that can be difficult to grasp initially First, an assembly is a logical grouping of one or more modules or resource files Second, an assembly is the smallest unit of reuse, security, and versioning Depending on the choices you make with your compilers or tools, you can produce a single-file or a
multifile assembly In the CLR world, an assembly is what we would call a component.
In Chapter 2, I’ll go over assemblies in great detail, so I don’t want to spend a lot of time on them here All I want to do now is make you aware that there is this extra conceptual notion that offers a way to treat a group of files as a single entity
Figure 1-2 should help explain what assemblies are about In this figure, some managed modules and resource (or data) files are being processed by a tool This tool produces a single PE32(+) file that represents the logical grouping of files What happens is that this PE32(+) file contains a block of data
Trang 33called the manifest The manifest is simply another set of metadata tables These tables describe the
files that make up the assembly, the publicly exported types implemented by the files in the assembly, and the resource or data files that are associated with the assembly
Tool combining multiplemanaged modules andresource files into
an assemblyC# compiler(CSC.exe),Visual Basic compiler(VBC.exe),Assembly Linker(AL.exe)
Assembly(Manifest: describes theset of files in the assembly)
Managed module(IL and metadata)Managed module(IL and metadata)Resource file(.jpeg, gif, html, etc.)Resource file(.jpeg, gif, html, etc.)
(.jpeg, gif, html, etc.)
FIGURE 1-2 Combining managed modules into assemblies
By default, compilers actually do the work of turning the emitted managed module into an bly; that is, the C# compiler emits a managed module that contains a manifest The manifest indicates that the assembly consists of just the one file So, for projects that have just one managed module and no resource (or data) files, the assembly will be the managed module, and you don’t have any ad-ditional steps to perform during your build process If you want to group a set of files into an assem-bly, you’ll have to be aware of more tools (such as the assembly linker, AL.exe) and their command-line options I’ll explain these tools and options in Chapter 2
assem-An assembly allows you to decouple the logical and physical notions of a reusable, securable, versionable component How you partition your code and resources into different files is completely
up to you For example, you could put rarely used types or resources in separate files that are part of
an assembly The separate files could be downloaded on demand from the web as they are needed
at run time If the files are never needed, they’re never downloaded, saving disk space and reducing installation time Assemblies allow you to break up the deployment of the files while still treating all of the files as a single collection
An assembly’s modules also include information about referenced assemblies (including their
version numbers) This information makes an assembly self-describing In other words, the CLR can
determine the assembly’s immediate dependencies in order for code in the assembly to execute
No additional information is required in the registry or in Active Directory Domain Services (AD DS) Because no additional information is needed, deploying assemblies is much easier than deploying unmanaged components
Trang 34Loading the Common Language Runtime
Each assembly you build can be either an executable application or a DLL containing a set of types for use by an executable application Of course, the CLR is responsible for managing the execution
of code contained within these assemblies This means that the NET Framework must be installed
on the host machine Microsoft has created a redistribution package that you can freely ship to install the NET Framework on your customers’ machines Some versions of Windows ship with the NET Framework already installed
You can tell if the NET Framework has been installed by looking for the MSCorEE.dll file in the
%SystemRoot%\System32 directory The existence of this file tells you that the NET Framework is installed However, several versions of the NET Framework can be installed on a single machine simultaneously If you want to determine exactly which versions of the NET Framework are installed, examine the subdirectories under the following directories
In addition, Windows Store applications or class libraries will run on Windows RT machines (which use
an ARM CPU) In other words, the one file will run on any machine that has the corresponding version
of the NET Framework installed on it
On extremely rare occasions, developers want to write code that works only on a specific version
of Windows Developers might do this when using unsafe code or when interoperating with aged code that is targeted to a specific CPU architecture To aid these developers, the C# compiler offers a /platform command-line switch This switch allows you to specify whether the resulting assembly can run on x86 machines running 32-bit Windows versions only, x64 machines running 64-bit Windows only, or ARM machines running 32-bit Windows RT only If you don’t specify a platform, the default is anycpu, which indicates that the resulting assembly can run on any version of Windows Users of Visual Studio can set a project’s target platform by displaying the project’s property pages, clicking the Build tab, and then selecting an option in the Platform Target list (see Figure 1-3)
unman-In Figure 1-3, you’ll notice the Prefer 32-Bit check box This check box is only enabled when form Target is set to Any CPU and if the project type produces an executable If you select the Prefer 32-Bit check box, then Visual Studio spawns the C# compiler specifying the /platform: anycpu32bitpreferred compiler switch This option indicates that the executable should run as a 32-bit
Trang 35Plat-executable even when running on a 64-bit machine If your application doesn’t require the additional memory afforded to a 64-bit process, then this is typically a good way to go because Visual Studio does not support edit-and-continue of x64 applications In addition, 32-bit applications can interop-erate with 32-bit DLLs and COM components should your application require this
FIGURE 1-3 Setting the platform target by using Visual Studio
Depending on the platform switch, the C# compiler will emit an assembly that contains either a PE32 or PE32+ header, and the compiler will also emit the desired CPU architecture (or agnostic) into the header as well Microsoft ships two SDK command-line utilities, DumpBin.exe and CorFlags.exe, that you can use to examine the header information emitted in a managed module by the compiler.When running an executable file, Windows examines this EXE file’s header to determine whether the application requires a 32-bit or 64-bit address space A file with a PE32 header can run with a 32-bit or 64-bit address space, and a file with a PE32+ header requires a 64-bit address space Windows also checks the CPU architecture information embedded inside the header to ensure that it matches the CPU type in the computer Lastly, 64-bit versions of Windows offer a technology that allows 32-bit
Windows applications to run This technology is called WoW64 (for Windows on Windows 64)
Table 1-2 shows two things First, it shows what kind of managed module you get when you specify various /platform command-line switches to the C# compiler Second, it shows how that application will run on various versions of Windows
Trang 36TABLE 1-2 Effects of /platform on Resulting Module and at Run Time
/platform Switch Resulting Managed
anycpu
(the default) PE32/agnostic Runs as a 32-bit application Runs as a 64-bit application Runs as a 32-bit application anycpu32bitpreferred PE32/agnostic Runs as a 32-bit
application Runs as a 32-bit application Runs as a 32-bit application
application Runs as a WoW64 application Doesn’t run
application
After Windows has examined the EXE file’s header to determine whether to create a 32-bit or bit process, Windows loads the x86, x64, or ARM version of MSCorEE.dll into the process’s address space On an x86 or ARM version of Windows, the 32-bit version of MSCorEE.dll can be found in the
64-%SystemRoot%\System32 directory On an x64 version of Windows, the x86 version of MSCorEE.dll can be found in the %SystemRoot%\SysWow64 directory, whereas the 64-bit version can be found
in the %SystemRoot%\System32 directory (for backward compatibility reasons) Then, the process’s primary thread calls a method defined inside MSCorEE.dll This method initializes the CLR, loads the EXE assembly, and then calls its entry point method (Main) At this point, the managed application is
up and running.1
Note Assemblies built by using version 1.0 or 1.1 of Microsoft’s C# compiler contain a
PE32 header and are CPU-architecture agnostic However, at load time, the CLR considers these assemblies to be x86 only For executable files, this improves the likelihood of the application actually working on a 64-bit system because the executable file will load in WoW64, giving the process an environment very similar to what it would have on a 32-bit x86 version of Windows
If an unmanaged application calls the Win32 LoadLibrary function to load a managed assembly, Windows knows to load and initialize the CLR (if not already loaded) in order to process the code contained within the assembly Of course, in this scenario, the process is already up and running, and this may limit the usability of the assembly For example, a managed assembly compiled with the /platform:x86 switch will not be able to load into a 64-bit process at all, whereas an execut-able file compiled with this same switch would have loaded in WoW64 on a computer running a 64-bit version of Windows
1 Your code can query Environment ’s Is64BitOperatingSystem property to determine if it is running on a 64-bit version of Windows Your code can also query Environment ’s Is64BitProcess property to determine if it is running in
a 64-bit address space.
Trang 37Executing Your Assembly’s Code
As mentioned earlier, managed assemblies contain both metadata and IL IL is a CPU-independent machine language created by Microsoft after consultation with several external commercial and academic language/compiler writers IL is a much higher-level language than most CPU machine languages IL can access and manipulate object types and has instructions to create and initialize ob-jects, call virtual methods on objects, and manipulate array elements directly It even has instructions
to throw and catch exceptions for error handling You can think of IL as an object-oriented machine language
Usually, developers will program in a high-level language, such as C#, Visual Basic, or F# The pilers for these high-level languages produce IL However, as any other machine language, IL can be written in assembly language, and Microsoft does provide an IL Assembler, ILAsm.exe Microsoft also provides an IL Disassembler, ILDasm.exe
com-Keep in mind that any high-level language will most likely expose only a subset of the facilities offered by the CLR However, the IL assembly language allows a developer to access all of the CLR’s facilities So, should your programming language of choice hide a facility the CLR offers that you re-ally want to take advantage of, you can choose to write that portion of your code in IL assembly or perhaps another programming language that exposes the CLR feature you seek
The only way for you to know what facilities the CLR offers is to read documentation specific to the CLR itself In this book, I try to concentrate on CLR features and how they are exposed or not exposed
by the C# language I suspect that most other books and articles will present the CLR via a language perspective, and that most developers will come to believe that the CLR offers only what the develop-er’s chosen language exposes As long as your language allows you to accomplish what you’re trying
to get done, this blurred perspective isn’t a bad thing
Important I think this ability to switch programming languages easily with rich
integra-tion between languages is an awesome feature of the CLR Unfortunately, I also believe that developers will often overlook this feature Programming languages such as C# and Visual Basic are excellent languages for performing I/O operations APL is a great language for performing advanced engineering or financial calculations Through the CLR, you can write the I/O portions of your application in C# and then write the engineering calculations part in APL The CLR offers a level of integration between these languages that is unprec-edented and really makes mixed-language programming worthy of consideration for many development projects
To execute a method, its IL must first be converted to native CPU instructions This is the job of the CLR’s JIT (just-in-time) compiler
Figure 1-4 shows what happens the first time a method is called
Just before the Main method executes, the CLR detects all of the types that are referenced by
Main’s code This causes the CLR to allocate an internal data structure that is used to manage access
Trang 38to the referenced types In Figure 1-4, the Main method refers to a single type, Console, causing the CLR to allocate a single internal structure This internal data structure contains an entry for each method defined by the Console type Each entry holds the address where the method’s implemen-tation can be found When initializing this structure, the CLR sets each entry to an internal, undocu-mented function contained inside the CLR itself I call this function JITCompiler.
When Main makes its first call to WriteLine, the JITCompiler function is called The JITCompiler function is responsible for compiling a method’s IL code into native CPU instructions Because the IL is being compiled “just in time,” this component of the CLR is frequently referred
to as a JITter or a JIT compiler.
Note If the application is running on an x86 version of Windows or in WoW64, the JIT
com-piler produces x86 instructions If your application is running as a 64-bit application on an x64 version of Windows, the JIT compiler produces x64 instructions If the application is run-ning on an ARM version of Windows, the JIT compiler produces ARM instructions
static void Main() {
1 In the assembly that implements the type (Console), look up the method (WriteLine) being called in the metadata
2 From the metadata, get the IL for this method
3 Allocate a block of memory
4 Compile the IL into native CPU instructions;
the native code is saved in the memory allocated in step 3
5 Modify the method’s entry in the Type’s table so that it now points to the memory block allocated
Trang 39When called, the JITCompiler function knows what method is being called and what type fines this method The JITCompiler function then searches the defining assembly’s metadata for the called method’s IL JITCompiler next verifies and compiles the IL code into native CPU instructions The native CPU instructions are saved in a dynamically allocated block of memory Then, JITCompiler goes back to the entry for the called method in the type’s internal data structure created by the CLR and replaces the reference that called it in the first place with the address of the block of memory containing the native CPU instructions it just compiled Finally, the JITCompiler function jumps to the code in the memory block This code is the implementation of the WriteLine method (the version that takes a String parameter) When this code returns, it returns to the code in Main, which continues execution as normal.
de-Main now calls WriteLine a second time This time, the code for WriteLine has already been verified and compiled So the call goes directly to the block of memory, skipping the JITCompiler
function entirely After the WriteLine method executes, it returns to Main Figure 1-5 shows what the process looks like when WriteLine is called the second time
Console
JITCompiler
Native CPUinstructions
static void Main() {
2 From the metadata, get the IL for this method
3 Allocate a block of memory
4 Compile the IL into native CPU instructions;
the native code is saved in the memory allocated in step 3
5 Modify the method’s entry in the Type’s table so that it now points to the memory block allocated
in step 3
6 Jump to the native code contained inside the memory block
bly that implements t
alled in thethe metadata, get the IL for this meate a block of mem
pile the IL into native CPU instructionative code is saved in the memoryated in
fy the method’s entry in the Type’s tnow points to the memory block
e native code contained ink
Native
FIGURE 1-5 Calling a method for the second time
Trang 40A performance hit is incurred only the first time a method is called All subsequent calls to the method execute at the full speed of the native code because verification and compilation to native code don’t need to be performed again.
The JIT compiler stores the native CPU instructions in dynamic memory This means that the piled code is discarded when the application terminates So if you run the application again in the future or if you run two instances of the application simultaneously (in two different operating system processes), the JIT compiler will have to compile the IL to native instructions again Depending on the application, this can increase memory consumption significantly compared to a native application whose read-only code pages can be shared by all instances of the running application
com-For most applications, the performance hit incurred by JIT compiling isn’t significant Most tions tend to call the same methods over and over again These methods will take the performance hit only once while the application executes It’s also likely that more time is spent inside the method than calling the method
applica-You should also be aware that the CLR’s JIT compiler optimizes the native code just as the back end
of an unmanaged C++ compiler does Again, it may take more time to produce the optimized code, but the code will execute with much better performance than if it hadn’t been optimized
There are two C# compiler switches that impact code optimization: /optimize and /debug The following table shows the impact these switches have on the quality of the IL code generated by the C# compiler and the quality of the native code generated by the JIT compiler
Compiler Switch Settings C# IL Code Quality JIT Native Code Quality
/optimize /debug
/optimize /debug(+/full/pdbonly) Unoptimized Unoptimized
/optimize+ /debug(/+/full/pdbonly) Optimized Optimized
With /optimize, the unoptimized IL code produced by the C# compiler contains many operation (NOP) instructions and also branches that jump to the next line of code These instructions are emitted to enable the edit-and-continue feature of Visual Studio while debugging and the extra instructions also make code easier to debug by allowing breakpoints to be set on control flow instruc-tions such as for, while, do, if, else, try, catch, and finally statement blocks When produc-ing optimized IL code, the C# compiler will remove these extraneous NOP and branch instructions, making the code harder to single-step through in a debugger because control flow will be optimized Also, some function evaluations may not work when performed inside the debugger However, the
no-IL code is smaller, making the resulting EXE/DLL file smaller, and the no-IL tends to be easier to read for those of you (like me) who enjoy examining the IL to understand what the compiler is producing