无法分配在c#中将派生类型用于EventArgs的事件处理程序

时间:2019-04-22 16:08:13

标签: c# inheritance

所以我有一个声明如下的事件:

public event EventHandler OnChangeDetected;

然后,我将以下处理程序分配给该事件。

myObject.OnChangeDetected += OnTableChanged;

我的理解是,这种类型的事件将要求我的OnTableChanged方法具有以下可以正确编译的签名。

public void OnTableChanged(object sender, EventArgs e)

现在,我想将OnTableChanged事件替换为以下签名。

public void OnTableChanged(SqlChangeNotifier sender, SqlNotificationEventArgs e)

但是,当我用派生类型替换参数时,它抱怨没有与委托EventHandler相匹配的“ OnTableChanged”重载。由于SqlChangeNotifier是从Object派生的,而SqlNotificationEventArgs是从EventArgs派生的,所以谁能解释为什么我不能拥有这些派生的参数类型,因为它们是从正确的基本类型继承的?

3 个答案:

答案 0 :(得分:1)

EventHandler是类型void EventHandler(object sender, EventArgs e)的委托。因此,订阅事件的处理程序的签名必须与此匹配。

现在void OnTableChanged(SqlChangeNotifier sender, SqlNotificationEventArgs e)比这更具体:它不能再使用任何 sender对象,并且事件参数也必须为SqlNotificationEventArgs类型。

现在的问题是,引发事件时,事件的原始发送者将尝试使用参数object sender, EventArgs e来调用事件处理程序,但是您的方法需要更特殊的类型。类型系统不能保证这些参数实际上是那些专门的类型。

如果您需要这些类型,则需要将事件的类型更改为限制性更强的委托人类型。

答案 1 :(得分:0)

您不能这样做,因为事件的订阅者应该获取派生实例,但是发布者只能提供基本实例。

但是您可以做相反的事情:

public static event KeyEventHandler ChangeDetected; // handler with derived args signature

private static void Program_ChangeDetected(object sender, EventArgs e) // base event args are OK
{
    Console.WriteLine("ChangeDetected");
}

static void Main(string[] args)
{
    ChangeDetected += Program_ChangeDetected;
    ChangeDetected?.Invoke(null, new KeyEventArgs(default)); // base event args are NOT OK
}

答案 2 :(得分:0)

有关此类更改的总体主题,请参见Contravariance and Covariance

请记住,事件系统只是调用一系列可以在运行时交换的方法的理想方式,因此,如果无法使用传递引发事件的完全相同的参数直接调用处理程序,则事件系统也无法做到这一点。

与方法类似,事件处理程序也有变种,这意味着类型为EventHandler<SpecializedEventArgs>(假设为SpecializedEventArgs : EventArgs)的delagate将接受签名为public void Handler(object sender, EventArgs args)的处理程序,因为对事件的调用最终会调用具有SpecializedEventArgs对象的处理程序,该对象可以通过简单的多态性隐式转换为EventArgs。 IE将编译以下内容:

public static event EventHandler<SpecializedEventArgs> Event;
public static void Handler(object sender, EventArgs args) { }

public static void Main() {
    Event += Handler;
//...