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

4

我正在开发一个工厂,它将根据类型返回接口的通用实现。

我的主要问题是通过下面的图所示:
enter image description here 为什么这些 typeof(TException) != exception.GetType()?换句话说,我需要更改什么才能获得正确的 TException 类型?

上述代码会导致 InvalidCast 异常,因为它试图将其转换为 IDocumedisExceptionHandler<DocumedisException> 而不是 IDocumedisExceptionHandler<FhirParsingException>

工厂实现:

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;
    }
}

旁问:除了使用object作为字典值,是否还有更好的方法?

在启动时注册处理程序:

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

FhirParsingExceptionHandler 实现 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;
    }
}

处理程序定义(其中TException是逆变的):

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

FhirParsingException 扩展自 DocumedisException

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

从中间件中检索处理程序:

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);
   [...]
}

这个回答解决了你的问题吗?[类型检查:typeof,GetType或is?](https://dev59.com/gHNA5IYBdhLWcg3wYMx-) - Pavel Anikhouski
@PavelAnikhouski 不完全是。它解释了差异,但没有说明我如何获取正确类型的TException。就像InBetween所解释的那样,它在编译时确定。 - Philippe
IDocumedisExceptionHandler<TException> 是如何声明的?它对 TException 是否具有协变性?看起来,你的问题主要与泛型有关。 - Pavel Anikhouski
@PavelAnikhouski 这是逆变的(只是因为resharper告诉我这样做)。我已经编辑了问题,包括接口。 - Philippe
然后你的转换无效,如果是协变泛型接口的情况下,它会起作用。你应该花些时间来完善你当前的方法和代码。 - Pavel Anikhouski
1个回答

5

typeof(TException)提供编译时exception类型。 exception.GetType()提供运行时exception类型。这两者不一定相同,编译器仅保证exception的运行时类型可以分配给TException变量。

考虑以下代码:

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

现在你拥有了:

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

放心,CheckTypes 将返回 false;泛型类型参数的类型是 Animal ,但 animal 的运行时类型实际上是 Turtle


这回答了主要的问题,谢谢!但我没有看到解决我的问题的方法,即从工厂中检索一个已键入的处理程序,期望一个精确的异常类型。我可以传递DocumedisException并在处理程序中进行转换,但我真的很想避免这种情况。 - Philippe
@Philipple 展示IDocumedisExceptionHandler<T>接口. 如果你能让它协变(IDocumedisExceptionHandler<out T>),那么转换应该会成功;就像IEnumerable<Turtle>可以转换为IEnumerable<Animal>一样。如果它是逆变或不变的,则在当前设置下无法进行转换。 - InBetween
我已经按照 Pavel 的要求添加了接口定义。我需要阅读一些关于协变和逆变的内容。 - Philippe
@Philippe 接口是逆变的,因此您尝试的转换无效。 - InBetween
在我可支配的时间内,我无法想出一种使其协变的方法。我通过将DocumedisException传递给处理程序来使其非泛型化。如果需要,处理程序可以进行强制转换。虽然不完美,但已经完全实用。 - Philippe
@philippe 做得好。有时候当类型系统与你作对时,最好的解决方案是退一步,从不同的角度来解决问题。泛型非常有用,但并不总是适合每个任务的最佳工具。 - InBetween

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接