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();
}
No comments:
Post a Comment