代码改变世界

深入理解 ASP.NET 动态控件 (Part 5 - 编译实验)

2008-03-24 12:04 by Cat Chen, ... 阅读, ... 评论, 收藏, 编辑

这次的文章是一个小小的动手实验,你需要准备好Visual Studio 2005或者Visual Studio 2008,以及最新版本的Reflector。通过这次的实验,你将对ASPX与C#代码如何合并编译为一个dll代码有所理解。

在实验开始之前,首先来一个小问题:如果不允许你使用ASPX,要你完全使用C#代码写一个具备复杂控件树的页面你会怎么写?把声明控件的代码都放在Page_Load里面吗?或者有更好的代码编写方法?先想想这个问题,然后继续往下看。

实验的第一步,也就是在Visual Studio里面创建一个ASP.NET项目,并编写一个简单的ASPX页面。例如下面这个例子:(以下代码仅包括HtmlForm内的主体部分)
<asp:MultiView ID="MultiView1" runat="server">
  <asp:View ID="View1" runat="server">
    <div>Please choose either of the followings:</div>
    <asp:RadioButton ID="RadioButton1" runat="server" />
    <asp:RadioButton ID="RadioButton2" runat="server" />
  </asp:View>
  <asp:View ID="View2" runat="server">
    <div>Please choose any of the following:</div>
    <asp:CheckBox ID="CheckBox1" runat="server" />
    <asp:CheckBox ID="CheckBox2" runat="server" />
  </asp:View>
</asp:MultiView>

在这个例子中,我们构建了一个简单的控件树,同时又不至于过于复杂,确保了编译出来的代码相对简单一些。接下来我们就需要将它编译了,最简单的手动编译方法就是用ASP.NET 2.0自带的aspnet_compiler.exe,这个文件默认会在这个目录中:C:\Windows\Microsoft.NET\Framework\v2.0.50727。你可以使用aspnet_compiler -h来查看完整的帮助,例如编译一个IIS默认站点中的ASP.NET子站点可以使用这样的代码:
aspnet_compiler  -v / -p C:\inetpub\wwwroot\site C:\output\site

接下来,我们到输出目录的bin子目录里把dll抓到Reflector里面看看吧。你会看到这个dll里面有三个namespace,分别是-(在Reflector中代表没有namespace)、__ASP、ASP。假设你编译的站点有一个Default.aspx,那么在无namespace的类当中就会有一个_Default的类,对应的就是Default.aspx.cs编译出来的类。大家应该还记得《深入理解 ASP.NET 动态控件 (Part 2 - 编译过程)》里面提到的,直接继承自Page的类是用后台代码编译出来的,_Default类就是这样一个具体例子了。我们打开_Default类来看看,就会发现MultiView1已经是其成员了,为什么呢?MultiView1仅仅在ASPX中声明,没有在C#中声明啊。回头看看《深入理解 ASP.NET 动态控件 (Part 2 - 编译过程)》就能解释了这种现象——Default.aspx.cs是标记为partial的,而在你手动编辑的文件中,这是唯一一个partial,另外一个partial由编译器根据Default.aspx自动生成,编译器解释完Default.aspx后在自动生成的partial中定义了MultiView1,因此两个partial合并编译后,_Default类自然就有了MultiView1这个成员了。

接下来,我们再看看ASP这个namespace下的default_aspx类,这个是ASPX文件继承自上述_Default类后编译的结果,它完整表述了ASPX文件中整个控件树的逻辑,而不仅仅是一个包含一堆成员控件定义的Page派生类。这个类的执行入口是FrameworkInitialize()方法,它通过调用__BuildControlTree()方法来构建控件树。在这个方法里面,你可以看到<!DOCTYPE ...>这样的字符串是被解释为LiteralControl的,LiteralControl在Render()时就会把这段文本原样输出。同时你还可以看到,它调用了另外两个方法,分别用来构建HtmlHead和HtmlForm,这两个方法通过类似的形式继续调用其他方法来构建更深层次的控件。

通过阅读default_aspx类的代码,你已经能够理解ASPX的控件树是如何转化为C#代码的了——采用的正是Builder模式。了解到这一点,这次动手试验的目的也就达到了。如果你看到文章开头的那个问题时,你已经想到了使用Builder模式,那么此时也就验证了你的想法是完全正确的。

下一次的文章将是与TemplateControl相关的,我们将继续动手做一些小实验,敬请期待。欢迎订阅我的blog: