詳解C#特性和反射(二)

Minotauros發表於2018-09-26

  使用反射(Reflection)使得程式在執行過程中可以動態的獲取物件或型別的型別資訊,然後呼叫該型別的方法和建構函式,或訪問和修改該型別的欄位和屬性;可以通過晚期繫結技術動態的建立型別的例項;可以獲取程式集中的所有型別資訊;可以在動態構建新型別;還可以檢索元素所新增的特性;
  ※反射相關的類基本都位於名稱空間System.Reflection中;
  ※動態構建新型別的類位於名稱空間System.Reflection.Emit中;


  一、訪問或修改型別的例項、靜態欄位:

public class MyClass
{
    public int myField;
    public static int myStaticField;
}

//使用方式:
//訪問或修改型別的例項欄位myField
MyClass myObj = new MyClass() { myField = 1 }; //建立例項
Type myType = typeof(MyClass); //獲取型別,或myObj.GetType()
FieldInfo fieldInfo = myType.GetField("myField"); //獲取型別中指定的欄位資訊
Console.WriteLine((int)fieldInfo.GetValue(myObj)); //1,獲取例項欄位的值
fieldInfo.SetValue(myObj, 2); //給例項欄位賦值
//訪問或修改型別的靜態欄位myStaticField
FieldInfo staticFieldInfo = myType.GetField("myStaticField"); //獲取型別中指定的欄位資訊
Console.WriteLine(staticFieldInfo.GetValue(null)); //0,獲取靜態欄位的值
staticFieldInfo.SetValue(null, 2); //給靜態欄位賦值

  ※與直接賦值相比,使用反射賦值用時約長75倍,使用以下程式碼多次測試:

public class MyClass
{
    public int myField;
}

class Program
{
    static void Main(string[] args)
    {
        Stopwatch stopwatch = new Stopwatch();
        MyClass myObj = new MyClass() { myField = 1 };
        Type myType = typeof(MyClass);
        FieldInfo fieldInfo = myType.GetField("myField");

        stopwatch.Start();
        for (int i = 0; i < 10_000_000; i++)
        {
            fieldInfo.SetValue(myObj, 2);
        }
        stopwatch.Stop();
        Console.WriteLine($"使用反射賦值1千萬次耗時:{stopwatch.ElapsedMilliseconds}");

        stopwatch.Reset();
        stopwatch.Start();
        for (int i = 0; i < 10_000_000; i++)
        {
            myObj.myField = 2;
        }
        stopwatch.Stop();
        Console.WriteLine($"直接賦值1千萬次耗時:{stopwatch.ElapsedMilliseconds}");
        Console.Read();
    }
}

  二、訪問或修改型別的例項、靜態屬性:

public class MyClass
{
    public int MyProperty { get; set; }
    public static int MyStaticProperty { get; set; }
}
//使用方式:
//訪問或修改型別的例項屬性MyProperty MyClass myObj = new MyClass() { MyProperty = 1 }; //建立例項 Type myType = typeof(MyClass); //獲取型別,或myObj.GetType() PropertyInfo propertyInfo = myType.GetProperty("MyProperty"); //獲取型別中指定的屬性資訊 Console.WriteLine((int)propertyInfo.GetValue(myObj, null)); //1,獲取例項屬性的值 propertyInfo.SetValue(myObj, 2, null); //給例項屬性賦值 //訪問或修改型別的靜態屬性MyStaticProperty PropertyInfo staticPropertyInfo = myType.GetProperty("MyStaticProperty"); //獲取型別中指定的屬性資訊 Console.WriteLine(staticPropertyInfo.GetValue(null, null)); //0,獲取靜態屬性的值 staticPropertyInfo.SetValue(null, 2); //給靜態屬性賦值

  ※在使用反射給屬性賦值時,如果該屬性不具有set訪問器,則會丟擲異常ArgumentException;

  三、呼叫型別的方法:

