天天看點

C#反射詳解程式集中繼資料反射的概念反射的作用文法

C#反射詳解

  • 程式集
  • 中繼資料
  • 反射的概念
  • 反射的作用
  • 文法
    • Type
      • 擷取Type
      • 得到類的程式集資訊
      • 得到類的程式集資訊
      • 擷取類中的所有公共成員
      • 擷取類的公共構造函數并調用
      • 擷取類的公共成員變量
      • 擷取類的公共成員方法
      • 其他
    • Activator
    • Assembly

程式集

程式集是經由編譯器編譯得到的,供進一步編譯執行的中間産物。

在Windows系統中,它一般表現為字尾為.dll(庫檔案)或者.exe(可執行檔案)的格式。

中繼資料

中繼資料是用來描述資料的資料。即程式中的類、類中的函數、變量等資訊就是程式的中繼資料。有關程式以及類型的資料被稱為中繼資料,他們儲存在程式集中。

反射的概念

程式正在運作時,可以檢視其它程式集或者自身的中繼資料。

一個運作的程式檢視本身或者其它程式的中繼資料的行為叫做反射。

說的通俗點就是在程式運作時,通過反射可以得到其它程式集或者自己程式集代碼的各種資訊,包括類、函數、變量等來執行個體化它們,執行它們,操作它們。

反射的作用

因為反射可以在程式編譯後獲得資訊,是以它提高了程式的拓展性和靈活性。

1、程式運作時得到所有中繼資料,包括中繼資料的特性。

2、程式運作時執行個體化對象,操作對象。

3、程式運作時建立新的對象,用這些對象執行任務。

文法

C#反射的文法主要包括三個類:分别是Type、Assembly和Activator。

首先建立一個測試類友善測試:

class Test
    {
        private int i = 1;
        public int j = 0;
        public string str = "123";
        public Test()
        {

        }
        public Test(int i)
        {
            this.i = i;
        }
        public Test(int i ,string str) : this(i)
        {
            this.str = str;
        }

        public void Speak()
        {
            Console.WriteLine(i);
        }
    }
           

Type

Type是類的資訊類,它是反射功能的擠出,通路中繼資料的主要方式。

使用Type的成員擷取有關類型(如構造函數、方法、字段、屬性和類的事件等)聲明的資訊。

擷取Type

得到類的程式集資訊

1、object中的GetType()可以擷取對象的Type

int a = 42;
            Type type = a.GetType();
            Console.WriteLine(type);
           

列印結果是System.Int32即為a的類型

2、通過typeof關鍵字,傳入類名可以擷取對象的Type

Type type2 = typeof(int);
            Console.WriteLine(type2);
           

列印結果是System.Int32即為int的類型

3、通過類的名字可以擷取類型,這裡要注意類名必須包含明明空間,否則會找不到。

Type type3 = Type.GetType("Int32");
            Console.WriteLine(type3);
            Type type4 = Type.GetType("System.Int32");
            Console.WriteLine(type4);
           

列印結果type3為空,type4為System.Int32

得到類的程式集資訊

可以通過Type得到類型所在程式集資訊。

Console.WriteLine(type.Assembly);
            Console.WriteLine(type2.Assembly);
            Console.WriteLine(type4.Assembly);
           

擷取類中的所有公共成員

需要引用命名空間using System.Reflection;

1、首先得到Type

這裡有一個小的知識點,通過typeof擷取Type一般是在能夠明确得到類名的情況下,即擷取同一程式集下的類的Type的時候;如果是不同程式集下的類,可以通過Type.GetType()擷取。

2、然後得到所有公共成員

MemberInfo[] infos = t.GetMembers();
            for (int i = 0; i < infos.Length; i++)
            {
                Console.WriteLine(infos[i]);
            }
           

MemberInfo即該類型下公共成員容器。

擷取類的公共構造函數并調用

1、擷取所有構造函數

擷取構造函數可以通過GetConstructor和GetConstructors獲得,通過ConstructorInfo接收。

ConstructorInfo[] ctors = t.GetConstructors();
            for (int i = 0; i < ctors.Length; i++)
            {
                Console.WriteLine(ctors[i]);
            }
           

2、擷取其中一個構造函數并執行。

2.1:得構造函數需要傳入Type數組,數組中内容按順序是參數類型。

2.2:執行構造函數,需要傳入object數組,表示按順序傳入的參數。

無參構造函數:

//得到無參構造函數
            ConstructorInfo info = t.GetConstructor(new Type[0]);
            //執行無參構造 無參構造沒有參數傳null
            Test obj = info.Invoke(null) as Test;
            Console.WriteLine(obj.j);
           

