Wednesday, January 27, 2010

Raising .NET events

This is a recycling of something I posted to Fabulous Adventures in Coding, in conversation with Pavel Minaev. Necessary background: the official .NET pattern for standard events (as opposed to, say, WPF routed events) recommends invoking them with a method along the lines of:
protected virtual OnFoo(FooEventArgs e) {...}

I thought of a few reasons for the "protected virtual OnFoo(FooArgs)" pattern. I can't come up with an aha moment, though.

There should be some way for a derived type to invoke its parent's event. That supports "protected" and the args.

There shouldn't be a way for nonderived types to invoke an event. That rules out "protected internal".

There shouldn't be a way for nonderived types to inspect the underlying delegate chain. Subscribers can put delegates to nonpublic methods in there; there's an expectation of privacy. That underscores why the backing delegate is private.

This is almost more of a mechanical problem than a logical one, but: there's a syntax problem in C#, at least--a logjam around accessibility. An event already has a modifier that is shorthand for its Add and Remove methods. How would we express accessibility of the event raiser, distinct from the accessibility of add/remove?

That covers everything but the reaons for "virtual". Leo mentioned the template method pattern. I think I'd call this a hook more than template--I think template implies both virtual and nonvirtual steps, where the base class performs the invariant steps nonvirtually. In this case, there is no invariant behavior, is there? Derived classes can suppress the actual raising of the event altogether. But the larger point stands: derived classes can decorate the base behavior.

If a derived class provides its own Add/Remove, it has to invoke its own local event; the base event is private. So there's an in-for-a-dime-in-for-a-dollar reason.

By making OnFoo virtual, you allow derived types to do a bit of covariance. Derived classes can read all the data from the FooArgs object and invoke an event that passes a SubFooArgs instead. It's cowboyish but possible. (I'm not coming up with a compelling example why you'd WANT to do this, but...)

Maybe this is a more realistic motivation: with a virtual OnFoo, you can precede (or simply replace) the base classes's event with a cancellable event of your own.

Okay. That's plenty of offtopic speculation from the likes of me. I'd still like to know if there's an aha-moment explanation, though. This is beginning to feel like an interview question...

No comments: