编译时信息在转换为对象时丢失,不能强制转换为泛型接口
本文关键字:转换 不能 泛型接口 对象 信息 编译 | 更新日期: 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))