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


  无法将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>时,就是说您有一种可以接受任何类型的事件的方法。它可以接受EventOneEventTwoSomeOtherEvent

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

public static void Foo(SomeOtherEvent arg)


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

有一个名词。您期望MyCallback相对于它的通用参数是协变的。实际上,这是矛盾的。如果我有一个可以接受任何类型事件的方法,则可以清楚地传入SomeEventSomeOtherEvent,这意味着我可以将MyCallback<Event>分配给MyCallback<SomeOtherEvent>,而不是相反。

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

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

08-06 03:16