重构ASP.NET程序----继承

时间:2021-04-16 00:55:39

原程序,可以从下面地址下载:http://download.cnblogs.com/insus/ASPDOTNET/Ref_Org.rar

数据库SQL Server 2008 R2,数据名为[Demo],它有四张表[UnitCode1], [UnitCode2], [UnitCode3], [UnitCode4],每个表有几个字段[Unit1~4],[Description],[CreateBy],[CreateDate],[UpdateBy],[UpdateDate],此四个表的主键分别为[Unit1],[Unit2],[Unit3]和[Unit4],其余字段名称四张表都一样。数据库还有各个表的相关的存储过程。

程序中有一个接口,是为了设置网页标题。数据库四张表对应的类别,程序应用了母版,有5个网页,Default.aspx, UnitCode1.aspx, UnitCode2.aspx, UnitCode3.aspx和UnitCode4.aspx 。每个网页分别也是对各自的表进行添加,显示,更新以及删除记录的功能。

其实,这就是一个小程序,基本的功能都齐全。

-------------------------------------------------------------------------------------------------------------------------------------

根据此篇博文,我们来学习一下继承。
打开程序,我们会到四个网页的cs代码与四个类别98%相同,只是每个表的主键名,以及存储过程名称等不一样。继承是把相同而共用的属性,方法,函数放置于父类中,这样继承的类,就是使用到这些共用的protected或public的程序块。尽可能简化,变化的地方尽量未端化去维护。

我们创建一个父类,比如叫BaseUnitCode.cs吧。

首先我们对比四个类别中,属性部分,只有

 private string _Unit1;      

public string Unit1
 {
      get { return _Unit1; }
      set { _Unit1 = value; }
}       


不相同,因此我们就把它留在原本的类别中。其余的属性都移至父类BaseUnitCode,还有一句,就是逻辑处理的类实例

BusinessBase objBusinessBase = new BusinessBase();


在每个类别中也一样,因此也移至父类,下面是刚才重构好的父类。
重构ASP.NET程序----继承


接下来,我看到每个类别的GetAll(), Insert(), Update(), Delete()方法中,只有一些参数名,参数值,以及存储过程名有差异。下面是每个方法移至父类之后,作相应的修改:

GetAll()方法:
重构ASP.NET程序----继承

Comment out的代码,就是移到父类的代码,需要修改方法以及修饰符为Protected,这因为只是想让继承这个父类的类访问到即可。而刚才上面属性,还是保持原样“public”。
由于存储程名称在每个类别都不一样,因此方法名改为带一个参数的方法。


Insert()方法:
重构ASP.NET程序----继承

重构之后,Insert()方法被改为带三个参数的方法,这样解决传入主键,主键值,以及Insert的存储过程不一样的问题。

同样,Update():

重构ASP.NET程序----继承



Delete()方法重构:
重构ASP.NET程序----继承

 
Ok,我们的父类重构好了:

重构ASP.NET程序----继承重构ASP.NET程序----继承BaseUnitCode.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Web;

/// <summary>
/// Summary description for BaseUnitCode
/// </summary>
namespace Insus.NET
{
    public class BaseUnitCode
    {
        private string _Description;
        private string _CreateBy;
        private string _UpdateBy;

        public string Description
        {
            get { return _Description; }
            set { _Description = value; }
        }
        public string CreateBy
        {
            get { return _CreateBy; }
            set { _CreateBy = value; }
        }
        public string UpdateBy
        {
            get { return _UpdateBy; }
            set { _UpdateBy = value; }
        }

        BusinessBase objBusinessBase = new BusinessBase();

        public BaseUnitCode()
        {
            //
            // TODO: Add constructor logic here
            //
        }     

        protected DataTable GetAll(string procedureName)
        {
            return objBusinessBase.GetDataToDataSet(procedureName).Tables[0];
        }

        protected void Insert(string paramName, string paramValue, string procedurename)
        {
            Parameter[] parameter = {
                                       new Parameter (paramName,SqlDbType.NVarChar,-1,paramValue),
                                       new Parameter ("@Description",SqlDbType.NVarChar,-1,_Description),
                                       new Parameter ("@CreateBy",SqlDbType.NVarChar,-1,_CreateBy)
                                    
                                    };
            objBusinessBase.ExecuteProcedure(procedurename, parameter);
        }

