转换为对象时丢失的编译时信息无法转换为通用接口<t> </t>

时间:2013-05-13 22:37:52

标签: c#

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

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

我正在对来自总线的消息进行反序列化(哪些产品对象应该与消息相关联DomainEventHandler<of_deserialized_type>

总而言之,我认为问题IDomainEventHandler<T>不是来自`IDomainEventHandler<object>,我希望有关如何最好地解决此问题的指导,并且仍然保持通用IDomainEventHandler<T>接口,即使对象也是如此被传递到Publish()

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

2 个答案:

答案 0 :(得分:2)

您无法将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 });

答案 1 :(得分:1)

在这种情况下,不支持没有反射的完全通用接口。对于这样一个简单的案例,反思是一个非常糟糕的主意。

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

显然它不是静态安全的,但正如我所说,这里不可能是静态安全的。只要您将实例分配给object testEventAsObject,就可以使此变量包含从编译器角度(stringint,任何内容)的任何内容。

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

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))