在接口中使用泛型时无法强制转换/匹配类型,原因
本文关键字:转换 原因 类型 接口 泛型 | 更新日期: 2024-10-26 05:05:17
In IMyMessage.cs
public interface IMyMessage
{
}
在IMyMessageReceiver中.cs
public interface IMyMessageReceiver<T> where T: IMyMessage
{
void HandleMessage(T message);
void Subscribe();
}
在 MyMessagePublisher 中.cs
public static class MyMessagePublisher
{
private static Dictionary<Type, List<IMyMessageReceiver<IMyMessage>>> _subscribers;
static MyMessagePublisher
{
_subscribers = new Dictionary<Type, List<IMyMessageReceiver<IMyMessage>>>();
}
public static function Subscribe<T>(IMyMessageReceiver<T> receiver) where T: IMyMessage
{
Type messageType = typeof (T);
List<IMyMessageReceiver<IMyMessage>> listeners;
if(!_subscribers.TryGetValue(messageType, out listeners))
{
// no list found, so create it
List<IMyMessageReceiver<T>> newListeners = new List<IMyMessageReceiver<T>>();
// ERROR HERE: Can't convert List<IMyMessageReceiver<T>> to List<IMyMessageReceiver<IMyMessage>>
_subscribers.add(messageType, newListeners);
}
// I would then find the right list and add the receiver it to it but haven't got this far
}
}
所以我的希望是使用一堆"IMyMessages"和"IMyMessageReceivers"来传递消息。我之前做了一个硬编码的方法,但厌倦了 100 个不同的发布/订阅函数名称,所以我想我会把它们很好地包装在泛型中。
我的问题是在使用泛型时我无法让代码工作。即使我指定类型 T 将是 IMyMessage,我也不能在预期 IMyMessage 的任何地方使用 T。也许我只是习惯于基础/扩展类,因为它在那里可以正常工作。我尝试了从铸造到真正通用的各种方法,但我总是遇到同样的问题。
这就是我可以看到它的工作原理。 由于您尝试以不受支持的方式使用协方差,因此您需要避免在几个位置使用泛型。 但这样做不会失去任何类型安全性。
创建一个非泛型IMessageReceiver
接口,以便不能使用泛型参数的类型可以改用这个接口:
public interface IMyMessageReceiver
{
void HandleMessage(IMyMessage message);
void Subscribe();
}
public interface IMyMessageReceiver<in T> : IMyMessageReceiver
where T : IMyMessage
{
void HandleMessage(T message);
}
如果需要,可以创建基类来简化操作:
public abstract class MyMessageReceiverBase<T> : IMyMessageReceiver<T>
where T : IMyMessage
{
public abstract void HandleMessage(T message);
public void HandleMessage(IMyMessage message)
{
if (!(message is T))
throw new InvalidOperationException();
HandleMessage((T)message);
}
public abstract void Subscribe();
}
然后,您可以将IMyMessageListeners
更改为使用非泛型版本,因为它实际上并不需要泛型类型:
public interface IMyMessageListeners
{
void Add(IMyMessageReceiver receiver);
// I added this since I think this is how you're going to use it
void Send(IMyMessage message);
}
此类的具体内容如下所示:
public class MyMessageListeners : IMyMessageListeners
{
readonly List<IMyMessageReceiver> _list = new List<IMyMessageReceiver>();
public void Add(IMyMessageReceiver receiver)
{
_list.Add(receiver);
}
public void Send(IMyMessage message)
{
foreach (var listener in _list)
listener.HandleMessage(message);
}
}
然后(最后),你的静态类将如下所示:
public static class MyMessagePublisher
{
static readonly Dictionary<Type, IMyMessageListeners> _subscribers = new Dictionary<Type, IMyMessageListeners>();
// I added this too, since I think this is how you intend to use it
public static void Publish<T>(T message) where T : IMyMessage
{
Type messageType = typeof(T);
IMyMessageListeners listeners;
if (_subscribers.TryGetValue(messageType, out listeners))
listeners.Send(message);
}
public static void Subscribe<T>(IMyMessageReceiver<T> receiver) where T : IMyMessage
{
Type messageType = typeof(T);
IMyMessageListeners listeners;
if (!_subscribers.TryGetValue(messageType, out listeners))
{
// no list found, so create it
listeners = new MyMessageListeners();
_subscribers.Add(messageType, listeners);
}
listeners.Add(receiver);
}
}
你可以像这样使用静态类:
MyMessagePublisher.Subscribe(new FooMessageReceiver());
MyMessagePublisher.Publish(new FooMessage());
型不支持协方差。因此,即使 T 实现了 IMyMessage,IMyMessageReceiver 也无法转换为 IMyMessageReceiver IMyMessage。这就是你得到错误的原因。
我认为你不应该在IMyMessageReceiver接口中使用泛型。我不确定您要实现的目标,但也许这样的事情可以解决问题:
public interface IMyMessageReceiver
{
void HandleMessage(IMyMessage message);
void Subscribe();
}
public class MyMessageReceiver<T> : IMyMessageReceiver where T: IMyMessage
{
void IMyMessageReceiver.HandleMessage(IMyMessage message)
{
HandleMessage(message as T);
}
public void HandleMessage(T message) {...}
public void Subscribe() {...}
}
鉴于您知道类型将始终IMyMessageReceiver<T>
您通过将 everthing 存储为对象并强制转换来模拟行为:
private static Dictionary<Type, List<object>> _subscribers;
public static function Subscribe<T>(IMyMessageReceiver<T> receiver) where T: IMyMessage
{
Type messageType = typeof (T);
List<object> listeners;
if(!_subscribers.TryGetValue(messageType, out listeners))
{
// no list found, so create it
List<object> newListeners = new List<object>();
newListeners.Add(receiver)
_subscribers.add(messageType, newListeners);
}
var messageReceivers = listeners.Cast<IMyMessageReceiver<T>>();
}
由于您知道字典中的列表将始终是给定的类型,因此您可以放心地投射它们!
在这种情况下,KeyedByTypeCollection<T>
是合适的:
public interface IMyMessage
{
}
public interface IMyMessageReceiver<T> where T : IMyMessage
{
void HandleMessage(T message);
void Subscribe();
}
public static class MyMessagePublisher
{
private static readonly KeyedByTypeCollection<IList> Subscribers;
static MyMessagePublisher()
{
Subscribers = new KeyedByTypeCollection<IList>();
}
public static void Subscribe<T>(IMyMessageReceiver<T> receiver) where T : IMyMessage
{
List<IMyMessageReceiver<T>> listeners = Subscribers.Find<List<IMyMessageReceiver<T>>>();
if (listeners == null)
{
listeners = new List<IMyMessageReceiver<T>>();
Subscribers.Add(listeners);
}
// Now you can use the listeners list
listeners.Add(receiver);
}
}