59
votes

I need to check the time amount to run GetTypes() after loading the dll. The code is as follows.

Assembly assem = Assembly.LoadFrom(file);
sw = Stopwatch.StartNew();
var types1 = assem.GetTypes();
sw.Stop();
double time1 = sw.Elapsed.TotalMilliseconds;

I'd like to unload and reload the dll to check the time to spend in running GetTypes() again.

  • How can I unload it? assem = null is good enough?
  • Is there an explicit way to call garbage collector to reclaim the resource allocated to assem?
6
Short answer is that you cannot unload Assemblies; you can only unload AppDomains. There are a number of limitations imposed when you load an Assembly into a new AppDomain, so I recommend doing some research on the topic to get a feel for how they work. That said, you can load the same Assembly again, even if the prior one was not unloaded. - Dan Bryant
Did someone tried what happens to the assemblies if you null them ? Do they get garbage collected or freed in any way ? I'm concerned about the memory. - Bitterblue
A very related post here. - RBT

6 Answers

72
votes

Can you use another AppDomain?

AppDomain dom = AppDomain.CreateDomain("some");     
AssemblyName assemblyName = new AssemblyName();
assemblyName.CodeBase = pathToAssembly;
Assembly assembly = dom.Load(assemblyName);
Type [] types = assembly.GetTypes();
AppDomain.Unload(dom);
48
votes

Instead of using LoadFrom() or LoadFile() you can use Load with File.ReadAllBytes(). With this it does not use the assembly file but will read it and use read data.

Your code will then look like

Assembly assem = Assembly.Load(File.ReadAllBytes(filePath));
sw = Stopwatch.StartNew();
var types1 = assem.GetTypes();
sw.Stop();
double time1 = sw.Elapsed.TotalMilliseconds;

From here We cannot unload the file unless all the domains contained by it are unloaded.

Hope this helps.:)

39
votes

Unfortunately you can not unload an assembly once it is loaded. But you can unload an AppDomain. What you can do is to create a new AppDomain (AppDomain.CreateDomain(...) ), load the assembly into this appdomain to work with it, and then unload the AppDomain when needed. When unloading the AppDomain, all assemblies that have been loaded will be unloaded. (See reference)

To call the garbage collector, you can use

GC.Collect(); // collects all unused memory
GC.WaitForPendingFinalizers(); // wait until GC has finished its work
GC.Collect();

GC calls the finalizers in a background thread, that's why you have to wait and call Collect() again to make sure you deleted everything.

4
votes

You can't unload assembly from the current AppDomain. But you can create new AppDomain, load assemblies into it, execute some code inside new AppDomain and then unload it. Check the following link: MSDN

3
votes

If you only want to load the initial assembly without any of its dependent assemblies, you can use Assembly.LoadFile in an AppDomain, and then unload the AppDomain when done.

Create a loader class to load and work with the assembly:

class Loader : MarshalByRefObject
{
    public void Load(string file)
    {
        var assembly = Assembly.LoadFile(file);
        // Do stuff with the assembly.
    }
}

Run the loader in a separate app domain like this:

var domain = AppDomain.CreateDomain(nameof(Loader), AppDomain.CurrentDomain.Evidence, new AppDomainSetup { ApplicationBase = Path.GetDirectoryName(typeof(Loader).Assembly.Location) });
try {
    var loader = (Loader)domain.CreateInstanceAndUnwrap(typeof(Loader).Assembly.FullName, typeof(Loader).FullName);
    loader.Load(myFile);
} finally {
    AppDomain.Unload(domain);
}
0
votes

Assembly cannot be unloaded unfortunately, and moreover - if you use appdomains - then it will prevent you to communicate with api's / assemblies of your main application.

Best description on problem can be found here:

Script Hosting Guideline http://www.csscript.net/help/Script_hosting_guideline_.html

If you want to run C# code without communication to your main application - then best approach is to integrate C# scripting API:

https://github.com/oleg-shilo/cs-script/tree/master/Source/deployment/samples/Hosting/Legacy%20Samples/CodeDOM/Modifying%20script%20without%20restart

And for integration you will need following packages:

C# script: http://www.csscript.net/CurrentRelease.html

Visual studio extension: https://marketplace.visualstudio.com/items?itemName=oleg-shilo.cs-script

If however you want to communicate from your C# script to your application - then using same appDomain with assembly name constantly changing is only way at the moment - but that unfortunately eats ram and disk space.

Code sample how to do it can be done - can be found from here:

https://github.com/tapika/cppscriptcore CsScriptHotReload.sln

And here is demo video:

https://drive.google.com/open?id=1jOECJj0_UPNdllwF4GWb5OMybWPc0PUV