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");
}
}
}