编译时信息在转换为对象时丢失,不能强制转换为泛型接口

本文关键字:转换 不能 泛型接口 对象 信息 编译 | 更新日期: 2023-09-27 18:13:18

我有一个实现泛型接口的具体类型版本的类。我发现,如果我在编译时传递一个对象到我的函数(即使它可能是正确的对象),它仍然被认为是一个对象,因此在运行时失败的错误:

Unable to cast object of type 'TestEventHandler' to type 'IDomainEventHandler '1[System.Object]'.

我正在对来自总线(该总线生产对象)的消息进行反序列化,该总线应该有一个与消息相关联的DomainEventHandler<of_deserialized_type>

总之,我认为问题是IDomainEventHandler<T>没有从`IDomainEventHandler<object>铸造,我将感谢指导如何最好地解决这个问题,即使对象被传递到Publish(),仍然保持通用的IDomainEventHandler<T>接口。

[TestClass]
public class InternalMessageHandlerTests
{
    class TestEvent
    {
    }
    class TestEventHandler : IDomainEventHandler<TestEvent>
    {
        public void HandleEvent(TestEvent domainEvent)
        {
        }
    }
    [TestMethod]
    public void Test()
    {
        TestEvent testEvent = new TestEvent();
        object testEventAsObject = testEvent; // compile time type information lost 
        Publish(testEvent); // this is OK :)
        Publish(testEventAsObject); // this fails :(
    }
    public void Publish<T>(T eventToPublish) where T : class
    {
        var handlerInstance = new TestEventHandler();
        IDomainEventHandler<T> converted = (IDomainEventHandler<T>)handlerInstance;
        converted.HandleEvent(eventToPublish);
    }
}

编译时信息在转换为对象时丢失,不能强制转换为泛型接口

不能将TestEventHandler转换为IDomainEventHandler<object>,因为这是不安全的。如果允许的话,你可以这样做:

IDomainEventHandler<object> converted = (IDomainEventHandler<object>)new TestEventHandler();
converted.HandleEvent("dsfs");

无效,因为TestEventHandler要求HandleEvent的参数为TestEvent

您可以使用反射调用Publish:

TestEvent testEvent = new TestEvent();
object testEventAsObject = testEvent;
var publishMethod = this.GetType().GetMethod("Publish").MakeGenericMethod(testEventAsObject.GetType());
publishMethod.Invoke(this, new object[] { testEventAsObject });

在这种情况下,不可能在没有反射的情况下支持完全泛型的接口。对于这种简单的情况,反射是一个非常糟糕的主意。

对于这些情况,我通常的方法是IDomainEventHandler,然后是DomainEventHandlerBase<T> : IDomainEventHandler,以便继承类获得泛型的所有优势,但外部接口可以接受对象。

显然它不是静态安全的,但正如我所说,这里不可能是静态安全的。一旦将实例赋值给object testEventAsObject,这个变量就有可能包含编译器角度的任何内容(string, int,任何内容)。

对于必须选择正确处理程序的服务总线之类的东西,它看起来像这样:

public interface IDomainEventHandler {
    void HandleEvent(object domainEvent);
    bool CanHandleEvent(object domainEvent);
}
public abstract class DomainEventHandlerBase<T> : IDomainEventHandler {
    public abstract void HandleEvent(T domainEvent);
    public abstract bool CanHandleEvent(T domainEvent);
    void IDomainEventHandler.HandleEvent(object domainEvent) {
        return HandleEvent((T)domainEvent);
    }
    bool IDomainEventHandler.CanHandleEvent(object domainEvent) {            
        return (domainEvent is T) && CanHandleEvent((T)domainEvent);
    }
}

我是直接写的(没有在VS中检查),所以有可能出错。

之后,当您收到消息时,只需选择

handlers.First(h => h.CanHandleEvent(domainEvent))