OnLoad vs. Page_Load vs. Load event
I get this question a lot for some reason. The more general question is whether it is better to override a virtual method on the page or control in question (OnLoad, OnInit, OnPreRender, etc), or to hook into the corresponding event. For page development you have yet another choice; that magical Page_Load method.
I'm not the first blog out there to talk about this, but I haven't really read them, just skimmed a few. Didn't want to taint my opinion too much... here's my take on it all.
Let's throw it all together... just so we know what we're talking about here.
public partial class _Default : System.Web.UI.Page
{
public _Default() {
Init += new EventHandler(_Default_Init);
Load += new EventHandler(_Default_Load);
}
void _Default_Load(object sender, EventArgs e) {
// load event
}
void _Default_Init(object sender, EventArgs e) {
// init event
}
protected override void OnLoad(EventArgs e) {
// do something here
base.OnLoad(e);
// or should I do something here?
}
protected void Page_Load(object sender, EventArgs e) {
// what on earth calls this anyway?
}
protected override void OnPreRender(EventArgs e) {
// pre pre render?
base.OnPreRender(e);
// post pre render?
}
}
I think I get this question so much just because my posted samples usually override OnLoad, OnInit, etc, even though the default page-generated code uses Page_Load. So it strikes people as odd, and then they start looking into it too deeply and thinking I have some super secret reason for doing it...
Not really. I just prefer it. But we may as well really look into it.
Performance?
Strictly speaking, calling a virtual method that is overridden should certainly be faster than invoking a delegate (which is what an event is). I don't have proof of this. I haven't done any performance testing. But it makes sense. Page_Load has its own story about this. It's hooked up through the AutoEventWireup feature, which can only be even slower than a traditional event handler. But it should be the last thing on your mind. The difference in performance is probably analogous to a jockey getting a hair cut before a big race, to gain a weight advantage! Yeah... it will help. Some law of thermodynamics probably proves that. But you'd probably get a thousand fold better results out of putting in one more practice instead! Better to worry about the bottlenecks in your app, not low level details like this. Still -- overriding OnX wins here.
Object Oriented?
A component listening to its own event just doesn't seem right to me. Why? Overriding the method is simpler, and less code. (I tried to think of a funny analogy for this one -- talking to yourself, reminding yourself of your own birthday, etc). As for Page_Load, if you were designing a base class for some purpose, would you consider documenting for derived classes that "hey, if you make a method named XYZ_Foo", it will be called when the Foo event occurs!" No, probably not... it's unnecessary complexity, isn't it? Just create a method for them to override! Why reinvent the wheel? You wouldn't automatically go to an event I don't think either, for the same reason.
Consistency/Deterministic Behavior?
What happens when multiple components all subscribe to the same event from the same component, and the event fires? Well you might ask yourself what order they hooked into the event in, and you'd expect them to fire in that order. Nope... .NET makes no promises, there's no first-come-first-serve service here. MulticastDelegates may very well always fire the listeners in the order they were added, but you still wouldn't easily know what order they were subscribed in. Also, components don't have to use one for their events. When you publish an event, you can provide your own custom add/remove implementations, which may not honor order at all. Maybe different components are loaded in a different order depending on who-knows-what? Maybe that isn't the case now, but it will be in 2 years when some poor intern has to fix your code? Never rely on the order of event handlers. That might sound easy enough, but it isn't hard in a complex system to accidentally create a dependency on the order of things. Such a dependency should be obvious through the design of the system.
Using the override approach gives you better control over that, if you know what you are doing. It turns out, the base implementation of OnLoad is what raises the Load event. OnInit raises the Init event, etc. Consider this:
protected override void OnLoad(EventArgs e) {
// do something here before the load event is raised
base.OnLoad(e); // the Load event is raised
// definitely comes after all listeners have been notified.
}
Now you have control over the order of things, at least with respect to the logic you are adding -- whether it comes before or after the event listeners. Furthermore, if anyone derives from this class, they can also control whether their logic comes before or after yours (or both) by deciding when to call base. And the behavior is consistent every time.
Mistakes?
Ok -- so should I put my logic before or after calling base??? And what if I don't call base??? What will blow up? Will the control fail to load?
This is an argument for not using the override method. If you forget to call base, the corresponding event will not be raised. Depending on lots of things, that may or may not cause a problem. It could cause a very subtle problem that you won't catch until much later. But thankfully tools like Visual Studio exist, and they insert the call to base for us automatically. In this OO age, it should be rare for someone to forget this.
Whether you put your logic before or after calling base really depends on the scenario. Personally I always call base last, at the bottom of the method, just because in general I think it makes sense for my component/control to do its own 'X' before raising the 'X' event for external listeners to respond to. If I want to know about anything those external listeners have done to me, then I could have more code after calling base. But let me just say I can't really even think of a time where this should matter. If you end up with code that needs to worry about this, then just pause and consider your design, it's a CodeSmell. You should be able to design it in a manner that doesn't depend on such subtlety.
Purity?
Trying to think like a purest -- I can't decide from that point of view what is right. If the purpose of OnX is to raise the X event, then you shouldn't override it if you're only purpose is to do something when the X event is raised. You should override it if you need to modify to ammend the strict and narrow task of raising the event, right? But listening to the event just smells worse to me. Perhaps OnX should be called RaiseX, and there should be protected OnX methods whose base implementations do nothing at all. That seems more pure... but that's not the way it is :) Purity and reality must collide in the real world to create... pureality.
And to me, that's within the OnLoad method, not the Load event.
So -- should you go around and convert all your apps one way or another? No way. Just stick with what you're used to. You're probably more likely to make mistakes and introduce bugs if you try to change your ways. Not that you would just because you read this :)
UPDATE 3/25 - clarified delegate ordering section.