BlackWasp
.NET Framework
.NET 1.1

.NET Assemblies

The seventeenth part of the C# Object-Oriented Programming tutorial considers .NET assemblies. The building blocks of .NET programs, assemblies are the files that contain all of the code, versioning information and resources for an application.

What is an Assembly?

Assemblies are an important concept in .NET programming. An assembly is a file that contains a program in its own right or a library of code, controls, resources or other information that can be utilised by a program. Generally speaking, an assembly is created as a single project within Visual Studio, or a similar development environment, and compiled into an executable (EXE) file or a dynamic linked library (DLL).

Assemblies should not be confused with namespaces. Although in many cases a single assembly will contain one namespace, it is possible for a namespace to be distributed throughout many compiled DLLs. Similarly, one assembly may contain any number of namespaces.

Assemblies store four types of information. These are:

  • The Assembly Manifest
  • Type Metadata
  • Microsoft Intermediate Language (MSIL) Code
  • Resources

Assembly Manifest

An assembly's manifest contains information that describes the assembly. The following key items are held:

  • Assembly Name. A simple text string that holds the name of the assembly. This is combined with the assembly's version number, culture and strong name to generate the assembly's identity.
  • Version Number. The version number is made up of major and minor version numbers and revision and build numbers. This information is used by the .NET framework to ensure that an appropriate version of the assembly is loaded when executing software.
  • Culture. The culture information for the assembly. This setting is used for globalised applications to determine the language or locale that the assembly supports. Culture information is used by satellite assemblies. These are special files containing location-specific details, such as translation of text strings to other languages. Satellite assemblies are loaded at run-time when available for the user's preferred culture. Their use is beyond the scope of this article.
  • Strong Name. Contains the public key from the software publisher when a strong name has been assigned. Signed assemblies are protected by a digital signature so that the .NET framework can prevent the execution of assemblies that have been modified after compilation. This reduces the risk that viruses and other malicious software could tamper with the contents of the files.
  • File List. Simple assemblies usually contain only a single file. However, you can create multiple file assemblies. In either case, the list of files within the assembly is hashed and held in this list.
  • Type Reference Information. Holds information used at run-time that maps a type reference to the file that contains the type declaration and code.
  • Referenced Assembly Information. Provides a list of other assemblies that are referenced by the current assembly. Each reference contains the first four items from this list, ie. the assembly identity.
  • Information Attributes. Additional information related to the assembly. These settings include data such as company details for the software vendor, copyright information and product and trademark details. This information is usually configured in the AssemblyInfo.cs file.

Type Metadata

The type metadata information held within an assembly contains details of all of the data types and members that are provided by the assembly. This allows the assembly to describe the compiled code that it contains. This is particularly useful when creating software that uses multiple .NET languages as each language can examine the type metadata and automatically understand the data types that are used. No custom language interoperability code needs to be written in this situation.

Microsoft Intermediate Language Code

The third element of an assembly is the program code. The C# classes that you develop are compiled into the Microsoft Intermediate Language (MSIL). This language is of a lower level than C# but is not fully compiled into machine instructions. The final compilation only occurs at run-time, giving the prospect that the MSIL code can be executed under the .NET Common Language Runtime (CLR) on various platforms including 32-bit and 64-bit Windows and, in some cases, non-Windows operating systems.

A second advantage of MSIL is that the code in an assembly can be utilised by any .NET language. This permits modules developed in C# to be used by Visual Basic and C++ developers for example.

There are disadvantages to the two-part compilation of .NET assemblies. The primary concern for many developers is performance. The additional run-time compilation does cause a noticeable pause when a large program starts and execution speed can be lower than with native code. For developers of packaged software, an additional problem of code security is introduced as code that is not protected can be "decompiled" to the original C# source code. This is alleviated somewhat by the use of code obfuscators. However, these are beyond the scope of this article. Thirdly, the use of MSIL requires that the appropriate version of the .NET framework be installed on all target systems.

Resources

The final section of an assembly holds its resources. Resources include all of the remaining items required by the assembly that are not included in the code. Examples include images, icons, sounds and localised text.

Version Information

DLL Hell

Prior to .NET assemblies, Win32 dynamic linked libraries (DLLs) that contained code used by multiple applications were controlled by the operating system. Each DLL would be registered with Windows but only one version would be accessible at any time. This gave rise to the phrase "DLL Hell", which described the problems that occurred when two programs used the same DLL but required different versions. If the newer version of a DLL were compatible with an older version then both programs would work as expected. However, if the newer version broke the compatibility the older application would fail.

Assembly Versions

.NET assemblies remove the DLL Hell problem by holding their own version information and the name and version for all other, linked assemblies rather than allowing this to be dictated by the operating system. Multiple versions of a DLL may now be installed onto the target computer and the CLR will use a set of rules to determine which version will be used by the executing program.

Assembly Locations

In order for the CLR to identify the correct version of a linked assembly to use, it is allowed to look in three locations. The first location checked is the Global Assembly Cache (GAC). This is a machine-wide repository for assemblies that can be shared by multiple applications.

If the required assembly is not found in the GAC, the location of the application files is checked using a search process called probing. The base folder of the application is tested first, followed by any folder with the same name as the assembly. For example, if the assembly to be found is named "MyAssembly" and the application is installed in the C:\MyProgram folder, the following four paths would be probed:

C:\MyProgram\MyAssembly.dll
C:\MyProgram\MyAssembly\MyAssembly.dll
C:\MyProgram\MyAssembly.exe
C:\MyProgram\MyAssembly\MyAssembly.exe

Assemblies located in the application folder are known as private assemblies. This is because the assembly can only be used by the applications installed in the folder so they cannot be publicly shared. However, because the assembly does not need to be installed in the GAC, entire programs can be installed using simple file copying.

NB: Assemblies can be located in other application sub-folders but the folders must be added to the probing section of the app.config file.

Finally, assemblies can be installed using a CodeBase. This uses an entry in the machine's configuration file to identify a location on a network or FTP site where the assembly can be found and downloaded for its first use.

For each location searched, the CLR will attempt to find an exact match for the required assembly version first. If a perfect match is not found but a range of versions are acceptable, the CLR will attempt to match with other versions before failing.

Internal Access Modifier

When classes are declared as public, they can be utilised by other classes in the same assembly or any other assembly. The internal access modifier changes this behaviour to limit the class' use to other classes within the same assembly. Classes in other assembles are unaware of the presence of items marked as internal.

The internal access modifier can be applied to members of classes such as methods and properties. This allows individual members to be hidden from classes in other assemblies. In this case, a public class from another assembly may be instantiated but only the public members of that class, and not those marked as internal, will be accessible.

public class InternalExample
{
    public void ThisIsVisibleToOtherAssemblies() { }
    internal void ThisIsInvisibleToOtherAssemblies() { }
}

NB: If no access modifier is provided when declaring a class, the default visibility is internal.

Protected Internal

The behaviour of the internal access modifier for class members can be changed by using protected internal. Methods and properties marked as protected internal are hidden from other assemblies except where a class is derived from the class in question. For derived classes in other assemblies, internal methods are still hidden but protected internal methods are visible. Inheritance and derived classes are examined in the next instalment of the C# Object Oriented Programming tutorial.

Link to this Page28 February 2008
RSS RSS Feed