System.Reflection 動態靠名稱name存取class內容

System.Reflection 相關使用技巧

System.Reflection 一直是我很少用到的東西,也算比較進階的東西了,多數時候都不會用到,但程式需求複雜到達一定程度這個工具就可以帶來祕技般的方便,另外說實話這是我第一次使用到工具.

很多狀況下,我們可能是動態決定要存取物件中的哪個 field 或是 method ,舉個我寫模擬器擷取圖的例子來說

下面是我擷取遊戲圖片的一個相關method

        static public Bitmap GetScreenFrame()
        {

           return new Bitmap(256 * ScreenSize, 240 * ScreenSize, 256 * ScreenSize * 4, PixelFormat.Format32bppRgb,
               (IntPtr)Pointer.Unbox((Pointer)typeof(NesCore).GetField("ScreenBuf" + ScreenSize + "x", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null)) );

            switch (ScreenSize)
            {
                case 1: return new Bitmap(256 * 1, 240 * 1, 256 * 1 * 4, PixelFormat.Format32bppRgb, (IntPtr)ScreenBuf1x);
                case 2: return new Bitmap(256 * 2, 240 * 2, 256 * 2 * 4, PixelFormat.Format32bppRgb, (IntPtr)ScreenBuf2x);
                case 3: return new Bitmap(256 * 3, 240 * 3, 256 * 3 * 4, PixelFormat.Format32bppRgb, (IntPtr)ScreenBuf3x);
                case 4: return new Bitmap(256 * 4, 240 * 4, 256 * 4 * 4, PixelFormat.Format32bppRgb, (IntPtr)ScreenBuf4x);
                case 5: return new Bitmap(256 * 5, 240 * 5, 256 * 5 * 4, PixelFormat.Format32bppRgb, (IntPtr)ScreenBuf5x);
                case 6: return new Bitmap(256 * 6, 240 * 6, 256 * 6 * 4, PixelFormat.Format32bppRgb, (IntPtr)ScreenBuf6x);
                case 8: return new Bitmap(256 * 8, 240 * 8, 256 * 8 * 4, PixelFormat.Format32bppRgb, (IntPtr)ScreenBuf8x);
                case 9: return new Bitmap(256 * 9, 240 * 9, 256 * 9 * 4, PixelFormat.Format32bppRgb, (IntPtr)ScreenBuf9x);
            }
            return null;
        }

簡單來說 ScreenSize這個參數可能會隨著設定改變,隨著改變不同,所使用的field就不同(因為screen buffer的size不同,所以有分成不同的screen data記錄著),最簡單也最直覺,說不定速度也是最快的方式就是使用switch,但這就給人很累贅的感覺,有些有潔癖的人可能就會覺得很焦躁或是dirty,這時候靠著 Reflection 的相關功能,就可以做大幅度簡化.

下面試修改版

        static public Bitmap GetScreenFrame()
        {

           return new Bitmap(256 * ScreenSize, 240 * ScreenSize, 256 * ScreenSize * 4, PixelFormat.Format32bppRgb,
               (IntPtr)Pointer.Unbox((Pointer)typeof(NesCore).GetField("ScreenBuf" + ScreenSize + "x", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null)) );
        }

一行ko掉....舒服不是嗎?

但.....這真的是我第一次使用,以前曾聽聞 Reflection 好用,但都沒機會用到,剛好今天想重構一下code,看到這段switch的寫法感覺很礙眼,查了一下方法,果然還有更精簡的方式.

不過這方式感覺上在需要效率的地方並不能過渡被使用,一下bind 一下封包 一下解包,這機制中間應該是需要不少cost,只是程式都幫你幹掉了.

ps.把長長一行內容塞到  ( .... ) 裡面通常來說並不認為是好的code style, 但個人習慣很喜歡把code行數給縮減掉,並且減少中間過程的參數建立,個人喜好拿捏,未必值得模仿.

更多可以參考 https://msdn.microsoft.com/zh-tw/library/system.reflection.fieldinfo.getvalue(v=vs.110).aspx