我有一个基类:
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);
}
}
}
即使Subscribe
方法类型受到限制,我也无法从DomainEventSubscriber<T> subscriber
where T : DomainEvent
转换为DomainEventSubscriber<DomainEvent>
:
eventSubscriber = (DomainEventSubscriber<DomainEvent>)subscriber;
我将如何进行此转换,或者我是否为自己设置了令人讨厌的代码味道?
答案 0 :(得分:5)
您需要一个具有协变类型参数T
的接口才能将其转换为基类型T
。例如,IEnumerable<out T>
就是这样的接口。请注意out
关键字,这意味着T
是协变的,因此只能出现在输出位置(例如,作为返回值和getter)。由于协方差,你可以施放IEnumerable<Dolphin>
到IEnumerable<Mammal>
:一系列可观的海豚数量肯定也是一系列可疑的哺乳动物。
但是,您无法将DomainEventSubscriber<T>
界面IDomainEventSubscriber<out T>
设为T
,然后会显示在HandleEvent
的输入位置。您可以将其设为接口IDomainEventSubscriber<in T>
。
请注意in
关键字,这意味着T
是逆变,并且只能出现在输入位置(例如作为方法参数)。例如,IEqualityComparer<in T>
就是这样的接口。由于相反,你可以施放IEqualityComparer<Mammal>
到IEqualityComparer<Dolphin>
:如果它可以比较哺乳动物,那么它肯定可以比较海豚,因为它们是哺乳动物。
但这也无法解决您的问题,因为您只能将逆变类型参数强制转换为 more 派生类型,并且您希望将其转换为基类型。
我建议您创建一个非通用的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);
}
}
}
答案 1 :(得分:3)
这个解决方案有很好的答案,我在这里指出一点,原因很简单,为什么你不能让它起作用。
可以继承泛型基类,但是,不同的类型参数只是不同的类。考虑List<int>
和List<String>
。虽然它们都具有List`1
的通用定义,但它们采用不同的类型参数;它们都不是另一个的基类。你的方法的作用如下:
public void Subscribe<T>(List<T> subscriber) where T: struct {
var eventSubscriber=(List<int>)subscriber;
// ...
}
这不会编译,甚至你将可编译的声明更改为:
public void Subscribe<T>(List<T> subscriber) where T: struct {
var eventSubscriber=(List<int>)(subscriber as object);
// ...
}
并传递List<byte>
的实例将在运行时无效。
因此,即使您已经指定了约束,它也无法正常工作。
除了现有的答案之外,另一种方法是为泛型类定义基类(也可以是抽象的),并使Subscribe
成为基类。但是,这需要您将抽象方法移动到基类并将方法签名更改为通用方法;所以它可能不适合你。
要了解类型差异和分配能力,您可能需要查看我的Q&amp; A样式问题:
How to find the minimum covariant type for best fit between two types?
您可以使用它来获取有关类型的一些信息,以便进行调试,我希望这对您了解类型有所帮助。
答案 2 :(得分:1)
以下是使用界面略有不同的内容:
public class DomainEvent
{
}
// The 'in' isn't actually needed to make this work, but it can be added anyway:
public interface IDomainEventSubscriber<in T> where T: DomainEvent
{
void HandleEvent(T domainEvent);
Type SubscribedToEventType();
}
public abstract class DomainEventSubscriber<T>: IDomainEventSubscriber<T> where T: DomainEvent
{
public abstract void HandleEvent(T domainEvent);
public Type SubscribedToEventType()
{
return typeof(T);
}
}
public class DomainEventPublisher
{
private List<DomainEventSubscriber<DomainEvent>> subscribers;
public void Subscribe<T>(IDomainEventSubscriber<T> subscriber)
where T: DomainEvent
{
DomainEventSubscriber<DomainEvent> eventSubscriber;
eventSubscriber = (DomainEventSubscriber<DomainEvent>)subscriber;
if (!this.Publishing)
{
this.subscribers.Add(eventSubscriber);
}
}
}