有時候,我們會以下拉式選單呈現的方式讓User選擇想要的功能,再執行該功能。例如以下拉式選單增加兩個項目,匯出DOC及匯出PDF,並建立兩Class:CExportDOC、CExportPDF。分別在Class中建構匯出文件的程式碼,然後在aspx檔的Code Behind中寫個switch條件式,去產生新的執行個體執行方法。Ex:
CExportDOC doc = new CExportDOC();
doc.ExecExport();
如此作法,當以後還要再加個新功能時,除了下拉式選單要修改,再增加一個item,Code Behind的switch也要多一條條件式,另外再增加一個Class撰寫功能。要改三個地方實在麻煩。
透過.NET的Reflection反射機制,我們可以動態的產生下拉式選單,而Code Behind也不用再以switch寫,我們要作的,只是專注在功能上的開發。以下為實作的範例 :
Step 1:建立一抽象類別AbsExport.cs,並建立一抽象方法ExecExport();
public abstract class AbsExport
{
public abstract string ExecExport();
}
Step 2:分別建立CExportDOC、CExportPDF類別,繼承AbsExport抽象類別,並實作ExecExport方法。
class CExportDOC:AbsExport
{
public override string ExecExport()
{
return "ExecExportDOC";
}
}
public class CExportPDF:AbsExport
{
public override string ExecExport()
{
return "ExecExportPDF";
}
}
Step 3:建立CExportContext類別,建立一GetItems方法,用意是藉由迴圈輪巡App_Code,判斷哪個類別是繼承於抽象類別,再以Dictionary型別傳出。
public static class CExportContext
{
public static Dictionary<string, string> GetItems()
{
Dictionary<string, string> dict = new Dictionary<string, string>();
Type[] types = Assembly.Load("App_Code").GetTypes();
foreach (Type type in types)
{
if (type.IsSubclassOf(typeof(AbsExport)))
{
dict[type.Name] = type.Name;
}
}
return dict;
}
}
#補充 Window Form寫法:
public static Dictionary<string, string> GetItems()
{
Dictionary<string, string> dict = new Dictionary<string, string>();
foreach (Type t in Assembly.Load("專案名稱").GetTypes())
{
if (t.IsSubclassOf(typeof(AbsExport)))
{
ClassDescriptionAttribute cls = t.GetCustomAttributes(true)[0] as ClassDescriptionAttribute;
dict[cls.ClassDescription] = t.Name;
}
}
return dict;
}
Step 4:建立網頁WfrmReflection.aspx,拉入一DropDownList控制項,命名為ddlExport。在Page_Load中取得字典檔,並綁定給ddlExport:
if (!IsPostBack)
{
ddlExport.DataSource = CExportContext.GetItems() ;
ddlExport.DataTextField = "Key";
ddlExport.DataValueField = "Value";
ddlExport.DataBind();
}
Step 5:在WfrmReflection.aspx中再拉入一Button,在Click事件裡以動態產生執行個體的方式執行ExecExport方法。
AbsExport ex = Assembly.Load("App_Code").CreateInstance(ddlExport.SelectedValue) as AbsExport;
string tmp = ex.ExecExport();
ClientScript.RegisterStartupScript(this.GetType(), "", "<script>alert('" + tmp + "')</script>");
CreateInstanc中接受字串型態的參數,所以當點了Button後,傳入ddlExport選中的類別名稱字串到CreateInstanc中,就能動態產生執行個體,以此方式取代了siwtch條件式,即使以後再有需要增加ExportXML的功能,也無須修改Code Behind了。
這裡有個缺陷,在下拉式選單中顯示的是類別名稱,不是很直觀,對於User來說,哪懂得這串英文是啥東東,所以,我們希望下拉式選單中是以「類別描述」來具體描述類別名稱,例如ExportDOC以「匯出Word文件」來表示;ExportPDF以「匯出PDF文件」來表示。
這時,我們能夠使用自訂屬性類別的方式,來幫類別訂定一個屬性(ClassDescriptionAttribute),並賦於其值。詳細介紹請參考MSDN 。
以下為實作ClassDescriptionAttribute類別的部分:
[AttributeUsage(AttributeTargets.Class)]
public class ClassDescriptionAttribute : Attribute
{
public string classDesc { get; set; }
public ClassDescriptionAttribute(string _classDesc)
{
classDesc = _classDesc;
}
}
然後宣告CExportDOC、CExportPDF類別的類別描述,如下圖:
而剛剛在CExportContext類別裡的GetItems方法也要小改一下,將
dict[type.Name] = type.Name;
改為
ClassDescriptionAttribute key =type.GetCustomAttributes(true)[0] as ClassDescriptionAttribute;
dict[key.classDesc] = type.Name;
GetCustomAttributes方法能取得剛剛我們自訂的classDesc屬性,再透過key.classDesc取得屬性值,也就是我們指定的類別描述文字。
所以我們再次運行WfrmReflection.aspx,可得到下拉式選單中的類別描述,而選取值為類別名稱的效果了。
後記:
其實ClassDescriptionAttribute這個屬性,微軟已幫我們作好了,只需要using System.ComponentModel,在類別上方加入[Description("類別描述")] ,而ClassDescriptionAttribute key =type.GetCustomAttributes(true)[0] as ClassDescriptionAttribute;改為 DescriptionAttribute key = type.GetCustomAttributes(true)[0] as DescriptionAttribute;一樣可以得到相同的效果。