Windows has suffered greatly from "DLL Hell" and Microsoft tried to fix the issue in .NET, insofar as it can be fixed.
The solution includes the following features:
1. The norm is for developers to install any assemblies on which an application depends into the application directory.
2. All .NET applications have a dependency on the Framework classes, installed in the GAC (Global Assembly Cache). However, provided it is installed on the system, apps will by default use the same version of the Framework with which they were compiled.
3. Apps can also use shared assemblies stored in the GAC or elsewhere, but any such shared assemblies must be strongly named - a way of sigining assembles to ensure that they are the version they claim to be. The GAC supports multiple versions of the same assemblies, and these can run side-by-side.
4. Administrators can override defaults by specifying things like which version of the Framework an application uses.
However, the .NET versioning solution is not perfect. One problem is that Microsoft might release an update to a specific version of the Framework that breaks applications - this is a problem inherent to any shared code, and realistically you can't eliminate it completely.
Another problem is that .NET today still has many dependencies on unmanaged libraries such as MDAC (Microsoft Data Access Components). In a sense, .NET itself has a dependency on the unmanaged Windows API. So DLL Hell can't be eliminated; however 3rd party DLL Hell should be much improved with .NET.
The downside of NOT sharing assemblies is that you use more resources (not too important these days), and that your apps don't benefit from bug-fixes without a lot more effort (this is a real issue - see the recent JPG security flaw for an example).
Further reading: [msdn.microsoft.com