我有一个实现通用接口的具体类型版本的类。我发现如果我在编译时将一个对象传递给我的函数(即使它可能是正确的对象),它仍然被认为是一个对象,因此在运行时因错误而失败:
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);
}
}
答案 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
,就可以使此变量包含从编译器角度(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))