一 窺視內部
常言道知彼知己百戰不殆,因此咱們第一步也是關鍵的一步就是要窺視RefClass類的結構(這裏咱們假設對RefClass並不理解)。html
首先咱們先要縱覽全局才能繼續深刻,因此咱們先在Main中寫入以下代碼:post
1 public class RefClass 2 { 3 private int _test3; 4 private int _test1 { get; set; } 5 protected int Test2 { get; set; } 6 public int Test3 { get; set; } 7 8 public void Show() 9 { 10 11 } 12 }
1 static void Main(string[] args) 2 { 3 Type t = typeof(RefClass); 4 MemberInfo[] minfos = t.GetMembers(); 5 foreach (MemberInfo minfo in minfos) 6 { 7 Console.WriteLine(minfo.Name); 8 } 9 Console.ReadKey(); 10 }
在這裏咱們獲取這個類的類型,而後獲取了其中的公共成員(可能不少人都會認爲GetMembers是獲取所有,但其實只是獲取公開的全部成員。)而後咱們經過foreach將全部的成員的名稱循環輸出。而後咱們能夠查看控制檯的輸出:spa
在這裏咱們能夠看到其中不只僅輸出了咱們所寫類中的成員,同時還輸出了父類的成員(若是不理解的這裏幫大家補充下基礎,Object是全部類的基類。),細心的讀者必定會發現這裏的輸出並無包含private和protected訪問權限的成員。這就應了上面的話:GetMembers默認返回公開的成員。僅僅只能看到這些公開的成員對咱們來講意義並不大,因此咱們須要查看到那些非公有的成員。下面咱們將上面的代碼改爲以下所示:3d
1 static void Main(string[] args)
2 {
3 Type t = typeof(RefClass);
4 MemberInfo[] minfos = t.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public );
5 foreach (MemberInfo minfo in minfos)
6 {
7 Console.WriteLine(minfo.Name);
8 }
9 Console.ReadKey();
10 }
從中咱們看到咱們使用了GetMembers的重載版本,而且傳入了枚舉類型,分別是「包含非公開」、「包含實例成員」和「包含公開」。而後咱們就能夠獲取到全部成員了。最終咱們將會得出下面這些成員:code
到這裏你可能會認爲咱們已經檢索結束了,可是你有沒有發現屬性不少,並且還包含了大量的父類中的屬性,假設咱們只關注該類中的成員,並不關注父類中的成員該如何作呢?其實咱們只須要加上一個枚舉類型(BindingFlags.DeclaredOnly):htm
1 MemberInfo[] minfos = t.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly );
而後咱們再查看結果:對象
此時就只包含該類中的成員了。下面咱們在RefClass類中添加兩個靜態方法,以下所示:blog
1 public class RefClass 2 { 3 private int _test3; 4 private int _test1 { get; set; } 5 protected int Test2 { get; set; } 6 public int Test3 { get; set; } 7 8 private static void Show2() 9 { 10 } 11 12 public static void Show3() 13 { 14 } 15 16 public void Show() 17 { 18 19 } 20 }
而後咱們繼續查看,能夠發現最終的結果並無輸出這些靜態成員。這個時候咱們只須要在GetMembers中加上一個枚舉:BindingFlags.Static便可。這裏咱們僅僅輸出了全部的成員,可是卻沒有區分出是方法仍是屬性因此咱們在Main中添加一個方法:get
複製代碼 1 static void Main(string[] args) 2 { 3 Type t = typeof(RefClass); 4 Func<MemberTypes, String> getType = (x) => 5 { 6 switch (x) 7 { 8 case MemberTypes.Field: 9 { 10 return "字段"; 11 } 12 case MemberTypes.Method: 13 { 14 return "方法"; 15 } 16 case MemberTypes.Property: 17 { 18 return "屬性"; 19 } 20 default: 21 { 22 return "未知"; 23 } 24 } 25 }; 26 MemberInfo[] minfos = t.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static ); 27 foreach (MemberInfo minfo in minfos) 28 { 29 Console.WriteLine(minfo.Name + ";類型:" + getType(minfo.MemberType)); 30 } 31 Console.ReadKey(); 32 }
這裏我用了一個局部方法來根據類型輸出對應的文本,由於篇幅的緣由我就只判斷了幾個基本的類型。最終輸出的結果以下:string
到此爲止咱們已經可以窺視整個結構。
二 深刻窺視字段
經過上面的內容咱們僅僅縱覽了全局,下面咱們將要繼續深刻,首先咱們先拿字段下手。這裏咱們不在使用GetMembers而須要使用GetFields(固然跟GetMembers同樣若是不傳入指定的枚舉只返回公開的字段),代碼以下所示:
1 static void Main(string[] args) 2 { 3 Type t = typeof(RefClass); 4 FieldInfo[] finfos = t.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); 5 foreach (FieldInfo finfo in finfos) 6 { 7 Console.WriteLine("字段名稱:{0} 字段類型:{1} ", finfo.Name, finfo.FieldType.ToString()); 8 } 9 Console.ReadKey(); 10 }
最終的輸出結果以下所示:
一直到這裏你們都會認爲咱們僅僅只是分析,感受沒有什麼實質的東西,下面就來點實質的東西,你能夠看到_test3、_test1和Test2是私有和保護類型,是不能夠獲取到它們的值的,可是咱們經過反射卻能夠,具體的代碼以下所示:
1 static void Main(string[] args) 2 { 3 Type t = typeof(RefClass); 4 RefClass rc = new RefClass(); 5 rc.Test3 = 3; 6 FieldInfo[] finfos = t.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); 7 foreach (FieldInfo finfo in finfos) 8 { 9 Console.WriteLine("字段名稱:{0} 字段類型:{1} rc中的值爲:{2}", finfo.Name, finfo.FieldType.ToString(), finfo.GetValue(rc)); 10 } 11 Console.ReadKey(); 12 }
能夠看到我實例化了這個類,而且設置了Test3爲3,下面我經過finfo.GetValue輸出了這個值,結果以下圖:
如今是否是感受有點酷了?這還沒完呢,咱們光獲取不算什麼,下面咱們還要修改它的值:
1 static void Main(string[] args) 2 { 3 Type t = typeof(RefClass); 4 RefClass rc = new RefClass(); 5 rc.Test3 = 3; 6 FieldInfo[] finfos = t.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); 7 foreach (FieldInfo finfo in finfos) 8 { 9 finfo.SetValue(rc, 100); 10 Console.WriteLine("字段名稱:{0} 字段類型:{1} rc中的值爲:{2}", finfo.Name, finfo.FieldType.ToString(), finfo.GetValue(rc)); 11 } 12 Console.ReadKey(); 13 }
這裏我只是在foreach中增長了一條語句finfo.SetValue(rc,100),下面咱們繼續看最終輸出的結果:
是否是如今感受能夠隨心所欲了?可是尚未完。
三 深刻窺視屬性
由於屬性存在get和set,而且二者都是方法,因此比較棘手。咱們須要經過屬性對象獲取get和set方法,在經過調用他們才達到修改這個屬性的值。好比下面的代碼:
1 static void Main(string[] args) 2 { 3 Type t = typeof(RefClass); 4 RefClass rc = new RefClass(); 5 rc.Test3 = 3; 6 PropertyInfo[] finfos = t.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); 7 foreach (PropertyInfo finfo in finfos) 8 { 9 MethodInfo getinfo = finfo.GetGetMethod(true); 10 Console.WriteLine("get方法的名稱{0} 返回值類型:{1} 參數數量:{2} MSIL代碼長度:{3} 局部變量數量:{4}", getinfo.Name, getinfo.ReturnType.ToString(), 11 getinfo.GetParameters().Count(), 12 getinfo.GetMethodBody().GetILAsByteArray().Length, 13 getinfo.GetMethodBody().LocalVariables.Count); 14 15 MethodInfo setinfo = finfo.GetSetMethod(true); 16 Console.WriteLine("get方法的名稱{0} 返回值類型:{1} 參數數量:{2} MSIL代碼長度:{3} 局部變量數量:{4}", setinfo.Name, setinfo.ReturnType.ToString(), 17 setinfo.GetParameters().Count(), 18 setinfo.GetMethodBody().GetILAsByteArray().Length, 19 setinfo.GetMethodBody().LocalVariables.Count); 20 21 setinfo.Invoke(rc, new object[] { 123 }); 22 object obj = getinfo.Invoke(rc, null); 23 Console.WriteLine("方法名:{0} 內部值:{1}", finfo.Name, obj); 24 } 25 Console.ReadKey(); 26 }
這裏咱們循環每一個屬性,經過GetGetMethod獲取get方法(調用該方法時若是傳入true則沒法獲取非公開的get方法set也是同樣),接着咱們輸出了該方法的返回類型和參數數量和MSIL代碼長度以及局部變量的數量,
固然你若是有興趣能夠繼續分析輸入參數以及局部變量等,這裏因爲篇幅的緣故就不能介紹太多了。最後咱們調用了set方法將值改變,而後再經過調用get方法獲取這個屬性的值。最終的結果以下所示:
四 深刻窺視方法
首先咱們須要將RefClass修改爲以下所示:
1 public class RefClass
2 {
3 private int _test3;
4 private int _test1 { get; set; }
5 protected int Test2 { get; set; }
6 public int Test3 { get; set; }
7
8 private static void Show2()
9 {
10
11 }
12
13 public static string Show3(string s)
14 {
15 int b;
16 int c;
17 return s;
18 }
19
20 public string Show(string s)
21 {
22 string a;
23 return s;
24 }
25 }
主要是在方法中增長局部變量而且加上返回值,避免最後輸出的時候沒有值。其實這裏的方法跟屬性部分相似,可是爲了可以完整的描述全部,因此筆者依然會講解一遍。下面咱們直接上代碼:
1 static void Main(string[] args) 2 { 3 Type t = typeof(RefClass); 4 RefClass rc = new RefClass(); 5 rc.Test3 = 3; 6 MethodInfo[] finfos = t.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Static ); 7 foreach (MethodInfo finfo in finfos) 8 { 9 if (finfo.GetParameters().Count() > 0 && finfo.GetParameters()[0].ParameterType == typeof(string) ) 10 { 11 object obj = finfo.Invoke(rc, new[] { "123" }); 12 MethodBody mbody = finfo.GetMethodBody(); 13 Console.WriteLine("擁有參數的方法名:{0} 返回值類型:{1} 參數1類型:{2} 參數1名稱:{3} 方法調用後返回的值:{4}", 14 finfo.Name, 15 finfo.ReturnType.ToString(), 16 finfo.GetParameters()[0].ParameterType.ToString(), 17 finfo.GetParameters()[0].Name, 18 obj.ToString()); 19 } 20 else 21 { 22 MethodBody mbody = finfo.GetMethodBody(); 23 Console.WriteLine("沒有參數的方法名:{0} 返回值類型:{1}", 24 finfo.Name, 25 finfo.ReturnType.ToString()); 26 } 27 } 28 Console.ReadKey(); 29 }
在這裏我進行了一些簡單的判斷好比判斷輸入參數的數量以及類型,若是不進行這些判斷就會致使程序沒法繼續執行,具體爲何能夠看下的輸出結果,你就能明白筆者爲何要這麼作了。下面就是具體的結果:
讀者必定發現了這其中還有get和set,你可能會認爲它們不是屬性嗎?怎麼跑到方法這裏來了,其實上面我已經說了。這些其實也是方法。這也是爲何上面我須要去判斷輸入參數的數量以及類型的緣故。
參考資料:https://www.cnblogs.com/yaozhenfa/p/CSharp_Reflection_1.html