我正在尝试以以下方式注册从基类派生的泛型,但出现错误:
无法将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);
}
}
最佳答案
当您有一个MyCallback<Event>
时,就是说您有一种可以接受任何类型的事件的方法。它可以接受EventOne
,EventTwo
或SomeOtherEvent
。
假设我调用RegisterCallback
并传入一个具有此签名的方法的委托:
public static void Foo(SomeOtherEvent arg)
如果您的代码可以工作,并且我可以将其分配给
MyCallback<Event>
,则可以在调用该方法时将EventOne
实例传递给该方法。这显然是个问题。有一个名词。您期望
MyCallback
相对于它的通用参数是协变的。实际上,这是矛盾的。如果我有一个可以接受任何类型事件的方法,则可以清楚地传入SomeEvent
或SomeOtherEvent
,这意味着我可以将MyCallback<Event>
分配给MyCallback<SomeOtherEvent>
,而不是相反。如果您想告诉编译器,“我知道实际上无法用任何类型的事件调用此方法,但是我希望您允许进行此检查,并且仅在给定参数的类型不合适时才在运行时失败。”那么您就可以做到这一点,假设您确实有一种方法可以确保使用正确的参数调用每个回调。您也不能只进行演员表转换;您需要将方法包装在执行强制转换的新方法中:
eventTable.Add("test", e => callback((T)e));