【问题标题】:C# infer Type from Generic with constraintC# 从带有约束的泛型推断类型
【发布时间】:2013-12-23 16:52:56
【问题描述】:

我正在尝试通过以下方式注册从基类派生的泛型,但出现错误:

无法将MyCallback<T> 表达式转换为类型MyCallback<Event>

我希望这些限制能让这成为可能,但我错过了什么吗?

public class Event
{ };

public delegate void MyCallback<T>(T arg1) where T : Event;

static class EventDispatcher
{

    public static Dictionary<string, MyCallback<Event>> eventTable = new Dictionary<string, MyCallback<Event>>();

    static void RegisterCallback<T>(MyCallback<T> callback) where T : Event
    {
        eventTable.Add("test", callback);
    }
}

【问题讨论】:

  • 你为什么在这里使用泛型?使用 .NET 3.5 或更高版本时应该使用普通的多态性。
  • 主要是为了强制正确的类型注册并允许更具体的委托处理。我想以编程方式约束 RegisterCallback 的调用者在其最终的回调委托中期望 T 类型的对象。这将允许我执行类似 RegisterCallback(MyFunction) 的操作,其中 MyFunction(ThisOddType objectIKnowAbout)
  • @user3130210 您打算如何确保使用正确的参数调用回调?

标签: c# events generics types type-inference


【解决方案1】:

当您有MyCallback&lt;Event&gt; 时,您是在说您有一个可以接受任何 类型事件的方法。它可以接受EventOneEventTwoSomeOtherEvent

假设我调用 RegisterCallback 并传入一个委托,该委托指向具有此签名的方法:

public static void Foo(SomeOtherEvent arg)

如果您的代码可以工作,并且我可以将其分配给MyCallback&lt;Event&gt;,那么我可以在调用该方法时将EventOne 实例传递给该方法。这显然是个问题。

有一个术语;您期望MyCallback 就其通用参数而言是协变。事实上,它是逆变的。如果我有一个可以接受任何类型事件的方法,我可以清楚地传入SomeEventSomeOtherEvent,这意味着我可以将MyCallback&lt;Event&gt;分配给MyCallback&lt;SomeOtherEvent&gt;,而不是反过来。

如果你想告诉编译器,“我知道这个方法实际上不能用任何类型的事件调用,但我希望你允许这个检查,并且只有在给定参数不正确时才会在运行时失败类型。”那么你可以这样做,假设你实际上有办法确保你用正确的参数调用每个回调。你也不能只做演员;您需要将该方法包装在执行强制转换的新方法中:

eventTable.Add("test", e => callback((T)e));

【讨论】:

    【解决方案2】:

    您需要让类型参数成为EventDispatcher 类的一部分:

    public class EventDispatcher<T> : where T : Event {
      public Dictionary<string, MyCallback<T>> eventTable = new Dictionary<string, MyCallback<T>>();
    
      void RegisterCallback(MyCallback<T> callback) {
        eventTable.Add("test", callback);
      }
    }
    

    这是因为eventTable 中声明的MyCallback&lt;Event&gt; 不会被编译为RegisteredCallback 中声明的相同类型,如您的示例那样编写。

    【讨论】:

    • 我希望能够将单独键入的事件注册到同一个字典中,这样它就可以保存 EventT1 和 EventT2 两者都派生自 Event。这样做会限制我只在这个单一类型的字典中注册事件?
    • 正确,继承通常不会传递给类型参数。像IEnumerable&lt;T&gt; 这样的一些接口支持协变和/或逆变。 Dictionary&lt;T,U&gt; 类型没有。
    • @CharlesLambert 他在这里不是试图依赖Dictionary 的协方差,而是MyCallback。当然,根据设计,它不可能是协变的。它可以(虽然目前不是)被做成逆变的。
    猜你喜欢
    • 1970-01-01
    • 2021-06-23
    • 2022-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多