Assemblies in .NET
Assemblies are the fundamental unit of deployment, versioning, reuse, activation, and security in the .NET Framework. An assembly is a collection of logically related types and resources that are compiled into a single DLL or EXE file.
What is an Assembly?
An assembly is the building block of all .NET applications. It's a collection of one or more files that contain the compiled code, metadata, and resources for a particular part of your application or library. Each assembly has a manifest that describes its contents, dependencies, and version information.
Key characteristics of assemblies:
- Self-Describing: Assemblies contain metadata that describes their contents, including types, members, and relationships.
- Unit of Deployment: Applications are deployed as a set of assemblies.
- Unit of Versioning: Assemblies have version information that the .NET runtime uses to manage different versions of the same component.
- Unit of Reuse: Assemblies can be shared and reused across multiple applications.
- Unit of Activation: The .NET runtime uses assemblies to load and execute code.
- Unit of Security: Security policies can be applied at the assembly level.
Assembly Manifest
Every assembly contains a special section called the assembly manifest. The manifest is metadata that describes the assembly itself and its dependencies.
The manifest includes information such as:
- The assembly's identity (name, version, culture, and public key token).
- A list of all the files that make up the assembly.
- The types defined in the assembly.
- A list of the assemblies that this assembly depends on.
- Information about permissions required by the assembly.
The manifest can be embedded directly within a module (a DLL or EXE file) or stored in a separate assembly manifest file (AMF). For most .NET applications, the manifest is embedded within the main executable file.
Types of Assemblies
Assemblies can be categorized into two main types:
1. Executable Assemblies (EXE)
These assemblies contain an entry point (a `Main` method) and can be directly executed by the operating system. They typically represent the main application. When you compile a C# or VB.NET project that creates an executable, it results in an assembly of this type.
// Example of an entry point in C#
namespace MyApplication {
class Program {
static void Main(string[] args) {
Console.WriteLine("Hello, Assemblies!");
}
}
}
2. Library Assemblies (DLL)
These assemblies contain types and code that are intended to be used by other assemblies. They do not have an entry point and cannot be executed directly. Examples include the .NET Framework class libraries (like System.dll) and third-party libraries you might use.
// Example of a class in a library assembly
namespace MyLibrary {
public class Calculator {
public int Add(int a, int b) {
return a + b;
}
}
}
Assembly Loading
When your application needs to use code from an assembly, the .NET Common Language Runtime (CLR) is responsible for loading it into memory. This process involves:
- Discovery: The CLR finds the required assembly using probing paths, the global assembly cache (GAC), or application-specific directories.
- Verification: The CLR verifies the assembly's integrity and manifest.
- Loading: The assembly's code is loaded into the application's domain.
- Execution: The CLR JIT-compiles the managed code and allows it to be executed.
Global Assembly Cache (GAC)
The Global Assembly Cache (GAC) is a machine-wide code cache used by the .NET Framework. Assemblies that are installed in the GAC can be shared by multiple applications. Assemblies must be strongly named (signed with a public/private key pair) to be placed in the GAC.
Benefits of the GAC include:
- Shared Components: Prevents multiple copies of the same library from being installed on a machine.
- Versioning: Allows different versions of an assembly to coexist.
- Security: Strong naming helps ensure the authenticity and integrity of assemblies.
Strong Naming
Strong naming provides a unique identity to an assembly. It consists of the assembly's simple name, version number, culture information, and a public key token generated from the public key used to sign the assembly. This mechanism helps prevent naming conflicts and ensures that an application is using the intended version of an assembly.
A strongly named assembly is signed with a private key, and its public key is embedded in the assembly itself. Other assemblies can then verify the signature using the public key, confirming the assembly's origin and integrity.
Conclusion
Understanding assemblies is crucial for developing, deploying, and managing .NET applications effectively. They are the fundamental units that encapsulate code, metadata, and resources, enabling versioning, reuse, and robust execution within the .NET ecosystem.