所以我有一个声明如下的事件:
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派生的,所以谁能解释为什么我不能拥有这些派生的参数类型,因为它们是从正确的基本类型继承的?
答案 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;
//...