Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic method called with base type

Tags:

c#

.net

I have a situation like this:

public class Foo{}

public interface IBar<in TEx>
    where TEx : Exception
{
    Foo Build(TEx ex);
}

public class FooFactory{
    public Foo Create<TEx>(TEx ex) where TEx : Exception{
         // blah
    }
}

If I call the FooFactory.Create method passing any Exception type everything works. Things start to get wierd when I do something like this:

// this is an external class, cannot be changed.
public class ExceptionContext{
    public Exception Ex{get; set;}
}

var context = new ExceptionContext(){ Ex = new MyCustomException (); }
var factory = new FooFactory();
var fooInstance = factory.Create(context.Ex);

When I call FooFactory.Create(), the passed exception is always of type System.Exception, not MyCustomException as I would expect.

Any suggestion?

EDIT: just to give a little bit of context, I am trying to run exception-specific code inside the OnException(ExceptionContext context) method of a custom implementation of Microsoft.AspNetCore.Mvc.Filters.IExceptionFilter .

like image 830
David Guida Avatar asked Dec 03 '25 14:12

David Guida


1 Answers

When I call FooFactory.Create(), the passed exception is always of type System.Exception

That's because the static type of ExceptionContext.Ex is Exception. The generic method is bound at compile time, which has no knowledge that the dynamic runtime type of the exception is different.

The next best bet is to specify the runtime type at compile time using an explicit cast:

var fooInstance = factory.Create((MyCustomException)context.Ex);

Which obviously assumes that the runtime type is MyCustomException. If it's not, you'll get an invalid cast exception.

You can also use dynamic to defer the method resolution to runtime:

dynamic fooInstance = factory.Create((dynamic)context.Ex);

The risk here is that anything that uses fooInstance will by dynamic as well, so you get no compile-time safety, and any errors (misspelled names, wrong parameter types) won't be discovered until run-time.

EDIT

Removed this part of the answer since ExceptionContext is not under the OPs control.

You could make ExceptionContext generic as well:

public class ExceptionContext<TEx> 
    where TEx : Exception
{
    public <TEx> Ex{get; set;}
}

var context = new ExceptionContext<MyCustomException>(){ Ex = new MyCustomException (); }
var factory = new FooFactory();
var fooInstance = factory.Create(context.Ex);

Or add a constructor if you want to use parameter inference:

public class ExceptionContext<TEx> 
    where TEx : Exception
{
    public ExceptionContext(TEx exception)
    {
        this.Ex = exception;
    {

    public <TEx> Ex{get; set;}
}

var context = new ExceptionContext(new MyCustomException());
var factory = new FooFactory();
var fooInstance = factory.Create(context.Ex);

But it's not clear if that is appropriate for your program overall.

like image 101
D Stanley Avatar answered Dec 06 '25 05:12

D Stanley