前言
之所以要寫(xiě)這篇關(guān)于C#反射的隨筆,起因有兩個(gè):
第一個(gè)是自己開(kāi)發(fā)軟件需要用到,比如剛剛寫(xiě)完的一個(gè)詞頻統計的軟件和正在寫(xiě)的一個(gè)關(guān)于整理自己知識結構的軟件。
其次就是想把所學(xué)的東西整理一下,完善自己在這方面的知識結構,以便能夠更好的融合到一些設計模式當中,比如抽象工廠(chǎng)模式等,來(lái)優(yōu)化自己的軟件代碼。
所以下決心自己寫(xiě)一篇,廢話(huà)不多說(shuō)開(kāi)始進(jìn)入主題。
前期準備
在VS2010中新建一個(gè)解決方案ReflectionSample,接著(zhù)創(chuàng )建兩個(gè)項目,第一個(gè)是類(lèi)庫項目ReflectionLibrary,第二個(gè)是控制臺應用程序項目ReflectionSample01。
在類(lèi)庫項目中創(chuàng )建一個(gè)類(lèi)Person,代碼如下所示:
publicclass Person
{
privateint age = 10;
privatestring name;
protectedstring email;
protectedbool sex;
publicstring Address { get;set; }
publicdouble Weight { get;set; }
publicvoid SayHello(stringname)
{
stringstr = "Hello " + name;
Console.WriteLine(str);
}
publicint GetAge(stringstr)
{
returnage;
}
publicstatic voidMyStatic()
{
Console.WriteLine("我是static方法.");
}
}
然后,把類(lèi)庫(ReflectionLibrary,Reflection)引用到控制臺應用程序中,添加如下using語(yǔ)句:
usingReflectionLibrary;
using System.Reflection;
窺視內部
常言道知己知彼百戰百勝,所以我們第一步也是關(guān)鍵的一步就是要窺視Person類(lèi)的結構(這里我們假設對Person并不了解)。
首先我們先要總攬全局才能繼續深入,所以我們先在Main中加入如下代碼:
static void Main(string[] args)
{
Typet = typeof(Person);
MemberInfo[]minfos = t.GetMembers();
foreach(MemberInfo minfo inminfos)
{
Console.WriteLine(minfo.Name);
}
}
在這里我們獲取這個(gè)類(lèi)的類(lèi)型,然后獲取了其中的公共成員(可能很多人都會(huì )認為GetMembers是獲取全部,但其實(shí)是獲取公開(kāi)的所有成員。)然后我們通過(guò)foreach將所有的成員的名稱(chēng)循環(huán)輸出。
在這里我們可以看到其中不僅輸出了我們所寫(xiě)類(lèi)中的成員,同時(shí)還輸出了父類(lèi)的成員(如果不理解的這里幫你們補充下基礎,object是所有類(lèi)的基類(lèi)),大家一定會(huì )發(fā)現這里的輸出并沒(méi)有包含private和protected訪(fǎng)問(wèn)權限的成員。這就應了上面的那句話(huà):GetMembers默認返回公開(kāi)的成員。
僅僅只能看到這些公開(kāi)的成員對我們來(lái)說(shuō)意義并不大,所以我們需要查看到哪些非公有的成員。
下面我們將上面的代碼改成如下所示:
static void Main(string[]args)
{
Typet = typeof(Person);
MemberInfo[]minfos = t.GetMembers(BindingFlags.NonPublic|
BindingFlags.Instance| BindingFlags.Public);
foreach(MemberInfo minfo inminfos)
{
Console.WriteLine(minfo.Name);
}
}
從中我們看到使用了GetMembers的重載版本,并且傳入了枚舉類(lèi)型,分別是“包含非公開(kāi)”、“包含實(shí)例成員”和“包含公開(kāi)”。然后我們就可以獲取到所有成員了。
最終我們將會(huì )得到下面這些成員:
到這里你可能會(huì )認為我們已經(jīng)檢索結束了,但是你有沒(méi)有發(fā)現屬性很多,而且還包含了大量的父類(lèi)中的屬性,假設我們只關(guān)注該類(lèi)中的成員,并不關(guān)注父類(lèi)中的成員該如何做呢?
其實(shí),我們只需要加上一個(gè)枚舉類(lèi)型(BindingFlags.DeclaredOnly):
MemberInfo[] minfos =t.GetMembers(BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.Public|
BindingFlags.DeclaredOnly);
然后我們再查看結果:
此時(shí)就只包含該類(lèi)中的成員了。
然后我們繼續查看,可以發(fā)現最終的結果并沒(méi)有輸出這些靜態(tài)成員。這個(gè)時(shí)候我們只需要在GetMembers中加上一個(gè)枚舉BindingFlags.Static:即可。
這里我們僅僅輸出了所有的成員,但是卻沒(méi)有區分是方法、字段還是屬性所以我們在Main中添加一個(gè)方法(這個(gè)方法的寫(xiě)法可以參見(jiàn)圖文:C#中的Lambda表達式簡(jiǎn)介,利用C#將Lambda表達式轉化成Expression樹(shù)。):
static void Main(string[]args)
{
Typet = typeof(Person);
Func<MemberTypes, String>getType = (x) =>
{
switch(x)
{
caseMemberTypes.Field:
return "字段";
caseMemberTypes.Method:
return "方法";
caseMemberTypes.Property:
return "屬性";
caseMemberTypes.Constructor:
return "構造函數";
default:
return "未知";
}
};
MemberInfo[]minfos = t.GetMembers(BindingFlags.NonPublic|
BindingFlags.Instance| BindingFlags.Public |
BindingFlags.DeclaredOnly|
BindingFlags.Static);
foreach(MemberInfo minfo inminfos)
{
Console.WriteLine(minfo.Name+ ";類(lèi)型:" +getType(minfo.MemberType));
}
}
這里我們用了一個(gè)局部方法來(lái)根據類(lèi)型輸出對應的文本,因為篇幅的原因就只判斷了幾個(gè)基本的類(lèi)型。
最終輸出的結果如下:
到此為止我們已經(jīng)能夠窺視整個(gè)結構。
深入窺視字段
通過(guò)上面的內容我們僅僅縱覽了全局,下面我們將要繼續深入,首先我們先拿字段下手。
這里我們不在使用GetMembers而需要使用GetFields(當然跟GetMembers一樣如果不傳入指定的枚舉只返回公開(kāi)的字段),代碼如下所示:
static void Main(string[]args)
{
Type t = typeof(Person);
FieldInfo[]finfos = t.GetFields(BindingFlags.NonPublic|
BindingFlags.Public| BindingFlags.Instance |
BindingFlags.DeclaredOnly);
foreach(FieldInfo finfo infinfos)
{
stringinfor = "字段名稱(chēng):" + finfo.Name +
"字段類(lèi)型:" +finfo.FieldType.ToString();
Console.WriteLine(infor);
}
}
最終的輸出結果如下所示:
一直到這里大家都會(huì )認為我們僅僅是分析,感覺(jué)沒(méi)有什么實(shí)質(zhì)的東西,你可以看到age,name,emai,sex是私有和保護類(lèi)型,是不可以獲取到它們的值的,但是我們通過(guò)反射卻可以,具體的代碼如下所示:
static void Main(string[]args)
{
Typet = typeof(Person);
Personp = new Person();
p.Address = "LSGOGroup";
FieldInfo[]finfos = t.GetFields(BindingFlags.NonPublic|
BindingFlags.Public| BindingFlags.Instance |
BindingFlags.DeclaredOnly);
foreach(FieldInfo finfo infinfos)
{
stringinfor = "字段名稱(chēng):" + finfo.Name
+ ",字段類(lèi)型:" +finfo.FieldType.ToString()
+ ",Person中的值:" + finfo.GetValue(p);
Console.WriteLine(infor);
}
}
可以看到我實(shí)例化了這個(gè)類(lèi),并設置了Address為“LSGOGroup”,下面通過(guò)finfo.GetValue輸出了這個(gè)值,結果如下圖: 
現在是不是感覺(jué)有點(diǎn)酷了?這還沒(méi)完呢,我們光獲取不算什么,下面我們還要修改它的值。
static void Main(string[]args)
{
Typet = typeof(Person);
Personp = new Person();
FieldInfo[]finfos = t.GetFields(BindingFlags.NonPublic|
BindingFlags.Public| BindingFlags.Instance |
BindingFlags.DeclaredOnly);
foreach(FieldInfo finfo infinfos)
{
if(finfo.FieldType == typeof(string))
{
finfo.SetValue(p, "LSGOGroup");
}
elseif ((finfo.FieldType == typeof(bool)))
{
finfo.SetValue(p, true);
}
else
{
finfo.SetValue(p, 16);
}
stringinfor = "字段名稱(chēng):" + finfo.Name
+ ",字段類(lèi)型:" +finfo.FieldType.ToString()
+ ",Person中的值:" + finfo.GetValue(p);
Console.WriteLine(infor);
}
}
這里只是在foreach中增加了finfo.SetValue語(yǔ)句,下面我們繼續看最終輸出結果:

是不是現在感覺(jué)可以為所欲為了?但是還沒(méi)有完。
通過(guò)微信學(xué)習的知識只能是碎片化的知識,作為新時(shí)代的我們希望能夠構建自己的知識結構,使我們的知識體系化,系統化,以后在遇到碎片化的知識,我們做的只是融合到自己的知識結構中,故我們將推出“與LSGO一起學(xué)”系列課程,幫助大家來(lái)構建知識框架,初步規劃有:
“與LSGO一起學(xué)C++”;
“與LSGO一起學(xué)C#”;
“與LSGO一起學(xué)Matlab”;
“與LSGO一起學(xué)數據結構”;
“與LSGO一起學(xué)設計模式”;
“與LSGO一起學(xué)可視化建模語(yǔ)言(UML)”;
“與LSGO一起學(xué)線(xiàn)性代數”;
“與LSGO一起學(xué)高等數學(xué)”
“與LSGO一起學(xué)概率論與數理統計”;
“與LSGO一起學(xué)抽象代數;
“與LSGO一起學(xué)點(diǎn)集拓撲”
“與LSGO一起學(xué)數字圖像處理”;
“與LSGO一起學(xué)智能計算”;
如果對這些內容感興趣,可以一起來(lái)學(xué)習討論。
我們的官網(wǎng): www.lsgogroup.com
聯(lián)系客服