Sorry, for most apps, this isn't exactly a supported scenario, though it probably could be done if you're willing to do a ton of additional plumbing. I have one possible solution, then one more speculative one.
First, you could throw out the satellite assembly model and read .resources files directly. Note that ResourceManager.CreateFileBasedResourceManager may be useful for you here, though you might need to explicitly read every resource from the .resources file at once (bloating your working set) so that you can close the .resources file. You may have to create your own subclass of ResourceSet and/or ResourceReader to get the lifetime management done correctly. There's a lot of flexibility here, especially if you do not load satellite assemblies. I'm confident that something approximating what you want could be built here within a day or three.
Now for my more speculative approach that works with the existing satellite assembly model. I wouldn't recommend this approach for the faint of heart, or for anyone that hasn't read at least one good (Jeffrey Richter-caliber) book on CLR internals. There are three pieces you'd need to do:
1) Replace the file on disk
2) Convince the CLR to load the new satellite assembly
3) Tell the ResourceManager to reload resources
For #1, you can play some games with moving the old file to a temp location. This hacky solution that creates races if you're running multiple copies of your code simultaneously is pretty obvious.
For #3, the ResourceManager has a ReleaseAllResources() method that will flush all of its internal caches & close handles, etc.
For #2, here's where you run into problems. The CLR loader won't let you load an assembly then reload it with different bits, at least not within the same appdomain. You'd have to run the relevant part of your code within a second appdomain, unload that appdomain to unload the assembly, then create a third appdomain to run your code again. This of course gets into remoting, marshal-by-ref objects, potential loader context problems, etc. (Our Orcas AddIn Model will make some of this easier, but that's even more plumbing.) Also, it means multiple copies of your application running simultaneously on the same machine won't work reliably, unless you create a named mutex on the machine and acquire it the first time you look up a resource in every appdomain. If you're willing to live with a bunch of work, read on.
The only condolence I can offer is if you're recycling your appdomains, at least you don't have to worry about #3, since you'll be forced to allocate a new ResourceManager instance, and after you unload the appdomain (and explicitly trigger a GC & call GC.WaitForPendingFinalizers()!), then replacing the file on disk will also be easy. Here's a rough idea of how this might work, though honestly, I don't know if anyone has ever tried this (I certainly haven't):
AppDomain domain = AppDomain.CreateDomain(...); // Make one worker appdomain
Foo myAddIn = domain.Create(...); // Or use the Orcas AddIn Model
String s = myAddIn.Lookup(); // A cross-appdomain call to a method that uses the ResourceManager.
domain.Unload();
// AppDomain Unload is sort of asynchronous now - the work is now moved to the finalizer thread. Trigger a GC then block until the finalizers have completed.
GC.Collect();
GC.WaitForPendingFinalizers();
// Replace the satellite assembly on disk here! Also, should it be updated in the GAC
domain = AppDomain.CreateDomain(...); // Make a second worker appdomain.
myAddIn = domain.Create(...);
s = myAddIn.Lookup();
I wouldn't imagine that any devs coming after you would want to support this approach.
Brian Grunkemeyer
CLR Speculative Feature Theorizing Person