轉(zhuǎn)載:http://www.cnblogs.com/yaozhenfa/p/CSharp_Reflection_1.html
前言
之所以要寫這篇關(guān)于C#反射的隨筆,起因有兩個:
- 第一個*是自己開發(fā)的網(wǎng)站需要用到
- 其次就是沒看到這方面比較好的文章。
所以下定決心自己寫一篇张漂,廢話不多說開始進(jìn)入*正題解幼。
前期準(zhǔn)備
在VS2012中新建一個控制臺應(yīng)用程序(我的命名是ReflectionStudy)桐早,這個項目是基于.net 4.0驻子。接著我們打開Program.cs文件进肯,按照如下在Program中寫一個我們自己的類:
public class RefClass
{
private int _test3;
private int _test1 { get; set; }
protected int Test2 { get; set; }
public int Test3 { get; set; }
public void Show()
{
}
}
窺視內(nèi)部
常言道知彼知己百戰(zhàn)不殆艇肴,所以我們第一步也是關(guān)鍵的一步就是要窺視RefClass類的結(jié)構(gòu)(這里我們假設(shè)對RefClass并不理解)腔呜。
首先我們先要縱覽全局才能繼續(xù)深入,所以我們先在Main中寫入如下代碼:
static void Main(string[] args)
{
Type t = typeof(RefClass);
MemberInfo[] minfos = t.GetMembers();
foreach (MemberInfo minfo in minfos)
{
Console.WriteLine(minfo.Name);
}
Console.ReadKey();
}
在這里我們獲取這個類的類型再悼,然后獲取了其中的公共成員(可能很多人都會認(rèn)為GetMembers是獲取全部核畴,但其實只是獲取公開的所有成員。)然后我們通過foreach將所有的成員的名稱循環(huán)輸出帮哈。
然后我們可以查看控制臺的輸出:
在這里我們可以看到其中不僅僅輸出了我們所寫類中的成員膛檀,同時還輸出了父類的成員(如果不理解的這里幫你們補充下基礎(chǔ),Object是所有類的基類。)咖刃,細(xì)心的讀者一定會發(fā)現(xiàn)這里的輸出并沒有包含private和protected訪問權(quán)限的成員泳炉。這就應(yīng)了上面的那句話:GetMembers默認(rèn)返回公開的成員。
僅僅只能看到這些公開的成員對我們來說意義并不大嚎杨,所以我們需要查看到那些非公有的成員花鹅。
下面我們將上面的代碼改成如下所示:
static void Main(string[] args)
{
Type t = typeof(RefClass);
MemberInfo[] minfos = t.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public );
foreach (MemberInfo minfo in minfos)
{
Console.WriteLine(minfo.Name);
}
Console.ReadKey();
}
從中我們看到我們使用了GetMembers的重載版本,并且傳入了枚舉類型枫浙,分別是“包含非公開”刨肃、“包含實例成員”和“包含公開”。然后我們就可以獲取到所有成員了箩帚。
最終我們將會得出下面這些成員:
到這里你可能會認(rèn)為我們已經(jīng)檢索結(jié)束了真友,但是你有沒有發(fā)現(xiàn)屬性很多,而且還包含了大量的父類中的屬性紧帕,假設(shè)我們只關(guān)注該類中的成員盔然,并不關(guān)注父類中的成員該如何做呢?
其實我們只需要加上一個枚舉類型(BindingFlags.DeclaredOnly):
1 MemberInfo[] minfos = t.GetMembers(BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly );
然后我們再查看結(jié)果:
此時就只包含該類中的成員了是嗜。
下面我們在RefClass類中添加兩個靜態(tài)方法愈案,如下所示:
public class RefClass
{
private int _test3;
private int _test1 { get; set; }
protected int Test2 { get; set; }
public int Test3 { get; set; }
private static void Show2()
{
}
public static void Show3()
{
}
public void Show()
{
}
}
然后我們繼續(xù)查看,可以發(fā)現(xiàn)最終的結(jié)果并沒有輸出這些靜態(tài)成員鹅搪。這個時候我們只需要在GetMembers中加上一個枚舉:BindingFlags.Static即可站绪。
這里我們僅僅輸出了所有的成員,但是卻沒有區(qū)分出是方法還是屬性所以我們在Main中添加一個方法:
static void Main(string[] args)
{
Type t = typeof(RefClass);
Func<MemberTypes, String> getType = (x) =>
{
switch (x)
{
case MemberTypes.Field:
{
return "字段";
}
case MemberTypes.Method:
{
return "方法";
}
case MemberTypes.Property:
{
return "屬性";
}
default:
{
return "未知";
}
}
};
MemberInfo[] minfos = t.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static );
foreach (MemberInfo minfo in minfos)
{
Console.WriteLine(minfo.Name + ";類型:" + getType(minfo.MemberType));
}
Console.ReadKey();
}
這里我用了一個局部方法來根據(jù)類型輸出對應(yīng)的文本丽柿,因為篇幅的原因我就只判斷了幾個基本的類型恢准。
最終輸出的結(jié)果如下:
到此為止我們已經(jīng)能夠窺視整個結(jié)構(gòu)。
深入窺視字段
通過上面的內(nèi)容我們僅僅縱覽了全局航厚,下面我們將要繼續(xù)深入顷歌,首先我們先拿字段下手。
這里我們不在使用GetMembers而需要使用GetFields(當(dāng)然跟GetMembers一樣如果不傳入指定的枚舉只返回公開的字段)幔睬,代碼如下所示:
static void Main(string[] args)
{
Type t = typeof(RefClass);
FieldInfo[] finfos = t.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
foreach (FieldInfo finfo in finfos)
{
Console.WriteLine("字段名稱:{0} 字段類型:{1} ", finfo.Name, finfo.FieldType.ToString());
}
Console.ReadKey();
}
最終的輸出結(jié)果如下所示:
一直到這里大家都會認(rèn)為我們僅僅只是分析眯漩,感覺沒有什么實質(zhì)的東西,下面就來點實質(zhì)的東西麻顶,你可以看到_test3赦抖、_test1和Test2是私有和保護(hù)類型,
是不可以獲取到它們的值的辅肾,但是我們通過反射卻可以队萤,具體的代碼如下所示:
static void Main(string[] args)
{
Type t = typeof(RefClass);
RefClass rc = new RefClass();
rc.Test3 = 3;
FieldInfo[] finfos = t.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
foreach (FieldInfo finfo in finfos)
{
Console.WriteLine("字段名稱:{0} 字段類型:{1} rc中的值為:{2}", finfo.Name, finfo.FieldType.ToString(), finfo.GetValue(rc));
}
Console.ReadKey();
}
可以看到我實例化了這個類,并且設(shè)置了Test3為3矫钓,下面我通過finfo.GetValue輸出了這個值要尔,結(jié)果如下圖:
現(xiàn)在是不是感覺有點酷了舍杜?這還沒完呢,我們光獲取不算什么赵辕,下面我們還要修改它的值:
static void Main(string[] args)
{
Type t = typeof(RefClass);
RefClass rc = new RefClass();
rc.Test3 = 3;
FieldInfo[] finfos = t.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
foreach (FieldInfo finfo in finfos)
{
finfo.SetValue(rc, 100);
Console.WriteLine("字段名稱:{0} 字段類型:{1} rc中的值為:{2}", finfo.Name, finfo.FieldType.ToString(), finfo.GetValue(rc));
}
Console.ReadKey();
}
這里我只是在foreach中增加了一條語句finfo.SetValue(rc,100)既绩,下面我們繼續(xù)看最終輸出的結(jié)果:
是不是現(xiàn)在感覺可以為所欲為了?但是還沒有完还惠。
深入窺視屬性
因為屬性存在get和set饲握,并且兩者都是方法,所以比較棘手蚕键。我們需要通過屬性對象獲取get和set方法救欧,在通過調(diào)用他們才達(dá)到修改這個屬性的值。
比如下面的代碼:
static void Main(string[] args)
{
Type t = typeof(RefClass);
RefClass rc = new RefClass();
rc.Test3 = 3;
PropertyInfo[] finfos = t.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
foreach (PropertyInfo finfo in finfos)
{
MethodInfo getinfo = finfo.GetGetMethod(true);
Console.WriteLine("get方法的名稱{0} 返回值類型:{1} 參數(shù)數(shù)量:{2} MSIL代碼長度:{3} 局部變量數(shù)量:{4}", getinfo.Name, getinfo.ReturnType.ToString(),
getinfo.GetParameters().Count(),
getinfo.GetMethodBody().GetILAsByteArray().Length,
getinfo.GetMethodBody().LocalVariables.Count);
MethodInfo setinfo = finfo.GetSetMethod(true);
Console.WriteLine("get方法的名稱{0} 返回值類型:{1} 參數(shù)數(shù)量:{2} MSIL代碼長度:{3} 局部變量數(shù)量:{4}", setinfo.Name, setinfo.ReturnType.ToString(),
setinfo.GetParameters().Count(),
setinfo.GetMethodBody().GetILAsByteArray().Length,
setinfo.GetMethodBody().LocalVariables.Count);
setinfo.Invoke(rc, new object[] { 123 });
object obj = getinfo.Invoke(rc, null);
Console.WriteLine("方法名:{0} 內(nèi)部值:{1}", finfo.Name, obj);
}
Console.ReadKey();
}
這里我們循環(huán)每個屬性锣光,通過GetGetMethod獲取get方法(調(diào)用該方法時如果傳入true則無法獲取非公開的get方法set也是一樣)笆怠,接著我們輸出了該方法的返回類型和參數(shù)數(shù)量和MSIL*****代碼長度以及局部變量的數(shù)量,
當(dāng)然你如果有興趣可以繼續(xù)分析輸入?yún)?shù)以及局部變量等誊爹,這里由于篇幅的緣故就不能介紹太多了骑疆。最后我們調(diào)用了set*方法將值改變,然后再通過調(diào)用get方法獲取這個屬性的值替废。
最終的結(jié)果如下所示:
深入窺視方法
首先我們需要將RefClass修改成如下所示:
public class RefClass
{
private int _test3;
private int _test1 { get; set; }
protected int Test2 { get; set; }
public int Test3 { get; set; }
private static void Show2()
{
}
public static string Show3(string s)
{
int b;
int c;
return s;
}
public string Show(string s)
{
string a;
return s;
}
}
主要是在方法中增加局部變量并且加上返回值,避免最后輸出的時候沒有值泊柬。其實這里的方法跟屬性部分類似椎镣,但是為了能夠完整的描述所有,所以筆者依然會講解一遍兽赁。
下面我們直接上代碼:
static void Main(string[] args)
{
Type t = typeof(RefClass);
RefClass rc = new RefClass();
rc.Test3 = 3;
MethodInfo[] finfos = t.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Static );
foreach (MethodInfo finfo in finfos)
{
if (finfo.GetParameters().Count() > 0 && finfo.GetParameters()[0].ParameterType == typeof(string) )
{
object obj = finfo.Invoke(rc, new[] { "123" });
MethodBody mbody = finfo.GetMethodBody();
Console.WriteLine("擁有參數(shù)的方法名:{0} 返回值類型:{1} 參數(shù)1類型:{2} 參數(shù)1名稱:{3} 方法調(diào)用后返回的值:{4}",
finfo.Name,
finfo.ReturnType.ToString(),
finfo.GetParameters()[0].ParameterType.ToString(),
finfo.GetParameters()[0].Name,
obj.ToString());
}
else
{
MethodBody mbody = finfo.GetMethodBody();
Console.WriteLine("沒有參數(shù)的方法名:{0} 返回值類型:{1}",
finfo.Name,
finfo.ReturnType.ToString());
}
}
Console.ReadKey();
}
在這里我進(jìn)行了一些簡單的判斷比如判斷輸入?yún)?shù)的數(shù)量以及類型状答,如果不進(jìn)行這些判斷就會導(dǎo)致程序無法繼續(xù)執(zhí)行,具體為什么可以看下的輸出結(jié)果刀崖,你就能明白筆者為什么要這么做了惊科。
下面就是具體的結(jié)果:
讀者一定發(fā)現(xiàn)了這其中還有get和set,你可能會認(rèn)為它們不是屬性嗎亮钦?怎么跑到方法這里來了馆截,其實上面我已經(jīng)說了。這些其實也是方法蜂莉。這也是為什么上面我需要去判斷輸入?yún)?shù)的數(shù)量以及類型的緣故蜡娶。