Philippe
Philippe

Reputation: 2029

Why is typeof(T) != instance.getType()?

I'm working on a factory which shall return a generic implementation of an interface according to a type.

My main question is being illustrated by
enter image description here Why are these typeof(TException) != exception.GetType()? Respectively, what do I have to change to have the correct type for TException?

The above code results in a InvalidCast exception because it tries to cast to IDocumedisExceptionHandler<DocumedisException> instead of IDocumedisExceptionHandler<FhirParsingException>

Factory implementation:

internal class DocumedisExceptionHandlerFactory : IDocumedisExceptionHandlerFactory
{
    private readonly IDictionary<Type, object> _exceptionHandlers = new ConcurrentDictionary<Type, object>();

    public void RegisterExceptionHandler<TException>(IDocumedisExceptionHandler<TException> exceptionHandler)
        where TException : DocumedisException
    {
        _exceptionHandlers.Add(typeof(TException), exceptionHandler);
    }

    public IDocumedisExceptionHandler<TException> GetDocumedisExceptionHandler<TException>(TException exception)
        where TException : DocumedisException
    {
        _exceptionHandlers.TryGetValue(exception.GetType(), out var exceptionHandler);
        return (IDocumedisExceptionHandler<TException>) exceptionHandler;
    }
}

Side question: Would there be a better way then using object as dictionary value?

Registration of the handler in startup:

var exceptionHandlerFactory = app.ApplicationServices.GetService<IDocumedisExceptionHandlerFactory>();
exceptionHandlerFactory.RegisterExceptionHandler(new FhirParsingExceptionHandler());

Where FhirParsingExceptionHandler implements IDocumedisExceptionHandler

internal class FhirParsingExceptionHandler : IDocumedisExceptionHandler<FhirParsingException>
{
    public void HandleException(FhirParsingException exception, out HttpStatusCode httpStatusCode, out OperationOutcome.IssueType issueType, out string message)
    {
        httpStatusCode = HttpStatusCode.BadRequest;
        issueType = OperationOutcome.IssueType.Invalid;
        message = exception.Message;
    }
}

Handler definition (where TException is contravariant):

public interface IDocumedisExceptionHandler<in TException>
    where TException : DocumedisException
{
    void HandleException(TException exception, out HttpStatusCode httpStatusCode, out OperationOutcome.IssueType issueType, out string message);
}

And FhirParsingException extends DocumedisException:

public class FhirParsingException : DocumedisException
{
   [...]
}

Retrieval of the handler from the middleware:

public async Task Invoke(HttpContext context)
{
   try
   {
      await _next.Invoke(context);
   }
   catch (Exception ex)
   {
      if (ex is DocumedisException documedisException)
      {
         await HandleDocumedisExceptionAsync(context, documedisException);
      }
      else
      {
         throw;
      }
   }
}

private async Task HandleDocumedisExceptionAsync<TException>(HttpContext context, TException ex, MedicationAnalyzerErrorCode? errorCode = null)
   where TException : DocumedisException
{
   var exceptionHandler = _documedisExceptionHandlerFactory.GetDocumedisExceptionHandler(ex);
   [...]
}

Upvotes: 4

Views: 89

Answers (1)

InBetween
InBetween

Reputation: 32750

typeof(TException) gives you the compile time type of exception. exception.GetType() gives you the runtime type of exception. These two need not be the same at all, the only guarantee the compiler makes is that the runtime type of exception will be assignable to a TException variable.

Consider the following:

class Animal { }
class Turtle: Animal { }
bool CheckTypes<T>(T animal) where T: Animal 
{
     return typeof(T) == animal.GetType();
}

And now you have:

Animal animal = new Turtle();
Feed(animal);

Rest assured, CheckTypes will return false; the type of the generic type argument is Animal but the runtime type of animal is really Turtle.

Upvotes: 5

Related Questions