- Before .NET
- Back to Introducing .NET
- The .NET Object
- The Parts of the .NET Framework
- From Source Code to EXE
- What About Visual Studio and Visual Basic?
- Visual Studio 2005
- Summary
- The Project
The Parts of the .NET Framework
So now you know all about objects, and you are probably thinking it's time to toss this book into the pile and start programming. But there are a few more parts of the .NET Framework still to discuss. These parts show up ad nauseum in the .NET documentation, and they each have a three-letter acronym (TLA), or thereabouts.
The Common Language Runtime
At the center of the .NET Framework is the Common Language Runtime (CLR), so named not because it is common or ordinary, but because all .NET-enabled languages share it in common. Everything you do in a .NET program is managed by the CLR. When you create a variable, thank the CLR and its data management system. When you say goodbye to a piece of data, thank the CLR and how it manages the release of data through its garbage collection system. Did you notice how the word "manage" keeps showing up in those sentences? My editor sure did. But "manage" is the mot juste, because that is what the CLR does. In fact, software written for the .NET Framework is called managed code. Any code that falls outside of the CLR's control, including COM (ActiveX) components used by your .NET application, is known as unmanaged code.
The CLR is a lot like Los Angeles International Airport. If you have ever been to LAX, you know that there is a whole lot of activity going on. Airplanes arrive and depart each minute. Cars by the thousands enter and leave the two-level roadway and the central parking structures. People and pickpockets move constantly between the eight main terminals and the massive international terminal. There's a lot happening, but so much of it is managed. Planes cannot take off or land without approval from the control tower. Access points and gates manage the roadways and parking garages. Friendly, courteous security personnel manage the flow of passengers and pickpockets into and out of the secure areas of the terminals.
The control and management structures in place at LAX ensure an orderly and secure flow of people between its planes and the city of Los Angeles. The control and management structures of the CLR ensure an orderly and secure flow of data between .NET code and the rest of the computer or connected network.
You'd probably like to know the secret of how the CLR is able to process programs written in any .NET language, including Visual Basic, C#, and Fortran. So would Microsoft's competitors. Actually, they do know, because there is no secret. All .NET-enabled languages convert (that is, "compile") your source code into Microsoft Intermediate Language (or MSIL, pronounced "missile," and more commonly abbreviated as just IL). For those of you familiar with assembly language, it looks a lot like that. For those of you not familiar with assembly language, it looks a lot like gibberish. For example, here is some Visual Basic source code for a console application (a non-Windows text-based program, like the old MS-DOS programs) that simply outputs "Hello, World!" from a code procedure called "Main."
Module Module1 Sub Main() Console.WriteLine("Hello, World!") End Sub End Module
That's the whole .NET program. When the Visual Basic compiler converts it to IL, the "Main" procedure looks like this (slightly modified to fit on this page).
.method public static void Main() cil managed { .entrypoint .custom instance void [mscorlib]System. STAThreadAttribute::.ctor() = ( 01 00 00 00 ) // Code size 11 (0xb) .maxstack 8 IL_0000: ldstr "Hello, World!" IL_0005: call void [mscorlib]System.Console::WriteLine(string) IL_000a: ret } // end of method Module1::Main
Yes, it is gibberish. But that's okay, because it fulfills the International Computer Book Association's requirement that every Chapter 1 include a "Hello, World" code sample. Also, the CLR understands it, and that's what really counts in .NET. As long as you can get your code into IL, .NET will process it. The Visual Basic compiler just happens to generate IL for you. Other .NET language compilers, including C#, target IL as well. You can even write your own IL code, but you're probably reading the wrong book for that. Just to put your mind at ease, this will be the last bit of IL you will see in this book.
The Common Language Specification
Languages that claim to support .NET cannot just say so for any old reason. They truly have to be compatible with .NET and its workings. This is done through the Common Language Specification (CLS). The CLS defines a minimum set of features that a language must implement before it is considered to be .NET-compliant, or more accurately, CLS-compliant.
A language can go way beyond that minimum if it wants, and .NET includes many additional features upon which language-specific features may be built. A language that only implements the minimum CLS-specified features may not be able to fully interact with components from languages that exceed the minimum specification. Visual Basic is, of course, CLS-compliant, and in fact goes way beyond that minimum.
The Common Type System
Because the CLR is controlling your source code anyway, Microsoft thought it would be good to have it control the source code's data as well. The .NET Framework does this through its Common Type System (CTS), which defines all of the core data types and data mechanisms used in .NET programs. This includes all numeric, string, and Boolean value types. It also defines the object, the core data storage unit in .NET.
The CTS divides all data objects into two buckets. The first bucket, called values types, stores actual data right in the bucket. If you have a 32-bit integer value, it gets put right in the value type bucket, ready for your immediate use. The other bucket contains reference types. When you look in this bucket, you see a map that tells you where to find the actual data somewhere else in the computer's memory. It seems like value types are easier to use, and they are, but they come with a few restrictions not imposed on reference types.
Programs and components written using the CTS standard can exchange data with each other without any hindrances or limitations. (There are a few .NET data types that fall outside of the "core" CTS types, but you only need to avoid them when you want to specifically interact with components that can only use the core CTS types.)
When you write your applications in Visual Basic, most of your code will appear in classes. Classes are reference types that include both data values and associated code. The data values included in a class are most often the core CTS data types, but they can also contain objects that you design elsewhere in your application. Visual Basic also includes structures, the weaker yet quicker younger brother of classes. Structures implement value types, and also include both data and code.
Classes and structures are just two of the data/code types available in Visual Basic. Interfaces are class and structure skeletons; they include design details of what should appear in a related class or structure, but don't include any actual implementation or working code. Delegates define a single procedure (but not its implementation), and are used to support events, those actions (initiated by the user, by the operating system, or by your code) that tell your code, "Get to work now!" Sea otters are aquatic mammals that are curiously related to the weasel, and like to eat sea urchins. Modules are blocks of code and data, but unlike classes and structures, you can't create independent objects from them. Enumerations group a set of related integer values, generally for use as a list of choices.
In .NET parlance, all of these terms (class, structure, interface, delegate, module, and enumeration, but not sea otter) are known collectively as types. You probably already knew that .NET had some confusing elements in it; you wouldn't have bought a book about it if it was easy. But despite all of the complex technology, it is this simple word "type" that causes the most confusion. You will likely experience some angst throughout this book each time you read it. The problem: It's too general. Not only does it refer to these core elements of the Common Type System, but it is also used when talking about just the Visual Basic-specific value types (more often called the Visual Basic "data types"). The nickname for structures is "user-defined types," yet another confusing use of "type." Programmers who used Visual Basic before its .NET incarnation also remember "Type" as the language statement used to create user-defined types. Arrrgh! Microsoft should have used some word other than "types" for the world of classes, interfaces, enumerations, and so on. "Bananas" would have been a better choice because it is only sometimes used to discuss software. But "type" is the word, so you better get used to seeing it. I will try to include as much context as possible when using the word throughout this volume.
The members of a type usually consist of simple data fields and code procedures, but you can also include other types as members. That is, a class can include a nested class if it needs to. Only certain types support nesting (see Chapter 8 for details). I also talk about access levels in that chapter. Each member has an access level that says what code can use that member. There are five access levels, ranging from Public (anybody and their brother can use the member) to Private (you have to be inside the type to even know it's there).
Chapter 6, "Data and Data Types," discusses the .NET type system in greater detail, including the information you crave on classes, structures, and other bananas.
.NET Class Libraries
Computers are actually quite stupid. While I can count all the way to 17, a computer tops out at 1; it only knows the digits 0 and 1. The CPU includes a set of simple operators used to manipulate the digits 0 and 1, and a few more operators that compare 1s and 0s in complex ways. The computer's last great trick is its ability to move 0s and 1s into and out of memory, but whoop-dee-doo. Sure it does these things at nearly the speed of light, but can it calculate p to three million decimal places?
Well, actually it can. Computers don't know anything about the letters of the alphabet, and they really only can handle the digits 0 and 1, yet here I am using a computer to write an award-winning book. It is the ability to combine the simple one-bit data values and operators into increasingly complex libraries of functionality that make useful computers possible.1
The .NET Framework is built upon decades of increasingly complex functionality. When you install the .NET Framework, the CLR and its associated type system represent the core of the framework. By itself, the framework includes all of the basic functionality needed to let you add 2 and 2 together and correctly get 4. And as a business application developer, you spend a lot of time doing just that. But what if you want to do something more complex, something that you know some other programmer has already done, like sorting a list of names or drawing a colored circle on a form? To get that answer, go to the class libraries, the .NET Class Libraries. These libraries, installed with the Framework, include a lot of pre-written (increasingly complex) functionality that you don't have to write from scratch.
There are two class libraries in .NET: the Base Class Library (BCL) and the Framework Class Library (FCL). The BCL is smaller, and contains the most essential features that a program just couldn't do without. It includes only those classes that are an absolute must for supporting applications on the framework if Microsoft were, say, to port the framework to Linux.
The FCL is larger, and includes everything else Microsoft thought you would want to have in your programs, but was not absolutely essential to have in the BCL. Don't even ask how many classes there are in the FCL; you don't want to know. I bet that Microsoft doesn't even really know the full number. I am convinced that those wacky pranksters at Microsoft have included "gag" classes in the FCL, but they are so deeply buried that few programmers ever encounter them.
With thousands (yes, thousands!) of classes, enumerations, interfaces, and other types included in the BCL and FCL, you would think that it would be hard to find just the class you need. But it's not that difficult, at least not overwhelmingly difficult. The .NET Framework includes a feature called namespaces. All types in .NET appear in a hierarchy—a tree-like structure—with just a few minimal entries at the root. Each node in the hierarchy is a namespace. You uniquely identify any class or other type in the libraries by naming all of the namespaces, from the root down to the local namespace that contains the class, separating each node with a period (.).
Unlike most hierarchies that have all branches starting from a single root node, the .NET namespace hierarchy has multiple root nodes. The largest root namespace is named System. It includes many classes, but it also includes several next-tier hierarchy nodes (namespaces). Because the framework includes features for both Windows-based and Web-based application development, there are namespaces that contain the Windows-specific and Web-specific development features. These namespaces appear just within the System namespace, and are called Windows and Web. All code related to on-screen Forms in the Windows namespaces appears in the Forms namespace, and within this namespace is the actual class that implements a form, named Form. Figure 1-3 presents an image of this namespace subset.
Figure 1-3 A hierarchy of namespaces and classes
In Visual Basic, you identify a class by qualifying it with all of its namespaces, starting from its root namespace. The Form class has the following fully qualified name:
System.Windows.Forms.Form
All classes and types exist somewhere in the hierarchy, although not every class descends from System. Many of the supporting features specific to Visual Basic appear in the Microsoft.VisualBasic namespace, which has "Microsoft" as its root node instead of "System." When you create new projects in Visual Basic, the name of the project is, by default, a new top-level node in the hierarchy. If you create a new Windows application, the default "Form1" form has the following fully qualified name:
WindowsApplication1.Form1
This new application's namespace is not just a second-class appendage hanging off of the System namespace. It is fully integrated into the full .NET namespace hierarchy; the WindowsApplication1 namespace is a root node, just like the System and Microsoft root nodes. Visual Basic includes features that let you alter the default namespace for your application, or place one of the application's classes in a specific namespace. You can even place your application's classes in the System namespace branch. Changing WindowsApplication1 to System.MySuperApp moves Form1 to:
System.MySuperApp.Form1
If your application is actually a component or library destined for use in programs, your app's classes will appear in the namespace you specify when the other program loads your component into its application area. Your code will look like it is part of the Microsoft-supplied namespaces. Is that cool or what?
Although you can add your classes to the System namespace, you will incur the wrath of other .NET programmers. The System namespace is supposed to be for "system" (read: Microsoft-supplied) features, and that's it. Also, there's a chance that two vendors might use the same namespace path. So, to avoid potential namespace conflicts and dirty looks from other programmers, you should name your application's classes as:
CompanyName.ApplicationName.ClassName
A single class or other type cannot be split across multiple namespaces, even within the same hierarchy branch. However, two classes or types may share a common name in different namespaces, even within the same branch.
All classes of the BCL and FCL appear intermingled throughout the entire namespace hierarchy. This means that you cannot necessarily tell whether a particular class is from the BCL or the FCL. Frankly, it doesn't really matter; your code won't care which library a class comes from, as long as it is available for use on the user's workstation.
Assemblies and Manifests
An assembly is a "unit of deployment" for the parts of a .NET application or library. In 99.9% of cases, an assembly is simply a .NET executable file (an "exe" file) or a .NET library of classes and other types (a "dll" file). It is possible to split an assembly between multiple files, but usually it is one file for one assembly.
What makes an ordinary exe or dll file an assembly is the presence of a manifest. For single-file assemblies, the manifest appears right in the file; it can also appear in a file of its own. The manifest is a chunk of data that lists important details about the assembly, including its name, version information, default culture, information on referencing external assemblies and types, and a list of all the files contained in the assembly. The CLR will not recognize an assembly without its manifest, so don't lose it.
Assemblies can include an optional strong name. This helps to ensure the integrity and authenticity of an assembly through a digital signature attached to the manifest. The strong name uses public key cryptography to guarantee that the assembly is unique and has not been tampered with. Visual Studio and the .NET Framework include tools that let you add a strong name to an assembly.
When you deploy your application, you will normally place all assembly files, configuration files, and any related files specific to your application in the application's install directory, just like in the old Jurassic days before .NET. Shared assemblies designed to be used by more than one application on a single machine can be stored in the global assembly cache (GAC). All assemblies placed in the GAC must have strong names. Some systems may only allow the system administrator to add assemblies to the GAC.
Metadata and Attributes
Assemblies are brought to you by the letter "m." In addition to manifests and type members, assemblies also contain metadata. The application code and data elements stored in an assembly parallel the code and data items found in the related Visual Basic source code; for each type and member in your source code, there is associated executable code in the deployed assembly. This makes sense, and is not much of a change from pre-.NET deployments. What is different is that the Visual Basic compiler now attaches additional information—metadata—to each type and member in the assembly. This metadata documents the name of the associated content, information about required data types, information on class inheritance for the element, and security permissions required before the element can be used by the user or other software.
Your Visual Basic source code can enhance the metadata for any element of your assembly through attributes. The metadata generated by an attribute is more than just some ID number. Attributes implement full .NET classes, with their own data values and associated logic. Any .NET code that knows how to process attributes can examine the attributes for a type or member and take action as needed. This includes Visual Studio, the Visual Basic compiler, and your own custom applications.
How's this for a mundane example: The .NET Framework includes an attribute named ObsoleteAttribute. This attribute lets you mark types or members of your assembly as obsolete or no longer supported. (Visual Studio uses this attribute to display a warning whenever you attempt to use an out-of-date BCL or FCL feature.) To use the attribute, add it to a member of your application using angle brackets.
Class MyClassWithOldMembers <ObsoleteAttribute> Sub DoSomeWork() End Sub End Class
This code defines a single class (MyClassWithOldMembers) with a single member procedure (DoSomeWork), a procedure that clearly does some work. The procedure is tagged with the ObsoleteAttribute attribute. By custom, all attribute names end in the word "Attribute." You can leave off this portion of the word if you wish, as long as the resulting word does not conflict with any Visual Basic language keywords.
Class MyClassWithOldMembers <Obsolete> Sub DoSomeWork() End Sub End Class
When you compile the class and store it in an assembly, the <ObsoleteAttribute> attribute is stored as part of DoSomeWork's definition. You can now write a separate Visual Basic application that scans an assembly and outputs the name and status of every type and member it finds. When that analysis program encounters the obsolete member, it would detect ObsoleteAttribute in the metadata, and output the status:
DoSomeWork Procedure: Obsolete, don't use it!
Most attributes are designed with a specific purpose in mind. Some attributes instruct Visual Studio to display the members of a class in specific ways. You've probably already played with the form-editing features of Visual Studio to design a simple Windows desktop application. When you add a control (such as a button or a list box) to a form and select that control, Visual Studio lets you edit details of that control through the Properties panel area (see Figure 1-4).
Figure 1-4 The Properties panel in Visual Studio
The Button control is implemented as a class, and many of its class members appear in the Properties panel, but not all of them. When the Button class was designed, attributes were added to its members that tell Visual Studio which members should appear in the Properties panel, and which should not. Visual Studio dutifully examines these attributes, and displays the requested properties only.
Versioning
Like you, my applications are perfect from their initial release, and I never have a reason to modify them or add additional features. But there are software development organizations—including one large company that, so as not to cause embarrassment, I will refer to only by its initial letter of "M"—that feel the need to "one-up" their competition by coming out with "improved" versions of their previously released software offerings. Let's say that "M" happened to have a popular word processor that includes version 1.0 of a spell-check component. "M" also happens to sell an email tool that depends specifically on version 1.0 of that same shared component. If, in a show of competitive machismo, "M" releases an update to the word processor and the spell-check component (now version 2.0), what happens to the email tool's spell-checking ability?
Not that this ever happens in real life. But if it did, the replacement of a vital shared component with a newer but somewhat incompatible version could cause real problems. A related problem is the deployment of multiple versions of a single component on the same workstation, all in different directories. Can any of them be safely deleted?
.NET solves these problems through versioning. All assemblies that use shared components identify exactly which versions of the shared components they require. While an application can be reconfigured to use a later version, it will only use the originally specified version of a shared component by default.
Multiple versions of a single shared component can be added to the GAC, a feature called side-by-side deployment. The CLR ensures that the right application links up with the right component. You can even run applications simultaneously that use different versions of the same component.