反射知識點整理

目錄

    1.背景
    2.定義
    3.利弊
    4.Type
    5.反射程序集
    6.反射基本類型
    7.反射特性
    8.動態建立對象
    9.動態調用方法

背景

     反射不經常使用,可是在開發一些基礎框架或公共類庫,使用反射會使系統更加靈活,有時甚至是惟一的選擇。反射是個龐大的話題,牽扯到的知識點也不少,包括程序集,自定義特性,泛型等等。

定義

     1.提供了在運行時對類型和類型成員的元數據(metadata)的訪問能力。
     2.通常會經過System.Reflaction名稱空間並配合Type類來實現。
     3.Type類封裝了類型的元數據,是進行反射的入口 

功用

     1.查看和遍歷類型和成員的元數據
     2.動態建立類型實例,動態調用所建立實例的方法、字段、屬性
     3.延遲綁定方法和屬性

利弊

     1.不足
        1.1 開銷很大,性能不佳
     2.優勢
        2.1 靈活
     3.總結
        犧牲性能換靈活,這是一種折中的選擇

Type類

//獲取Type實例三種方法:
1.1 Type t = Type.GetType("System.IO.Stream");
1.2 Type t = typeof(System.IO.Stream);
1.3 Type t = "Hello".GetType();
//Type類型的基本信息
Type t = "Hello".GetType();
t.Name;     //獲取類型名稱
t.FullName;//獲取類型全名
t.Namespace;//獲取名稱空間
t.BaseType;//獲取對基類的Type類型的引用
t.UnderlyingSystemType;
t.Attributes;//獲取TypeAttributes位標記
t.IsValueType;
t.IsByRef;
t.IsEnum;
t.IsClass;
t.IsInterface;
t.IsSealed;
t.IsPrimitive;//是否基類型
t.IsAbstract;
t.IsPublic;
t.IsVisible;
……
//Type類型的成員信息
//包含哪些字段,字段名稱,類型,可訪問性--FieldInfo
//包含哪些屬性,屬性名稱,類型,可訪問性--PropertyInfo
//包含哪些方法,方法名稱,返回值,參數個數,參數類型,參數名稱--MethodInfo
//事件 EventInfo  參數ParameterInfo
t.GetFields();
t.GetProperties();
t.GetMethods();
t.GetProperties();
t.GetConstructor();
t.GetMembers(BindingFlags.ExactBinding | BindingFlags.DeclaredOnly);
……
//位標記
//.NET中的枚舉咱們通常有兩種用法:
//一是表示惟一的元素序列,例如一週裏的各天;
//還有就是用來表示多種複合的狀態,這個時候通常須要爲枚舉加上[Flags]特性標記爲位域
//例如:
[Flags]  
public enum BindingFlags
    Default = 0, 
    IgnoreCase = 1, 
    DeclaredOnly = 2, 
    Instance = 4, 
    Static = 8, 
    Public = 16, 
    NonPublic = 32, 
    FlattenHierarchy = 64, 
    InvokeMethod = 256, 
    CreateInstance = 512, 
    GetField = 1024, 
    SetField = 2048
}

反射程序集

     1.獲取Assembly的幾種方法
1.1 Assembly am = Assembly.LoadFrom("Demo.dll");
1.2 Assembly am = Assembly.Load("Demo");
1.3 Assembly am = typeof(int).Assembly;
1.4 獲取當前程序集 
Assembly am = Assembly.GetExecutingAssembly();
 
am.FullName;//程序集名稱
am.Location; //路徑
am.GetTypes();//所有類型
am.GetType();//某個類型
am.GetModules();
am.GetModule();
am.GetCustomAttributes();
 
Module[] modules = am.GetModules();
foreach (Module module in modules)
{
    Console.WriteLine("模塊:" + module); 
    //Demo.dll
 
    foreach (Type t in module.GetTypes())
        Console.WriteLine("類型:" + module); 
        //Demo.BaseClass Demo.Delegate Demo.Struct Demo.Interface Demo.DemoClass
}

反射基本類型

     由於程序集包含不少類型,一個類型又包含不少成員,一個成員又包含不少其餘信息,層層嵌套,爲了閱讀方便,去掉最外套的循環。   
     
     1.獲取基本類型
     2.獲取成員信息和MemberInfo類型
     3.字段信息和FieldInfo
     4.屬性信息和PropertyInfo
     5.方法信息和MethodInfo
     6.……
      //反射枚舉
public static class EnumManager<TEnum>
{
    private static DataTable GetDataTable()
    {
        Type t = typeof (TEnum);
        FieldInfo[] fieldInfos = t.GetFields();
 
        DataTable table=new DataTable();
        table.Columns.Add("Name", Type.GetType("System.String"));
        table.Columns.Add("Value", Type.GetType("System.Int32"));
 
        foreach (FieldInfo field in fieldInfos)
        {
            if (field.IsSpecialName)
            {
                DataRow row = table.NewRow();
                row[0] = field.Name;
                row[1] = Convert.ToInt32(field.GetRawConstantValue());
                table.Rows.Add(row);
            }
        }
 
        return table;
    }
}   

