url:http://weblogs.asp.net/infinitiesloop/archive/2008/03/24/onload-vs-page-load-vs-load-event.aspx
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.
由于某些原因,关于这个问题我已经思考过很多。最普遍的问题就是重写页面或者控件中的一个虚拟方法是否更好,或者是陷入等同于这样的事情上。对于页面开发你可以有其他的选择,那就是不可思议的page_load方法。
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...
我想我涉及这个问题太多了,因为我发的例子经常重写onload,oninit等等。及时page页面默认产生的代码是page_load.
它经常影响大家,所以大家就开始进一步探索它并认为我有一些高级秘密的原因去做它...
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.
严格地说,调用一个重写的虚拟方法应该比调用一个代理快。我没有这方面的证据。我还没有做个任何的性能测试。但是它是有意义的。Page_Load在这方面有它自己的故事。它悬挂在AutoEventWireup特性上面,只是稍微比传统的事件处理方式慢些。但是它是最重要的东西。性能方面的不同就十分像一个骑士在一个很大的比赛前把头发给剪掉了来减轻体重一样!是的,它会起作用的,一些热力学的原理可以证明这些。但是投入更多的联系可能比这达到的结果更好一些。最好担心你应用程序中的瓶颈,而不是像这样低水平的细节。仍然-重写在这里获胜了。
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.
我认为一个组件只听它自己的事件是不对的。为什么呢?重写方法是非常简单的,并且只需很少的代码(我试图想过一个有趣的比喻-自言自语,提醒你自己自己的生日,等等)。作为Page_Load,如果你为了某些目的设计一个基类,你需要考虑派生类的文档“嗨,如果你设计一个方法XYZ_Foo,它将会调用当Foo事件发生时。”。不,可能不是...它没有必要这么复杂,不是吗?只需要为它们创建一个重载方法就可以了!为什么要偷偷重新发明车轮呢?我也不认为你不会自动达到一个时间,因为同样的原因。
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.
当多个组件都调用同一个组件中的同一个事件的时候会发生什么呢?时间触发了?当然你也会问自己它们会以什么顺序进入这个事件中,你期望他们也按照那个顺序触发。不是....NET没有做出这样的许诺,这里没有先到先得的服务。多点传送的代理可能会按照他们添加的顺序激发听众,但是你仍然没法轻易地知道他们调用的顺序。组件也没必要使用它为他们的事件。当你发布一个事件的时候,你可以提供你自己的可能一定不按顺序来的添加/移除的习惯。可能不同的组件根据大家都不知道的逻辑按照不同的顺序被加载?可能那不是现在讨论的,但是两年以后有人需要修改你的代码的时候,这就是很重要的事情。永远不用依赖事件的处理顺序。那可能听起来很容易,但是在一个复杂的系统里偶然地创建一个按照顺序的依赖就不是很难。这个依赖应该很显然的通过系统设计而实现。
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:
使用重写能使你很好地控制它,如果你知道你要做什么。它关掉了,基本的onload执行就是load事件中要执行的。初始化的时候执行初始化的事件,等等。这样想:
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 :)