OnLoad与Page_Load的差异分析

  记得最开始学习ASP.NET的时候,我们就被告知:Page_Load方法里面可以写页面加载的代码。

  于是我们就懵懵懂懂写了很长时间的Page_Load方法。最近回过头思考,为什么一个普通的方法,能被自动调用呢?于是就得知了AutoEventWireup属性。

  %@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="Default" %

  一般我们新建页面的时候,AutoEventWireup就为true。MSDN的解释是:指示控件的事件是否自动匹配(Autowire)。如果启用事件自动匹配,则为 true;否则为 false。默认值为 true。那么我们先得到一个结论是:AutoEventWireup为true时,Page_Load、Page_Init之类的方法名能被自动调用。

  下面我们反编译源代码来看看里面是怎么回事。首先反编译所有页面的父类:Page类。

public class Page : TemplateControl, IHttpHandler { }

  大致浏览一下,没有找到“Page_Load之类的字符串,说明不是在Page类处理的,继续查找Page类的父类TemplateControl类。

public abstract class TemplateControl : Control, INamingContainer, IFilterResolutionService{ // Fields private static object _emptyEventSingleton; private static Hashtable _eventListCache; private static IDictionary _eventObjects; private static object _lockObject; private int _maxResourceOffset; private BuildResultNoCompileTemplateControl _noCompileBuildResult; private const string _onTransactionAbortEventName = "OnTransactionAbort"; private const string _onTransactionCommitEventName = "OnTransactionCommit"; private const string _pageAbortTransactionEventName = "Page_AbortTransaction"; private const string _pageCommitTransactionEventName = "Page_CommitTransaction"; private const string _pageDataBindEventName = "Page_DataBind"; private const string _pageErrorEventName = "Page_Error"; private const string _pageInitCompleteEventName = "Page_InitComplete"; private const string _pageInitEventName = "Page_Init"; private const string _pageLoadCompleteEventName = "Page_LoadComplete"; private const string _pageLoadEventName = "Page_Load"; private const string _pagePreInitEventName = "Page_PreInit"; private const string _pagePreLoadEventName = "Page_PreLoad"; private const string _pagePreRenderCompleteEventName = "Page_PreRenderComplete"; private const string _pagePreRenderEventName = "Page_PreRender"; private const string _pageSaveStateCompleteEventName = "Page_SaveStateComplete"; private const string _pageUnloadEventName = "Page_Unload";。。。。。。。。}

