Assemblies and the Global Assembly Cache in C#Assemblies in the .Net platform solve the "DLL Hell" problem that has existed for as long as libraries have existed.
This article is part of a series of articles. Please use the links below to navigate between the articles.
- Learn to Program in C# - Full Introduction to Programming Course
- Introdution to Programming - C# Programming Fundamentals
- Guide to C# Data Types, Variables and Object Casting
- C# Operators: Arithmetic, Comparison, Logical and more
- Application Flow Control and Control Structures in C#
- Introduction to Object Oriented Programming for Beginners
- Introduction to C# Object-Oriented Programming Part 2
- C# Collection Types (Array,List,Dictionary,HashTable and More)
- Error and Exception Handling in C#
- Events, Delegates and Extension Methods
- Complete Guide to File Handling in C# - Reading and Writing Files
- Introduction to XML and XmlDocument with C#
- What is LINQ? The .NET Language Integrated Query
- Introduction to Asynchronous Programming in C#
- Working with Databases using Entity Framework
- All About Reflection in C# To Read Metadata and Find Assemblies
- Debugging and Testing in C#
- Introduction to ASP.Net MVC Web Applications and C#
- Windows Application Development Using .Net and Windows Forms
- Assemblies and the Global Assembly Cache in C#
- Working with Resources Files, Culture & Regions in .Net
- The Ultimate Guide to Regular Expressions: Everything You Need to Know

Assemblies in the .Net platform solve a problem that has existed for as long as libraries have existed. The problem, known as "DLL Hell", can be caused by one application overwriting a shared library of another application, usually with a different version.
Imagine you write an application with a shared library and then, years later, create a new application with an updated library. What do you do with the new library so as not to break old programs? You could give it a different name, store it in a different location, or overwrite the old ones.
Problems can also arise when a DLL is registered on a computer and, for some reason, the information is corrupted or modified so that the reference no longer links to the library. Registering a DLL in the system registry stores information about that library so other applications can access the data from a central store.
.Net assemblies overcome these issues by storing information about themselves rather than having that information stored in a central location. The assembly will hold information about itself, such as version and vendor.
Microsoft has defined an assembly and its role in .NET as:
In the .NET Framework, an assembly is a physical unit that can be executed, deployed, versioned, and secured. All .NET Framework applications contain one or more assemblies
Versioning
The platform will assist you by providing several features to ensure proper versioning between components in an application:
- Enforce versioning rules so that an application that needs version 1.1.1.1 will not end up getting version 1.0.0.0.
- Shared assemblies are signed with a strong name (which includes a public key, a simple name, a version, and a culture) to make them unique. Assemblies that are not shared do not require strong names.
The information the assembly holds about itself is the metadata stored in the manifest. The manifest is like the unique fingerprint of the assembly.
Assembly Deployment
Assemblies can be deployed using various methods, including direct file copy (xcopy), Windows Installer setup projects, and Merge Modules setup projects.
To directly copy an assembly, all you need to do is copy the file to the application's executable or bin folder, and it is available to use. However, this can only be done with a private assembly. Shared assemblies need to be installed in the machine's Global Assembly Cache (GAC) using the gacutil
tool or by copying the assembly to the GAC folder within Windows Explorer. Installation in the GAC results in a uniquely named folder and the assembly files are copied into that folder.
Windows installer and merge module projects will be installed for the user, and these are the recommended methods for deployment.
What Is in the Assembly?
The assembly contains the intermediate code, resources, and metadata for itself. We can have a look inside the assembly using the ildasm
(Intermediate Language Disassembler) tool that comes as part of Visual Studio. To access it, open the Visual Studio Command Prompt and type ildasm.exe
. This will launch a Windows application that you can use to explore any .Net application.
Once launched, you can open any .Net application and view information within the file, including the manifest, types, classes, namespaces, methods and source code.
Visual Studio provides easy methods for creating and using a .Net assembly. This tutorial will look at creating an assembly and how to incorporate different methods into our projects.
Creating a Private Assembly
We shall first start with a blank Visual Studio C# solution. Add a new class library to the solution, then build and run it. You will notice that although the project was built successfully, you cannot run the application. Unlike a console application, a class library has no entry points. Class libraries are built into assemblies that merely hold classes, code, and resources.
Add TestMethod in the class library to show a "Hello World!" message box. I changed the default namespace and class name in this example to better reflect the assembly's purpose.
using System;
using System.Text;
using System.Windows.Forms;
namespace TestClassLibrary
{
public static class TestClass
{
public static void TestMethod()
{
MessageBox.Show("Hello World");
}
}
}
If you look inside the bin/Debug folder for the project, you will see a .dll file instead of the usual .exe. This is our new assembly.
To test our assembly, we must create a project capable of running, such as a Console, Windows, or ASP.Net web application. Other class libraries can also use the method I describe below, but we still need to create a console application to call that class library!
Add a console application project to the current solution and set it as the default start-up project. This will cause the console application to run when we click on play or launch the project. Let's try and use the method we just created:
using System;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
TestClassLibrary.TestClass.TestMethod();
}
}
}
When we try to build the application, you will notice that it failed with a message stating that
"The type or namespace name could not be found (are you missing a directive or an assembly reference?)"
We are seeing this message because we haven't told the console application about the class library (even though it is part of the same solution). We need to add a reference to the class library to do this. This can be done by right-clicking on the console application in Solution Explorer and selecting "Add Reference".