反射特性

     特性:特性是一種特殊的類型,能夠加載到程序集的各類類型上,包括模塊,類,接口,結構,構造函數,方法,方法參數等。特性是爲程序集添加元數據的一種機制,經過他能夠爲編譯器提供指示或對數據的說明。 

1.自定義特性:

  //AllowMultiple=true表示能夠重複添加到一個類型上
[AttributeUsage(AttributeTargets.Class,AllowMultiple=true,Inherited = false)]
public class RecordAttribute:Attribute
{
    private string recordType;
    private string author;
    private DateTime date;
    private string memo;
 
    public RecordAttribute(string recordType, string author, string date)
    {
        this.recordType = recordType;
        this.author = author;
        this.date = Convert.ToDateTime(date);
    }
 
    public string RecordType { get { return recordType; } }
    public string Author { get { return author; } }
    public DateTime Date { get { return date; } }
    public string Memo { get { return memo; }set { memo = value; }}
}

2.使用特性

//特性使用:僅寫成一行,無論是構造函數參數仍是屬性,所有寫到構造函數的圓括號中
//對應屬性,採用「屬性=值」的格式
[Record("更新","jackyfei","2016-08-24")]
[Record("建立","張飛洪","2016-08-14",Memo = "這個類是演示用的")]
public class DemoClass
{
}

3.反射特性

//使用特性對類型進行標記,目的是爲了在程序中的某處使用它。
Type t = typeof(DemoClass);
object[] records = t.GetCustomAttributes(typeof (RecordAttribute), false);
 
foreach (RecordAttribute record in records)
{
    Console.WriteLine("{0}", record);
    Console.WriteLine(" 類型:{0}", record.RecordType);
    Console.WriteLine(" 做者:{0}", record.Author);
    Console.WriteLine(" 日期:{0}", record.Date.ToShortDateString());
    Console.WriteLine(" 備註:{0}", record.Memo);
}
//獲取到特性對象後,就能夠任意處理了,好比說導出程序註釋

動態建立對象

     前面的知識內容,主要是利用反射查看類型,查看特性,程序集,都是屬於查看元數據的信息的內容。接下來將學習如何用反射動態建立一個對象。之因此叫動態,是由於能夠字符串能夠由各類途徑得到  
     
[Record("更新", "jackyfei", "2016-08-24")]
[Record("建立", "張飛洪", "2016-08-14", Memo = "這個類是演示用的")]
public class Calculator
{
    private int x;
    private int y;
 
    public Calculator()
    {
        x = 0;
        y = 0;
        Console.WriteLine("x:{0},y:{1}", x, y);
    }
 
    public Calculator(int x,int y)
    {
        this.x = x;
        this.y = y;
        Console.WriteLine("x:{0},y:{1}",x,y);
    }
 
    public string GetStr()
    {
        return "hello,jackyfei";
    }
 
    public int Add()
    {
        int total = 0;
        total = x + y;
        return total;
    }
 
    public static int Add(int x,int y)
    {
        int total = x + y;
        return total;
    }
 

1. 使用無參構造函數創造對象

//建立對象一
Assembly am = Assembly.GetExecutingAssembly();
DemoClass demoClass= am.CreateInstance("NameSpace.DemoClass", true) as DemoClass;
Console.WriteLine(demoClass.GetStr());
 
//建立對象二
//第一個參數是程序集的名稱,null表示當前程序集;
ObjectHandle handler = Activator.CreateInstance(null, "NameSpace.DemoClass");
handler.Unwrap();

2. 使用有參構造函數創造對象

Assembly am2 = Assembly.GetExecutingAssembly();
Object[] parameters=new object[2];
parameters[0] = 3;
parameters[1] = 5;
 
//BindingFlags用於限定類型成員的搜索,Default意思是不使用這個策略
am2.CreateInstance("NameSpace.DemoClass", true, BindingFlags.Default,
null, parameters, null, null);

動態調用方法

     這裏的調用方法不是將上面的動態建立的對象強制類型轉換後再調用,並且利用發射,基於字符串來調用。

1.1 InvokeMember()調用方式

Type t = typeof (Calculator);
Calculator c=new Calculator(3,5);
int rst =(int)t.InvokeMember("Add", BindingFlags.InvokeMethod, null, c, null);
Console.WriteLine("x+y={0}", rst);
 
object[] p = {6, 7};
int rst2 = (int)t.InvokeMember("Add", BindingFlags.InvokeMethod, null, t, p);
Console.WriteLine("x+y={0}", rst2);
//調用靜態方法,他不是基於某個具體的類型實例,而是基於類型自己,
//因此傳遞的參數是typeof(Calculator)

1.2 GetMethod()調用方式

Type t = typeof (Calculator);
Calculator c=new Calculator(3,5);
 
//BindingFlags.Instance 調用實例方法
MethodInfo mi = t.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public);
int rst = (int)mi.Invoke(c, null);
Console.WriteLine("x+y={0}", rst);
 
//BindingFlags.Static 調用靜態方法
object[] p = { 6, 9 };
MethodInfo mi2 = t.GetMethod("Add", BindingFlags.Static | BindingFlags.Public);
int rst2 = (int)mi2.Invoke(null, p);
Console.WriteLine("x+y={0}", rst2);
相關文章
相關標籤/搜索