  找到了!里面黑茫茫一片的字符串,呵呵。继续仔细查找入口,发现了如下方法:

internal void HookUpAutomaticHandlers(){ //指示是否支持自动事件。SupportAutoEvents属性是只读的,并且在所有情况下都为 true if (this.SupportAutoEvents) { object obj2 = _eventListCache[base.GetType()]; IDictionary dictionary = null; if (obj2 == null) { lock (_lockObject) { obj2 = _eventListCache[base.GetType()]; if (obj2 == null) { dictionary = new ListDictionary(); //GetDelegateInformation将匹配的方法加到字典中 this.GetDelegateInformation(dictionary); if (dictionary.Count == 0) { obj2 = _emptyEventSingleton; } else { obj2 = dictionary; } _eventListCache[base.GetType()] = obj2; } } } //这里将找到的类似Page_Load这些方法与Page.Load这些事件指定的方法比对 //将没有重复的添加到事件中 if (obj2 != _emptyEventSingleton) { dictionary = (IDictionary) obj2; foreach (string str in dictionary.Keys) { EventMethodInfo info = (EventMethodInfo) dictionary[str]; bool flag = false; MethodInfo methodInfo = info.MethodInfo; Delegate delegate2 = base.Events[_eventObjects[str]]; if (delegate2 != null) { foreach (Delegate delegate3 in delegate2.GetInvocationList()) { if (delegate3.Method.Equals(methodInfo)) { flag = true; break; } } } if (!flag) { IntPtr functionPointer = methodInfo.MethodHandle.GetFunctionPointer(); EventHandler handler = new CalliEventHandlerDelegateProxy(this, functionPointer, info.IsArgless).Handler; base.Events.AddHandler(_eventObjects[str], handler); } } } }}

  上面的方法黑压压一片,归纳起来就是2点:查找页面上Page_Load方法,添加到一个字典中,再与Page.Load事件进行比对,将不重复的方法添加到Page.Load事件。也就是说如果页面上有Page_Load方法,并且Page.Load+=new EventHandler(Page_Load);为Page.Load添加了委托方法,那么Page_Load方法只会执行一次。

  但是HookUpAutomaticHandlers()方法是由谁来调用的?AutoEventWireup属性又在什么地方用到了?这点我也还没弄懂,推测是在ASP.NET的页面生命周期中,由Page之前的模块(比如HttpHandler或者HttpModule)来判断AutoEventWireup的值,如果为true则调用HookUpAutomaticHandlers()方法。

  参考:.NET (C#) Internals: ASP.NET 应用程序与页面生命周期

  接下我们来看看TemplateControl.GetDelegateInformation方法

private void GetDelegateInformation(IDictionary dictionary){ if (HttpRuntime.IsFullTrust) { this.GetDelegateInformationWithNoAssert(dictionary); } else { this.GetDelegateInformationWithAssert(dictionary); }}

  进一步查看

private void GetDelegateInformationWithAssert(IDictionary dictionary){ this.GetDelegateInformationWithNoAssert(dictionary);}

  那么关键就在TemplateControl.GetDelegateInformationWithNoAssert方法了:

private void GetDelegateInformationWithNoAssert(IDictionary dictionary){ if (this is Page) { this.GetDelegateInformationFromMethod("Page_PreInit", dictionary); this.GetDelegateInformationFromMethod("Page_PreLoad", dictionary); this.GetDelegateInformationFromMethod("Page_LoadComplete", dictionary); this.GetDelegateInformationFromMethod("Page_PreRenderComplete", dictionary); this.GetDelegateInformationFromMethod("Page_InitComplete", dictionary); this.GetDelegateInformationFromMethod("Page_SaveStateComplete", dictionary); } this.GetDelegateInformationFromMethod("Page_Init", dictionary); this.GetDelegateInformationFromMethod("Page_Load", dictionary); this.GetDelegateInformationFromMethod("Page_DataBind", dictionary); this.GetDelegateInformationFromMethod("Page_PreRender", dictionary); this.GetDelegateInformationFromMethod("Page_Unload", dictionary); this.GetDelegateInformationFromMethod("Page_Error", dictionary); if (!this.GetDelegateInformationFromMethod("Page_AbortTransaction", dictionary)) { this.GetDelegateInformationFromMethod("OnTransactionAbort", dictionary); } if (!this.GetDelegateInformationFromMethod("Page_CommitTransaction", dictionary)) { this.GetDelegateInformationFromMethod("OnTransactionCommit", dictionary); }}

  又看到了熟悉的"Page_Load"字符串。GetDelegateInformationFromMethod光看方法名应该就能猜到它的作用是去查找页面上指定名称的方法:

private bool GetDelegateInformationFromMethod(string methodName, IDictionary dictionary){ EventHandler handler = (EventHandler) Delegate.CreateDelegate(typeof(EventHandler), this, methodName, true, false); if (handler != null) { dictionary[methodName] = new EventMethodInfo(handler.Method, false); return true; } VoidMethod method = (VoidMethod) Delegate.CreateDelegate(typeof(VoidMethod), this, methodName, true, false); if (method != null) { dictionary[methodName] = new EventMethodInfo(method.Method, true); return true; } return false;}

  上面的代码的作用是:以不论大小写的方式查找指定名称的方法,如果找到带参数的则添加到字典中,然后返回。如果找不到带参数的,则查找无参的指定名称的方法,找到了添加到字典中。带参数的方法签名必须为:Page_Load(object sender, EventArgs e)无参的方法签名必须为:Page_Load()也就是说,Page_Load不分大小写,可以写成Page_loAd,同时存在带参数的和无参的,只会取带参数的。没有带参数的时候才会去取无参的。如果同时存在名称分别为Page_Load与Page_loAd两个带参(或者都是无参)方法,那么取写在后面的方法(就是在代码中谁写在后面就取谁)。

  Page_Load的执行时间是在Control类(TemplateControl类的父类)执行完OnLoad方法后执行。页面上的OnLoad其实是重载父类的OnLoad方法,利用多态去执行,从效率上来说自然比较Page_Load那种利用事件去加载的形式要高,所以微软的某篇文档(地址忘记了)中说:如果要考虑效率,则AutoEventWireup始终设置为false。

  下面用几个例子来证明上面的结论:(AutoEventWireup都设置为true)

  例子一:

public partial class Default : Page{ protected void Page_LoaD(object sender, EventArgs e) { Response.Write("3"); } protected void page_LoaD(object sender, EventArgs e) { Response.Write("2"); } protected void Page_Load(object sender, EventArgs e) { Response.Write("1"); }}输出1,因为Page_Load方法不分大小写,

多个带参的Page_Load方法只取最后一个

  例子二:

public partial class Default : Page{ protected void Page_Load(object sender, EventArgs e) { Response.Write("1"); } protected void Page_Load() { Response.Write("2"); }}输出1,因为如果存在带参的Page_Load,就不去管无参的了

  例子三:

public partial class Default : Page{ protected void Page_Load(object sender, EventArgs e) { Response.Write("1"); } public Default() { Page.Load += new EventHandler(Page_Load); }}输出1,因为重复的方法是不会添加到Load事件的委托链中

所以只会执行1次

  例子四:

public partial class Default : Page{ protected void Page_Load(object sender, EventArgs e) { Response.Write("1"); } protected void Page_LoaD(object sender, EventArgs e) { Response.Write("2"); } public Default() { Page.Load += new EventHandler(Page_Load); }}输出12,这里注意委托链里面方法的顺序,先在构造函数中加了Page_Load方法,

然后查找匹配Page_Load名字的方法,找到了Page_LoaD(因为它写在后面),

接着查找是否有重复的,查找结果是没有,于是将Page_LoaD加到委托链中

  例子五:

public partial class Default : Page{ protected void Page_Load(object sender, EventArgs e) { Response.Write("2"); } protected override void OnLoad(EventArgs e) { Response.Write("1"); base.OnLoad(e); Response.Write("3"); }}输出123。首先由于override父类的OnLoad,所以先执行页面的OnLoad方法,

输出1,然后执行父类的OnLoad方法,一直上推到执行完Control类的OnLoad

方法后,执行Load事件的委托链方法,执行Page_Load方法,输出2。最后回到

页面的OnLoad方法输出3

  结论:AutoEventWireup为true时,里面的一些执行规则很奇特,比如Page_Load方法可以不分大小写之类的,这些都是反编译以后才发现的,MSDN里面貌似都找不到相应的解释。而且如果页面继承MyBasePage类,MyBasePage类继承Page类,页面与MyBasePage类中都有Page_Load方法,出现的情况更复杂(比如MyBasePage类的Page_Load方法加不加virtual关键字,运行的结果都可能会不一样),这样反而会影响开发者的逻辑,增加开发的复杂度。同时事件机制效率相对较低,因此建议将AutoEventWireup设为false,只用override OnLoad的方式,这样尽可能将一切都控制在开发者手中。(以上结论对Page_Init()等方法都是一样的)

时间: 2024-10-31 09:08:02

OnLoad与Page_Load的差异分析的相关文章

OnLoad vs. Page_Load vs. Load event [转]

from:http://weblogs.asp.net/infinitiesloop/archive/2008/03/24/onload-vs-page-load-vs-load-event.aspx  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 virtu

OnLoad与Page_Load“.NET研究”的差异分析

记得最开始学习ASP.NET的时候,我们就被告知:Page_Load方法里面可以写页面加载的代码. 于是我们就懵懵懂懂上海闵行企业网站制作写了很长时间的Page_Load方法.最近回过头思考,为什么一个普通的方法,能被自动调用呢?于是就得知了AutoEventWireup属性. %@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits=&quo

各浏览器对link标签onload/onreadystatechange事件支持的差异分析_javascript技巧

1,onload事件 复制代码 代码如下: <!DOCTYPE HTML> <HTML> <HEAD> <meta charset="utf-8" /> <title>Link Element onload</title> <link type="text/css" rel="stylesheet" href="http://i3.sinaimg.cn/rny

ASP.NET中基类Page_Load方法后执行的原因

加载对应Load事件和OnLoad方法,对于这个事件,相信大多数朋友都会比较熟悉,用VS.Net生成的页面中的Page_Load方法就是响应Load事件的方法,对于每一次请求,Load事件都会触发,Page_Load方法也就会执行,相信这也是大多数人了解ASP.Net的第一步. Page_Load方法响应了Load事件,这个事件是在System.Web.WebControl.Control类中定义的(这个类是Page和所有服务器控件的祖宗),并且在OnLoad方法中被触发. 很多人可能碰到过这样

asp.net继承母版页Page_Load()方法不执行

问题描述 B页面继承了A母版页,画面启动时,B页面的Page_Load()方法不执行了.母版页的Page_Load()执行了.怎么让B页面的Page_Load()也执行 解决方案 解决方案二:引用楼主Mr_Mason的回复: B页面继承了A母版页,画面启动时,B页面的Page_Load()方法不执行了.母版页的Page_Load()执行了.怎么让B页面的Page_Load()也执行 怎么可能解决方案三:你在母版页的page_load里面有没有写其他代码???按道理和理论上他是应该会执行的解决方案

将onload 之后的图片作为背景

问题描述 将onload 之后的图片作为背景 我在Onload 的时候将图片缩小了,现在我要将这缩小后的图片作为背景 怎么弄? 解决方案 css3可以定背景图片大小(使用background-size样式),IE8-不支持 CSS3 background-size 属性 你要兼容IE8-的话可以将父容器relative定位,里面放absolute定位的img作为背景 <div style="position:relative;color:white">123 <img

Javascript实例教程:共享onload事件

文章简介:不管你打算在页面加载完毕时执行多少个函数,他都可以应付自如.这个方案需要额外编写一些代码,但好处是一旦有了那些代码,把函数绑定到window.onload事件就非常易行了. 假设两个函数:firstFunction和secondFunction.如果想让这两个函数都在页面加载时得到执行,该怎么办?如果把它们逐渐绑定到onload事件上,它们当中将只有最后那个才会被实际执行: window.onload = firstFunction; window.onload = secondFun

网页设计:document.onLoad的触发时间

设计|网页|网页设计 昨天在写一个网页特效时,总是提示错误.一直到今天通过一个示例才知道原因. 我一直认为使用javascript的document.onLoad指定一个函数,跟在Body标签中加入onLoad是一样的不过能过今天的示例发现,document.onLoad并不是在页面加载完成时引发.示例代码如下:(测试环境Win2003 + IE7)  1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"&

window.onload与$(document).ready()的区别分析

  本文实例讲述了window.onload与$(document).ready()的区别.分享给大家供大家参考.具体分析如下: window.onload是Javascript中得函数,意思是:等待网页中所有内容加载完毕之后(包括图片); 而$(documetn).ready()是在网页中的所有DOM结构绘制完毕之后就可以执行了,可能有与DOM关联的元素还没有加载完,所以相比之下更快一些; 比如举个简单的例子: ? 1 2 3 4 5 6 window.onload=function(){ a