天天看點

反射發送實戰(-)InvokeMember

反射是.net中的進階功能之一,利用反射可以實作許多以前看來匪夷所思的功能,下面是我看了《Programming C#》(O'Reilly)之後對于反射的一點實踐,本想直接做個應用程式來說明問題,但苦于工作繁忙并考慮到以簡單為主,故先對反射發送(reflection emit)的使用做一些介紹。文章最後再給出一個執行個體。

下面的程式在運作時生成了一個Test.cs檔案,并調用csc編譯成Test.dll檔案,然後利用Type.InvokeMember()方法調用其中的SayHello()方法,然後和原始方法對比一下性能。

using System;

using System.Diagnostics;

using System.IO;

using System.Reflection;

namespace InvokeMember

{

    ///

    /// Class1 的摘要說明。

    ///

    class Class1 { ///

        /// 應用程式的主入口點。

        ///

        [STAThread]

        static void Main( string [] args)

        {

            // 循環次數

            const int iterations = 100 ;

            // 計算所用時間

            DateTime startTime = DateTime.Now;

            for ( int i = 0 ;i < iterations;i ++ ) { // 對照方法

                Console.WriteLine( " Hello,World " );

            }

            TimeSpan elasped = DateTime.Now - startTime;

            Console.WriteLine( " Looping Elapsed milliseconds: " + elasped.TotalMilliseconds + " for {0} iterations " ,iterations);

            // 使用反射發送

            ReflectionTest t = new ReflectionTest();

            // 計算所用時間

            startTime = DateTime.Now;

            for ( int i = 0 ;i < iterations;i ++ )

            {

                t.DoOperation();

            }

            elasped = DateTime.Now - startTime;

            Console.WriteLine( " Looping Elapsed milliseconds: " + elasped.TotalMilliseconds + " for {0} iterations " ,iterations);

            Console.ReadLine();

        }

    }

    ///

    /// Reflection 的摘要說明。

    ///

    public class ReflectionTest

    {

        // 儲存動态生成并編譯的類的type對象

        Type theType = null ;

        // 儲存動态生成類的執行個體

        object theClass = null ;

        ///

        /// 供Client調用的方法

        ///

        public void DoOperation()

        {

            // 未初始化

            if (theType == null )

            {

                // 初始化

                GenerateCode();

            }

            // 調用方法時的參數數組(此處為空)

            object [] arguments = new object [ 0 ];

            // 調用動态生成類的方法

            theType.InvokeMember( " SayHello " , // 要調用的方法名

                BindingFlags.Default | BindingFlags.InvokeMethod, // Binding标志,具體參看msdn

                null , // 使用預設Binding對象

                theClass, // 在theClass執行個體上調用此方法

                arguments // 調用方法時的參數數組

                );

        }

        ///

        /// 運作時生成代碼

        ///

        private void GenerateCode()

        {

            // 檔案名

            string fileName = " Test " ;

            // 打開檔案,如果不存在,則建立

            Stream s = File.Open(fileName + " .cs " ,FileMode.Create);

            // 建立一個StreamWriter來寫入資料

            StreamWriter wrtr = new StreamWriter(s);

            // 寫入動态建立類的源代碼

            wrtr.WriteLine( " // 動态建立Test類 " );

            // 類名

            string className = " TestClass " ;

            wrtr.WriteLine( " using System; " );

            wrtr.WriteLine( " class {0} " ,className);

            wrtr.WriteLine( " { " );

            wrtr.WriteLine( " /tpublic void SayHello() " );

            wrtr.WriteLine( " /t{ " );

            wrtr.WriteLine( " /t/tConsole.WriteLine(/"Hello,World/"); " );

            wrtr.WriteLine( " /t} " );

            wrtr.WriteLine( " } " );

            // 關閉StreamWriter和檔案

            wrtr.Close();

            s.Close();

            // 啟動程序編譯源檔案

            // 指定參數

            ProcessStartInfo psi = new ProcessStartInfo();

            // 啟動cmd.exe

            psi.FileName = " cmd.exe " ;

            // cmd.exe的參數,/c-close,完成後關閉;後為參數,指定cmd.exe使用csc來編譯剛才生成的源檔案

            string compileString = " /c C://WINNT//Microsoft.NET//Framework//v1.1.4322//csc.exe /optimize+ /target:library {0}.cs " ;

            psi.Arguments = String.Format(compileString,fileName);

            // 運作時的風格-最小化

            psi.WindowStyle = ProcessWindowStyle.Minimized;

            // 啟動程序

            Process proc = Process.Start(psi);

            // 指定目前在此程序退出前等待

            proc.WaitForExit();

            // 從編譯好的dll檔案load一個Assembly

            Assembly a = Assembly.LoadFrom(fileName + " .dll " );

            // 建立類的執行個體

            theClass = a.CreateInstance(className);

            // 取得此類執行個體的類型

            theType = a.GetType(className);

            // 删除源檔案

            // File.Delete(flieName + ".cs");

        }

    }

}

程式運作結果:

Hello,World

Hello,World

.

.

.

Looping Elapsed milliseconds:93.75 for 100 iterations

Hello,World

Hello,World

.

.

.

Looping Elapsed milliseconds:2875 for 100 iterations

性能上不占優勢,主要是因為要進行寫檔案和編譯的工作,但是後面的方法會對性能進行優化,到最後一個方法時性能會有大幅提高,但是最後一種方法的實用性不如前兩種。