Finally, we'll go through several demos and see what the fuss is about versioning and how assemblies and .NET versioning policies help you to avoid DLL hell.
Tutorial "Querying Metadata with Reflection," described assemblies as physical files that consist of one or more portable executable (PE) files generated by a .NET compiler. In the context of that tutorial, that definition was acceptable. However, assemblies are more complicated than that. Here's a more complete definition: an assembly is the packaging of a manifest, one or more modules, and, optionally, one or more resources. Using assemblies allows you to semantically group functional units into a single file for purposes of deployment, versioning, and maintenance.
All PE files that use the .NET runtime consist of an assembly or a group of assemblies. When you compile an application by using the C# compiler, you're actually creating an assembly. You might not realize that fact unless you're specifically attempting to place multiple modules in a single assembly or taking advantage of some assembly-specific feature such as versioning. However, it's important to realize that any time you build an EXE or a DLL (using the
/t:library switch), you're creating an assembly with a manifest that describes the assembly to the .NET runtime. In addition, you can create a module (using the
/t:module switch) that is really a DLL (with an extension of .netmodule) without a manifest. In other words, although logically it's still a DLL, it does not belong to an assembly and must be added to an assembly either by using the
/addmodule switch when compiling an application or by using the Assembly Generation tool. You'll see how to do this later in the section "Building Assemblies."