        protected void Update(string paramName, string paramValue, string procedurename)
        {
            Parameter[] parameter = {
                                       new Parameter (paramName,SqlDbType.NVarChar,-1,paramValue),
                                       new Parameter ("@Description",SqlDbType.NVarChar,-1,_Description),
                                       new Parameter ("@UpdateBy",SqlDbType.NVarChar,-1,_UpdateBy)
                                    };
            objBusinessBase.ExecuteProcedure(procedurename, parameter);
        }
       
        protected void Delete(string paramName, string paramValue, string procedurename)
        {
            Parameter[] parameter = {
                                       new Parameter (paramName,SqlDbType.NVarChar,-1,paramValue)
                                    };
            objBusinessBase.ExecuteProcedure(procedurename, parameter);
        }
    }
}


接下来,我们需要在每一个UnitCode1.cs至UnitCode4.cs分别继承这个父类,下面只演法UnitCode1.cs,其余的UnitCode2至UnitCode4参考就是了。

 重构ASP.NET程序----继承

 下面是重构好的类别:

重构ASP.NET程序----继承重构ASP.NET程序----继承UnitCode1.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Web;

/// <summary>
/// Summary description for UnitCode1
/// </summary>
namespace Insus.NET
{
    public class UnitCode1 : BaseUnitCode
    {
        private string _Unit1;
        public string Unit1
        {
            get { return _Unit1; }
            set { _Unit1 = value; }
        }

        public UnitCode1()
        {
            //
            // TODO: Add constructor logic here
            //
        }

        public DataTable GetAll()
        {
            return GetAll("usp_UnitCode1_GetAll");
        }
        public void Insert()
        {
            Insert("@Unit1", _Unit1, "usp_UnitCode1_Insert");
        }
        public void Update()
        {
            Update("@Unit1", _Unit1, "usp_UnitCode1_Update");
        }
        public void Delete()
        {
            Delete("@Unit1", _Unit1, "usp_UnitCode1_Delete");
        }
    }
}

 

重构ASP.NET程序----继承重构ASP.NET程序----继承UnitCode2.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Web;

/// <summary>
/// Summary description for UnitCode1
/// </summary>
namespace Insus.NET
{
    public class UnitCode2 : BaseUnitCode
    {
        private string _Unit2;
        public string Unit2
        {
            get { return _Unit2; }
            set { _Unit2 = value; }
        }
        
        public UnitCode2()
        {
            //
            // TODO: Add constructor logic here
            //
        }

        public DataTable GetAll()
        {
            return GetAll("usp_UnitCode2_GetAll");
        }
        public void Insert()
        {
            Insert("@Unit2", _Unit2, "usp_UnitCode2_Insert");
        }
        public void Update()
        {
            Update("@Unit2", _Unit2, "usp_UnitCode2_Update");
        }
        public void Delete()
        {
            Delete("@Unit2", _Unit2, "usp_UnitCode2_Delete");
        }
    }
}

 

重构ASP.NET程序----继承重构ASP.NET程序----继承UnitCode3.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Web;

/// <summary>
/// Summary description for UnitCode3
/// </summary>
namespace Insus.NET
{
    public class UnitCode3 : BaseUnitCode
    {
        private string _Unit3;

        public string Unit3
        {
            get { return _Unit3; }
            set { _Unit3 = value; }
        }
       
        public UnitCode3()
        {
            //
            // TODO: Add constructor logic here
            //
        }

        public DataTable GetAll()
        {
            return GetAll("usp_UnitCode3_GetAll");
        }
        public void Insert()
        {
            Insert("@Unit3", _Unit3, "usp_UnitCode3_Insert");
        }
        public void Update()
        {
            Update("@Unit3", _Unit3, "usp_UnitCode3_Update");
        }
        public void Delete()
        {
            Delete("@Unit3", _Unit3, "usp_UnitCode3_Delete");
        }
    }
}

 

重构ASP.NET程序----继承重构ASP.NET程序----继承UnitCode4.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Web;