有參構造函數:

//得到有參構造函數
            ConstructorInfo info2 = t.GetConstructor(new Type[] { typeof(int), typeof(string) });
            //執行有參構造函數
            obj = info2.Invoke(new object[] { 2 ,"asd"}) as Test;
            Console.WriteLine(obj.str);
           

擷取類的公共成員變量

1、得到所有成員變量

FieldInfo[] fileldInfos = t.GetFields();
            for (int i = 0; i < fileldInfos.Length; i++)
            {
                Console.WriteLine(fileldInfos[i]);
            }
           

2、得到指定名稱的公共成員變量

FieldInfo infoJ = t.GetField("j");
            Console.WriteLine(infoJ);
           

3、通過反射擷取和設定對象的值

擷取和設定可以分别通過GetValue和SetValue進行,這裡我們使用上面得到的obj來測試。

3.1、通過反射擷取對象的某個變量的值

3.2、通過反射設定指定對象的某個變量的值

infoJ.SetValue(obj, 100);
            Console.WriteLine(infoJ.GetValue(obj));
           

擷取類的公共成員方法

擷取成員方法可以通過Type類中的GetMethod方法來得到類中的方法,MethodInfo是方法的反射資訊。

如果存在方法重載,用Type數組表示參數類型。

這裡為了友善測試,我們使用string類來檢視裡面的方法。

Type strType = typeof(string);
            //得到所有函數
            MethodInfo[] methods = strType.GetMethods();
            for (int i = 0; i < methods.Length; i++)
            {
                Console.WriteLine(methods[i]);
            }
            //得到指定的Substring函數(int,int)重載
            MethodInfo method = strType.GetMethod("Substring", new Type[] { typeof(int), typeof(int) });
            Console.WriteLine(method);
           

調用方法(注意:如果是靜态方法,Invoke中的第一個參數傳null即可)

string str = "Hello,World!";
            //第一個參數相當于哪個對象要執行這個成員方法,
            object result = method.Invoke(str, new object[] { 7, 5 });
            Console.WriteLine(result);
           

其他

擷取其他成員變量大同小異,這裡隻提示方法并不一一贅述。

1、得到枚舉:GetEnumName、GetEnumNames

2、得到事件:GetEvent、GetEvents

3、得到接口:GetInterface、GetInterfaces

4、得到屬性:GetProperty、GetProtertys

Activator

Activator是用于快速執行個體化對象的類,用于将Type對象快捷執行個體化為對象。

先得到Type,然後快速執行個體化一個對象。

Activator.CreateInstance預設調用無參構造函數。

1、無參構造函數:

Type test = typeof(Test);
            Test testObj = Activator.CreateInstance(test) as Test;
            Console.WriteLine(testObj.str);
           

2、有參構造函數:

如果要調用有參構造函數,在後面一次添加參數即可。

testObj = Activator.CreateInstance(test, 99) as Test;
            testObj = Activator.CreateInstance(test, 55, "11111") as Test;
            Console.WriteLine(testObj.str);
           

Assembly

Assembly類其實就是程式集類。主要用來加載其他程式集,加載後才能用Type來使用其他程式集中的資訊,如果想要使用不是自己程式集中的内容,需要先加載程式集(比如dll檔案)。

三種加載程式集的函數:

1、一般用來加載同一檔案下的其他程式集

Assembly assembly = Assembly.Load(“程式集名稱”);

2、一般用來加載不再同一檔案下的其他程式集

Assembly assembly = Assembly.LoadFrom(“包含程式集清單的檔案的名稱或路徑”);

Assembly assembly = Assembly.LoadFile(“要加載的檔案的完全限定路徑”);

使用方法:

1、首先加載一個指定程式集:

Assembly assembly = Assembly.LoadFrom (@"C:\Users\01\Desktop\ConsoleApp1\TestDLL\bin\Debug\TestDLL.dll");
            Type[] types = assembly.GetTypes();
            for (int i = 0; i < types.Length; i++)
            {
                Console.WriteLine(types[i]);
            }
           

2、再加載程式集中的一個類對象,之後才能使用反射

//得到dll中的Class1類
            Type c = assembly.GetType("TestDLL.Class1");
            object o = Activator.CreateInstance(c);
            //調用Class1類中的Speak方法
            MethodInfo speak = c.GetMethod("Speak");
            speak.Invoke(o,null);
           

其中TestDLL.dll為自己封裝的dll,内容如下:

namespace TestDLL
{
    public class Class1
    {
        public int i = 0;
        public string str = "123";
        public void Speak()
        {
            Console.WriteLine("speak");
        }

        
    }
}