Why events does not support binding inherited types?
In these delegates:
EventHandler
public delegate void EventHandler(object sender, EventArgs e);
FormClosingEventHandler
public delegate void FormClosingEventHandler(object sender, FormClosingEventArgs e);
FormClosingEventArgs
is inherited from EventArgs
. Why can't I bind an FormClosing
event with an event handler from EventHandler
delegate?
I know that the event handler's signiture must match its delegate, but why it doesn't suppot matching inherited types ?
Well, it's interesting...
You can bind an eventhandler using a method group conversion with compatible types:
public void GenericHandlerMethod(object sender, EventArgs e) {}
...
// Valid
foo.FormClosingEvent += GenericHandlerMethod;
This will actually create an instance of FormClosingEventHandler
, not EventHandler
.
However you can't subscribe directly with an existing delegate of type EventHandler:
EventHandler genericHandler = GenericHandlerMethod;
// Invalid
foo.FormClosingEvent += genericHandler;
... but you can create a new delegate based on an existing one, if the types are compatible:
EventHandler generic = GenericHandlerMethod;
FormClosingEventHandler closingHandler = new FormClosingEventHandler(generic);
// Valid
foo.FormClosingEvent += closingHandler;
Basically you need to remember that all the syntactic sugar is effectively calling a method like this:
foo.AddFormClosingHandler(handler);
where the method has a signature of:
public void AddFormClosingHandler(FormClosingHandler handler)
Now remember that although they have compatible signatures, there's no reference conversion available from EventHandler
to FormClosingHandler
. It's not like one inherits from the other.
It gets even more confusing with generic covariance/contravariance, but we'll leave it there for now... hopefully that's given you something to chew on and options for working round the restrictions.
A delegate which points directly to an object method holds three pieces of information:
A delegate which is produced by using Delegate.Combine
to combine a total of N single-cast delegates will contain N targets, N methods, and ONE delegate type. Given the (IMHO rather icky and unfortunate) way in which Delegate.Combine
and Delegate.Remove
are used, there's no way the system could allow existing uses of Combine
to accept delegates of different types.
For example, a routine might need an EventHandler<IFoo>
. If classes Moe
and Larry
both implement IFoo
and IBar
, such a routine should be able to accept an EventHandler<Moe>
or an EventHandler<Larry>
. If each delegate type had its own definition for Combine
, it might be possible to feed EventHandler<IFoo>.Combine()
a delegate of type EventHandler<Moe>
and one of EventHandler<Larry>
, and have it generate a combined delegate handler of type EventHandler<IFoo>
. Unfortunately, there's one Delegate.Combine()
method for all delegate types, and there's no way that it can look at an EventHandler<Moe>
and an EventHandler<Larry>
and figure out what type the combined delegate should be (even if Delegate.Combine
had the ability to identify types to which both event handlers could be cast, it would have no way of knowing whether to use EventHandler<IFoo>
or EventHandler<IBar>
).
上一篇: 调用另一个类的定时器事件
下一篇: 为什么事件不支持绑定继承类型?