I was doing a little experimentation recently related to building dynamic assemblies in .NET. One thing I found interesting was that if you create an instance of a dynamic Type then use the “dynamic” keyword to consume it, your dynamic assembly will never be unloaded.
That sort of defeats the purpose if you ask me. I was back to using reflection to call my dynamic objects. Not fun.
So I whipped up a quick dynamic coercion routine. Basically what it does is takes any instance and any interface and builds a wrapper object that implements the interface and calls a matching method on the instance. The instance does not need to implement the interface or even know about it. The coercion method will give the appearance of casting the instance into a interface it doesn’t really implement.
var instance = CreateDynamicAdder(); var adder = instance.Dynamic().Coerce<IAdder>();
Dynamic() is an extension point for type System.Object. Coerce is a extension method:
public static T Coerce<T>(this IDynamicExtensionPoint extensionPoint) { if (!typeof(T).IsInterface) throw new ArgumentException("Type T must be an interface."); if (extensionPoint == null) throw new ArgumentNullException("extensionPoint"); var assemblyBuilder = Dynamic.CreateDynamicAssembly(); var wrapperType = BuildWrapperType( assemblyBuilder, typeof(T), extensionPoint.Instance.GetType()); return (T)Activator.CreateInstance(wrapperType, extensionPoint.Instance); }
So in the first code snippet I get an object that has a method I want to call that I know looks like this:
int Add(int x, int y)
I don’t know the exact Type and I don’t have any interfaces I can cast it into, but I know that this method is there, possibly through documentation, convention or debugging. The Coercion routine will allow me to call dynamic objects with an illusion of strong typing.
So I create an interface that matches the signature I am expecting.
public interface IAdder { int Add(int x, int y); }
And coerce the instance into this interface. What’s interesting is that when I do it this way, both the dynamic assembly creating the adder as well as the dynamic assembly creating the wrapper are unloaded when I drop references to them.
static void Main(string[] args) { var numberOfAssemblies = AppDomain.CurrentDomain.GetAssemblies().Count(); Console.WriteLine("Starting assembly count: " + numberOfAssemblies); var instance = CreateDynamicAdder(); var adder = instance.Dynamic().Coerce<IAdder>(); int z = adder.Add(7, 11); Console.WriteLine("Add result: " + z); instance = null; adder = null; numberOfAssemblies = AppDomain.CurrentDomain.GetAssemblies().Count(); Console.WriteLine("Assembly count before collect: " + numberOfAssemblies); GC.Collect(); GC.WaitForPendingFinalizers(); numberOfAssemblies = AppDomain.CurrentDomain.GetAssemblies().Count(); Console.WriteLine("Assembly count after collect: " + numberOfAssemblies); Console.ReadKey(true); }
Here is the resulting screen shot, proving the dynamic assemblies are unloaded.
Download the full sample here:
You must log in to post a comment.