System.Reflection 命名空間中的類別,連同 System.Type,可讓您取得已載入組件和其中所定義類型的資訊,例如類別、介面和實值型別。
您也可以使用反映在執行階段建立類型執行個體,並叫用和存取它們。
我習慣將 Reflection 叫做反射,感覺這一篇在挖坑給自己跳,這邊只會介紹動態屬性存取與調用方法。
昨天提到了 Type 類型,這個類型有一個調用成員的方法 InvokeMember。
InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args)
一樣是多載方法,以下內容皆引用 Microsoft Docs 內容。
name:字串,包含要叫用的建構函式、方法、屬性或欄位成員的名稱。
invokeAttr:位元遮罩,由一或多個 BindingFlags 組成,而這些旗標會指定執行搜尋的方式。
binder:定義一組屬性並啟用繫結的物件,可包含多載方法的選擇、引數類型的強制,以及透過反映的成員引動過程。
target:要在其上叫用指定成員的物件。
args:包含引數的陣列,這些引數會傳遞給要叫用的成員。
除了 binder 都是今天會用到的引數。
從上面描述來看,只有 invokeAttr 這個引數需要多做介紹,name 就成員名稱,target 就對象物件,args 就給對應成員的引數陣列。invokeAttr 其實就是一個 enum (DAY 14 提到的列舉),透過當時講的 | 符號來疊加狀態,想了解狀態的欄位名稱與內容,就請自行前往參考連結囉。
在開始操作以前,我們需要使用System.Reflection
命名空間。
這邊簡單的使用 BindingFlags.InvokeMethod、BindingFlags.SetProperty、BindingFlags.GetProperty 三種方法。
static void Main (string[] args) {
var memberType = typeof (Member);
var member = new Member () { ID = 1, Name = "Miffy" };
memberType.InvokeMember ("Print", BindingFlags.InvokeMethod, null, new Member (), null);
memberType.InvokeMember ("Print", BindingFlags.InvokeMethod, null, member, null);
memberType.InvokeMember ("PrintWithMessage", BindingFlags.InvokeMethod, null, member, new Object[] { "s" });
memberType.InvokeMember ("Name", BindingFlags.SetProperty, null, member, new Object[] { "Lulu" });
var memberName = memberType.InvokeMember ("Name", BindingFlags.GetProperty, null, member, null);
}
class Member {
public int ID { set; get; } = 0;
public string Name { set; get; } = "Mio";
public void Print () { Console.WriteLine ($"ID = {ID}, Name = {Name}"); }
public void PrintWithMessage (string message) { Console.WriteLine ($"ID = {ID}, Name = {Name}, Message = {message}"); }
}
個人是滿少的情況下會去使用,只有當屬性名稱為不具有意義但有排序性的情況下(如:Time1, Time2, Time3),才會這樣動態存取,不然就是本人還沒有深到去碰觸這一塊,畢竟只是個菜鳥 QQ
在操作時也可以使用 GetProperties 傳回的 PropertyInfo 類型,並使用他的 GetValue 與 SetValue 方法如
GetValue (object obj, object[] index);
傳回指定的物件的屬性值,和索引屬性的選擇性索引值。
obj:其屬性值將被傳回的物件。
index:索引屬性的選擇性索引值。 索引屬性的索引以零為起始。 非索引屬性的這個值應為 null。
var member = new Member () { ID = 1, Name = "Miffy" };
var memberType = typeof (Member);
var memberProperties = memberType.GetProperties ();
foreach (var property in memberProperties) {
var value = property.GetValue (member, null);
}
SetValue (object obj, object value)
使用索引屬性的選擇性索引值,設定指定的物件的屬性值。
obj:將設定其屬性值的物件。
value:新的屬性值。
var member = new Member () { ID = 1, Name = "Miffy" };
var memberType = typeof (Member);
var memberProperties = memberType.GetProperties ();
foreach (var property in memberProperties) {
if (property.Name == "Name")
property.SetValue (member, "NekoSan");
}
以及 GetMethods 傳回的 MethodInfo 類型,使用 Invoke 方法如
Invoke (object obj, object[] parameters)
obj:要在其上叫用指定成員的物件。
parameters:包含引數的陣列,這些引數會傳遞給要叫用的成員。
var member = new Member () { ID = 1, Name = "Miffy" };
var memberType = typeof (Member);
var memberMethods = memberType.GetMethods ();
foreach (var method in memberMethods) {
if (method.Name == "Print")
method.Invoke (member, null);
}
今天簡單的介紹了反射的使用,如何動態調用成員,在使用上,往往會把 name 的字串換成變數使用,藉由變數內容來對應成員名稱,做到程式編譯時期能夠想用啥方法就用啥方法,我也是因為這原因才接觸到反射的,所以更艱深的部分我就不曉得了。
感謝閱讀,今天的內容可能有誤,閱讀後請再三求證喔。
參考連結
.NET Framework 中的反映 | Microsoft Docs
動態載入和使用類型 | Microsoft Docs
Type.InvokeMember Method (System) | Microsoft Docs
BindingFlags Enum (System.Reflection) | Microsoft Docs