【问题标题】:C# Adding a derived generic type to a Dictionary or ListC# 将派生的泛型类型添加到字典或列表
【发布时间】:2021-04-17 16:53:03
【问题描述】:

当我在 C# 中尝试以下操作时,我得到一个 NPE。这可能不是这样工作的:

private readonly Dictionary<Type, List<Subscription<Event>>> subscriptionMap = new Dictionary<Type, List<Subscription<Event>>>();;

public void Subscribe<T>(Action<T> action) where T : Event {
  ...

  subscriptionMap[type].Add(new DefaultSubscription<T>(action) as Subscription<Event>);

  ...
}

如果没有as,我会收到编译器错误,并且它只会在列表中添加一个空值。我应该注意到SubscriptionEvent 都是接口。在 Java 中,我会简单地说 &lt;? extends Event&gt;&lt;? extends Subscription&lt;?&gt;&gt;,这不会有任何问题。

非常感谢您对此事的任何帮助!

【问题讨论】:

  • 什么是DefaultSubscription&lt;T&gt;,它与Subscription 有什么关系?
  • 它正在执行Subscription&lt;T : Event&gt;
  • 请注意,仅仅因为两种类型表现出特定的继承/实现关系,并不意味着由这些类型参数化的泛型表现出 same 关系。您看到 null 正是因为实例无法转换为“基本”类型。
  • 在这种情况下,我应该如何基于基本接口创建一个通用列表来存储订阅组合?
  • 即使我在没有模板的情况下制作Subscription 混凝土,我也有与Action&lt;T&gt; 相同的问题,我无法将其传递给Subscription,因为我无法将其转换为Action&lt;Event&gt;出于什么原因。

标签: c# generics interface


【解决方案1】:

通用委托不保证匹配签名。

如果Subscribe&lt;T&gt;(Action&lt;T&gt;) 使用具体实现来调用Action&lt;T&gt; 会发生什么?

Subscribe<ConcreteEvent>((ConcreteEvent e) => { });
...
private void Subscribe<T>(Action<T> action) 
    where T : Event
{
    action(new Event()); // Does not match method signature
    subscriptionMap[type].Add(new DefaultSubscription<T>(action) as Subscription<Event>);
}

这会导致编译器错误。

方法签名需要具体的子类型,还是Event 基类型,我们不能说。我们唯一可以肯定的是,该方法接受一个实现Event 的参数。

因此,我们需要更改Subscribe 方法,以便知道Action&lt;T&gt; 的签名。由于Action&lt;T&gt; 的委托是contravariant,这意味着您可以将T 的任何子类型传递到操作中,我们可以将签名更改为Action&lt;Event&gt;

public void Subscribe(Action<Event> action)
{
  subscriptionMap[type].Add(new DefaultSubscription<Event>(action));
}

【讨论】:

  • @Dharman 感谢您指出这一点。我改变了答案。
猜你喜欢
  • 2019-11-22
  • 1970-01-01
  • 1970-01-01
  • 2010-10-19
  • 2019-07-01
  • 2016-07-03
  • 2010-12-10
  • 2014-03-22
  • 2012-05-29
相关资源
最近更新 更多