Reflection反射,會提供 Type 型別的物件,用來描述組件、模組和型別。 開發者可以使用反映來動態建立型別的執行個體、將型別繫結至現有物件,或從現有物件取得型別,並叫用其方法或存取其欄位和屬性。 在程式碼中使用屬性,反映存取這些屬性。
它通常運用在個別取出單一相似作用的類別上,例如遊戲時的小兵的類別,反射方法是 C# 比較進階的技巧。程式有個論調是說某個語言具有很強的動態性,有時候會區分動態和靜態的不同技術與作法。諸如動態繫結Dynamic Binding、動態聯結Dynamic Linking、動態載入Dynamic Loading)等等。其實「動態」一詞其實沒有絕對的嚴格定義。
以下是一個簡單的例子,使用 Object 基底類別所有型別繼承的靜態方法 GetType,來取得變數的型別:
int i = 42;
System.Type
type = i.GetType();
System.Console.WriteLine(type);
另一個反映範例,會使用反映來取得載入之組件的完整名稱。
System.Reflection.Assembly
info = typeof(System.Int32).Assembly;
System.Console.WriteLine(info);
Reflection用法時機:
- Assembly-用來定義並載入組件、載入組件資訊清單 (Assembly Manifest) 中列出的模組,和從這個組件找出型別並建立它的執行個體。
- ConstructorInfo-例如名稱、參數、存取修飾詞 (例如 public 或 private),以及建構函式的實作 (Implementation) 詳細資訊 (例如 abstract 或 virtual)。使用 Type 的GetConstructors 或 GetConstructor 方法,叫用特定的建構函式。
- MethodInfo-例如名稱、傳回型別、參數、存取修飾詞 (例如 public 或 Private),以及方法的實作詳細資訊 (例如 abstract 或 virtual)。使用 Type 的 GetMethods、GetMethod方法,叫用特定方法。
- Module-例如包含模組和模組中類別的組件。可以取得所有全域方法或是在模組上定義的其他特定非全域方法。
- System.Reflection.Emit-命名空間的類別提供反映的特殊形式,允許在Run Time建置型別。
- FieldInfo,例如名稱、存取修飾詞 (例如 public 或 private),以及欄位的實作詳細資訊 (例如 static),並取得或設定欄位值。
- EventInfo-例如名稱、事件處理常式資料型別、自訂屬性、宣告型別和事件的反映型別,並加入或移除事件處理常式。
- ParameterInfo 探索資訊,例如參數的名稱、資料型別、參數是否為輸入或輸出參數,和方法簽章 (Signature) 中參數的位置。
- CustomAttributeData-在應用程式定義域的僅限反映內容中工作時,關於自訂屬性的資訊。CustomAttributeData 用在檢查屬性,而不需要建立這些屬性的執行個體。
- PropertyInfo-例如名稱、資料型別、宣告型別、反映型別和屬性的唯讀或可寫入狀態,並取得或設定屬性值。
使用Type.InvokeMember 方法可以在執行時期動態繫結物件屬性或方法。
//建立使用者類別
public class User
{
private string name;
public string Name
{
get { return name; }
set
{
name = value;
propertyNames.Add("name");
}
}
private List<string>
propertyNames = new List<string>();
public List<string>
PropertyNames
{
get { return propertyNames;
}
set {
propertyNames = value; }
}
public object GetPropertyValue(string propName)
{
switch (propName)
{
case "name":
return Name;
default:
return "";
}
}
}
//建立產品的類別
public class Product
{
private int id;
public int Id
{
get { return id; }
set
{
id = value;
propertyNames.Add("id");
}
}
private string proName;
public string ProName
{
get { return proName;
}
set
{
proName = value;
PropertyNames.Add("proname");
}
}
private List<string>
propertyNames = new List<string>();
public List<string>
PropertyNames
{
get { return propertyNames;
}
set {
propertyNames = value; }
}
public object GetPropertyValue(string propName)
{
switch (propName)
{
case "id":
return Id;
case "proname":
return ProName;
default:
return "";
}
}
}
在這兩個資料物件中,除了本身的一些屬性之外,額外增加了 PropertyNams 屬性跟 GetPropertyValue 方法,PropertyNams 屬性用來存放有給予值的屬性名稱以便於之後呼叫,GetPropertyValue 方法用來取得與屬性名稱對應之值。
在將資料物件處理完成後,就要來實際使用 InvokeMember 方法在執行時期將產生的物件屬性顯示,建立一個 aspx 網頁,並在畫面上拖入兩個按鈕,在兩個按鈕事件中分別建立不同的資料物件,並將物件傳至 PrintData 方法:
//建立使用者參數
User user = new User();
user.Name = "E-Troy";
Print(user);
//建立產品參數
Product product = new Product();
product.Id = 9487;
product.ProName = "E-Troy's
SuperComputer";
Print(product);
//將結果呈現
Type ot = objVo.GetType();
List<string> names = (List<string>)ot.InvokeMember(
"PropertyNames",
System.Reflection.BindingFlags.GetProperty,
null, ot, new object[] { });
foreach (string name in names)
{
this.resultLabel.Text = objType.InvokeMember(
"GetPropertyValue",
System.Reflection.BindingFlags.InvokeMethod,
null, ot, new object[] { name }) + "\n";
}
在 將結果呈現的方法中,先使用 getType() 取得該物件型別,因為了讓方法於不同的資料物件都能使用,傳入的資料物件是依附在 object,取得了 ot之後,使用Reflection反射的方式將資料物件的 PropertyNames 屬性取得,invokeAttr 參數使用的是 BindingFlags.GetProperty
(取得公開屬性),而取得 PropertyNames後就可以使用迴圈將資料讀出,在讀取資料時將 invokeAttr 參數改成 BindingFlags.InvokeMethod 變成呼叫方法。
-雲遊山水為知已逍遙一生而忘齡- 電腦神手