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.

15 Comments

  • I've often wondered about that question myself. Thanks for clarifying!

  • Nice article Dave but my tender Blue eyes can see squat with that black background and dark green font. ;-)

  • You mentionned that events with multiple subscribers may not have deterministic behavior... are you sure about that?
    As far as I knew, multicast delegates (and by extension, events with multiple subscribers) did indeed act "first come first server". Now in an ASP.NET context, you may not know in which order the events are subscribed to, but they should still call their listeners in the right order.
    Is there something I'm missing? I have production code that depends on this and its been purring along for as long as I can remember.

  • I can understand why they would decide for that magical Page_Load: for beginners. Problem is you have a nice design and foundation and you then go on a get it all dirty with inconsistencies like this. What ticks me off though is that they don't really "fix" these issues in newer versions, they just pile them on. I wish every version would be a "ok, we know the mistakes we're made last time, we will correct them in this version; it's gonna be a bit painful to fix your code, but going forward it's going to be easier / less complex" -- I can dream, right?...

  • One of the primary concerns is that the firing and ordering of events is random whereas virtual method calls are not. If given the choice, I'd use a virtual method, especially when the order of processing is important.

  • And to make matters worse, we have the Declaritive syntax of adding event handlers to events.

    Example: in .aspx file you have OnClick = "button1_click"
    Which is the same as in .cs Button1.Click += new EventHandler(button1_Click);

    This of course adds a delegate to the "Click" Event of a button, it doesn't have anything to do with the actuall method OnClick(EventArgs e), that raises the Click event.

    Seriously, who thought that was a good idea?

    I guess it makes sense to have all the events listed together in Intellisense, and adding the "On" prefix to the event name accomplishes that (only in the aspx file though)

    Really?

  • I also go with override it makes it clearer for me!

  • Mark -- dark green? It's the same green you get by default in VS. I guess it's the background you don't like. Sorry about that. One of these days I want a code snippet viewer that lets you switch modes. If you select the text maybe that's enough to read it real quick.

  • pbz -- we can dream. But backward compatibility is very important. It isn't too important for some, but a must-have for others.

  • Viscious -- the delcarative way too hook an event is nice in some scenarios. For one, its the only (easy) way to hook the Init event of a child control before it's Init event has already fired. Not really arguing for or against the feature... but it does come in handy sometimes.

  • I'm not entirely sure about the declarative way being the only way to hook a child control's Init event, if I remember correctly you can override your own OnInit and wire up your event handler to the child before calling base.OnInit(). I could be wrong, its been years since I tried that and even then it was in forms, not ASP.
    Anyway, thanks for a great post, I've been wondering about this for ages.

  • Rory -- by the time your Init event occurs, your child controls have already had theirs. Init occurs from the bottom of the tree, up.

  • i agree, it's much better and easier specially when using a custom base class for your asp.net pages

  • Just some more insight, for those of you who are curious:

    Virtual functions are actually *slower* than delegates - assuming of course perfectly optimal delegates/virtual functions (which I would hope/assume Microsoft has done).

    Here is why, from a technical perspective:
    A virtual function requires 2 items: the object pointer, and the vtable entry for MyClass. These 2 items will be in completely different places in memory, the former being dynamic and the latter being static.
    Although the vtable entry will likely be in the cache (assuming you have lots of MyClass objects), it is not necessarily the case. If it's not the case, and both pieces of memory were not in the cache, we now have 2 cache misses. So we may get 2 cache misses in a worst-case scenario.

    On the other hand, we have a delegate, which consists of the object pointer and the function pointer right next to each other. When we load the object pointer into the cache, the function pointer "tags along", causing one less cache miss. So with a delegate, the most cache misses we'll get is 1.



    On that note, the most logical reason Microsoft took this route is likely due to the fact that it's simply "less complex" work, not a speed perspective. The beauty of delegates is that it allows for separation of code, so that all event handling and setting up is in one central place, making it easier to read and manage.

    Take for instance the example above: We would need to modify the OnLoad function to make 2 additional calls, and also the OnInit function, and also the... you get the point.

    Whereas with the way it's done in .NET, they likely have put it all in the constructor, using the += event addition. By doing some "code reuse" (calling all events in the same way), they've minimized what can go wrong.

  • One big disadvantage of OnLoad is that you must remember to call the base function. Since I often use multiple returns in my functions to make it easier to read, that means, to avoid a bug, I have to either call it first (if I can run last, which is not often) or put in a try catch throw finally statement to make sure it gets called on all non-exception paths. The event is easier for me to write bug free code in most cases because someone else is responsible for calling other callbacks. When I am writing a simple page with no inheritance (except from the Page class), I do use Page_Load just because it wires up the event for me (which is one less thing I have to remember to do). When I am writing my own custom Page class (derived from Page), I override the OnXxx functions and make sure I call the base class in a try catch throw finally block, so I can control what happens when. So I say, most people SHOULD use Page_Load, because it is hardest to mess up. Most people may never need anything else. But if you do, it is good to know the options.

    -Guy

Comments have been disabled for this content.