An assembly consists of assembly metadata describing the complete assembly, type metadata describing the exported types and methods, MSIL code, and resources. All these parts can be inside of one file or spread across several files.
In this example (see Figure 15-5), the assembly metadata, type metadata, MSIL code, and resources are all in one file — Component.dll. The assembly consists of a single file.
Figure 15-5
The second example shows a single assembly spread across three files (see Figure 15-6). Component.dll has assembly metadata, type metadata, and MSIL code, but no resources. The assembly uses a picture from picture.jpeg that is not embedded inside Component.dll, but is referenced from within the assembly metadata. The assembly metadata also references a module called util.netmodule, which itself includes only type metadata and MSIL code for a class. A module has no assembly metadata, thus the module itself has no version information; it also cannot be installed separately. All three files in this example make up a single assembly; the assembly is the installation unit. It would also be possible to put the manifest in a different file.
Figure 15-6
An important part of an assembly is a manifest, which is part of the metadata. It describes the assembly with all the information that's needed to reference it and lists all its dependencies. The parts of the manifest are as follows:
Identity. (name, version, culture, and public key)
A list of files. Files belonging to this assembly. A single assembly must have at least one file, but may contain a number of files.
A list of referenced assemblies. All assemblies used from the assembly are documented inside the manifest. This reference information includes the version number and the public key, which is used to uniquely identify assemblies. The public key is discussed later in this chapter.
A set of permission requests. These are the permissions needed to run this assembly. You can find more information about permissions in Chapter 16, ".NET Security."
Exported types. These are included if they are defined within a module and the module is referenced from the assembly; otherwise they are not part of the manifest. A module is a unit of reuse. The type description is stored as metadata inside the assembly. You can get the structures and classes with the properties and methods from the metadata. This replaces the type library that was used with COM to describe the types. For the use of COM clients it's easy to generatea type library out of the manifest. The reflection mechanism uses the information about the exported types for late binding to classes. See Chapter 11, "Reflection," for more information about reflection.
You might be a little bit confused by the meanings of namespaces, types, assemblies, and components. How does a namespace fit into the assembly concept? The namespace is completely independent of an assembly. You can have different namespaces in a single assembly, but the same namespace can be spread across assemblies. The namespace is just an extension of the type name — it belongs to the name of the type. Thus, the real name of the class Demo you used before is Wrox.ProCSharp.Assemblies.AppDomains.Demo.
The diagram in Figure 15-7 should help to make this concept clearer. It shows three assemblies, which you build later in this chapter — an assembly written with C++/CLI, one with Visual Basic, and one with C#. All these assemblies have classes in the same namespace: Wrox.ProCSharp.Assemblies.CrossLanguage. The assembly HelloCSharp, in addition, has a Math class that's in the namespace Wrox.Utils.
Figure 15-7
Assemblies can be shared or private. A private assembly is found either in the same directory as the application, or within one of its subdirectories. With a private assembly, it's not necessary to think about naming conflicts with other classes or versioning problems. The assemblies that are referenced during the build process are copied to the application directory. Private assemblies are the normal way to build assemblies, especially when applications and components are built within the same company.
When using shared assemblies, you have to be aware of some rules. The assembly must be unique and therefore must also have a unique name called a strong name. Part of the strong name is a mandatory version number. Shared assemblies will mostly be used when a vendor, different from that of the application, builds the component, or when a large application is split into subprojects.
Assemblies can be viewed using the command-line utility ildasm, the MSIL disassembler. You can open an assembly by starting ildasm from the command line with the assembly as an argument or by selecting the File Open menu.
Figure 15-8 shows ildasm opening the example that you build a little later in the chapter, HelloCSharp. exe. ildasm shows the manifest and the HelloCSharp type in the Wrox.ProCSharp.Assemblies. CrossLanguage namespace. When you open the manifest, you can see the version number and the assembly attributes, as well as the referenced assemblies and their versions. You can see the MSIL code by opening the methods of the class.
Figure 15-8
The following table lists the symbols used with ildasm.
Symbol | Description |
---|---|
| Represents a namespace. |
| Represents a reference type, a class. Similar symbols are used by value types (structs) that have a light color, delegates that are real classes with the MSIL code, interfaces that have an "I" in the graphic, and enumerations with an "E." |
| Represents a method and get and set accessors of a property; an "S" in the graphic means that this method is static. |
Represents a field. | |
| Represents an event. |
| Represents a property. |
This means that more information is available (for example, manifest information or information about a class declaration). |