C#之玩轉(zhuǎn)反射

轉(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)這里的輸出并沒有包含privateprotected訪問權(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赦抖、_test1Test2私有保護(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è)置了Test33矫钓,下面我通過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)在感覺可以為所欲為了?但是還沒有完还惠。

深入窺視屬性
因為屬性存在getset饲握,并且兩者都是方法,所以比較棘手蚕键。我們需要通過屬性對象獲取getset方法救欧,在通過調(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)了這其中還有getset,你可能會認(rèn)為它們不是屬性嗎亮钦?怎么跑到方法這里來了馆截,其實上面我已經(jīng)說了。這些其實也是方法蜂莉。這也是為什么上面我需要去判斷輸入?yún)?shù)的數(shù)量以及類型的緣故蜡娶。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市映穗,隨后出現(xiàn)的幾起案子窖张,更是在濱河造成了極大的恐慌,老刑警劉巖蚁滋,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宿接,死亡現(xiàn)場離奇詭異赘淮,居然都是意外死亡,警方通過查閱死者的電腦和手機睦霎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門梢卸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人碎赢,你說我怎么就攤上這事低剔。” “怎么了肮塞?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵襟齿,是天一觀的道長。 經(jīng)常有香客問我枕赵,道長猜欺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任拷窜,我火速辦了婚禮开皿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘篮昧。我一直安慰自己赋荆,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布懊昨。 她就那樣靜靜地躺著窄潭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪酵颁。 梳的紋絲不亂的頭發(fā)上嫉你,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天,我揣著相機與錄音躏惋,去河邊找鬼幽污。 笑死,一個胖子當(dāng)著我的面吹牛簿姨,可吹牛的內(nèi)容都是我干的距误。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼款熬,長吁一口氣:“原來是場噩夢啊……” “哼深寥!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起贤牛,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤惋鹅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后殉簸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體闰集,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡沽讹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了武鲁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片爽雄。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖沐鼠,靈堂內(nèi)的尸體忽然破棺而出挚瘟,到底是詐尸還是另有隱情,我是刑警寧澤饲梭,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布乘盖,位于F島的核電站,受9級特大地震影響憔涉,放射性物質(zhì)發(fā)生泄漏订框。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一兜叨、第九天 我趴在偏房一處隱蔽的房頂上張望穿扳。 院中可真熱鬧,春花似錦国旷、人聲如沸矛物。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽泽谨。三九已至,卻和暖如春特漩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背骨杂。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工涂身, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人搓蚪。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓蛤售,卻偏偏與公主長得像,于是被迫代替她去往敵國和親妒潭。 傳聞我的和親對象是個殘疾皇子悴能,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)雳灾,斷路器漠酿,智...
    卡卡羅2017閱讀 134,629評論 18 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,749評論 25 707
  • 陪伴,一個簡單純樸的詞, 一種溫暖人心的力量, 一種給人依靠的信賴, 一種愜意芬芳的欣慰。 有你的陪伴谎亩,春風(fēng)十里不...
    手中沙生活匯閱讀 469評論 0 2
  • 曾經(jīng)有人說要和我一起回家 可是 歲月沉默 各自天涯 歲月這么久 愛你卻再也說不出口 此城這么小 最后炒嘲,還是沒能如愿...
    北環(huán)路真人閱讀 125評論 0 0
  • 每逢月夜宇姚,總覺得天地都會安靜下來。 月白星稀夫凸,天高云淡浑劳,樹影斑駁,縱橫交錯夭拌,鳥飛無聲魔熏,月照孤影。 似乎是要求得某種...
    五陵豪杰也愛貓閱讀 323評論 6 16