/// <summary>
/// Summary description for UnitCode1
/// </summary>
namespace Insus.NET
{
    public class UnitCode4 : BaseUnitCode
    {
        private string _Unit4;

        public string Unit4
        {
            get { return _Unit4; }
            set { _Unit4 = value; }
        }
       
        public UnitCode4()
        {
            //
            // TODO: Add constructor logic here
            //
        }

        public DataTable GetAll()
        {
            return GetAll("usp_UnitCode4_GetAll");
        }
        public void Insert()
        {
            Insert("@Unit4", _Unit4, "usp_UnitCode4_Insert");
        }
        public void Update()
        {
            Update("@Unit4", _Unit4, "usp_UnitCode4_Update");
        }
        public void Delete()
        {
            Delete("@Unit4", _Unit4, "usp_UnitCode4_Delete");
        }
    }
}

 
类别重构完成,接下来,我们对UnitCode1.aspx.cs至UnitCode4.aspx.cs进行重构。因为这四个页面的类,也有很多相同的代码。重构之前,先创建一个页面的基类,暂叫它为BasePage,此类别继承了

重构ASP.NET程序----继承

 
每个.aspx.cs继承刚才写的BasePage类,把InsusJsUtility objInsusJsUtility = new InsusJsUtility(); 这句拿掉,并移至BasePage中,根据继承的精神,它足够条件移了。
重构ASP.NET程序----继承


移至BasePage之后,需要添加修饰符protected,这样每个.aspx.cs才可以访问得到。

重构ASP.NET程序----继承重构ASP.NET程序----继承BasePage.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

/// <summary>
/// Summary description for BasePage
/// </summary>
namespace Insus.NET
{
    public class BasePage : System.Web.UI.Page
    {
        protected InsusJsUtility objInsusJsUtility = new InsusJsUtility();
    }
}

 
发现每个.aspx.cs代码,有两句重复了,需要删除。
重构ASP.NET程序----继承

 
接下来,我们眼睛注意到每个.aspx.cs的Data_Binding()方法中,均有此一句:

((ISetValable)this.Master).SetValue("单位码X");


因此Insus.NET把它封装入BasePage类中:
重构ASP.NET程序----继承

 
然后在每个.aspx.cs中,拿掉((ISetValable)this.Master).SetValue("单位码X"); 这句,并改为如下图高亮语句。
重构ASP.NET程序----继承


到此为止,Insus.NET暂停对.cs代码重构,转而看到Html代码。如下图中的插入记录代码,在四处个网页中每个.aspx都是相同的,因此Insus.NET对这些重构。

重构ASP.NET程序----继承

 

对这部分的重构,只有创建用户控件(ascx),然后搬移过去,完成之后,再把这个用户控件拉至网页.aspx中:

重构ASP.NET程序----继承重构ASP.NET程序----继承InsertForm.ascx
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="InsertForm.ascx.cs" Inherits="Sys_InsertForm" %>
<table class="table">
    <tr class="tableRow">
        <td class="tableCell" style="width: 35%;">单位码</td>
        <td class="tableCell">说明</td>
        <td style="width: 12%; text-align: center;" class="tableCell">操作</td>
    </tr>
    <tr>
        <td class="tableCell">
            <asp:TextBox ID="TextBoxUnitCode" runat="server" CssClass="textbox" BackColor="#ffff6f"></asp:TextBox>
            <asp:RequiredFieldValidator ID="RequiredFieldValidator2" runat="server" ControlToValidate="TextBoxUnitCode"
                Display="none" ErrorMessage="单位码必须填写。" ValidationGroup="GeneralInsert"></asp:RequiredFieldValidator>
        </td>
        <td class="tableCell">
            <asp:TextBox ID="TextBoxDescription" runat="server" CssClass="textbox"></asp:TextBox>
        </td>
        <td style="width: 12%;" class="tableCell">
            <asp:ValidationSummary ID="ValidationSummary1" runat="server" EnableClientScript="true"
                ShowMessageBox="true" ShowSummary="false" ValidationGroup="GeneralInsert" />
            <asp:Button ID="ButtonCreate" runat="server" OnClick="ButtonCreate_Click" Text="创建"
                ValidationGroup="GeneralInsert" />
        </td>
    </tr>
