2017年7月4日 星期二

C# - Reflection反射運用 Part 1 (Type.InvokeMember)取得不同資料物件屬性



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  GetMethodsGetMethod方法,叫用特定方法。
  • 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 變成呼叫方法。


-雲遊山水為知已逍遙一生而忘齡電腦神手

沒有留言:

張貼留言