4
votes

I have a simple generic error handler IErrorHandler<T> where T is an exception. I'm using structuremap to get the concrete implementation of the handler based on the exception raised in Application_Error. So here is the code I use to get the handler.

var exceptionType = ex.GetType();
var handlerType = typeof(IErrorHandler<>).MakeGenericType(exceptionType);

var handler = IoCContainer.GetInstance(handlerType); // this returns an object

At this point, Structuremap has returned the correct implementation. But handler is an object, so I can't call any functions that the interface has. I can use reflection to find the Handle method, but that's fragile.

var handle = handlerType.GetMethod("Handle");
handle.Invoke(handler, new[] {ex});

Is there a better way to cast an open generic when the type the generic is using is only known at runtime?

1
Is the Handle method itself generic? - bornfromanegg
That´s the drawback on using reflection. There´s no way to get a compile-time-type, you´re allways stuck on object. You can only cast to some non-generic base-interface that your generic ones derives from. - HimBromBeere

1 Answers

3
votes

That´s the drawback on using reflection. There´s no way to get a compile-time-type, when you provide the type-parameter at runtime. Thus you´re allways stuck on object. You can only cast to some non-generic base-interface that your generic ones derives from. Define your Handle-method there instead:

interface IErrorHandler
{
    void Handle(Exception e);
}
interface IErrorHandler<T> where T: Exception { }

Now you can use this:

var exceptionType = ex.GetType();
var handlerType = typeof(IErrorHandler<>).MakeGenericType(exceptionType);

var handler = (IErrorHandler) IoCContainer.GetInstance(handlerType); /
handler.Handle(ex);

You could also add a generic method to your generic interface. Now all classes should also implement that method, whereas calls to the non-generic version should be re-directed to the generic one.

class MyClass<T> : IErrorHandler<T> where T: Exception
{
    void Handle(Exception e) { this.Handle((T)e); }
    void Handle<T>(T e { /* your code here */ }
}

Doing this will however not prevent you from a caller casting your instance to the non-generic interface and passing any exception to it. This will still yield in an InvalidCastException.