public class MyClass
{
  public void MyFunc(int num)
  {
    Console.WriteLine("MyFunc(int num) execute, the parameter is: " + num);
  }
  public static void MyStaticFunc(int num)
  {
      Console.WriteLine("MyStaticFunc(int num) execute, the parameter is: " + num);
  }
}
//使用方式:
//呼叫型別的例項方法MyFunc MyClass myObj = new MyClass(); //建立例項 Type myType = typeof(MyClass); //獲取型別,或myObj.GetType() MethodInfo methodInfo = myType.GetMethod("MyFunc"); //獲取型別中指定的方法資訊 methodInfo.Invoke(myObj, new object[] { 10 }); //呼叫例項方法,並傳入引數,無參傳null //MyFunc(int num) execute, the parameter is: 10 //呼叫型別的例項方法MyStaticFunc MethodInfo staticMethodInfo = myType.GetMethod("MyStaticFunc"); //獲取型別中指定的方法資訊 staticMethodInfo.Invoke(null, new object[] { 20 }); //呼叫靜態方法,並傳入引數,無參傳null //MyStaticFunc(int num) execute, the parameter is: 20

  四、呼叫型別的建構函式同時建立例項:

public class MyClass
{
    public MyClass()
    {
        Console.WriteLine("MyClass() execute.");
    }
    public MyClass(int num)
    {
        Console.WriteLine("MyClass(int num) execute, the parameter is: " + num);
    }
}
//使用方式:
//呼叫無參的建構函式 Type myType = typeof(MyClass); //獲取型別,或myObj.GetType() ConstructorInfo constructorInfo = myType.GetConstructor(new Type[] { }); //獲取型別中指定的建構函式資訊,傳入該建構函式的引數列表的型別陣列,無參傳空陣列 MyClass myObj = constructorInfo.Invoke(null) as MyClass; //通過呼叫建構函式建立例項,無參傳null //MyClass() execute. //呼叫帶引數的建構函式 constructorInfo = myType.GetConstructor(new Type[] { typeof(int) }); //獲取型別中指定的建構函式資訊,傳入該建構函式的引數列表的型別陣列 myObj = constructorInfo.Invoke(new object[] { 20 }) as MyClass; //通過呼叫建構函式建立例項,並傳入引數 //MyClass(int num) execute, the parameter is: 20

  ※也可以使用Type類中的例項方法InvokeMember()來呼叫指定成員;

  五、使用反射查詢特性:

  1.如果元素使用了特性,在沒有檢索並對其進行操作前該特性沒有任何價值,可以使用反射在程式執行過程中獲取元素新增的特性然後對其進行操作,使用特性基類Attribute中的靜態方法GetCustomAttributes(MemberInfo element)或名稱空間System.Reflection中的擴充套件方法GetCustomAttributes(this MemberInfo element)來獲取型別或成員的所有特性資訊:

Attribute[] attributes = Attribute.GetCustomAttributes(typeof(MyClass));
//IEnumerable<Attribute> attributes = typeof(MyClass).GetCustomAttributes();
foreach (var item in attributes)
{
  if (item is MyselfAttribute)
  {
    MyselfAttribute attribute = item as MyselfAttribute;
    Console.WriteLine(attribute .ClassName + " " + attribute .Author); //MyClass Me
  }
}

  2.這兩個方法都有對應的過載方法,可以傳入要檢索的指定特性的型別,這樣即可得到元素中所有指定型別的特性資訊:

Attribute[] attributes = Attribute.GetCustomAttributes(typeof(MyClass), typeof(MyselfAttribute));
//IEnumerable<Attribute> attributes = typeof(MyClass).GetCustomAttributes(typeof(MyselfAttribute));
foreach (var item in attributes)
{
  MyselfAttribute attribute = item as MyselfAttribute;
  Console.WriteLine(attribute.ClassName + " " + attribute.Author); //MyClass Me
}

  ※如果未找到任何特性或指定型別的特性,這些方法會返回一個空陣列;

  3.也可以使用基類Attribute中的靜態方法GetCustomAttribute(MemberInfo element, Type attributeType)或名稱空間System.Reflection中的擴充套件方法GetCustomAttribute(this MemberInfo element, Type attributeType)來獲取型別或成員的指定特性資訊;
  ※如果未找到指定型別的特性,會返回null;
  ※在檢索的元素中存在多個相同的指定型別的特性時,會丟擲異常Reflection.AmbiguousMatchException;

 

  型別資訊、晚期繫結、動態建立型別等會在下一篇中介紹。

 


 

如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的認可是我寫作的最大動力!

作者:Minotauros
出處:https://www.cnblogs.com/minotauros/

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。

相關文章