You can select an existing .Net assembly from this box or browse a file or project. Click the Projects tab and select our class library.
We must add the namespace to the using statements or get a refactor.
using System;
using System.Collections.Generic;
using System.Text;
using TestClassLibrary;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
TestClass.TestMethod();
}
}
}
When you build the project, it should compile and run properly.
Using third-party Assemblies
To use a third-party assembly, you can use the same method as above: add a reference to the assembly and add in a clause (or explicitly reference classes and data types).
Creating a Shared Assembly
The procedure for creating a shared assembly is the same as for a private assembly, except that a few extra steps are involved before you can use it. All shared assemblies must be strongly named before being installed into the global assembly cache. Strongly named assemblies ensure the assembly has a unique manifest consisting of versioning information and a public key.
Satellite Assemblies
If you plan on localising your application (making your application customisable for different languages and cultures), you should use satellite assemblies and neutral code.
Neutral code does not contain any hard coding or language/culture-specific code. For example, there are no hardcoded strings or symbols, no hardcoded date formatting or region-specific calculations, e.g. VAT.
When creating a satellite assembly, you should work with the naming convention suggested by Microsoft, The format of which is
where the culture identifier consists of ISO language and culture strings. You can find a list of culture codes in this list List of .Net Culture Codes.
Satellite assemblies use a hub and spoke design, with your neutral code at the centre (hub) and various culture-specific satellite assemblies plugging into it (spokes). As a result, the runtime requires that you place resources in specific locations to be easily located and used. If you do not compile and name resources as expected, or if you do not place them in the correct locations, the common language runtime will not be able to locate them. The compiler will first look in the global assembly cache for any localised satellite assemblies. If it does not find any, it will look in the following locations:
- The global assembly cache for an assembly matching the requested culture for the application
- The directory of the currently executing assembly for a directory matching the requested culture.
- The global assembly cache again, this time for the parent assembly of the requested resource.
- The runtime next checks the directory of the currently executing assembly to see if it contains a parent directory. If a parent directory exists, the runtime searches the directory for a valid satellite assembly for the parent culture.
- The runtime next searches parent assemblies, as in the previous step, through many potential levels.
- The default culture is used if a resource is not found.
Creating Satellite Assemblies
By definition, satellite assemblies can only contain resources. They cannot contain any executable code. Satellite assemblies are compiled from resource files, which can be added to a project from the add new item menu.
Resource files can be directly compiled into the executable; however, for scalability, a satellite assembly allows new cultures and languages to be added without recompilation or distribution. Satellite assemblies can be used so the client downloads and installs a common executable and then downloads and installs one satellite assembly for their location.
Satellite assemblies are created using the Assembly Linker (al.exe) command line tool.
Use the following command to create a satellite assembly for the application TestApp from the file strings.ja-JP.resources
al /t:lib /embed:strings.ja-JP.resources /culture:ja-JP /out:TestApp.ja-JP.resources.dll
The output filename can be anything since the runtime does not look at the filename to gather the culture. Therefore, you must specify the culture using the /culture switch.
For more details on how to localise an application, please refer to the Resources and Localisation tutorials.
Using Satellite Assemblies
Satellite Assemblies are loaded by the runtime as specified above. To use a satellite assembly and load a culture-specific version, use the ResourceManager class. This class will load the appropriate assembly for the current or default culture if none are present.
static ResourceManager rm = new ResourceManager("strings", Assembly.GetExecutingAssembly());
This line of code will load the strings resource file from the assembly created above, assuming that ja-JP is the current system locale.
You can access or modify the current culture using the CultureInfo class:
CultureInfo ci = Thread.CurrentThread.CurrentCulture; Thread.CurrentThread.CurrentUICulture = en.US;
Creating Strong Named Assemblies in Microsoft .Net
Strong Names are a way of uniquely identifying assemblies written for the .Net platform. They are not, as commonly believed, a tool for enabling security.
Strong Names use cryptographic techniques (RSA, El Gamal, etc...) together with a hashing algorithm (Md5, SHA, Blowfish, etc...) to create a digital signature of an assembly. A strong name also consists of a public/private key pair used to encrypt/decrypt the assembly.
Strong Names allow assemblies to be versioned and authenticated. Signed Assemblies allow the assembly to have a unique identifier, which allows multiple versions of an assembly to exist on a machine without colliding.
If you are developing assemblies that will be installed into the Global Assembly Cache (GAC), then the assembly must be strongly named.
The other benefit of creating strongly named assemblies is that the assembly can be authenticated. Because the strong name contains the author's private key, you can be sure that a particular author created the assembly. A strongly named assembly can only reference other strongly named assemblies to ensure integrity.
Signing an Assembly
The first thing that you need to do is create a public/private key pair that will be used to encrypt the assembly.
To create a key/pair you can use the strong name utility:
sn -k <file name>
This will create the file containing the keys defaulting to RSA encryption. Keep this file safe; you will need it to sign future assemblies if you wish to authenticate them.
There are a few different methods for strongly naming an assembly, either from within Visual Studio or the command line.
If you wish to give developers access to the private key, you can add an attribute to the project. This code should go within the AssemblyInfo.cs file.
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("c:\mykey.sn")]
You can delay signing the assembly if you do not wish to give your developers access to your private key.
[assembly: AssemblyDelaySign(true)]
[assembly: AssemblyKeyFile("c:\mykey.sn")]
Then, the assembly must have verification turned off. Otherwise, the assembly will not load on the developer's machine:
c:\> sn -Vr myassembly.dll
When the code is ready to be released, you can fully sign the assembly using the sn.exe tool.
c:\> sn -R myassembly.dll mykey.sn