OnLoad与Page_Load的差异分析
来源:互联网 发布:ubuntu安装 闪下划线 编辑:程序博客网 时间:2024/05/15 17:27
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"
;
。。。。。。。。
}
找到了!里面黑茫茫一片的字符串,呵呵。继续仔细查找入口,发现了如下方法:
上面的方法黑压压一片,归纳起来就是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()等方法都是一样的)
- OnLoad与Page_Load的差异分析
- OnLoad与Page_Load的差异分析
- OnLoad和Page_Load的区别
- Page_Load基类,重写OnLoad
- C# Page_Load和OnLoad的区别和关系
- page_load 与page_init 的区别
- page_load 与page_init 的区别
- page_load 与page_init 的区别
- page_load 与page_init 的区别
- 重写OnLoad比Page_Load()好
- 总结一下Page_Load和OnLoad
- jQuery中$(document).ready()方法与传统JavaScript中的window.onload的差异
- 电子书与纸质书的差异分析
- AngularJs与jquery 差异的分析
- 那些被漏掉的JQuery总结(一)——Window.Onload和document.ready的对比(补充Page_load)
- DOMContentLoaded 与 onload的区别
- onload 与$.ready 的区别
- DOMContentLoaded与 onload的区别
- [转]GCC内嵌汇编
- Debian命令行下网络参数设置的方法
- 关于中间人
- android中网络请求方式的总结
- 关于eclipse qemu调试内核,找不到…
- OnLoad与Page_Load的差异分析
- Make sure to have the zlib libs …
- emacs 的块编辑(列模式)
- jhost邀请码 2012 5月 - 2012 年6…
- 【转】MIPS寄存器介绍
- ubuntu 触控板 打字关闭
- [转]把手教你源代码制作龙芯64位系…
- [转]root sysroot chroot rootfs解…
- Android中Bitmap,byte[],Drawable相互转化