Thursday, May 15, 2014

Dynamic generics in .NET

Ever found yourself in a situation where you're given a System.Type object and need to call some generic method, say M<T>, with that type as argument? Sticky situation.
       static void CallIt( Type type ) {
             this.CallMe<type>(); // How do I do that?!
       }

       static void CallMe<T>() {
             Console.WriteLine( "Boo-ya!" );
       }
You can't just do it directly. Generic instantiation (i.e. adding <T> to signature) is a compile-time construct, while Type object only exists at runtime.

It would be preferable, of course, to make the whole CallIt method generic and not pass a Type object at all. But you can't always do that. Examples include all kinds of late binding - from dependency injection to object serialization.
Fortunately, .NET framework is there to help you. You can make yourself a generic proxy with non-generic interface, and then create instances of it dynamically at runtime.
Here's an illustration:
       interface IProxy {
             void CallIt();
       }

       class Proxy<T> : IProxy {
             public void CallIt() { CallMe<T>(); }
       }

       static void CallIt( Type type ) {
            var proxyOfT = typeof( Proxy<> ).MakeGenericType( type );
            var proxy = Activator.CreateInstance( proxyOfT ) as IProxy;
            proxy.CallIt();
       }

Notice how I first construct a generic type Proxy<T> - dynamically, at runtime, - using the Type.MakeGenericType method, and then call Activator.CreateInstance to create an instance of that type. 

Of course, because Activator.CreateInstance returns an object, I have to then cast that object to IProxy. I know this cast will succeed, because my class Proxy<T> does implement that interface.

Now all that's left to do is call IProxy.CallIt - and voila! - I am now inside Proxy<T>.CallIt implementation, where I'm free to use the generic parameter as I please.

Of course, to avoid the performance hit of Activator.CreateInstance every time, you probably want to cache the proxy instance. To make the whole thing thread-safe, I will use ConcurrentDictionary for that purpose:

       static readonly ConcurrentDictionary<Type, IProxy> _proxies 
          = new ConcurrentDictionary<Type, IProxy>();

       void CallIt( Type type ) {
             var proxy = _proxies.GetOrAdd( type, _ => {
                    var proxyOfT = typeof( Proxy<> ).MakeGenericType( type );
                    return Activator.CreateInstance( proxyOfT ) as IProxy;
             } );
             proxy.CallIt();
       }