60

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?
prosseek
  • 182,215
  • 215
  • 566
  • 871
  • 5
    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 Jun 06 '11 at 21:26
  • 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 Nov 01 '13 at 11:26
  • A very related post [here](https://stackoverflow.com/q/123391/465053). – RBT Nov 16 '17 at 01:17

6 Answers6

72

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);
Rene de la garza
  • 1,259
  • 1
  • 8
  • 7
  • 11
    Fails for me : it throws at dom.Load a "FileNotFoundException". The funny thing is that the file really exists, and the exception message contains the exact version of the file (which is not 1.0.0.0, so i'm sure it eventually found my file...). – AFract May 23 '14 at 14:49
  • 2
    In my case the "FileNotFoundException" was caused by one of the referenced assemblies. I ended up using Assembly.ReflectionOnlyLoadFrom in conjuction with a method that loads all the referenced assemblies into the reflection only context. – Xtr Jan 15 '15 at 14:51
  • for some reason this loads the assembly with the same name (as mentioned) present in the application directory, rather than the assembly in the directory mentioned...any idea why this might happen ?? – mrid Jan 31 '18 at 18:13
  • Unfortunately this does not work for .NET Core as AppDomains are not implemented. – Roger Far Sep 19 '18 at 14:17
  • System.PlatformNotSupportedException: 'Secondary AppDomains are not supported on this platform.' – Stef Heyenrath Dec 22 '22 at 20:02
53

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.:)

Hardik
  • 764
  • 5
  • 10
  • 2
    You can't pass an byte array to `Assembly.LoadFrom()`. I edited your post, but somebody changed it back. – roli09 Feb 21 '17 at 09:07
  • 1
    @roli09 Actually, you can, and it works very well. I load dlls as plugins dynamically and then when its the moment of uninstall them I should erase the file. The Hardik method works perfectly, no AppDomain hassles or something. roli09 You should test the code before doing any modifications to comments, this should have been the right answer, – tapatio Aug 05 '17 at 22:34
  • 2
    @tapatio It works because Hardik is using `Assembly.Load` and not `Assembly.LoadFrom` anymore. – roli09 Aug 07 '17 at 08:13
  • Does this leak memory when `assem` is set to null? – Professor of programming Mar 27 '18 at 00:58
  • 2
    @Hardik is correct: The Assembly Load method holds a reference to the file loaded, preventing that file from being overwritten or deleted by another process. So it's a clever workaround to use the File object's ReadAllBytes method, which does not hold a reference. Once those bytes are read and the file is closed, the result is passed to the Assembly Load method. Quite clever! – Jazimov Jul 17 '18 at 00:55
  • 1
    This is now the best solution although it currently has less than half of the points the leading answer. – pauldendulk May 14 '19 at 08:19
  • 1
    @Hardik Thank you! Thank you so much! It saved my day! – eXPerience Jan 07 '20 at 20:36
  • 1
    But this wont work when your assembly has dependencies. We will get LoaderExceptions. What to do in this case? – coding_cs Jan 11 '22 at 18:11
39

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.

slfan
  • 8,950
  • 115
  • 65
  • 78
  • 2
    I'm loading (and potentially unloading) dozens of dlls. What are the performance implications (and other relevant implications) of creating, say, 70 AppDomains? Should I avoid doing that if possible? – pushkin Sep 05 '18 at 15:13
  • I suppose if it's just for this operation that would work, but in a situation where I actually want my application to instantiate some objects from the assembly, use them, destroy them and unload the assembly, wouldn't loading it in a separate AppDomain cause some accessibility headache? – Matheus Rocha Dec 28 '19 at 12:29
4

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);
}
Edward Brey
  • 40,302
  • 20
  • 199
  • 253
4

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

Yuriy Rozhovetskiy
  • 22,270
  • 4
  • 37
  • 68
0

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

TarmoPikaro
  • 4,723
  • 2
  • 50
  • 62