public abstract class DomainEventSubscriber<T> where T : DomainEvent { public abstract void HandleEvent(T domainEvent); public Type SubscribedToEventType() { return typeof(T); } }
还有一个存储DomainEventSubscriber引用的类:
public class DomainEventPublisher { private List<DomainEventSubscriber<DomainEvent>> subscribers; public void Subscribe<T>(DomainEventSubscriber<T> subscriber) where T : DomainEvent { DomainEventSubscriber<DomainEvent> eventSubscriber; eventSubscriber = (DomainEventSubscriber<DomainEvent>)subscriber; if (!this.Publishing) { this.subscribers.Add(eventSubscriber); } } }
即使订阅方法类型受到约束,我也无法从DomainEventSubscriber转换为< T>订阅者,其中T:DomainEvent到DomainEventSubscriber< DomainEvent>:
eventSubscriber = (DomainEventSubscriber<DomainEvent>)subscriber;
我将如何进行这种转换,或者我是否为一个令人讨厌的代码味道做好准备?
解决方法
您需要具有协变类型参数T的接口才能将其转换为基类型T.例如,IEnumerable< out T>就是这样一个界面.注意out关键字,这意味着T是协变的,因此只能出现在输出位置(例如返回值和getter).由于协方差,你可以施放IEnumerable< Dolphin> IEnumerable<哺乳动物>:一系列可观的海豚数量肯定也是一系列哺乳动物.
逆变
但是,您无法使DomainEventSubscriber< T>接口IDomainEventSubscriber< out T>因为T然后出现在HandleEvent的输入位置.您可以将其设置为接口IDomainEventSubscriber< in T>.
注意in关键字,这意味着T是逆变的,并且只能出现在输入位置(例如作为方法参数).例如,IEqualityComparer< in T>就是这样一个界面.由于逆转,你可以施放IEqualityComparer< Mammal> IEqualityComparer< Dolphin>:如果它可以比较哺乳动物,那么肯定它可以比较海豚,因为它们是哺乳动物.
但这也无法解决您的问题,因为您只能将逆变类型参数强制转换为更多派生类型,并且您希望将其转换为基类型.
解
我建议你创建一个非泛型的IDomainEventSubscriber接口,并从中派生出你当前的类:
public interface IDomainEventSubscriber { void HandleEvent(DomainEvent domainEvent); Type SubscribedToEventType(); } public abstract class DomainEventSubscriber<T> : IDomainEventSubscriber where T : DomainEvent { void IDomainEventSubscriber.HandleEvent(DomainEvent domainEvent) { if (domainEvent.GetType() != SubscribedToEventType()) throw new ArgumentException("domainEvent"); HandleEvent((T)domainEvent); } public abstract void HandleEvent(T domainEvent); public Type SubscribedToEventType() { return typeof(T); } }
然后在内部使用IDomainEventSubscriber而不是DomainEventSubscriber< DomainEvent>:
public class DomainEventPublisher { private List<IDomainEventSubscriber> subscribers; public void Subscribe<T>(DomainEventSubscriber<T> subscriber) where T : DomainEvent { if (!this.Publishing) { this.subscribers.Add(eventSubscriber); } } }