要深入理解ASPNET动态控件首先就要深入理解整个ASPNET对页面的处理过程由你书写好一个ASPX文件(可能还有一个codebehind文件)到你在浏览器中看到的HTML页面这中间到底发生了什么事这其中的第一步就是解释ASPX文件并进行编译也就是这篇文章要讨论的内容
由于ASPNET编译器本身就是一个大话题所以我决定在本系列文章把这个题目再细分成几篇文章来写开头第一篇简单叙述编译过程中涉及的各个步骤让大家了解ASPX中的声明性代码和C#/VBNET代码如何合并在一起并编译成assembly在这篇文章之后再深入了解编译过程中的一些细节看看一个ASPX中声明性定义的静态控件到底是如何运行起来的
开始讲编译过程了首先大家来看两张图这张是ASPNET x的编译流程图
)thisstylewidth=; border= x的编译流程图>
接下来这张是ASPNET 的编译流程图
image onmousewheel=javascript:return big(this) height= alt= hspace= src=http://imgeducitycn/img_///gif width= onload=javascript:if(thiswidth>)thisstylewidth=; border= 的编译流程图>
这两张图来自官方文档ASPNET 的内部变化大家要注意到代码嵌入(codebeside inline)与代码隐藏(codebehind)的编译模式是不同的代码嵌入仅进行一次编译声明性代码与C#/VBNET代码都一起编译到一个类里面代码隐藏则将声明性代码与C#/VBNET代码分开几次进行翻译/编译这些代码之间是局部与局部(partial)的关系或是基类与派生类的关系
图上引人关注的地方就是代码隐藏编译时存在两次的继承自关系第一次继承是很好理解的用过VS/的人都记得代码中明确声明本页面的类继承自Page类那么第二次继承又是怎么来的呢?
先把上面的问题放一边我们换一种思路来思考重新想一想我们的C#/VBNET代码有什么如果我们在ASPX中放上了一个TextBox那么两边的代码都会出现它的定义ASPX代码是<asp:TextBox id=myTextBox runat=server />C#代码是TextBox myTextBox = new TextBox();myTextBoxID = myTextBox;然后我们在此TextBox的后面用HTML写上<div>Please write down something</div>那么这段HTML仅在ASPX中存在定义而不在C#代码中存在定义
接下来我们将C#代码给编译了然后用ASPNET引擎运行它(确实能够如此运行但这不是我们当前关心的事)你猜我们能够看到什么?我们应该能够看到一个TextBox至于后面那段文字呢聪明的你应该马上想到它没在C#代码中被定义的所以不可能被看到
现在我们明白到了有一部分逻辑是仅仅在ASPX中有所定义我们需要将它们添加到C#编译结果上如何添加这部分的逻辑?ASPNET选择了继承机制从C#编译结果的那个类继承然后在派生类中加入仅在ASPX中定义的逻辑至于作为声明性语言的ASPX如何编译成MSIL则属于下一篇文章讨论的内容在这里就不解释了
需要说明的是这两次编译中的第一次必须手动进行的例如在VS/中执行编译第二次编译在运行时进行自动进行因此改动了ASPX无需重新手动编译而改动了C#/VBNET代码则需要手动编译
ASPNET
上面我们解释ASPNET 的代码隐藏编译时也提到了其中的问题一个TextBox控件要在两边同时声明这明显违反了DRY(Dont Repeat Yourself)原则ASPNET 为了解决这个问题而引入了新的机制
所谓的新机制就是C#代码中的那个partial关键字大家可能都习惯了它的存在但有没有人曾经想过一个这样的Page继承类的其他partial在哪里呢?如果你在VS中作一次项目内搜索就会发现这个类的其它partial是不存在的这时候你就该去看看官方文档(例如我上面给出那个)官方文档会告诉你另外一个partial就是ASPX它们会好像两个普通的partial文件那样合并编译所以在ASPNET 中我们仅需要一次合并编译就解决了所有问题然后我要告诉你官方文档所说的是错误的ASPNET 的编译还是好像ASPNET 那样只不过根据ASPX中的控件定义生成对应C#定义的工作由IDE转交给了ASPNET编译器至于细节你可以去参考我之前写的两篇文章《ASPNET 解决了 CodeBehind 需要控件声明同步的问题》与《ASPNET 的编译模型并非完全像 MS 说的那样》
在ASPNET编译器捡起了定义同步这项工作后整个编译过程就都在它的职责范围内了不再好像ASPNET x那样先由C#/VBNET编译器负责隐藏代码的编译再由ASPNET编译器负责二次编译既然ASPNET编译器同时负责两次编译那就能够省去第一次编译手工进行的麻烦编译工作都由它在运行时负责就好了
现在我们已经对整个编译过程有了了解大多数编译步骤都很容易理解无非是叫C#/VBNET编译器出来做些本职工作只有一个除外仅在ASPX中声明的逻辑是如何被编译为MSIL的因为我们将此作为下一步深入理解的目标并在下一篇文章中讨论
这里有一些简单的问题或者是小实验通过它们可以加深大家对文章的理解大家可以将答案直接写在文章评论中
我在Web应用的根目录新建了一个用户控件MyUserControlascx隐藏文件中定义类名称为MyUserControl我现在需要在页面上动态加载此用户控件请问以下哪种方法正确?为什么?(提示ASCX的编译方式与ASPX类似)
) thisPageControlsAdd(new MyUserControl());
) thisPageControlsAdd(thisPageLoadControl(~/MyUserControlascx));
在讨论ASPNET 编译的时候我说到可以直接运行隐藏代码编译出来的类并且说应该能看到一个TextBox事实上这个TextBox可能也无法看到不过我手上没有VS/所以没办法验证大家有兴趣的话可以自己去动手做一下实验看看那个TextBox到底是否会出现在实验之前让我先说说如何让隐藏代码编译结果直接运行
) 打开MSDN找到IHttpHandler这个条目然后看看它的示例代码以及如何在nfig中配置一个路径使用特定的IHttpHandler
) 由于Page类本身实现了IHttpHandler所以隐藏代码编译后的Page继承类也一定是IHttpHandler在nfig中配置一个使用IHttpHandler的路径并指向你要测试的隐藏代码类
) 在浏览器中访问你配置的路径你就能够看到纯隐藏代码编译后的执行结果