Figure 1-1. The CLR, CTS, CLS, and base class library relationship

Figure 1-1. The CLR, CTS, CLS, and base class library relationship

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 Basic2005 (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 (a 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):

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).

Figure 1-2. .NETLanguages is one of many sites documenting known .NET programming languages.

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.

Figure 1-3. All .NET-aware compilers emit IL instructions and metadata.

■ 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 metadata. 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 referencing 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:

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 singlefile 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 {

.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 programming 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 similarities 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(l0, 84); Console.WriteLine("10 + 84 is {0}.", ans); Console.ReadLine();

public class Calc {

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 {

.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 metadata. 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]


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 compiler 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

.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:

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 common 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 Types

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 numerical 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 example, 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.

Table 1-1. The Intrinsic CTS Data Types

CTS Data Type

VB 2008 Keyword

C# Keyword

C++/CLI Keyword




unsigned char




signed char








int or long








unsigned short




unsigned int or unsigned long




unsigned int64
















wchar t













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

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 understand 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.

Figure 1-4. mscoree.dll in action

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.IOnamespace 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, enumerations, 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.

Browse: MySolution


Was this article helpful?

0 0

Post a comment