</table>


由于每个.aspx创建事件不一样,为了保持原有在.aspx.cs的事件,Insus.NET决定在用户控件中再public Click 事件。
重构ASP.NET程序----继承

上图中,还写两个属性,分别是两个文本框的属性,这是为了让.aspx还能与用户控件的两个文本框的交互。

 用户控件重构好之后,当然需要拉至网页中去,有一个地方是需要注意的,在用户控件,还要写上OnClick事件,OnClick="ButtonCreate_Click":
重构ASP.NET程序----继承

 
在.aspx.cs代码页中,一些代码需要异动,参考下图高亮位置:
重构ASP.NET程序----继承

 改程序,就得一步一个脚印,现在我们把目光放在每个.aspx的GridView控件上,它是显示记录,编辑记录以及删除等功能集成。有很高的相似度。只是Gridview的ID,DataKeyNames,以及OnRowEditing,OnRowCancelingEdit,OnRowUpdating,OnRowDeleting事件名称不一样,最后是绑定主键时,也不一样:

<ItemTemplate>
     <%# Eval("UnitX") %>
</ItemTemplate>


这部分重构,相似度,但好象又很具有独立性,无法分开。现在Insus.NET决定对这些ID以及事件名改为一样。删除箭头所指的数字:
重构ASP.NET程序----继承

改完之后,如下代码一样,Insus.NET只列了个网页,如Unitcode4(部分):

<asp:GridView ID="GridViewUnitCode" runat="server" DataKeyNames="Unit4" AutoGenerateColumns="false" ShowHeader="false" CellPadding="2" CellSpacing="0" Width="100%" BorderWidth="1px" BorderColor="#c0c0c0" BorderStyle="solid"
                HeaderStyle-Height="25" RowStyle-Height="25" HeaderStyle-BackColor="#efebde" OnRowEditing="GridViewUnitCode_RowEditing"
                OnRowCancelingEdit="GridViewUnitCode_RowCancelingEdit" OnRowUpdating="GridViewUnitCode_RowUpdating"
                OnRowDeleting="GridViewUnitCode_RowDeleting">
                <Columns>
                    <asp:TemplateField>
                        <ItemStyle BorderStyle="solid" BorderWidth="1px" BorderColor="#c0c0c0" Width="35%" />
                        <ItemTemplate>
                            <%# Eval("Unit4") %>
                        </ItemTemplate>
                    </asp:TemplateField>


当然在每个UnitCode1~3.aspx.cs的事件中,也应该修改,参考下图,把箭头的数据全删除。
重构ASP.NET程序----继承


重名命重构,往往改动的地方都较多。全部改完之后,所有UnitCode1~4.aspx只差下图高亮位置的差异了:
重构ASP.NET程序----继承


怎样解决这些差异,它是一个表字段,而且是主键。动态产生或是加载是否可行,想到了,行动就是了。在GridView中,去掉DataKeyNames="Unit1"属性。在显示ItemTemple中,改为一个标签,并为GridView添加一个事件 OnRowDataBound="GridViewUnitCode_RowDataBound"。
重构ASP.NET程序----继承


在.aspx.cs中,添加一个变量,四个网页的变量值不同,分别为
string _DataKeyName = "Unit1";
string _DataKeyName = "Unit2";
string _DataKeyName = "Unit3";
string _DataKeyName = "Unit4";

参考下面动画:

重构ASP.NET程序----继承

 
程序经此一改,每个页面的html又一样了。所以我们可以把其中一页的GridView html代码块搬移至一个用户控件之内。创建一个用户控件:

