Back to description
We learn from failure, not from success!
Bram Stoker’s Dracula
The Microsoft Windows platform has evolved substantially over... more
The Microsoft Windows platform has evolved substantially over time. There have been clear ups and downs along the way, but Microsoft’s platform has generally maintained a leadership position in the industry. The downs have been responsible for birthing the technologies that compose this book’s table of contents. This chapter briefly discusses this inflection point and provides an overview of the architecture of technologies we discuss throughout the book. Chapter 2 begins our exploration with a look at the Common Type System, the foundation on top of which all code on the platform is built.
... less
A type system is a tractable syntactic method for proving the absence of certain program behaviors by classifying phrases... more
A type system is a tractable syntactic method for proving the absence of certain program behaviors by classifying phrases according to the kinds of values they comprise.
Benjamin C. Pierce, Types and Programming Languages
Ultimately, all programs are built from data types. At the core of every language are built-in data types, ways of combining them to form new types, and ways of naming the new types so they can be used like the built-in types.
Jim Miller, The Common Language Infrastructure Annotated Standard
The Common Language Runtime (CLR)or more precisely any implementation of the Common Language Infrastructure (CLI) specificationexecutes code inside the bounds of a well-defined type system, called the Common Type System (CTS). The CTS is part of the CLI, standardized through the ECMA and International Organization for Standardization (ISO) international standards bodies, with representatives from industry and academia. It defines a set of structures and services that programs targeting the CLR may use, including a rich type system for building abstractions out of built-in and custom abstract data-types. In other words, the CTS constitutes the interface between managed programs and the runtime itself.
In addition to being the interface, the CTS introduces a set of rules and axioms that define verifiable type safety. The process of verification categorizes code as either type-safe or -unsafe, the former categorization of which will guarantee safe execution within the engine. Type-safe execution avoids a set of memory corruption risks, which executing unverifiable programs could lead to. The runtime permits execution of such programs, however, leading to great power and flexibility at the risk of encountering corruption and unexpected failures.
This unified type system governs all access, manipulation, and combination of data in memory. It enables static detection and resolution of certain classes of programming errors, a structured way in which to build and reuse abstractions, assistance to compiler authors through a safe and abstract virtual execution system (VES), and a self-description mechanism for programs using rich metadata. Type safety and metadata are two primary platform features that have provided the largest productivity, security, and reliability benefits the platform has to offer. Other factors include runtime services, such as Garbage Collection, and the wealth of APIs that the Framework offers. Each of these will be discussed extensively in future chapters.
Thinking in terms of “pure CTS” is often difficult. Nearly all programmers work with a concrete language, such as C#, VB, C++/CLI, or Python, when writing managed libraries and applications. Languages provide their own unique view of the runtime system, either abstracting away, hiding, or sometimes even exaggerating certain parts in different ways. But they all compile down to the same fundamental set of constructs. This diversity is one reason why the CLR is such a great programming environment and can readily support an array of unique languages. With that said, it can also be a source of challenges when attempting to understand and/or bridge two languages’ unique view over the same underlying type system. This chapter should help to clarify this.
We think in generalities, but we live in details.
Alfred North Whitehead
The Common Language Runtime... more
The Common Language Runtime (CLR) is the virtual execution system responsible for running all managed code. It’s an implementation of the CLIand therefore the CTS (see Chapter 2)and offers a broad set of services, each of which takes a part in the execution of your code. These services include, but are not limited to, automatic memory management using an intelligent Garbage Collected (GC) memory heap, built on top of standard Windows memory facilities; metadata and module conventions to control the discovery, loading, layout, and analysis of managed libraries and programs; a rich exceptions subsystem to enable programs to communicate and respond to failures in structured ways; native and legacy code interoperability, supporting integration with, for example, Win32 and COM code; just-in-time(JIT) compilation of managed code to native, including support for both 32- and 64-bit (x64/AMD64, IA64) architectures; and a sophisticated code identity-based security infrastructure, among other things. Running managed code necessarily involves many moving parts.
Physically, the CLR is little more than a collection of DLLs containing sophisticated algorithms that interact with Windows via calls to various Win32 and COM APIs. Managed programs are just Windows DLLs whose code bootstraps the CLR as part of the Windows executable load sequence. If you examine the native code generated by the JIT, you’ll notice calls into CLR DLLs scattered throughout your code. This, like many things on the CLR, is entirely transparent to your programs, and is easy to take for granted. It sounds pretty simple, doesn’t it? But remember, this is the foundation on top of which all of your code runs. There is a lot of depth here. Having a solid understanding will enable you to become an expert in this technology, write better managed code, and give you an edge up on the next difficult bug you’re trying to troubleshoot.
This chapter of course oversimplifies the runtime system by necessity for explanatory purposes. The CLR is a very complex machine that does some amazing things for your code. We’ll drill into some facets of this machine in this chapter, detailing how the CLR accomplishes many of its tasks and what it means for your code. Many topics, however, such as assemblies and metadata (see Chapter 4), security (see Chapter 9), and unmanaged interoperability (see Chapter 11), are deferred to later chapters. Larger amounts of text are dedicated to them because they are deep topics and because as a managed code developer you will end up working directly or on more frequent occasion with them than many topics. Other topics are left for your own research due to the specialized knowledge and/or incredible depth necessary to form a solid understanding.
Windows itself is basically a set of dynamic-link libraries.
Charles Petzold, Programming Windows
Throughout the history... more
Throughout the history of programming languages and environments, the great has often been separated from the good by the quality of module system used. Managed code has a very rich module system, from which an obvious conclusion can be drawn. When you develop code on the .NET Framework, it is compiled into a format and a unit of execution called an assembly. An assembly can take on the form of an EXE, for standalone, executable programs; or DLL, for libraries on which other programs and libraries may depend. As noted in previous chapters, the CLR integrates with Windows at a number of levels. One such level is the binary format for its programs and libraries. Regardless of whether you create an EXE or DLL, an assembly always follows the Portable Executable/Common Object File Format (PE/COFF, or just PE for short) file format, the common format for binary code on Windows.
The purpose of having a consistent binary format is to facilitate distribution and reuse across other Windows machines. PE files are common to and recognized on all 32-bit Windows platforms. An assembly can be used to package up your program’s reusable types and functions, for example, which can then be shared across an entire organization or sold to other Windows users for money. Your language compiler is responsible for generating an assembly, and usually offers various input switches to control the way in which it does so. For example, the csc.exe C# compiler supplies a range of input options to control the output format. Furthermore, a set of tools (e.g., al.exe) and types in the framework (e.g., System.Reflection.AssemblyVersionAttribute) permit you to add or alter information in the binary image, too.
csc.exe
al.exe
System.Reflection.AssemblyVersionAttribute
Assemblies contain many types of interesting data, such as a strong name to uniquely identify an assembly and enable precise version-based binding, a culture, and a rich set of metadata structures populated based on the code being compiled, for example. This chapter discusses the basics of assemblies, how the runtime loads, resolves, and binds to assemblies and their references, and deployment. We briefly discuss deployment, including how to share assemblies across a machine using the Global Assembly Cache (GAC), a centralized store for system-wide shared assemblies. ClickOnce, a new 2.0 technology that enables secure and simple policy-based deployment and updating, is not detailed in this chapter. Please refer to Further Reading for more information on ClickOnce.
Informally, data is “stuff” that we want to manipulate, and procedures are descriptions of the rules for manipulating the... more
Informally, data is “stuff” that we want to manipulate, and procedures are descriptions of the rules for manipulating the data. Thus, any powerful programming language should be able to describe primitive data and primitive procedures and should have methods for combining and abstracting procedures and data.
Harold Abelson and Gerald Jay Sussman, Structure and Interpretation of Computer Programs
This chapter details the fundamental building blocks you will use when developing managed code. This includes the CLR’s primitive types and others from the Base Class Library (BCL). Primitives compose a technology’s core data structures, including such things as numbers, characters, strings, and Booleans. The ability to work effectively with these and to combine them in powerful ways will be instrumental to your success on the platform. You saw in Chapter 2 how types are formed out of other types. Now we’ll walk through each primitive in detail, focusing on their storage capabilities, how they are represented in the C#, VB, C++, and IL languages, and the operations available on them.
We’ll also take a look at common nonprimitive BCL types, such as common base types, interfaces, utilities, and exceptions. The classes and interfaces covered are common to many other types in the .NET Framework, including some of the primitives themselves. Utility types are simple types that enable you to accomplish very general and fundamental tasks; it’s often hard to write a useful program without using them in some manner. The exception types are used by the Framework to communicate unexpected conditions using the infrastructure described in Chapter 3.
In order to use a computer properly, we need to understand the structural relationships present within data, as well as the... more
In order to use a computer properly, we need to understand the structural relationships present within data, as well as the basic techniques for representing and manipulating such structure within a computer.
Donald E. Knuth, The Art of Computer Programming: Volume 1, Fundamental Algorithms
Lists of information are the simplest possible data structure associating multiple data elements together to form a special relationship. Storing and retrieving data in and from them are some of the most rudimentary and commonplace operations a modern software program must accomplish. The .NET Framework offers numerous data structures for this purpose, from arrays as a first-class citizen in the type system to powerful and extensible list classes, stacks and queues, dictionaries, and more. Many powerful APIs to perform activities like sorting and searching are also available. This chapter presents these types and their capabilities, and provides the knowledge needed to effectively choose between them based on your scenario.
In particular, this chapter is a tour of the more advanced array operations and the types in the System.Collections and System.Collections.Generic namespaces. You will find that the types in these namespaces have been carefully factored into clean class and interface hierarchies, making them powerful, extensible, and most importantly, intuitive to use. With the introduction of generics in the CLR 2.0, the System.Collections namespace will likely not be of great interest to you. We discuss it in this chapter for completeness but focus primarily on the new generic APIs. You’ll recognize many similarities between the two.
System.Collections
System.Collections.Generic
A computer terminal is not some clunky old television with a typewriter in front of it. It is an interface where the mind... more
A computer terminal is not some clunky old television with a typewriter in front of it. It is an interface where the mind and body can connect with the universe and move bits of it about.
Douglas Adams
A distributed system is a collection of independent computers that appears to its users as a single coherent system.
Andrew Tanenbaum and Maarten van Steen Distributed Systems: Principles and Paradigms
Programs need a way of sharing data with other programs and external devices. Without such capabilities, a program would be an entirely isolated black box, never communicating with the outside world. Such an environment might be conducive to solving fixed numerical problems (which never see the light of day), but certainly is not appropriate for 99.999% of the applications built today. The input and output (I/O) libraries of the .NET Framework provide the building blocks using which to break down the walls of an application, facilitating engagement with the outside world.
The Framework’s I/O system is built on top of standard Win32 functionality. Rather than having to work with and manipulate file HANDLEsfor example, via calls to OpenFile, ReadFile, WriteFile, and CloseHandleyou are presented with higher-level abstractions that manage HANDLE manipulation transparently. And these same abstractions can be used to interact with the standard input, output, and error streams, and even the network. This eliminates the need to learn with several sets of programming conventions, as is the case in Win32. For purposes of Visual Basic 6 interoperability, the Microsoft.VisualBasic.FileSystem type (found in the Microsoft.VisualBasic.dll assembly) additionally exposes a set of VB6-like interfaces.
HANDLE
OpenFile
ReadFile
WriteFile
CloseHandle
Microsoft.VisualBasic.FileSystem
Microsoft.VisualBasic.dll
The common abstraction weaved across the System.IO and System.Net namespaces is a stream. This fundamental concept enables you to work with bidirectional sources and destinations of data in a general-purpose fashion. Layered on top of streams are readers and writers, tightly encapsulated classes that wrap streams and present an even higher-level interface for programming them. In addition to the basic idea of streams each use in the Framework will be detailed in this chapter, alongside related file-system and network programming features. Specifically, we will discuss how to read and modify attributes of files and directories, communicate via network sockets, Transmission Control Protocol (TCP), and HyperText Transfer Protocol (HTTP), along with a discussion of sending e-mail with the Simple Mail Transport Protocol (SMTP) APIs.
System.IO
System.Net
Having to retrofit internationalization or scalability is a pain, certainly. The only bigger pain is not needing to, because... more
Having to retrofit internationalization or scalability is a pain, certainly. The only bigger pain is not needing to, because your initial version was too big and rigid to evolve into something users wanted.
Paul Graham
In today’s global business environment, seldom does an application live in isolation. This is true both in terms of heterogeneous software living alongside an application and in terms of the culture and language preferences of its users. In the not-so-distant past, people were willing to put up withand indeed came to expectprograms that communicated using just a single language and with a bias toward a single culture. This decision was often left to the preference of the management and developers responsible for creating the software. And furthermore, in many cases this meant that this communication medium was U.S. English.
Mainstream technology has now progressed to a point where this is not acceptable in business and home environments. Applications are now expected to respect the language and cultural preference of their users, at least those targeted at international markets. The process of doing so is called internationalization and is the topic of this chapter. An internationalized application delivers UI, text, and data content in a culture- and language-aware manner.
Internationalizing your application can lead to substantial competitive advantage for a company, open up new markets that would have otherwise have been excluded by a single-language approach, and, in other cases, might simply fulfill a requirement so that global corporations can share the same software infrastructure across worldwide satellite offices. If none of these are concerns for you, then the simple fact that internationalization provides a way to create more familiar and usable interfaces for your users might be justification enough. Just to be entirely clear, however: Internationalization is a costly endeavor and should not be undertaken without understanding your users’ scenarios and the need for it.
Never underestimate the time, expense, and effort an opponent will expend to break a code.
Robert Morris... more
Robert Morris
Writing secure code is extremely important in today’s highly connected world. With dynamic features that load and execute code from the Internet, a never-ending supply of third-party components, and a generally increasingly synthesized execution environment, seldom is a piece of software run in perfect isolation. As we move further in this direction, you can never be too paranoid about rogue code gaining the ability to do things you didn’t want it to.
As an example of one such attack vector you might be concerned above, Internet Explorer can be used to host managed code controls on a web page. Clearly if that code isn’t run with some sort of security isolation, it would be able to do some nasty things to the end user. (Like format the disk, for example.) Similarly, you might be an ISV writing a sophisticated and complex application that hosts extensible add-ins. If those add-ins were able to compromise the integrity of your host application, your own customers might become the target of nasty security attacks. This could cause a quick outbound deluge of trust in you as a software vendor (and probably market share). Code access security (CAS) enables the style of sandboxing necessary for these types of programs.
Security is, of course, everybody’s responsibility. But your interest in it is likely to vary depending on what type of software you are writing. For example, an ISV shipping a host application as described above is apt to be significantly more concerned when compared to a small-business IT shop developing a tiny application for 10 users. A vendor of a reusable Framework will want to ensure components work well inside a sandbox, so that they can be used inside of that ISV’s sandboxed application, for example. But if you’re an enterprise developer, your focus will likely shift from code access security to other topics like accessing Windows authentication and role-based security information, and figuring out how to integrate that information across the network. My hope is that this chapter has enough of each of those topics to be of interest to a broad range of software architects and developers.
We’ll begin the chapter with a tour of the CLR’s native integral security sandboxing mechanism, CAS. CAS employs a different principal than most developers are accustomed to, namely that it governs permissions based on the identity of software and not necessarily its user. Notice the key shift here: it’s not the person running the software that needs to be trusted; it’s the piece of software. Managed code running in a sandboxed environment (called partially trusted code) is monitored by the runtime to ensure that any privileged operations it attempts are carefully monitored. Such actions are permitted or denied based on configuration.
We’ll also take a look at how to access more traditional authentication and authorization information in this chapter, and how you might use it to implement or work with role-based security systems. We’ll also take a look at how Windows Access Control List (ACL) security is now (as of 2.0) exposed in a richer way throughout many of the Base Class Libraries. This affects various IO and Windows kernel object managed APIs.
[I]f you want your application to benefit from the continued exponential throughput advances in new processors, it will need... more
[I]f you want your application to benefit from the continued exponential throughput advances in new processors, it will need to be a well-written concurrent (usually multi-threaded) application.
Herb Sutter
Threading, or more generally concurrency and parallelism, offers a way to execute multiple units of code simultaneously. The Windows platform has evolved over time from offering only sequential execution (e.g., in MS-DOS with quasi-multitasking in the form of terminate and stay resident programs [TSRs]), to cooperative multitasking in 16-bit Windows, to real multi-threading in 32-bit Windows. Threads are OS primitives that enable you to partition logically independent tasks which are then scheduled for execution by the OS in a manner that maximizes utilization of physical resources. On real parallel machinese.g., multiprocessor, multi-core, and/or hyper-threaded (HT) architecturesthis can result in code executing genuinely simultaneously. On other machines, the OS scheduling algorithm simulates parallel execution by constantly switching between runnable threads after permitting each to run for a finite time-slice. At the most fundamental level, this enables multiple processes to be running at once; this is because each process uses at least one thread. Drilling deeper, individual processes are able to partition work into multiple threads of execution.
COM is Love.
Don Box
I have a feeling that very little software written today, except for some FORTRAN programs, will be in... more
I have a feeling that very little software written today, except for some FORTRAN programs, will be in use anywhere near AD 3400, but I have faith that Windows 2000 should have shipped by that time.
Dale Rogerson
If it isn’t evident by now, the term managed code refers to any code that executes under the control of the CLR. In other words, things which begin life as C#, VB, C++/CLI source (or one of the many other languages available) and whose compiler transforms the high-level code that you write into metadata and IL. Of course, the CLR’s JIT then turns this into its native equivalent, making use of several managed services such as the GC and security along the way. Unmanaged code, on the other hand, does not have such a rich lifetime. It can indeed execute in some form of structured container or management infrastructureCOM is a great example of thatalthough the two worlds are generally quite different and distinct. Regardless of how great we like to think managed code is, one fact still remains: a lot of code has been written using unmanaged technologies such as C++, Win32, and COM, and is still living, breathing, and doing quite well out there in the world.
It’s not a very cost-effective proposition to spend months (or years) on a monolithic rewrite project to port an entire application from unmanaged to managed code. These are usually those massively costly projects that you always hear about failing after months and months of hard work, costly checks, and early warning signs that perhaps the project was doomed from the beginning. Yes, old code is old, is perhaps difficult to extend, and in some cases requires specialized expertise to maintain. But such codebases have probably survived through years of bug fixes and maintenance. In other words, it’s mature. It’d be a shame for your company to blindly throw away and not leverage those existing assets.
Furthermore, some applications are better suited to using unmanaged code for some components, especially those with demanding memory management requirements. For example, most high-end games need to manage memory very tightly to ensure good frame rates; many scientific applications similarly must work with raw memory segments due to the sheer magnitude of maps, reduction, and massive memory calculations and manipulation.
But at the same time, other components of these same programs would benefit greatly from managed code. Games have large amounts of AI and character-scripting components; the authors of that style of logic probably don’t care much about memory management, and hence a Garbage Collected execution environment and a managed scripting language would make their lives much simpler. Similarly, a scientific application might use the UI for data visualization; the complex calculations in the background might utilize unmanaged code, while Windows Presentation Foundation, WinForms, or ASP.NET could be used to develop the UI and input components. (They sure beat MFC.)
Fortunately, the .NET Framework enables bidirectional interoperability with C++, Win32, and COM. This means that you can continue to leverage those existing valuable assets while still investing in the future and building on top of a rock solid managed execution environment. You might be surprised at the level of integration the runtime supports. In fact, the designers and architects of the CLR knew up front that enabling existing customer code to continue running was very important. Web services are yet another technique for interoperating with legacy code, albeit one strategy not discussed in this book. This chapter takes a look at some tools, technologies, and techniques necessary to take advantage of the rich unmanaged interoperability found in the CLR and .NET Framework.
The .NET Framework offers an incredibly extensible tracing infrastructure, all the way from basic asserts to industrial strength... more
The .NET Framework offers an incredibly extensible tracing infrastructure, all the way from basic asserts to industrial strength event logging. This chapter will take a look at what trace functionality is available out of the box and what you can build on your own. We’ll also see how to filter, redirect, and customize the tracing infrastructure without touching a line of code, via the powerful configuration subsystem. Writing and reading messages to and from the Windows Event Log, for instance, is as simple as adding a few lines to your configuration file.
Although we don’t venture beyond it in great detail, tracing is certainly not the extent of the .NET Framework’s support for diagnostics:
Performance counters can be used to report back quantitative metrics about what’s going on inside your program and can be instrumental in identifying bottlenecks, leaks, or opportunities for fine-tuning. In fact, the CLR itself is constantly reporting back many interesting metrics that you can view and analyze using existing tools. You can even create custom performance counters to report information specific to your application.
The Windows Event Log is useful for nondebug tracing and error information, both for you while debugging your own program and or system administrators managing deployed applications. The tracing infrastructure surfaces it as an easy choice of where to log trace output, but there is also a feature-rich set of APIs with which you can directly interact with the Event Log.
Lastly, a set of tools have support built right into the runtime, enabling you to debug and profile your application as it is running (you can even modify its contents with the new 2.0 Edit & Continue feature). The runtime itself also publishes events for common problems, a feature also new to 2.0 called Managed Debugging Assistants (MDAs for short).
Making intelligent use of the tracing facilities available can help to reduce the number of commented out Console.WriteLines scattered throughout your program. With some of the improvements in version 2.0, the tracing subsystem is more approachable and easier to use than ever before.
Console.WriteLine
[R]egular expression culture is a mess, and I share some of the blame for making it that way. Since my mother always told... more
[R]egular expression culture is a mess, and I share some of the blame for making it that way. Since my mother always told me to clean up my own messes, I suppose I'll have to do just that.
Larry Wall, Apocalypse 5 (Perl 6)
Regular expressions provide an extremely powerful, concise, and expressive way to work with and process text. Processing text might sound a tad boring, but this task has historically been and continues to be a fundamental requirement of administrative scripts or utility applications. Often, one’s ability to hack together a simple yet useful program depends entirely on one’s ability to effectively crunch and operate on textual input. UNIX and the Perl language, for example, have pioneered the use of regular expressions for decades proving the technology solid and making it ubiquitous.
This chapter will walk you through the primary facets of the regular expression syntax and demonstrate the rich support available in the .NET Framework. All of the types we will work with in this chapter come from the System.Text.RegularExpressions namespace, found in the System.dll assembly. What you will learn about the syntax of regular expressions will be readily transferable to other technologies. In fact, that’s one of the great things about the technology: it’s everywhere (and you may not even realize it until you learn more about it).
System.Text.RegularExpressions
System.dll
Regular expression syntax is relatively compact. The ability to combine the simple pieces of the language together using powerful means of combination is where the technology really shines. Unfortunately, writability and readability are sometimes at odds with one another. The result can be some very-difficult-to-maintain expressions, especially for those not as proficient with regular expressions as the author was. But once you learn the fundamentals, it becomes relatively easy to break expressions up into their smaller building blocks, which themselves are easier to understand. Nonetheless, it’s not uncommon to crack open a source file in which you wrote an overly clever regular expression six months ago and be dumbfounded for the first 30 minutes of trying to figure out what it does. (Especially if there’s a subtle bug in it.)
We need a language that lets us scribble and smudge and smear, not a language where you have to sit with a teacup of types... more
We need a language that lets us scribble and smudge and smear, not a language where you have to sit with a teacup of types balanced on your knee and make polite conversation with a strict old aunt of a compiler.
Paul Graham, Hackers and Painters
Most of the topics we’ve covered in previous chapters took a look at the CLR and its type system from the standpoint of code written in a high-level language (e.g., C#, VB, C++/CLI) and compiled to IL. Such code interacts statically with the CLR, meaning that compilers decide during compilation (using early binding) what types your program will work with and what code will get executed. Conversely, dynamic code defers such decisions to runtime (using techniques like late binding), enabling programs to use information only available at runtime to make key decisions like which types to use and which code to execute. This can even include some degree of dynamic code generation.
Some constructs built right into the CLR’s type system fall in between the two categories. For example, virtual methods delay until runtime the decision of precisely which code to execute by using an object identity-based virtual table lookup. Even JIT compilation can be considered a form of dynamism because the actual native code that gets executed isn’t generated until runtime (except in the case of NGen). But in addition to those, there is a set of libraries available that enables you to take it a step further by manipulating metadata, instantiating types dynamically, executing bits of IL, and even generating new metadata to be saved to disk or executed. This interaction occurs through runtime API interactions with CLR data structures, and not by statically emitted IL. This is called reflection.
Reflection provides a means by which to extend your programs in a fully dynamic way. This capability is useful for writing metadata-driven harnesses, creating object model inspection or reporting utilities, generating or executing code using some program input (e.g., compilers or configuration-based type generation), creating applications that make runtime decisions about what code to load and runfor example hosting add-ins or plug-insamong many other uses. The reflection system generally consists of two pillars, each isolated into its own namespace: System.Reflection and System.Reflection.Emit:
System.Reflection
System.Reflection.Emit
System.Reflection enables you to inspect metadata, while System.Reflection.Emit enables you to generate it. An extended feature of the reflection library, delegates, permits you to form typed function pointers over code and then pass instances around in a first-class way. It has direct support built into the runtime and offers both static fast path and fully dynamic capabilities.
System.Reflection.Emit provides abstractions that actually generate metadata and code at runtime. You can generate new types, type members, IL, new assemblies, and so forth. This information can then be instantiated for use immediately or saved to disk as a DLL or EXE for later use. The library can also emit debug symbols so that any code that you generate gets the ability to step through execution in a debugger for free. A new feature in version 2.0 called Lightweight Code Generation (LCG) allows for dynamic and quick creation of new functions for immediate execution.
As with anything, we’ll take a look at how to use these constructs, but more importantly at how to select the right tool for the job. Dynamic programming can be several orders of magnitude slower than static programming because you delay until runtime many decisions that would have otherwise been made at compile time. Hence, using them incorrectly can have a notable impact on your application performance. Dynamic programming is extremely powerful, on the other hand, and is made possible because of the CLR’s fully self-descriptive metadata system. When used correctly, it can enable you to write code that you never even thought was possible.
The transaction notion is not a panacea. Rather, it is a convenience for a general class of applications. There are probably... more
The transaction notion is not a panacea. Rather, it is a convenience for a general class of applications. There are probably many applications which will be developed only when the application programmer can be relieved of concerns about failures, concurrency, location, and replication.
Jim Gray
Software operations are composed of several independent steps. Although a single method call, for instance, is viewed as a logically distinct unit, this is in the eye of the caller. A single method might just execute a single SQL UPDATE statement over 1000 rows that must be applied by the database atomically. On the other hand, however, another method might need to execute a SQL UPDATE statement, manipulate state on a shared COM+ component, and initiate web service message that results in some middle-tier processing, as a single atomic operation. The former is reasonably simple, while the second is trickier. System.Transactions supports both.
UPDATE
System.Transactions
This appendix lists the entire set of IL instructions in the CIL and MSIL instruction sets. Chapters 2 and 3 describe nearly... more
This appendix lists the entire set of IL instructions in the CIL and MSIL instruction sets. Chapters 2 and 3 describe nearly all of the facets discussed below in detail and with more context. Metadata, defs, refs, and specs, and the assembly file format are all discussed in Chapter 4. Lastly, Chapter 5 describes the base classes referred to by many instructions, such as int32, int64, float32, and native int. It’s recommended that you use those chapters to decipher any information that lacks sufficient context in the following reference information.
int32
int64
float32
native int
Purchase Before purchasing this product, please be sure you have met all software and system requirements, and that you understand any limits placed upon its use.
Return Policy Wrox Chapters on Demand are non-returnable and non-refundable.
Watermarking Wrox Chapters on Demand are sold with a small unique watermark at the bottom of each page identifying the purchaser name, email address, and order number.
Reader Software Wrox Chapters on Demand are offered as PDFs, and they can be viewed using the Adobe Reader, ADE, or a compatible PDF reader. If you do not have the Reader installed, it can be downloaded for free at Adobe.com.
Test Download As Wrox Chapters on Demand purchases are non-returnable, it is advisable that you test your system and software configurations with a free sample download before you place an order.
Usage Rights for a Wrox Chapters on Demand File Any Wrox Chapters on Demand product you purchase from this site will come with certain restrictions that allow Wiley to protect the copyrights of its products. After you purchase and download this title, you:
If you have any questions about these restrictions or need any further assistance please refer to Technical Support (www.wiley.com/techsupport) or call (877) 762-2974 (8 a.m. - 5 p.m. EST, Monday - Friday).
Related Books