C#反射基礎知識和實戰應用

雲霏霏發表於2014-07-14

首先來說一下什麼是反射? 

反射提供了封裝程式集、模組和型別的物件(Type型別)

可以使用反射動態的建立型別的例項,將型別繫結到現有物件,或從現有物件中獲取型別,然後,可以呼叫型別的方法或訪問其欄位和屬性 。

總之,有了反射,以前很多實現不了的功能都可以實現。

下面先來寫一個小例子,體驗一下反射是怎麼一回事:

開啟VS2010,新建一個控制檯應用程式,在program.cs裡面寫程式碼

首先引入名稱空間:

using System.Reflection;

下如下程式碼:

         PropertyInfo len = typeof(string).GetProperty("Length");
         string s = "Hello,reflection!";
         int length = (int)len.GetValue(s, null);
         Console.WriteLine(length.ToString());

這裡通過反射獲取string的Length屬性,並通過呼叫PropertyInfo 的GetValues方法獲取屬性值,其中GetValues方法的原型如下:

public virtual object GetValue(object obj, object[] index);

第一個引數obj是擁有此屬性的類的例項,在這個例子中,為字串s,s擁有Length屬性。

第二個引數為索引值,微軟解釋如下:

Optional index values for indexed properties. This value should be null for non-indexed properties.

一般情況下用null,大家可以自己深入研究一下。

GetValues方法返回的是object型別,所以必須強制轉換型別。

 

下面通過反射來獲取string的一個方法,看看方法是如何通過反射得到的,程式碼如下:

string s = "Hello,reflection!";
MethodInfo method = typeof(string).GetMethod("Contains");
bool result = (bool)method.Invoke(s, new object[] { "Hello" });
Console.WriteLine(result);

其中,Invoke的方法定義如下:

public object Invoke(object obj, object[] parameters);

這個就很好理解了,第一個引數為擁有此方法的類的例項,還是為string例項s.

第二個引數就是一個object陣列的引數。

這裡呼叫的是string中的Contains方法,判斷string中是否包含某個字串。

 

下面通過反射來例項化一個string物件,程式碼如下:

     Type t = Type.GetType("System.String");
         char[] para = new char[] { 'H', 'e', 'l', 'l', 'o' };
         var o = Activator.CreateInstance(t, para);
         Console.WriteLine(o);

這個跟獲取方法相似,唯一不同的就是string的構造方法引數是char[]陣列,所以必須傳入符合的型別。這裡例項化了一個string,值為Hello。

 

看到這裡,你對反射已經有了初步的瞭解,下面開始進入實戰應用:

在解決方案上面點選滑鼠右鍵,新增專案,選中類庫,輸入名稱,新增一個類庫。

在類庫中新增Custom類,程式碼如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ReflectionDll
{
   public class Custom 
   {
      public string Name { get; set; }
      public string Address { get;set; }
      public int Age { get; set; }
      public DateTime BirthDay { get; set; }

      public string GetInfo(string name = "",int age = 0)
      {
         if (name == "" && age == 0)
         {
            return "Custom Name: " + this.Name + ",Age: " + this.Age;
         }
         else
         {
            return "Custom Name: " + name + ",Age: " + age;
         }
      }
   }
}

這裡只宣告瞭幾個屬性和一個方法,供演示使用。寫好後編譯一下,在控制檯專案裡面新增引用這個類庫(為了方便,否則每次編譯都要手動拷貝DLL到控制檯專案下面),這樣VS會自動將生成的DLL拷貝到控制檯debug目錄下,方便呼叫。下面開始使用反射來載入這個DLL,程式碼如下:

        string path = Environment.CurrentDirectory + "\\ReflectionDll.dll";
         Assembly assem = Assembly.LoadFile(path);
         Type customType = assem.GetType("ReflectionDll.Custom");
         var custom = Activator.CreateInstance(customType);

注意了,這裡首先要獲取DLL的物理路徑,所以上面是否新增引用是無所謂的。有了路徑後,通過Assembly的LoadFile方法載入DLL,再獲取類的Type,注意GetType方法裡面的引數必須為類的全稱,及名稱空間 + 類名,否則無法找到。

最後一行,建立了一個類的例項,型別為object型別。

下面來獲取Custom的所有屬性,程式碼如下:

 PropertyInfo[] propertys = customType.GetProperties();

         Console.WriteLine("******************************");
         foreach (PropertyInfo pro in propertys)
         {
            Console.WriteLine("PropertyName:" + pro.Name + "\n" +
               "PropertyType:" + pro.PropertyType + "\n" +
               "ClassName:" + pro.ReflectedType + "\n" +
               "DLLName:" + pro.Module + "\n");
         }
         Console.WriteLine("******************************");

通過呼叫GetProperties方法獲取所有屬性,儲存到PropertyInfo[]陣列中,然後遍歷陣列輸出屬性值。

下面是各個屬性的含義:

Name                      屬性名稱

PropertyType          屬性資料型別

ReflectedType         所在的類的命名控制元件 + 類名

Module                  所在的DLL檔名稱

 

設定某個屬性的值,方法如下:

     PropertyInfo p = customType.GetProperty("Name");
     p.SetValue(custom, "CustomName",null);

是不是很容易啊。。。

下面來說一下,呼叫類的方法,和一些屬性。程式碼如下:

         MethodInfo _method = customType.GetMethod("GetInfo");
         //顯示方法名稱
         Console.WriteLine("Invoke method:" + _method.Name);
         //顯示返回的資料型別
         Console.WriteLine("Return type:" + _method.ReturnParameter);
         ParameterInfo[] parameters =  _method.GetParameters();
         foreach (ParameterInfo pa in parameters)
         {
            Console.WriteLine(pa.Name + pa.ParameterType + pa.Position + pa.Member);
         }
         
         object[] paras = new object[] { "Jack",24 };

         Console.WriteLine(_method.Invoke(custom, paras));
同屬性一樣,引數可以通過GetParameters()來獲取,獲取的引數資訊如下:

Name 引數名稱
ParameterType 引數資料型別
Position 引數的位置
Member 輸出所有引數

呼叫有引數的方法時,需要傳入引數,New一個object陣列,將引數按順序寫入即可。

補充內容:
當類中存在過載方法時,直接獲取方法名會報異常,可以通過引數來獲取指定的過載方法:
MethodInfo _method = customType.GetMethod("GetInfo",new Type[] {typeof(string),typeof(int)});

 



相關文章