重构ASP.NET程序----继承重构ASP.NET程序----继承OperationForm.ascx
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="OperationForm.ascx.cs" Inherits="Sys_OperationForm" %>
<asp:GridView ID="GridViewUnitCode" runat="server" AutoGenerateColumns="false" ShowHeader="false" CellPadding="2" CellSpacing="0" Width="100%" BorderWidth="1px" BorderColor="#c0c0c0" BorderStyle="solid"
    HeaderStyle-Height="25" RowStyle-Height="25" HeaderStyle-BackColor="#efebde" OnRowEditing="GridViewUnitCode_RowEditing"
    OnRowCancelingEdit="GridViewUnitCode_RowCancelingEdit" OnRowUpdating="GridViewUnitCode_RowUpdating"
    OnRowDeleting="GridViewUnitCode_RowDeleting" OnRowDataBound="GridViewUnitCode_RowDataBound">
    <Columns>
        <asp:TemplateField>
            <ItemStyle BorderStyle="solid" BorderWidth="1px" BorderColor="#c0c0c0" Width="35%" />
            <ItemTemplate>
                <asp:Label ID="LabelUnitCode" runat="server" Text=""></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField>
            <ItemStyle BorderStyle="solid" BorderWidth="1px" BorderColor="#c0c0c0" />
            <ItemTemplate>
                <%# Eval("Description") %>
            </ItemTemplate>
            <EditItemTemplate>
                <asp:TextBox ID="TextBoxDescription" runat="server" Text='<%# Eval("Description") %>' CssClass="textbox"></asp:TextBox>
            </EditItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField>
            <ItemStyle BorderStyle="solid" BorderWidth="1px" BorderColor="#c0c0c0" Width="8%" />
            <ItemTemplate>
                <asp:Button ID="ButtonEdit" runat="server" Text="编辑" CommandName="Edit" CausesValidation="false" />
            </ItemTemplate>
            <EditItemTemplate>
                <asp:ValidationSummary ID="ValidationSummary2" runat="server" EnableClientScript="true"
                    ShowMessageBox="true" ShowSummary="false" ValidationGroup="GrieviewUpdate" />
                <asp:Button ID="ButtonUpdate" runat="server" Text="更新" CommandName="Update" ValidationGroup="GrieviewUpdate" />
                <asp:Button ID="ButtonCancel" runat="server" Text="取消" CommandName="Cancel" CausesValidation="false" />
            </EditItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="删除">
            <ItemStyle BorderStyle="solid" BorderWidth="1px" BorderColor="#c0c0c0" Width="4%" />
            <ItemTemplate>
                <asp:Button ID="ButtonDelete" runat="server" Text="删除" CommandName="Delete" CausesValidation="false" />
                <ajaxToolkit:ConfirmButtonExtender ID="ConfirmButtonExtender1" runat="server" TargetControlID="ButtonDelete"
                    ConfirmText="确认删除记录?">
                </ajaxToolkit:ConfirmButtonExtender>
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>


在OperationForm.ascx.cs中,需要处理几个问题,一是GridView的事件,主键,还是数据绑定的问题等。现在Insus.NET先解决GridView事件:
宣告几个事件,除了GridViewUnitCode_RowDataBound(object sender, GridViewRowEventArgs e)无需处理,因为它与页没有任何交互,只是为GridView显示数据而已。

重构ASP.NET程序----继承

 

Ok, 我们把用户控件拉至网页,并写好事件:
重构ASP.NET程序----继承

下面是我们要解决主键,数据绑定,还有在GrieView显示主键的问题,在用户控件中,写一个只写属性,因为只需要为用户控件写入属性,不必为从用户控件获取值。

 private string _DataKeyName;

    public string DataKeyName
    {
        set { _DataKeyName = value; }
    }


接下来,我们又需要去到每一个.aspx.cs中,为刚才的写好的属性赋值,你将看到下图高亮代码行,这样子,在每个网页运行时,就把网页的主键字符名称传至用户控件内。
重构ASP.NET程序----继承

 
处理Gridview显示主键时,把下面的方法全搬至用户控件中,其余网页相同的事件删除。

 protected void GridViewUnitCode_RowDataBound(object sender, GridViewRowEventArgs e)
    {
        if (e.Row.RowType != DataControlRowType.DataRow) return;
        var drv = e.Row.DataItem as DataRowView;

        if (e.Row.FindControl("LabelUnitCode") != null)
        {
            var lbl = e.Row.FindControl("LabelUnitCode") as Label;
            lbl.Text = drv[_DataKeyName].ToString();
        }
    }


