I apologize in advance if the title doesn\'t make sense. I\'m very new to appdomains and assembly loading and don\'t really know how to state what I\'m trying to ask.
<Big thanks to Hans Passant and dthorpe for explaining what was happening.
I found dthorpe's great explanation on how the JIT compiler works here: C# JIT compiling and .NET
To quote dthorpe here:
Yes, JIT'ing IL code involves translating the IL into native machine instructions.
Yes, the .NET runtime interacts with the JIT'ed native machine code, in the sense that the runtime owns the memory blocks occupied by the native machine code, the runtime calls into the native machine code, etc.
You are correct that the .NET runtime does not interpret the IL code in your assemblies.
What happens is when execution reaches a function or code block (like, an else clause of an if block) that has not yet been JIT compiled into native machine code, the JIT'r is invoked to compile that block of IL into native machine code. When that's done, program execution enters the freshly emitted machine code to execute it's program logic. If while executing that native machine code execution reaches a function call to a function that has not yet been compiled to machine code, the JIT'r is invoked to compile that function "just in time". And so on.
The JIT'r doesn't necessarily compile all the logic of a function body into machine code at once. If the function has if statements, the statement blocks of the if or else clauses may not be JIT compiled until execution actually passes through that block. Code paths that have not executed remain in IL form until they do execute.
The compiled native machine code is kept in memory so that it can be used again the next time that section of code executes. The second time you call a function it will run faster than the first time you call it because no JIT step is necessary the second time around.
In desktop .NET, the native machine code is kept in memory for the lifetime of the appdomain. In .NET CF, the native machine code may be thrown away if the application is running low on memory. It will be JIT compiled again from the original IL code the next time execution passes through that code.
With the information from that question, and the information from Hans Passant, it is very clear what is happening:
Main()
function) into native code. This
requires that it resolve all references.Main()
function (and it can't execute code that hasn't been compiled).LoaderLibrary.LoaderLibrary.Test();
and results in the error
Could not load file or assembly or one of its dependencies
The way to get around this as suggested by Hans Passant is to load your assemblies in a code block that gets JIT compiled earlier than any code block that references those assemblies.
By adding the [MethodImpl(MethodImplOptions.NoInlining)] to methods that reference the dynamically loaded assemblies, it will prevent the optimizer from trying to inline the method code.