|
|
|
|
Pro VB 2008 and the .NET 3.5 Platform
Last Updated 2/3/2009 3:43:09 PM
This chapter provides you with an overview of the functionality supplied by the .NET
base class libraries, sometimes abbreviated as the BCL or alternatively as the FCL (being the
Framework class libraries).
'
Every few years or so, the modern-day programmer must be willing to perform a self-inflicted
knowledge transplant to stay current with the new technologies of the day. The languages (C++,
Visual Basic 6.0, Java), frameworks (OWL, MFC, ATL, STL), architectures (COM, CORBA, EJB), and
APIs (including .NET's Windows Forms and GDI+ libraries) that were touted as the silver bullets of
software development eventually become overshadowed by something better or at the very least
something new. Regardless of the frustration you can feel when upgrading your internal knowledge
base, it is frankly unavoidable. To this end, this book will examine the details of Microsoft's current
offering within the landscape of software engineering: the .NET platform and the latest version of
the Visual Basic programming language.
The point of this chapter is to lay the conceptual groundwork for the remainder of the book. It
begins with a high-level discussion of a number of .NET-related topics such as assemblies, the common intermediate language (CIL), and just-in-time (JIT) compilation. In addition to previewing
some key features of the Visual Basic 2008 programming language, you will also come to understand the relationship between various aspects of the .NET Framework, such as the common language runtime (CLR), the Common Type System (CTS), and the Common Language Specifica-
tion (CLS).
This chapter also provides you with an overview of the functionality supplied by the .NET
base class libraries, sometimes abbreviated as the BCL or alternatively as the FCL (being the
Framework class libraries). Finally, this chapter investigates the language-agnostic and platform-
independent nature of the .NET platform (yes it's true! .NET is not confined to the Windows family
of operating systems). As you would hope, a majority of these topics are explored in much more
detail throughout the remainder of this text.
UNDERSTANDING THE PREVIOUS STATE OF AFFAIRS
Before examining the specifics of the .NET universe, it's helpful to consider some of the issues that
motivated the genesis of Microsoft's current platform. To get in the proper mind-set, let's begin this
chapter with a brief and painless history lesson to remember our roots and understand the limitations of the previous state of affairs. After completing this quick tour of life as we knew it, we turn
our attention to the numerous benefits provided by Visual Basic 2008 and the .NET platform.
Life As a C/Win32 API Programmer
Traditionally speaking, developing software for the Windows family of operating systems involved
using the C programming language in conjunction with the Windows application programming
interface (API) and the Windows Software Development Kit (SDK). While it is true that numerous
applications have been successfully created using this time-honored approach, few of us would disagree that building applications using the raw API/SDK is a complex undertaking.
The first obvious problem is that C is a very terse language. C developers are forced to contend
with manual memory management, complex pointer arithmetic, and ugly syntactical constructs.
Furthermore, given that C is a procedural language, it lacks the benefits provided by the object-oriented approach. When you combine the thousands of global functions and data types defined by
the Windows API to an already formidable language, it is little wonder that there are so many buggy
applications floating around today.
Life As a C++/MFC Programmer
One vast improvement over raw C/API development is the use of the C++ programming language.
In many ways, C++ can be thought of as an object-oriented layer on top of C. Thus, even though
C++ programmers benefit from the famed "pillars of OOP" (encapsulation, inheritance, and polymorphism), they are still at the mercy of the painful aspects of the C language (e.g., manual memory
management, complex pointer arithmetic, and ugly syntactical constructs).
Despite its complexity, many C++ frameworks exist today. For example, the Microsoft Foundation Classes (MFC) provides the developer with a set of C++ classes that simplifies the construction
of Windows applications. The main role of MFC is to wrap a "sane subset" of the raw Windows API
behind a number of classes and numerous code-generation tools (aka wizards). Regardless of the
helpful assistance offered by the MFC framework (as well as many other C++-based toolkits), the
fact of the matter is that C++ programming remains a difficult and error-prone experience, given its
historical roots in C.
Life As a Visual Basic 6.0 Programmer
Due to a heartfelt desire to enjoy a simpler lifestyle, many programmers avoided the world of C(++)-
based frameworks altogether in favor of kinder, gentler languages such as Visual Basic 6.0 (VB6).
VB6 is popular due to its ability to build sophisticated graphical user interfaces (GUIs), code
libraries (e.g., ActiveX servers), and data access logic with minimal fuss and bother. Much more
than MFC, VB6 hides the complexities of the raw Windows API from view using a number of integrated programming wizards, intrinsic data types, classes, and VB6-specific functions.
The major limitation of VB6 (which has been rectified given the advent of the .NET platform) is
that it is not a fully object-oriented language; rather, it is "object aware." For example, VB6 does not
allow the programmer to establish "is-a" relationships between types (i.e., no classical inheritance)
and has no intrinsic support for parameterized class construction. Moreover, VB6 doesn't provide
the ability to build multithreaded applications unless you are willing to drop down to low-level
Windows API calls (which is complex at best and dangerous at worst).
Life As a Java/J2EE Programmer
Enter Java. Java is an object-oriented programming language that has its syntactic roots in C++. As
many of you are aware, Java's strengths are far greater than its support for platform independence.
Java (as a language) cleans up many unsavory syntactical aspects of C++. Java (as a platform) provides programmers with a large number of predefined "packages" that contain various type
definitions. Using these types, Java programmers are able to build "100% Pure Java" applications
complete with database connectivity, messaging support, web-enabled front ends, and a rich desktop user interface (UI).
Although Java is a very elegant language, one potential problem is that using Java typically
means that you must use Java front-to-back during the development cycle. In effect, Java offers little
hope of language integration, as this goes against the grain of Java's primary goal (a single programming language for every need). In reality, however, there are millions of lines of existing code out
there in the world that would ideally like to commingle with newer Java code. Sadly, Java makes this
task problematic.
Pure Java is often not appropriate for many graphically or numerically intensive applications
(in these cases, you may find Java's execution speed leaves something to be desired). A better
approach for such programs would be to use a lower-level language (such as C++) where appropriate. Again, while Java does provide a limited ability to access non-Java APIs, there is little support
for true cross-language integration.
Life As a COM Programmer
The Component Object Model (COM) was Microsoft's first attempt at a unified component framework. COM is an architecture that says in effect, "If you build your classes in accordance with the
rules of COM, you end up with a block of reusable binary code."
The beauty of a binary COM server is that it can be accessed in a language-independent manner. Thus, VB6 programmers can build COM classes that can be used by C++ programs. Delphi
programmers can use COM classes built using C, and so forth. However, as you may be aware,
COM's language independence is somewhat limited. For example, there is no way to derive a new
COM class using an existing COM class (as COM has no support for classical inheritance).
Another benefit of COM is its location-transparent nature. Using constructs such as applica-
tion identifiers (AppIDs), stubs, proxies, and the COM runtime environment, programmers can
avoid the need to work with raw sockets, manual remote procedure calls, and other low-level
details. For example, consider the following VB6 COM client code:
' The MyCOMClass type could be written in
' any COM-aware language, and may be located anywhere
' on the network (including your local machine).
Dim myObj As MyCOMClass
Set myObj = New MyCOMClass ' Location resolved using AppID.
myObj.DoSomeWork
Although COM can be considered a very successful object model, it is extremely complex
under the hood. To help simplify the development of COM binaries, numerous COM-aware
frameworks have come into existence (most notably VB6). However, framework support alone
is not enough to hide the complexity of COM. Even when you choose a relatively simply COM-
aware language such as VB6, you are still forced to contend with fragile registration entries and
numerous deployment-related issues (collectively termed DLL hell).
Life As a Windows DNA Programmer
To further complicate matters, there is a little thing called the Internet. Over the last several years,
Microsoft has been adding more Internet-aware features into its family of operating systems and
products. Sadly, building a web application using COM-based Windows Distributed interNet Applications Architecture (DNA) is also quite complex.
Some of this complexity is due to the simple fact that Windows DNA requires the use of numerous technologies and languages (ASP, HTML, XML, JavaScript, VBScript, COM(+), as well as a data
access API such as ADO). One problem is that many of these technologies are completely unrelated
from a syntactic point of view. For example, JavaScript has a syntax much like C, while VBScript is a
subset of VB6. The COM servers that are created to run under the COM+ runtime have an entirely
different look and feel from the ASP pages that invoke them. The result is a highly confused mishmash of technologies.
Furthermore, and perhaps more important, each language and/or technology has its own type
system (that may look nothing like another's type system). Beyond the fact that each API ships with
its own collection of prefabricated code, even basic data types cannot always be treated identically.
A BSTR in C++ is not quite the same as a String in VB6, both of which have very little to do with a
char* in C.
THE .NET SOLUTION
So much for the brief history lesson. The bottom line is that life as a Windows programmer has been
less than perfect. The .NET Framework is a rather radical and brute-force approach to streamlining
the application development process. The solution proposed by .NET is "Change everything" (sorry,
you can't blame the messenger for the message). As you will see during the remainder of this book,
the .NET Framework is a completely new model for building systems on the Windows family of
operating systems, as well as on numerous non-Microsoft operating systems such as Mac OS X and
various Unix/Linux distributions. To set the stage, here is a quick rundown of some core features
provided courtesy of .NET:
- Comprehensive interoperability with existing code: This is (of course) a good thing. ActiveX
components can commingle (i.e., interop) with newer .NET applications and vice versa.
Also, Platform Invocation Services (PInvoke) allows you to call C-based libraries (including
the underlying API of the operating system) from .NET code.
- Integration among .NET programming languages: .NET supports cross-language inheritance,
cross-language error handling, and cross-language debugging of code.
- A common runtime engine shared by all .NET-aware languages: One aspect of this engine is a
well-defined type system that each .NET-aware language "understands."
- A comprehensive base class library: This library provides shelter from the complexities of raw
Windows API calls and offers a consistent object model used by all .NET-aware languages.
- No more COM plumbing: Legacy COM interfaces (such as IUnknown and IDispatch), COM
type libraries, and the COM-centric Variant data type have no place in a native .NET binary.
- A truly simplified deployment model: Under .NET, there is no need to register a binary unit
into the system registry. Furthermore, .NET allows multiple versions of the same *.dll to
exist in harmony on a single machine.
As you can most likely gather from the previous bullet points, the .NET platform has nothing to
do with COM (beyond the fact that both frameworks originated from Microsoft). In fact, the only
way .NET and COM types can interact with each other is using the interoperability layer.
NOTE:
Coverage of the .NET interoperability layer can be found in Chapter 19.
|
INTRODUCING THE BUILDING BLOCKS OF THE .NET PLATFORM (THE CLR, CTS, AND CLS)
Now that you know some of the benefits provided by .NET, let's preview three key (and interrelated)
entities that make it all possible: the CLR, CTS, and CLS. From a programmer's point of view, .NET
can be understood as a new runtime environment and a comprehensive base class library. The runtime layer is properly referred to as the common language runtime, or CLR. The primary role of the
CLR is to locate, load, and manage .NET types on your behalf. The CLR also takes care of a number
of low-level details such as memory management, loading external libraries, and performing security checks.
Another building block of the .NET platform is the Common Type System, or CTS. The CTS
specification fully describes the underlying type system and programming constructs supported by
the runtime, specifies how these entities can interact with each other, and details how they are represented in the .NET metadata format (more information on metadata later in this chapter).
Understand that a given .NET-aware language might not support each and every feature
defined by the CTS. The Common Language Specification (CLS) is a related specification that
defines a subset of common types and programming constructs that all .NET programming languages can agree on. Thus, if you build .NET types that only expose CLS-compliant features, you
can rest assured that all .NET-aware languages can consume them. Conversely, if you make use of a
data type or programming construct that is outside of the bounds of the CLS, you cannot guarantee
that every .NET programming language can interact with your .NET code library.
The Role of the Base Class Libraries
In addition to the CLR and CTS/CLS specifications, the .NET platform provides a base class library
that is available to all .NET programming languages. Not only does this base class library encapsulate various primitives such as threads, file input/output (I/O), graphical rendering, and interaction
with various external hardware devices, but it also provides support for a number of services
required by most real-world applications.
For example, the base class libraries define types that facilitate database access, XML document manipulation, programmatic security, and the construction of web-enabled (as well as
traditional desktop and console-based) front ends. From a high level, you can visualize the relation-
ship between the CLR, CTS, CLS, and the base class library, as shown in Figure 1-1.
WHAT VISUAL BASIC 2008 BRINGS TO THE TABLE
Because .NET is such a radical departure from previous Microsoft technologies, it should be clear
that legacy COM-based languages such as VB6 are unable to directly integrate with the .NET platform. Given this fact, Microsoft introduced a brand-new programming language, Visual Basic .NET
(VB .NET), with the release of .NET 1.0. As developers quickly learned, although VB .NET had a similar look and feel to VB6, it introduced such a large number of new keywords and constructs that
many programmers (including myself ) eventually regarded VB .NET as a new member of the BASIC
family rather than "Visual Basic 7.0."
For example, unlike VB6, VB .NET provided developers with a full-blown object-oriented language that is just as powerful as languages such as C++, Java, or C#. For example, using VB .NET,
developers are able to build multithreaded desktop applications, websites, and XML web services;
define custom class construction subroutines; overload members; and define callback functions
(via delegates). In a nutshell, here are some of the core features provided courtesy of VB .NET:
- Full support for classical inheritance and classical polymorphism.
- Strongly typed keywords to define classes, structures, enumerations, delegates, and interfaces. Given these new keywords, VB .NET code is always contained within a *.vb file (in
contrast to the VB6-centric *.cls, *.bas, and *.frm files).
- Full support for interface-based programming techniques.
- Full support for attribute-based programming. This brand of development allows you to
annotate types and their members to further qualify their behavior (more details in
Chapter 16).
With the release of .NET 2.0, the VB .NET programming language was referred to as Visual
Basic 2005 (VB 2005). While VB 2005 is fully backward-compatible with VB .NET, it added numerous
new additional bells and whistles, most notability the following:
- The ability to redefine how intrinsic operators of the language (such as the + symbol) can be
interpreted by your custom classes or structures. Formally speaking, this feature is termed
operator overloading.
- The introduction of the My namespace. This namespace provides instant access to machine-
and project-specific information (which greatly reduces the amount of code you need to
author manually).
- The ability to build generic types and generic members. Using generics, you are able to build
very efficient and type-safe code that defines numerous "placeholders" specified at the time
you interact with the generic item.
- The ability to customize the process of registering, unregistering, or sending events using the
Custom keyword.
- Support for signed data types (SByte, ULong, etc.).
- The ability to define a single type across multiple code files using the Partial keyword.
As you might guess, .NET 3.5 adds even more functionality to the Visual Basic programming
language (now officially named Visual Basic 2008), including the following core features:
- Support for strongly typed query statements (' la LINQ'Language Integrated Query), which
can interact with a variety of data stores (databases, XML documents, collection objects)
- Support for anonymous types, which allow you quickly model the "shape" of a type rather
than its behavior
- The ability to extend the functionality of a compiled type using extension methods
- Support for lambda expressions, which greatly simplify how we can work with .NET delegate
types
- A new object initialization syntax, which allows you to set property values at the time of
object creation
Perhaps the most important point to understand about Visual Basic 2008 is that it can only
produce code that can execute within the .NET runtime (therefore, you could never use VB 2008 to
build a native ActiveX COM server). Officially speaking, the term used to describe the code targeting
the .NET runtime is managed code. The binary unit that contains the managed code is termed an
assembly (more details on assemblies in just a bit). Conversely, code that cannot be directly hosted
by the .NET runtime is termed unmanaged code.
ADDITIONAL .NET-AWARE PROGRAMMING LANGUAGES
Understand that Visual Basic 2008 is not the only language that can be used to build .NET applications. When the .NET platform was first revealed to the general public during the 2000 Microsoft
Professional Developers Conference (PDC), several vendors announced they were busy building
.NET-aware versions of their respective compilers.
At the time of this writing, dozens of different languages have undergone .NET enlightenment.
In addition to the five languages that ship with Visual Studio 2008 (Visual Basic 2008, C#, J#, C++/CLI,
and JScript .NET), there are .NET compilers for Smalltalk, COBOL, and Pascal (to name a few).
Although this book focuses (almost) exclusively on Visual Basic 2008, be aware of the following
website (please note that this URL is subject to change):
http://www.dotnetlanguages.net
Once you have navigated to this page, click the Resources link located on the page's topmost
menu system. Here you will find a list of numerous .NET programming languages and related links
where you are able to download various compilers (see Figure 1-2).
While I assume you are primarily interested in building .NET programs using the syntax of
VB 2008, I encourage you to visit this site, as you are sure to find many .NET languages worth investigating at your leisure (LISP .NET, anyone?).
Life in a Multilanguage World
As developers first come to understand the language-agnostic nature of .NET, numerous questions
arise. The most prevalent of these questions would have to be, "If all .NET languages compile down
to "managed code," why do we need more than one compiler?" There are a number of ways to
answer this question. First, we programmers are a very particular lot when it comes to our choice
of programming language (myself included). Some prefer languages full of semicolons and curly
brackets, with as few keywords as possible (such as C#, C++, and J#). Others enjoy a language that
offers more "human-readable" syntax (such as Visual Basic 2008). Still others may want to leverage
their mainframe skills while moving to the .NET platform (via COBOL .NET).
Now, be honest. If Microsoft were to build a single "official" .NET language that was derived
from the C family of languages, can you really say all programmers would be happy with this
choice? Or, if the only "official" .NET language was based on Fortran syntax, imagine all the folks
out there who would ignore .NET altogether. Because the .NET runtime couldn't care less which
language was used to build an assembly, .NET programmers can stay true to their syntactic preferences, and share the compiled code among teammates, departments, and external organizations
(regardless of which .NET language others choose to use).
Another excellent byproduct of integrating various .NET languages into a single unified software solution is the simple fact that all programming languages have their own sets of strengths
and weaknesses. For example, some programming languages offer excellent intrinsic support for
advanced mathematical processing. Others offer superior support for financial calculations, logical
calculations, interaction with mainframe computers, and so forth. When you take the strengths of a
particular programming language and then incorporate the benefits provided by the .NET platform,
everybody wins.
Of course, in reality the chances are quite good that you will spend much of your time building
software using your .NET language of choice. However, once you learn the syntax of one .NET language, it is very easy to master another. This is also quite beneficial, especially to the consultants of
the world. If your language of choice happens to be Visual Basic 2008, but you are placed at a client
site that has committed to C#, you are still able to leverage the functionality of the .NET Framework,
and you should be able to understand the overall structure of the code base with minimal fuss and
bother. Enough said.
AN OVERVIEW OF .NET ASSEMBLIES
Despite the fact that .NET binaries take the same file extension as COM servers and unmanaged
Windows binaries (*.dll or *.exe), they have absolutely no internal similarities. For example, .NET
assemblies are not described using COM type libraries and are not registered into the system registry. Perhaps most important, .NET binaries do not contain platform-specific instructions, but
rather platform-agnostic intermediate language (IL) as well as type metadata. Figure 1-3 shows the
big picture of the story thus far.
NOTE: There is one point to be made regarding the abbreviation "IL." During the development of .NET, the official
term for IL was Microsoft intermediate language (MSIL). However, with the final release of .NET 1.0, the term was
changed to common intermediate language (CIL). Thus, as you read the .NET literature, understand that IL, MSIL,
and CIL are all describing the same exact entity. In keeping with the current terminology, I will use the abbreviation
CIL throughout this text.
|
When a *.dll or *.exe has been created using a .NET-aware compiler, the resulting module is
bundled into an assembly. You will examine numerous details of .NET assemblies in Chapter 15.
However, to facilitate the discussion of the .NET runtime environment, you do need to understand
some basic properties of this new file format.
As mentioned, an assembly contains CIL code, which is conceptually similar to Java bytecode
in that it is not compiled to platform-specific instructions until absolutely necessary. Typically,
"absolutely necessary" is the point at which a block of CIL instructions (such as a method implementation) is referenced for use by the .NET runtime.
In addition to CIL instructions, assemblies also contain metadata that describes in vivid detail
the characteristics of every "type" living within the binary. For example, if you have a class named
SportsCar, the type metadata describes details such as SportsCar's base class, which interfaces are
implemented by SportsCar (if any), as well as a full description of each member supported by the
SportsCar type.
.NET metadata is a dramatic improvement to COM type metadata. As you may already know,
COM binaries are typically described using an associated type library (which is little more than a
binary version of Interface Definition Language [IDL] code). The problems with COM type information are that it is not guaranteed to be present and the fact that IDL code has no way to document
the externally referenced servers that are required for the correct operation of the current COM
server. In contrast, .NET metadata is always present and is automatically generated by a given .NET-aware compiler.
Finally, in addition to CIL and type metadata, assemblies themselves are also described using
metadata, which is officially termed a manifest. The manifest contains information about the current version of the assembly, culture information (used for localizing string and image resources),
and a list of all externally referenced assemblies that are required for proper execution. You'll examine various tools that can be used to investigate an assembly's types, metadata, and manifest
information over the course of the next few chapters.
Single-File and Multifile Assemblies
In a great number of cases, there is a simple one-to-one correspondence between a .NET assembly
and the binary file (*.dll or *.exe). Thus, if you are building a .NET *.dll, it is safe to consider that
the binary and the assembly are one and the same. Likewise, if you are building an executable desktop application, the *.exe can simply be referred to as the assembly itself. As you'll see in Chapter 15,
however, this is not completely accurate. Technically speaking, if an assembly is composed of a
single *.dll or *.exe module, you have a single-file assembly. Single-file assemblies contain all the
necessary CIL, metadata, and associated manifest in an autonomous, single, well-defined package.
Multifile assemblies, on the other hand, are composed of numerous .NET binaries, each of
which is termed a module. When building a multifile assembly, one of these modules (termed the
primary module) must contain the assembly manifest (and possibly CIL instructions and metadata
for various types). The other related modules contain a module-level manifest, CIL, and type meta-
data. As you might suspect, the primary module documents the set of required secondary modules
within the assembly manifest.
So, why would you choose to create a multifile assembly? When you partition an assembly into
discrete modules, you end up with a more flexible deployment option. For example, if a user is ref-
erencing a remote assembly that needs to be downloaded onto his or her machine, the runtime will
only download the required modules. Therefore, you are free to construct your assembly in such a
way that less frequently required types (such as a type named HardDriveReformatter) are kept in a
separate stand-alone module.
In contrast, if all your types were placed in a single-file assembly, the end user may end up
downloading a large chunk of data that is not really needed (which is obviously a waste of time).
Thus, as you can see, an assembly is really a logical grouping of one or more related modules that
are intended to be initially deployed and versioned as a single unit.
The Role of the Common Intermediate Language
Now that you have a better feel for .NET assemblies, let's examine the role of the common intermediate language (CIL) in a bit more detail. CIL is a language that sits above any particular platform-specific instruction set. Regardless of which .NET-aware language you choose, the associated
compiler emits CIL instructions. For example, the following Visual Basic 2008 code models a trivial
calculator. Don't concern yourself with the exact syntax for now, but do notice the format of the
Add() function in the Calc class:
' Calc.vb
Imports System
Namespace CalculatorExample
' Defines the program's entry point.
Module Program
Sub Main()
Dim ans As Integer
Dim c As New Calc()
ans = c.Add(10, 84)
Console.WriteLine("10 + 84 is {0}.", ans)
Console.ReadLine()
End Sub
End Module
' The VB 2008 calculator.
Class Calc
Public Function Add(ByVal x As Integer, ByVal y As Integer) As Integer
Return x + y
End Function
End Class
End Namespace
Once the VB 2008 compiler (vbc.exe) compiles this source code file, you end up with a single-
file executable assembly that contains a manifest, CIL instructions, and metadata describing each
aspect of the Calc and Program types.
NOTE: Chapter 2 examines the details of compiling code using the VB compiler.
|
If you were to open this assembly using the ildasm.exe utility (examined a little later in this
chapter), you would find that the Add() method is represented using CIL such as the following:
.method public instance int32 Add(int32 x, int32 y) cil managed
{
// Code size 9 (0x9)
.maxstack 2
.locals init ([0] int32 Add)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldarg.2
IL_0003: add.ovf
IL_0004: stloc.0
IL_0005: br.s IL_0007
IL_0007: ldloc.0
IL_0008: ret
} // end of method Calc::Add
Don't worry if you are unable to make heads or tails of the resulting CIL code for this method.
In reality, a vast majority of .NET developers could care less about the details of the CIL program-
ming language. Simply understand that the Visual Basic 2008 compiler translates your code
statements into terms of CIL.
Now, recall that this is true of all .NET-aware compilers. To illustrate, assume you created this
same application using C#, rather than VB 2008 (again, don't sweat the syntax, but do note the simi-
larities in the code bases):
// Calc.cs
using System;
namespace CalculatorExample
{
// Defines the program's entry point.
public class Program
{
static void Main()
{
Calc c = new Calc();
int ans = c.Add(10, 84);
Console.WriteLine("10 + 84 is {0}.", ans);
Console.ReadLine();
}
}
// The C# calculator.
public class Calc
{
public int Add(int x, int y)
{ return x + y; }
}
}
If you examine the CIL for the Add() method, you find similar instructions (slightly tweaked by
the C# compiler):
.method public hidebysig instance int32 Add(int32 x, int32 y) cil managed
{
// Code size 8 (0x8)
.maxstack 2
.locals init ([0] int32 CS$1$0000)
IL_0000: ldarg.1
IL_0001: ldarg.2
IL_0002: add
IL_0003: stloc.0
IL_0004: br.s IL_0006
IL_0006: ldloc.0
IL_0007: ret
} // end of method Calc::Add
SOURCE CODE: The Calc.vb and Calc.cs code files are included under the Chapter 1 subdirectory.
|
Benefits of CIL
At this point, you might be wondering exactly what is gained by compiling source code into CIL
rather than directly to a specific instruction set. One benefit is language integration. As you have
already seen, each .NET-aware compiler produces nearly identical CIL instructions. Therefore, all
languages are resolved to a well-defined binary arena that makes use of the same identical type
system.
Furthermore, given that CIL is platform-agnostic, the .NET Framework itself is platform-agnostic, providing the same benefits Java developers have grown accustomed to (i.e., a single code
base running on numerous operating systems). In fact, .NET distributions already exist for many
non-Windows operating systems (more details at the conclusion of this chapter). In contrast to the
J2EE platform, however, .NET allows you to build applications using your language of choice.
Compiling CIL to Platform-Specific Instructions
Due to the fact that assemblies contain CIL instructions, rather than platform-specific instructions,
CIL code must be compiled on the fly before use. The entity that compiles CIL code into meaningful
CPU instructions is termed a just-in-time (JIT) compiler, which sometimes goes by the friendly
name of Jitter. The .NET runtime environment leverages a JIT compiler for each CPU targeting the
runtime, each optimized for the underlying platform.
For example, if you are building a .NET application that is to be deployed to a handheld device
(such as a Pocket PC or .NET-enabled cell phone), the corresponding Jitter is well equipped to run
within a low-memory environment. On the other hand, if you are deploying your assembly to a
back-end server machine (where memory is seldom an issue), the Jitter will be optimized to
function in a high-memory environment. In this way, developers can write a single body of code
that can be efficiently JIT-compiled and executed on machines with different architectures.
Furthermore, as a given Jitter compiles CIL instructions into corresponding machine code, it
will cache the results in memory in a manner suited to the target operating system. In this way, if a
call is made to a method named PrintDocument(), the CIL instructions are compiled into platform-specific instructions on the first invocation and retained in memory for later use. Therefore, the
next time PrintDocument() is called, there is no need to recompile the CIL.
The Role of .NET Type Metadata
In addition to CIL instructions, a .NET assembly contains full, complete, and accurate metadata,
which describes each and every type (class, structure, enumeration, and so forth) defined in the
binary, as well as the members of each type (properties, functions, events, and so on). Thankfully,
it is always the job of the compiler (not the programmer) to define the latest and greatest type meta-
data. Because .NET metadata is so wickedly meticulous, assemblies are completely self-describing
entities'so much so, in fact, that .NET binaries have no need to be registered into the system
registry.
To illustrate the format of .NET type metadata, let's take a look at the metadata that has been
generated for the Add() method of the VB Calc class you examined previously (the metadata generated for the C# version of the Add() method is similar):
TypeDef #2 (02000003)
---------------------------------------------------
TypDefName: CalculatorExample.Calc (02000003)
Flags : [Public] [AutoLayout] [Class]
[AnsiClass] [BeforeFieldInit] (00100001)
Extends : 01000001 [TypeRef] System.Object
Method #1 (06000003)
---------------------------------------------------
MethodName: Add (06000003)
Flags : [Public] [HideBySig] [ReuseSlot] (00000086)
RVA : 0x00002090
ImplFlags : [IL] [Managed] (00000000)
CallCnvntn: [DEFAULT]
hasThis
ReturnType: I4
2 Arguments
Argument #1: I4
Argument #2: I4
2 Parameters
(1) ParamToken : (08000001) Name : x flags: [none] (00000000)
(2) ParamToken : (08000002) Name : y flags: [none] (00000000)
Despite what you may be thinking, metadata is a very useful entity (rather than an academic
detail) consumed by numerous aspects of the .NET runtime environment, as well as by various
development tools. For example, the IntelliSense feature provided by Visual Studio 2008 is made
possible by reading an assembly's metadata at design time. Metadata is also used by various object-browsing utilities, debugging tools, and the Visual Basic 2008 compiler itself. To be sure, metadata is
the backbone of numerous .NET technologies including Windows Communication Foundation,
reflection services, late binding facilities, XML web services/the .NET remoting layer, and the object
serialization process. Chapter 16 will formalize the role of .NET metadata.
The Role of the Assembly Manifest
Last but not least, remember that a .NET assembly also contains metadata that describes the
assembly itself (technically termed a manifest). Among other details, the manifest documents all
external assemblies required by the current assembly to function correctly, the assembly's version
number, copyright information, and so forth. Like type metadata, it is always the job of the com-
piler to generate the assembly's manifest. Here are some relevant details of the manifest generated
when compiling the Calc.vb code file seen earlier in this chapter (assume we instructed the compiler to name our assembly Calc.exe):
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
.ver 2:0:0:0
}
...
.assembly Calc
{
...
.ver 0:0:0:0
}
.module Calc.exe
.imagebase 0x00400000
.subsystem 0x00000003
.file alignment 512
.corflags 0x00000001
In a nutshell, this manifest documents the list of external assemblies required by Calc.exe (via
the .assembly extern directives) as well as various characteristics of the assembly itself (version
number, module name, etc.). Chapter 15 will examine the usefulness of manifest data in much
more detail.
UNDERSTANDING THE COMMON TYPE SYSTEM
A given assembly may contain any number of distinct "types." In the world of .NET, "type" is simply
a general term used to refer to a member from the set {class, interface, structure, enumeration, delegate}. When you build solutions using a .NET-aware language, you will most likely interact with
each of these types. For example, your assembly may define a single class that implements some
number of interfaces. Perhaps one of the interface methods takes an enumeration as an input
parameter and returns a structure to the caller.
Recall that the Common Type System (CTS) is a formal specification that documents how types
must be defined in order to be hosted by the CLR. Typically, the only individuals who are deeply
concerned with the inner workings of the CTS are those building tools and/or compilers that target
the .NET platform. It is important, however, for all .NET programmers to learn about how to work
with the five types defined by the CTS in their language of choice. Here is a brief overview.
CTS Class Types
Every .NET-aware language supports, at the very least, the notion of a class type, which is the cornerstone of object-oriented programming (OOP). A class may be composed of any number of
members (such as properties, methods, and events) and data points (field data, otherwise known
as member variables). In Visual Basic 2008, classes are declared using the Class keyword:
' A class type.
Public Class Calc
Public Function Add(ByVal x As Integer, ByVal y As Integer) As Integer
Return x + y
End Function
End Class
If you have a background in VB6 class development, be aware that class types are no longer
defined within a *.cls file, given the fact that we now have a specific keyword for defining class
types (recall that all VB code is now contained within *.vb files). Chapters 5 and 6 will examine the
details of building class types with Visual Basic 2008.
CTS Interface Types
An interface is nothing more than a named collection of member definitions that may be supported
(i.e., implemented) by a given class or structure. Unlike COM, .NET interfaces do not derive a com-
mon base interface such as IUnknown. In VB 2008, interface types are defined using the Interface
keyword, for example:
' Classes or structures that implement this interface
' know how to render themselves.
Public Interface IDraw
Sub Draw()
End Interface
On their own, interfaces are of little use. However, when a class or structure implements a given
interface in its unique way, you are able to request access to the supplied functionality using an
interface reference in a "polymorphic manner." Interface-based programming will be fully explored
in Chapter 9.
CTS Structure Types
The concept of a structure is also formalized under the CTS. In a nutshell, a structure can be
thought of as a lightweight alternative to class types, which have value-based semantics (see
Chapter 12 for full details). Typically, structures are best suited for modeling numerical, geometric,
or mathematical data types and are created in VB 2008 using the Structure keyword:
' A structure type.
Public Structure Point
Public xPos As Integer
Public yPos As Integer
Public Sub New(ByVal x As Integer, ByVal y As Integer)
xPos = x
yPos = y
End Sub
Public Sub Display()
Console.WriteLine("({0}, {1}", xPos, yPos)
End Sub
End Structure
CTS Enumeration Type
Enumerations are a handy programming construct that allows you to group name/value pairs. For
example, assume you are creating a video game application that allows the player to select one of
three character categories (Wizard, Fighter, or Thief ). Rather than keeping track of arbitrary numer-
ical values to represent each possibility, you could build a custom enumeration using the VB 2008
Enum keyword:
' An enumeration type.
Public Enum CharacterType
Wizard = 100
Fighter = 200
Thief = 300
End Enum
The CTS demands that enumerated types derive from a common base class, System.Enum. As
you will see in Chapter 4, this base class defines a number of interesting members that allow you to
extract, manipulate, and transform the underlying name/value pairs programmatically.
CTS Delegate Types
Delegates are perhaps the most complex .NET type; however, they are very important as they are
used to form the foundation of the .NET event architecture. To be sure, anytime you wish to handle
the click of a button, intercept mouse activity, or process postbacks to a web server, delegates will
be used in the background. Simply put, a delegate is used to store method addresses that can be
invoked at a later time. In Visual Basic 2008, delegates are declared using the Delegate keyword:
' This delegate type can "point to" any method
' returning an integer and taking two integers as input.
Public Delegate Function BinaryOp(ByVal x As Integer, _
ByVal y As Integer) As Integer
Chapters 11 and 18 will examine the details of .NET delegate types, including numerous related
details such as multicasting (i.e., forwarding a request to multiple recipients) and asynchronous
(i.e., nonblocking) method invocations.
NOTE: VB 2008 provides numerous keywords (see Chapter 11) that remove the need to manually define delegate types. However, you are able to define delegates directly when you wish to build more intricate and powerful
solutions.
|
CTS Type Members
Now that you have previewed each of the types formalized by the CTS, realize that most types
can contain any number of members. Formally speaking, a type member is constrained by the set
{constructor, finalizer, shared constructor, nested type, operator, method, property, indexer, field,
read-only field, constant, event}.
The CTS defines various "adornments" that may be associated with a given member. For exam-
ple, each member has a given visibility level marked with a specific VB 2008 keyword (e.g., Public,
Private, Protected, etc.). Some members may be declared as "abstract" to enforce a polymorphic
behavior on derived types as well as "virtual" to define a canned (but overridable) implementation.
Also, most members may be configured as shared (bound at the class level) or instance (bound at
the object level). The construction of type members is examined over the course of the next several
chapters.
NOTE: As described in Chapter 10, VB 2008 supports the construction of generic types and generic members.
|
Intrinsic CTS Data Types
The final aspect of the CTS to be aware of for the time being is that it establishes a well-defined set
of fundamental data types. Although a given language typically has a unique keyword used to
declare an intrinsic CTS data type, all language keywords ultimately resolve to the same type
defined in an assembly named mscorlib.dll. Consider Table 1-1, which documents how key CTS
data types are expressed in various .NET languages.
NOTE: VB keywords for signed data types (SByte, UShort, UInteger, and ULong) are supported only in
.NET 2.0 or higher. |
UNDERSTANDING THE COMMON LANGUAGE SPECIFICATION
As you are aware, different languages express the same programming constructs in unique,
language-specific terms. For example, in VB 2008 you typically denote string concatenation using
the ampersand operator (&), while in C# you always make use of the plus sign (+). Even when two
distinct languages express the same programmatic idiom (e.g., a method with no return value), the
chances are very good that the syntax will appear quite different on the surface:
' A VB 2008 subroutine.
Public Sub MyMethod()
' Some interesting code...
End Sub
// A C# method with no return value.
public void MyMethod()
{
// Some interesting code...
}
As you have already seen, these minor syntactic variations are inconsequential in the eyes of
the .NET runtime, given that the respective compilers (vbc.exe or csc.exe, in this case) emit a similar set of CIL instructions. However, languages can also differ with regard to their overall level of
functionality. For example, a .NET language may or may not have a keyword to represent unsigned
data, and may or may not support pointer types. Given these possible variations, it would be ideal
to have a baseline to which all .NET-aware languages are expected to conform.
The Common Language Specification (CLS) is a set of rules that describe in vivid detail the
minimal and complete set of features a given .NET-aware compiler must support to produce code
that can be hosted by the CLR, while at the same time be accessed in a uniform manner by all languages that target the .NET platform. In many ways, the CLS can be viewed as a subset of the full
functionality defined by the CTS.
The CLS is ultimately a set of rules that compiler builders must conform to, if they intend their
products to function seamlessly within the .NET universe. Each rule is assigned a simple name (e.g.,
"CLS Rule 6") and describes how this rule affects those who build the compilers as well as those
who (in some way) interact with them. The cr'me de la cr'me of the CLS is the mighty Rule 1:
- Rule 1: CLS rules apply only to those parts of a type that are exposed outside the defining
assembly.
Given this rule, you can (correctly) infer that the remaining rules of the CLS do not apply to the
logic used to build the inner workings of a .NET type. The only aspects of a type that must conform
to the CLS are the member definitions themselves (i.e., naming conventions, parameters, and
return types). The implementation logic for a member may use any number of non-CLS techniques,
as the outside world won't know the difference.
To illustrate, the following Add() method is not CLS-compliant, as the parameters and return
values make use of unsigned data (which is not a requirement of the CLS):
Public Class Calc
' Exposed unsigned data is not CLS compliant!
Public Function Add(ByVal x As ULong, ByVal y As ULong) As ULong
Return x + y
End Function
End Class
However, if you were to simply make use of unsigned data internally as follows:
Public Class Calc
Public Function Add(ByVal x As Integer, ByVal y As Integer) As Integer
' As this ULong variable is only used internally,
' we are still CLS compliant.
Dim temp As ULong = 0
...
Return x + y
End Function
End Class
you would have still conformed to the rules of the CLS, and could rest assured that all .NET languages are able to invoke the Add() method.
Of course, in addition to Rule 1, the CLS defines numerous other rules. For example, the CLS
describes how a given language must internally represent text strings, how enumerations should be
represented internally (the base type used for storage), how to define shared members, and so forth.
Luckily, you don't have to commit these rules to memory to be a proficient .NET developer. Again,
by and large, an intimate understanding of the CTS and CLS specifications is only of interest to
tool/compiler builders.
Ensuring CLS Compliance
As you will see over the course of this book, VB 2008 does define a few programming constructs that
are not CLS-compliant. The good news, however, is that you can instruct the VB 2008 compiler to
check your code for CLS compliance using a single .NET attribute:
' Tell the compiler to check for CLS compliance.
<Assembly: System.CLSCompliant(True)>
Chapter 16 dives into the details of attribute-based programming. Until then, simply under-
stand that the <CLSCompliant()> attribute will instruct the VB 2008 compiler to check each and
every line of code against the rules of the CLS. If any CLS violations are discovered, you receive a
compiler error and a description of the offending code.
UNDERSTANDING THE COMMON LANGUAGE RUNTIME
In addition to the CTS and CLS specifications, the next TLA (three-letter abbreviation) to contend
with at the moment is the CLR. Programmatically speaking, the term runtime can be understood
as a collection of external services that are required to execute a given compiled unit of code. For
example, when developers make use of the Microsoft Foundation Classes (MFC) to create a new
application, they are aware that their program requires the MFC runtime library (e.g., mfc42.dll).
Other popular languages also have a corresponding runtime. VB6 programmers are also tied to a
runtime module or two (e.g., msvbvm60.dll). Java developers are tied to the Java Virtual Machine
(JVM) and so forth.
The .NET platform offers yet another runtime system. The key difference between the .NET
runtime and the various other runtimes I just mentioned is the fact that the .NET runtime provides
a single well-defined runtime layer that is shared by all languages and platforms that are .NET-aware.
The crux of the CLR is physically represented by a library named mscoree.dll (aka the Common Object Runtime Execution Engine). When an assembly is referenced for use, mscoree.dll is
loaded automatically, which in turn loads the required assembly into memory. The runtime engine
is responsible for a number of tasks. First and foremost, it is the entity in charge of resolving the
location of an assembly and finding the requested type within the binary by reading the contained
metadata. The CLR then lays out the type in memory, compiles the associated CIL into platform-specific instructions, performs any necessary security checks, and then executes the code in
question.
In addition to loading your custom assemblies and creating your custom types, the CLR will
also interact with the types contained within the .NET base class libraries when required. Although
the entire base class library has been broken into a number of discrete assemblies, the key assembly
is mscorlib.dll. mscorlib.dll contains a large number of core types that encapsulate a wide variety
of common programming tasks as well as the core data types used by all .NET languages. When you
build .NET solutions, you automatically have access to this particular assembly.
Figure 1-4 illustrates the workflow that takes place between your source code (which is making
use of base class library types), a given .NET compiler, and the .NET execution engine.
THE ASSEMBLY/NAMESPACE/TYPE DISTINCTION
Each of us understands the importance of code libraries. The point of libraries found within VB6,
J2EE, or MFC is to give developers a well-defined set of existing code to leverage in their applications. However, the VB 2008 language does not come with a language-specific code library. Rather,
VB 2008 developers leverage the language-neutral .NET libraries. To keep all the types within the
base class libraries well organized, the .NET platform makes extensive use of the namespace concept.
Simply put, a namespace is a grouping of related types contained in an assembly. For example,
the System.IO namespace contains file I/O'related types, the System.Data namespace defines core
database access types, the System.Windows.Forms namespace defines GUI elements, and so on. It is
very important to point out that a single assembly (such as mscorlib.dll) can contain any number
of namespaces, each of which can contain any number of types (classes, interfaces, structures, enu-
merations, or delegates).
To clarify, Figure 1-5 shows a screen shot of the Visual Studio 2008 Object Brower utility (you'll
learn more about this tool in Chapter 2). This tool allows you to examine the assemblies referenced
by your current solution, the namespaces within a particular assembly, the types within a given
namespace, and the members of a specific type. Note that mscorlib.dll contains many different
namespaces, each with its own semantically related types.
The key difference between this approach and a language-specific library such as the Java API
is that any language targeting the .NET runtime makes use of the same namespaces and same types.
For example, the following three programs all illustrate the ubiquitous "Hello World" application,
written in VB 2008, C#, and C++/CLI:
' Hello world in VB 2008
Imports System
Public Module MyApp
Sub Main()
Console.WriteLine("Hi from VB 2008")
End Sub
End Module
// Hello world in C#
using System;
public class MyApp
{
static void Main()
{
Console.WriteLine("Hi from C#");
}
}
// Hello world in C++/CLI
#include "stdafx.h"
using namespace System;
int main(array<System::String ^> ^args)
{
Console::WriteLine(L"Hi from C++/CLI");
return 0;
}
Notice that each language is making use of the Console class, which is defined within a namespace named System. Beyond minor syntactic variations, these three applications look and feel very
much alike, both physically and logically.
Clearly, your primary goal as a .NET developer is to get to know the wealth of types defined in
the (numerous) .NET namespaces. The most fundamental namespace to get your hands around is
named System. This namespace provides a core body of types that you will need to leverage time
and again as a .NET developer. In fact, you cannot build any sort of functional .NET application
without at least making a reference to the System namespace. Table 1-2 offers a rundown of some
(but certainly not all) of the .NET namespaces.
NOTE: Chapter 2 will illustrate the use of the .NET Framework 3.5 SDK documentation, which provides details
regarding every namespace and type found within the base class libraries. |
Accessing a Namespace Programmatically
It is worth reiterating that a namespace is nothing more than a convenient way for us mere humans
to logically understand and organize related types. Consider again the System namespace. From
your perspective, you can assume that System.Console represents a class named Console that is
contained within a namespace called System. However, in the eyes of the .NET runtime, this is not
so. The runtime engine only sees a single entity named System.Console.
In Visual Basic 2008, the Imports keyword simplifies the process of referencing types defined in
a particular namespace. Here is how it works. Let's say you are interested in building a traditional
desktop application. The main window renders a bar chart based on some information obtained
from a back-end database and displays your company logo. While learning the types each name-
space contains takes study and experimentation, here are some possible candidates to reference in
your program:
' Here are all the namespaces used to build this application.
Imports System ' General base class library types.
Imports System.Drawing ' Graphical rendering types.
Imports System.Windows.Forms ' GUI widget types.
Imports System.Data ' General data-centric types.
Imports System.Data.SqlClient ' MS SQL Server data access types.
Once you have specified some number of namespaces (and set a reference to the assemblies
that define them, which is explained in Chapter 2), you are free to create instances of the types they
contain. For example, if you are interested in creating an instance of the Bitmap class (defined in the
System.Drawing namespace), you can write the following:
' Explicitly list the namespaces used by this file.
Imports System
Imports System.Drawing
Public Class Program
Public Sub DisplayLogo()
' Create a 20 x 20 pixel bitmap.
Dim companyLogo As New Bitmap(20, 20)
...
End Sub
End Class
Because your application is importing System.Drawing, the compiler is able to resolve the
Bitmap class as a member of this namespace. If you did not specify the System.Drawing namespace,
you would be issued a compiler error. However, you are free to declare variables using a fully qualified name as well:
' Not listing System.Drawing namespace!
Imports System
Public Class Program
Public Sub DisplayLogo()
' Create a 20 x 20 pixel bitmap.
Dim companyLogo As New System.Drawing.Bitmap(20, 20)
...
End Sub
End Class
While defining a type using the fully qualified name provides greater readability, I think you'd
agree that the VB 2008 Imports keyword reduces keystrokes. In this text, I will avoid the use of fully
qualified names (unless there is a definite ambiguity to be resolved) and opt for the simplified
approach of the Imports keyword.
However, always remember that this technique is simply a shorthand notation for specifying a
type's fully qualified name, and each approach results in the exact same underlying CIL (given the
fact that CIL code always makes use of fully qualified names) and has no effect on performance or
the size of the generated assembly.
Referencing External Assemblies
In addition to specifying a namespace via the VB 2008 Imports keyword, you also need to tell the
VB 2008 compiler the name of the assembly containing the actual CIL definition for the referenced
type. As mentioned, many core .NET namespaces live within mscorlib.dll. However, the System.
Drawing.Bitmap type is contained within a separate assembly named System.Drawing.dll. A vast
majority of the .NET Framework assemblies are located under a specific directory termed the global
assembly cache (GAC). On a Windows machine, this can be located under C:\Windows\assembly, as
shown in Figure 1-6.
Depending on the development tool you are using to build your .NET applications, you will
have various ways to inform the compiler which assemblies you wish to include during the compilation cycle. You'll examine how to do so in the next chapter, so I'll hold off on the details for now.
Using ildasm.exe
If you are beginning to feel a tad overwhelmed at the thought of gaining mastery over every name-
space in the .NET platform, just remember that what makes a namespace unique is that it contains
types that are somehow semantically related. Therefore, if you have no need for a user interface
beyond a simple console application, you can forget all about the System.Windows and System.Web
namespaces (among others). If you are building a painting application, the database namespaces
are most likely of little concern. Like any new set of prefabricated code, you learn as you go. (Sorry,
there is no shortcut to "magically" know all the assemblies, namespaces, and types at your disposal;
then again, that is why you are reading this book!)
The Intermediate Language Disassembler utility (ildasm.exe) allows you to load up any .NET
assembly and investigate its contents, including the associated manifest, CIL code, and type meta-
data. By default, ildasm.exe should be installed under C:\Program Files\Microsoft SDKs\Windows\
V6.0A\Bin (if you cannot find ildasm.exe in this location, simply search your machine for an application named "ildasm.exe").
NOTE: If you have installed Visual Studio 2008, you can also load ildasm.exe by opening a Visual Studio 2008
Command Prompt (see Chapter 2 for details) and typing the name of the tool (ildasm) and pressing the return key.
|
Once you loaded this tool, proceed to the File -> Open menu command and navigate to an
assembly you wish to explore. By way of illustration, Figure 1-7 shows the Calc.exe assembly built
by compiling the Calc.vb code file seen earlier in this chapter. As you can see, ildasm.exe presents
the structure of an assembly using a familiar tree-view format.
Viewing CIL Code
In addition to showing the namespaces, types, and members contained in a given assembly,
ildasm.exe also allows you to view the CIL instructions for a given member. For example, if you
were to double-click the Main() method of the Program type, a separate window would display the
underlying CIL (see Figure 1-8).
Viewing Type Metadata
If you wish to view the type metadata for the currently loaded assembly, press Ctrl+M. Figure 1-9
shows the metadata for the Calc.Add() method.
Viewing Assembly Metadata (aka the Manifest)
Finally, if you are interested in viewing the contents of the assembly's manifest, simply double-click
the MANIFEST icon in the main ildasm.exe window (see Figure 1-10).
To be sure, ildasm.exe has more options than shown here, and I will illustrate additional features of the tool where appropriate in the text. As you read through this book, I strongly encourage
you to open your assemblies using ildasm.exe to see how your VB 2008 code is represented in terms
of platform-agnostic CIL code. Although you do not need to become an expert in CIL code to be a
proficient VB 2008 programmer, understanding some basics of CIL will only strengthen your programming muscle.
Using Lutz Roeder's Reflector
While using ildasm.exe is a very common task when you wish to dig into the guts of a .NET binary,
the one gotcha is that you are only able to view the underlying CIL code, rather than looking at an
assembly's implementation using your managed language of choice. Thankfully, many .NET object
browsers are available for download, including the very popular reflector.exe. This free tool can be
downloaded from http://www.aisto.com/roeder/dotnet. Once you have installed this application,
you are able to run the tool and plug in any assembly you wish using the File -> Open menu option.
Figure 1-11 shows our Calc.exe application once again.
Notice that reflector.exe supports a Disassembler window (opened by pressing the spacebar)
as well as a drop-down list box that allows you to view the underlying code base in your language of
choice (including, of course, CIL code).
I'll leave it up to you to check out the number of intriguing features found within this tool. Do
be aware that over the course of the remainder of the book, I'll make use of both ildasm.exe as well
as reflector.exe to illustrate various concepts.
DEPLOYING THE .NET RUNTIME
Deploying the .NET Runtime
It should come as no surprise that .NET assemblies can be executed only on a machine that has the
.NET Framework installed. For an individual who builds .NET software, this should never be an
issue, as your development machine will be properly configured at the time you install the freely
available .NET Framework 3.5 SDK (as well as commercial .NET development environments such as
Visual Studio 2008).
However, if you deploy an assembly to a computer that does not have .NET installed, it will fail
to run. For this reason, Microsoft provides a setup package named dotNetFx35setup.exe that can be
freely shipped and installed along with your .NET software. This installation program can be downloaded from Microsoft from its .NET download area (http://msdn.microsoft.com/netframework).
Once dotNetFx35setup.exe is installed, the target machine will now contain the .NET base class
libraries, .NET runtime (mscoree.dll), and additional .NET infrastructure (such as the GAC).
NOTE: The Vista operating system is preconfigured with all of the necessary .NET runtime infrastructure. How-
ever, if you are deploying your application to other versions of the Windows operating system, you will want to
ensure the target machine has the .NET runtime environment installed and configured.
|
THE PLATFORM-INDEPENDENT NATURE OF .NET
To close this chapter, allow me to briefly comment on the platform-independent nature of the .NET
platform. To the surprise of most developers, .NET assemblies can be developed and executed on
non-Microsoft operating systems (Mac OS X, numerous Linux distributions, and Solaris, to name a
few). To understand how this is possible, you need to come to terms with yet another abbreviation
in the .NET universe: CLI (Common Language Infrastructure).
When Microsoft released the .NET platform, it also crafted a set of formal documents that
described the syntax and semantics of the C# and CIL languages, the .NET assembly format, core
.NET namespaces, and the mechanics of a hypothetical .NET runtime engine (known as the Virtual
Execution System, or VES). Better yet, these documents have been submitted to ECMA Interna-
tional as official international standards (http://www.ecma-international.org). The specifications
of interest are
- ECMA-334: The C# Language Specification
- ECMA-335: The Common Language Infrastructure (CLI)
NOTE: Microsoft has not defined a formal specification regarding the Visual Basic 2008 programming language.
The good news, however, is that the major open source .NET distributions ship with a compatible BASIC compiler.
|
The importance of these documents becomes clear when you understand that they enable
third parties to build distributions of the .NET platform for any number of operating systems
and/or processors. ECMA-335 is perhaps the more "meaty" of the two specifications, so much so
that is has been broken into six partitions, as shown in Table 1-3.
Be aware that Partition IV (Profiles and Libraries) defines only a minimal set of namespaces
that represent the core services expected by a CLI distribution (collections, console I/O, file I/O,
threading, reflection, network access, core security needs, XML manipulation, and so forth). The
CLI does Idefine namespaces that facilitate web development (ASP.NET), database access
(ADO.NET), or desktop GUI application development (via Windows Forms or Windows Presentation Foundation).
The good news, however, is that the mainstream .NET distributions extend the CLI libraries
with Microsoft-compatible equivalents of ASP.NET, ADO.NET, and Windows Forms (among other
APIs) in order to provide full-featured, production-level development platforms. To date, there are
two major implementations of the CLI (beyond Microsoft's Windows-specific offering). Although
this text focuses on the creation of .NET applications using Microsoft's .NET distribution, Table 1-4
provides information regarding the Mono and Portable.NET projects.
Both Mono and Portable.NET provide an ECMA-compliant C# compiler, .NET runtime engine,
code samples, and documentation, as well as numerous development tools that are functionally
equivalent to the tools that ship with Microsoft's .NET Framework 3.5 SDK. Furthermore, Mono and
Portable.NET collectively ship with a Visual Basic, Java, and C compiler.
NOTE: If you wish to learn more about Mono or Portable.NET, check out Cross-Platform .NET Development: Using
Mono, Portable.NET, and Microsoft .NET by M. J. Easton and Jason King (Apress, 2004). |
SUMMARY
The point of this chapter was to lay out the conceptual framework necessary for the remainder of
this book. I began by examining a number of limitations and complexities found within the technologies prior to .NET, and followed up with an overview of how .NET and Visual Basic 2008 attempt
to streamline the current state of affairs.
.NET basically boils down to a runtime execution engine (mscoree.dll) and base class library
(mscorlib.dll and associates). The common language runtime (CLR) is able to host any .NET
binary (aka assembly) that abides by the rules of managed code. As you have seen, assemblies contain CIL instructions (in addition to type metadata and the assembly manifest) that are compiled to
platform-specific instructions using a just-in-time (JIT) compiler. In addition, you explored the role
of the Common Language Specification (CLS) and Common Type System (CTS).
This was followed by an examination of the ildasm.exe and reflector.exe object browsing
utilities, as well as coverage of how to configure a machine to host .NET applications using
dotNetFx35setup.exe. I wrapped up by briefly addressing the platform-independent nature of the
.NET platform using alternative open source .NET distributions such as Mono or Portable.NET.
|
Page:
1 |
|
|