为了让.aspx.cs能与用户控件更好的交互,需要在站点创建一个接口:

重构ASP.NET程序----继承重构ASP.NET程序----继承IGridViewControlable.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI.WebControls;

/// <summary>
/// Summary description for IGridViewControlable
/// </summary>
namespace Insus.NET
{
    public interface IGridViewControlable
    {
        GridView GetGridViewControl();
    }
}


在用户控件OperationForm.ascx.cs实作它。
重构ASP.NET程序----继承


在每一个.aspx.cs代码页中,写一个get属性:

重构ASP.NET程序----继承重构ASP.NET程序----继承View Code
GridView GridViewUnitCode
    {
        get
        {
            return ((IGridViewControlable)this.OperationForm1).GetGridViewControl();
        }
    }

 
写到此,还没有完毕,精彩还在后头。我们再写一个基类,此基将为前面写好的两个用户控件继承。写用户控件的基类,跟页面的基类完全一样。看到否,这基类是继承了System.Web.UI.UserControl:
重构ASP.NET程序----继承

基类写好,去分别打开以前写好的两个用户控件,继承这个用户控件的基类:
重构ASP.NET程序----继承

 

重构ASP.NET程序----继承


然后把在InsertForm.ascx.cs中下面代码移至BaseUserControl.cs控件中:

 public event EventHandler Click;

    protected void ButtonCreate_Click(object sender, EventArgs e)
    {
        if (Click != null)
        {
            Click(this, e);
        }
    }


相同的情况,把OperationForm.cs中下面代码也移至BaseUserControl.cs控件中,移至之后,需要在这个基类的用户控件中,引用命名空间 using System.Web.UI.WebControls;

重构ASP.NET程序----继承重构ASP.NET程序----继承View Code
 public event GridViewEditEventHandler RowEditing;
    public event GridViewCancelEditEventHandler RowCancelingEdit;
    public event GridViewUpdateEventHandler RowUpdating;
    public event GridViewDeleteEventHandler RowDeleting;

    protected void GridViewUnitCode_RowEditing(object sender, GridViewEditEventArgs e)
    {
        if (RowEditing != null)
            RowEditing(sender, e);
    }
    protected void GridViewUnitCode_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e)
    {
        if (RowCancelingEdit != null)
            RowCancelingEdit(sender, e);
    }
    protected void GridViewUnitCode_RowUpdating(object sender, GridViewUpdateEventArgs e)
    {
        if (RowUpdating != null)
            RowUpdating(sender, e);
    }
    protected void GridViewUnitCode_RowDeleting(object sender, GridViewDeleteEventArgs e)
    {
        if (RowDeleting != null)
            RowDeleting(sender, e);
    }


也就是说,当多用户控件*用的属性,方法或是函数,也可以写在基类中。
接下来,我们还看到每个.aspx.cs还在一段相同的代码:

GridView GridViewUnitCode
    {
        get
        {
            return ((IGridViewControlable)this.OperationForm1).GetGridViewControl();
        }
    }


Insus.NET也想把它移至基类BasePage.cs中去,直接cut and paste,它会提示找不到this.OperationForm1这个物件。不管怎样,出错就出错,移过去再说,其余的删除。解决问题,还是使用接口吧:

重构ASP.NET程序----继承重构ASP.NET程序----继承IUserControlable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;

/// <summary>
/// Summary description for IUserControlable
/// </summary>
namespace Insus.NET
{
    public interface IUserControlable
    {
        UserControl GetUserControl();
    }
}


然后每个UnitCode1~4.aspx.cs均实作这个接口,下仅在一个类演示:
重构ASP.NET程序----继承

 

我们打开BasePage基类,看下动画,很简单把刚才找不到物件的问题解决了:
重构ASP.NET程序----继承


ok,此博文到此为止,望看过的网友,能从中学习或温习到继承知识,了解到类别与父类,网页与基类页,用户控件与用户控件基类,还有的是网页与用户控件之间的交互通讯等。
最终重构好的程序,可以下载与博文开头的原程序对比。
http://download.cnblogs.com/insus/ASPDOTNET/Ref_Org_inhert.rar