在使用.NET建立的程式或組件時,中繼資料(metadata)和代碼(code)都儲存於“自成一體”的單元中,這個單元稱為裝配件。我們可以在程式運行期間訪問這些資訊。
在System.Reflection中有這樣一個class————Assembly,我們可以通過它來載入一個裝配件。方法如下:
Assembly assm=Assembly.LoadFrom(fileName);
其中filename是要載入的裝配件的檔案名稱(帶路徑)。
接下來,我們就可以通過使用System.Reflection內提供的Info classes來擷取裝配件中的資訊了。首先讓我們看一下這些Info classes:
MethodInfo 擷取某個“成員函數”的資訊,並提供對此“成員函數”中繼資料的訪問。
ParameterInfo 擷取某個“參數”的資訊,並提供對此“參數”中繼資料的訪問。
Constructorinfo 擷取某個“建構函式”的資訊,並提供對此“建構函式”中繼資料的訪問。
PropertyInfo 擷取某個“屬性”的資訊,並提供對此“屬性”中繼資料的訪問。
FieldInfo 擷取某個“資料成員”的資訊,並提供對此“資料成員”中繼資料的訪問。
EventInfo 擷取某個“事件”的資訊,並提供對此“事件”中繼資料的訪問。
上面列出的這些classes(除ParameterInfo外)的訪問操作,要通過一個Type對象來完成。比如我們要獲得一個裝配件的“成員函數”就要這樣做:
System.Reflection.Assembly ass=System.Reflection.Assembly.LoadFrom(fileName);
Type[] tp=ass.GetTypes();
System.Reflection.MethodInfo[] mi=tp[0].GetMethods();
使用同樣的方法我們還可以得到其它的資訊,如下:
獲得“建構函式”資訊:System.Reflection.ConstructorInfo[] ci=tp[0].GetConstructors();
獲得“屬性”資訊:System.Reflection.PropertyInfo[] pi=tp[0].GetProperties();
獲得“資料成員”資訊:System.Reflection.FieldInfo[] fi=tp[0].GetFields();
獲得“事件”資訊:System.Reflection.EventInfo[] ei=tp[0].GetEvents();
此外,我們可以通過ParameterInfo類來擷取“成員函數”和“建構函式”的參數資訊,如下:
擷取“成員函數”的參數資訊:System.Reflection.ParameterInfo[] pi=mi[0].GetParameters();
擷取“建構函式”的參數資訊:System.Reflection.ParameterInfo[] pi=ci[0].GetParameters();
ParameterInfo類有兩個重要的屬性:Name和ParameterType。通過它們我們可以得到“參數”的名稱和資料類型。
由於.NET將class的資訊以“中繼資料”的形式封裝在程式或是組件中,又提供了一系列可以擷取“中繼資料”的方法,所以我們可以程式運行期間來動態訪問這些資訊。
我們在編寫程式的時候經常會遇到這樣的情況:程式中要用到某種計算,而且這種計算的計算方式很多,我們不得不在編寫程式時就要考慮的十分全面,將各種情況到考慮到。但是這樣做又非常的費力,因為我們無法預測到程式編好後,還會出現什麼樣的計算方式。如果計算方式是在交付給客戶後,客戶新提出的我們就不得不將新的計算方式寫人程式中,然後重新編譯,再交給客戶。這樣做是相當麻煩的,而且只為了這麼一小段程式,就要重新編譯整個工程,似乎代價也挺大的。
使用MS.NET中System.Reflection中的一些方法,可以協助我們很好的解決上面的問題。
首先,在遇到上面提出的問題的時候,我們先要進行一下分析,這種計算需要一些什麼參數?在不同的計算方式中,它們共同的參數是什嗎?不同的計算方式中特有的參數是否可以通過共有的參數計算出來,或是通過其它方法獲得?分析完後,提取出可用的共同參數。
接下來,我們就可以編寫計算方法了。將這種計算的每一種方式都寫成一個DLL一個類中的方法,並將其編譯為一個DLL檔案。MS.NET中,類的格式要定死,也就是說編寫的類的namespace和class要一樣,類中的方法名稱也必須是一樣的。而且,方法的參數就是上面所說的共同參數。
然後,將編譯好的DLL檔案放在同一檔案夾內,隨程式一起發布就可以了。
在程式中可以這樣處理所要用到的不同計算方式:給每一種計算方式起一個名字(客戶能夠明白的),然後將這些名字放在下拉式清單方塊的text屬性中,並將對應的DLL檔案名稱放在下拉式清單方塊的value屬性中。這樣,使用者選擇不同的計算方式就可以調用不同DLL檔案中的計算方法了。
下面是一個簡單的樣本:
我將計算方式的名字和DLL檔案名稱放在一個xml檔案中,程式載入時將它們讀取到下拉式清單方塊中。方法如下:(asp.net中)
// 在此處放置使用者代碼以初始化頁面
if(Page.IsPostBack==false)
{
// 頁面首次載入時讀取XML檔案
System.Xml.XmlDocument xmlDoc=new System.Xml.XmlDocument();
System.Xml.XmlNode xmlNd;
int i;
xmlDoc.Load(Server.MapPath("MyConfig.xml"));
xmlNd=xmlDoc.SelectSingleNode("//dllfile");
// 將讀出的XML檔案的有關內容寫入下拉式清單方塊中
for(i=0;i<xmlNd.SelectNodes("field").Count;i++)
{
ListItem it=new ListItem();
it.Text=xmlNd.SelectNodes("field").Item(i).Attributes["text"].Value;
it.Value=xmlNd.SelectNodes("field").Item(i).Attributes["filename"].Value;
this.ddlType.Items.Add(it);
}
}
然後,在輸入完參數之後,可以使用下面的代碼來完成計算並將結果顯示出來。我寫的方法是計算後返回一個DataTable。
DataTable dt=null;
// 載入類所在的dll檔案
Assembly ass=Assembly.LoadFrom(Server.MapPath(this.ddlType.SelectedItem.Value));
// 擷取類型
Type tp=ass.GetType("MyNamespace.MyClass");
// 擷取方法
MethodInfo mi=tp.GetMethod("MyMethodl");
// 建立執行個體
Object obj=System.Activator.CreateInstance(tp);
// 付值參數數組(要求類型一致性)
Object[] objArray=new object[7];
objArray[0]=Convert.ToDouble(this.textBox1.Text);
objArray[1]=Convert.ToInt32(this.textBox2.Text);
objArray[2]=Convert.ToDouble(this.textBox3.Text);
// 調用方法
dt=(DataTable)mi.Invoke(obj,objArray);
// 有傳回值就綁定到DataGrid
if(dt!=null)
{
this.grdResult.DataSource=dt;
this.grdResult.DataBind();
}
作者:綠蔭(yistudio)
E-mail:yistudio@hotmail.com