简体   繁体   English

C#从具有约束的泛型推断类型

[英]C# infer Type from Generic with constraint

I'm trying to register a generic that derives from a base class in the following way, but getting the error : 我正在尝试以以下方式注册从基类派生的泛型,但出现错误:

cannot convert MyCallback<T> expression to type MyCallback<Event> 无法将MyCallback<T>表达式转换为MyCallback<Event>类型

I was hoping the constraints would make this possible but am I missing something? 我希望约束能够使之成为可能,但我缺少什么吗?

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);
    }
}

You need to have the type parameter be part of the EventDispatcher class: 您需要将type参数作为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);
  }
}

This is because the MyCallback<Event> declared in eventTable is not going to be compiled into the same type declared in RegisteredCallback when written like your example. 这是因为在像您的示例那样编写时,在eventTable声明的MyCallback<Event>不会被编译成在RegisteredCallback声明的相同类型。

When you have a MyCallback<Event> you're saying that you have a method that can take any type of event. 当您拥有MyCallback<Event> ,就是说您拥有可以接受任何类型的事件的方法。 It can accept an EventOne , or an EventTwo , or a SomeOtherEvent . 它可以接受EventOneEventTwoSomeOtherEvent

Let's say I call RegisterCallback and pass in a delegate pointing to a method with this signature: 假设我调用RegisterCallback并传入一个指向具有此签名的方法的委托:

public static void Foo(SomeOtherEvent arg)

If your code would work, and I could assign that to a MyCallback<Event> , then I could pass in an EventOne instance to that method when calling it. 如果您的代码可以运行,并且可以将其分配给MyCallback<Event> ,则可以在调用该方法时将EventOne实例传递给该方法。 That's obviously a problem. 这显然是个问题。

There's a term for that; 这个有一个名词。 you're expecting MyCallback to be covariant with respect to it's generic argument. 您期望MyCallback就其通用参数而言是协变的。 In fact, it's contravariant . 实际上,这是反变的 If I have a method that can accept any type of event, I can clearly pass in a SomeEvent , or a SomeOtherEvent , meaning I could assign a MyCallback<Event> to a MyCallback<SomeOtherEvent> , rather than the other way around. 如果我有一个可以接受任何类型的事件的方法,则可以清楚地传递SomeEventSomeOtherEvent ,这意味着我可以将MyCallback<Event>分配给MyCallback<SomeOtherEvent> ,而不是相反。

If you want to tell the compiler that, "I know that this method cannot actually be called with any type of event, but I want you to allow this check and only fail at runtime if the given argument is not of the proper type." 如果您想告诉编译器,“我知道实际上不能用任何类型的事件来调用此方法,但是我希望您允许进行此检查,并且仅在给定参数类型不正确时在运行时失败。” then you can do that, assuming you actually have a way of ensuring you call each callback with the proper arguments. 那么,假设您确实有一种方法可以确保使用正确的参数调用每个回调, 就可以做到这一点。 You can't just do a cast either; 您也不能只进行演员表转换; you need to wrap the method in a new method that does the cast: 您需要将方法包装在执行强制转换的新方法中:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM