How does .NET locate the dll of the namespace I'm using
?
yeah, we do mention the path in /referene:c:\program files** but after building & deploying and when the software is installed on some user's machine. It may not be at the same path as I (developer) mentioned it 开发者_StackOverflow中文版would be. I mean it could be some where else right?
- So, how does .NET find it?
- How does .NET know at the runtime what all namespaces are there in a dll?
- What if the user has the same dll but he renamed it? Does .net fails to load it then?
You can see the various probing steps in action if you run fuslogvw.exe (from the .NET SDK) and enable logging of all binds to disk. You will be able to see the various paths that .NET CLR uses to resolve assembly references. The rule of thumb though is to try the Global Assembly Cache first then check the local directory along with a bunch of other alternate paths.
Technically, there aren't any namespaces in the DLL.
On CLR level, there are no namespaces at all, only full class names. A CLR class name can consist of an arbitrarily long sequence of any Unicode characters - e.g. @#$%
would be a perfectly fine class name, as far as CLR is concerned.
Now, by convention (CLS, to be specific), class names are restricted to certain Unicode characters (alphanumerics and _
, and a bunch of other exotic Unicode categories - see the spec for more info) and dot, and dot is used to denote namespaces. This is purely a convention between compilers (and other tools).
So, whenever an assembly references some type for any reason, it simply uses its complete CLR name, such as System.String
. But there is more - in fact, it uses a fully qualified name, which includes an assembly as well. You can see those if you look at ildasm
output - they look something like [mscorlib]System.String
, so the runtime knows where to look.
In other words, CLR really sees assembly mscorlib.dll having class System.String
, and assembly B.exe referencing that class as [mscorlib]System.String
. Your using
statement doesn't generate any code in the output DLL/EXE on its own; it's there just so that you don't have to write System.String
all the time.
It's compiler's job to translate your code saying String
, in a scope of a using System;
statement, in a project which references mscorlib.dll
, to [mscorlib]System.String
. It's all done at compile-time. The only thing CLR does at runtime is resolving mscorlib
to locate the actual mscorlib.dll on disk (and the other answer explains how exactly that happens).
Assembly location rules are explained in http://msdn.microsoft.com/en-us/library/yx7xezcf.aspx
The names of assemblies and namespaces are unrelated concepts.
Compiled assembly reference other assemblies by containing their name.
When an assembly is loaded, its references are loaded by searching for the name as described here. The most common location for assemblies are the Global Assembly Cache of a machine, and the base directory of an application.
After loading the assembly and all its references (and the references of the references, etc) the runtime loads the required types from the assemblies.
For example, if you use the type String
as in
using System;
...
String x = "Hello World";
the C# compiler looks up String
and resolves it to the System.String
type in the mscorlib
assembly, and transforms the code to something similar to this:
[mscorlib]System.String x = "Hello World";
So when the statement is executed the runtime knows both the full name of the type System.String
and the name of the assembly that contains the type.
The only answer that answers the question is Pavel Minaev's comment.
Take the following basic C# Program.
using System;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello");
}
}
}
The compiler resolves Console
by looking at all the assemblies that are referenced using /r:
in the compilation command line. If there are more than 2 definitions of Console
for the global namespace in the assemblies that are referenced, which is extended using the using
statements, then there will be a compiler error -- to resolve this you would need to explicitly use System.Console
to indicate the System
namespace rather than the global namespace.
Similarly, if you use System.Console
, then if there is more than one definition of System.Console
in the assemblies that are referenced then there is a compiler error. This would either be 2 classes in the System
namespace with the same name, which would need to be resolved by the programmer by renaming that, or defining a class called System
in the global namespace that has been extended by using System
, where System.Console
is a subclass, which would be impossible to distinguish even if it were using global::
Once it has found a referenced assembly that contains the definition, it can now work with that definition and produce the correct IL.
When you compile this simple application, you get the following in ildasm
:
// Metadata version: v4.0.30319
.assembly extern /*23000001*/ mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly /*20000001*/ ConsoleApp1
{
.custom /*0C000001:0A000001*/ instance void [mscorlib/*23000001*/]System.Runtime.CompilerServices.CompilationRelaxationsAttribute/*01000001*/::.ctor(int32) /* 0A000001 */ = ( 01 00 08 00 00 00 00 00 )
.custom /*0C000002:0A000002*/ instance void [mscorlib/*23000001*/]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute/*01000002*/::.ctor() /* 0A000002 */ = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
// --- The following custom attribute is added automatically, do not uncomment -------
// .custom /*0C000003:0A000003*/ instance void [mscorlib/*23000001*/]System.Diagnostics.DebuggableAttribute/*01000003*/::.ctor(valuetype [mscorlib/*23000001*/]System.Diagnostics.DebuggableAttribute/*01000003*//DebuggingModes/*01000004*/) /* 0A000003 */ = ( 01 00 07 01 00 00 00 00 )
.custom /*0C000004:0A000004*/ instance void [mscorlib/*23000001*/]System.Reflection.AssemblyTitleAttribute/*01000005*/::.ctor(string) /* 0A000004 */ = ( 01 00 0B 43 6F 6E 73 6F 6C 65 41 70 70 31 00 00 ) // ...ConsoleApp1..
.custom /*0C000005:0A000005*/ instance void [mscorlib/*23000001*/]System.Reflection.AssemblyDescriptionAttribute/*01000006*/::.ctor(string) /* 0A000005 */ = ( 01 00 00 00 00 )
.custom /*0C000006:0A000006*/ instance void [mscorlib/*23000001*/]System.Reflection.AssemblyConfigurationAttribute/*01000007*/::.ctor(string) /* 0A000006 */ = ( 01 00 00 00 00 )
.custom /*0C000007:0A000007*/ instance void [mscorlib/*23000001*/]System.Reflection.AssemblyCompanyAttribute/*01000008*/::.ctor(string) /* 0A000007 */ = ( 01 00 00 00 00 )
.custom /*0C000008:0A000008*/ instance void [mscorlib/*23000001*/]System.Reflection.AssemblyProductAttribute/*01000009*/::.ctor(string) /* 0A000008 */ = ( 01 00 0B 43 6F 6E 73 6F 6C 65 41 70 70 31 00 00 ) // ...ConsoleApp1..
.custom /*0C000009:0A000009*/ instance void [mscorlib/*23000001*/]System.Reflection.AssemblyCopyrightAttribute/*0100000A*/::.ctor(string) /* 0A000009 */ = ( 01 00 12 43 6F 70 79 72 69 67 68 74 20 C2 A9 20 // ...Copyright ..
20 32 30 32 31 00 00 ) // 2021..
.custom /*0C00000A:0A00000A*/ instance void [mscorlib/*23000001*/]System.Reflection.AssemblyTrademarkAttribute/*0100000B*/::.ctor(string) /* 0A00000A */ = ( 01 00 00 00 00 )
.custom /*0C00000B:0A00000B*/ instance void [mscorlib/*23000001*/]System.Runtime.InteropServices.ComVisibleAttribute/*0100000C*/::.ctor(bool) /* 0A00000B */ = ( 01 00 00 00 00 )
.custom /*0C00000C:0A00000C*/ instance void [mscorlib/*23000001*/]System.Runtime.InteropServices.GuidAttribute/*0100000D*/::.ctor(string) /* 0A00000C */ = ( 01 00 24 64 66 65 35 65 36 32 61 2D 65 61 31 33 // ..$dfe5e62a-ea13
2D 34 66 37 64 2D 39 36 39 32 2D 37 35 39 39 64 // -4f7d-9692-7599d
31 31 66 31 63 36 61 00 00 ) // 11f1c6a..
.custom /*0C00000D:0A00000D*/ instance void [mscorlib/*23000001*/]System.Reflection.AssemblyFileVersionAttribute/*0100000E*/::.ctor(string) /* 0A00000D */ = ( 01 00 07 31 2E 30 2E 30 2E 30 00 00 ) // ...1.0.0.0..
.custom /*0C00000E:0A00000E*/ instance void [mscorlib/*23000001*/]System.Runtime.Versioning.TargetFrameworkAttribute/*0100000F*/::.ctor(string) /* 0A00000E */ = ( 01 00 1C 2E 4E 45 54 46 72 61 6D 65 77 6F 72 6B // ....NETFramework
2C 56 65 72 73 69 6F 6E 3D 76 34 2E 36 2E 31 01 // ,Version=v4.6.1.
00 54 0E 14 46 72 61 6D 65 77 6F 72 6B 44 69 73 // .T..FrameworkDis
70 6C 61 79 4E 61 6D 65 14 2E 4E 45 54 20 46 72 // playName..NET Fr
61 6D 65 77 6F 72 6B 20 34 2E 36 2E 31 ) // amework 4.6.1
.hash algorithm 0x00008004
.ver 1:0:0:0
}
.module ConsoleApp1.exe
// MVID: {5BC9CD36-3807-4339-8AAD-6E73A42CE87B}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00020003 // ILONLY 32BITREQUIRED
// Image base: 0x00000000003B0000
.class /*02000002*/ private auto ansi beforefieldinit ConsoleApp1.Program
extends [mscorlib/*23000001*/]System.Object/*01000010*/
{
.method /*06000001*/ private hidebysig static
void Main(string[] args) cil managed
// SIG: 00 01 01 1D 0E
{
.entrypoint
// Method begins at RVA 0x2050
// Code size 13 (0xd)
.maxstack 8
IL_0000: /* 00 | */ nop
IL_0001: /* 72 | (70)000001 */ ldstr "Hello" /* 70000001 */
IL_0006: /* 28 | (0A)00000F */ call void [mscorlib/*23000001*/]System.Console/*01000011*/::WriteLine(string) /* 0A00000F */
IL_000b: /* 00 | */ nop
IL_000c: /* 2A | */ ret
} // end of method Program::Main
.method /*06000002*/ public hidebysig specialname rtspecialname
instance void .ctor() cil managed
// SIG: 20 00 01
{
// Method begins at RVA 0x205e
// Code size 8 (0x8)
.maxstack 8
IL_0000: /* 02 | */ ldarg.0
IL_0001: /* 28 | (0A)000010 */ call instance void [mscorlib/*23000001*/]System.Object/*01000010*/::.ctor() /* 0A000010 */
IL_0006: /* 00 | */ nop
IL_0007: /* 2A | */ ret
} // end of method Program::.ctor
} // end of class ConsoleApp1.Program
As can be seen, the assembly that contains the type in a certain namespace that is being referenced is resolved by the compiler, so the runtime does not need to search the manifests of all of the dependent assemblies. Instead, looks for the assembly in the square brackets and then uses the manifest in the image that is currently running to acquire more details. It then searches the global assembly cache if it is strong named and then it searches certain directories for a .config
file or for the assemblies themselves. On Mono, these directories are the directory that contains the image that is currently being executed on the virtual machine and the MONO_PATH
environment variable -- it searches the GAC last.
The runtime dynamically loads assemblies when an object of type of a class in the assembly is first referenced as opposed to instantiated – the instantiation is deferred. When you use DllImport
, the .dll
is also dynamically loaded when it is required by the runtime. For internal calls, you would need to internally call your own native function from C# that dynamically loads a .dll
using LoadLibrary
and then get GetProcAddress
to make the internal call binding, before making the real call.
An extern alias
can be used to access an assembly that is referenced using /r:
explicitly: /r:GridV1=grid.dll
and then extern alias GridV1
and then GridV1::Namespace.Class
. The ::
operator accesses a member of an aliased namespace, which is either the global
alias, an extern alias, or an alias created by the using alias directive. These alias directives do not extend the global namespace unlike the regular using
or using static
.
精彩评论