上方廣告

Lady Kukki 手作糖霜餅乾

2012年7月2日 星期一

NET4.0新特性 dynamic

以往我們如果有一個dll檔,如果要使用C#來呼叫,我們都會使用反射(Reflection)的方式來呼叫相關的Methods,我先前也有一篇文章有提到關於Reflection的基本操方式

最近因為因緣巧合的情況之下發現了NET4.0所提供的新特性dynamic,根據MSDN的說法 
dynamic 型別可讓它發生所在的作業略過編譯時期型別檢查,講白話文的意思就是dynamic可以讓C#語法有了類似VB的特性弱型別語言的特性,宣告的時候不需要知道型別,且編譯的過程中編譯器預設也不會對dynamic進行檢查。
首先先大概看一下兩者之間的差異。
Reflection的調用方式
Type type = assembly.GetType("BaseMethods.CallMe");
object instance = Activator.CreateInstance(type);
type.InvokeMember("GetName", BindingFlags.InvokeMethod, null, instance, new object[] { "Lawrence" });

dynamic的調用方式
Type type = assembly.GetType("BaseMethods.CallMe");
dynamic obj = Activator.CreateInstance(type);
obj.GetName("Lawrence");

就程式碼看起來好像差異性不大,感覺就是比較輕鬆的呼叫而已,但是如果真的只是比較好呼叫,需要這麼大費周章的再弄一個新特性出來嗎?當然不是,dynamic的新特性大大的提升了呼叫時的效能,下面我就寫一個簡單的例子來看效能上有多大的提升。

我們先寫一個取得呼叫名稱的類別,並將此類別編譯成dll檔,如下所示。
namespace BaseMethods
{
    public class CallMe
    {
        public string GetName(string name)
        {
            return string.Format("Hello {0} !", name);
        }
    }
}

接下來我們就直接跑迴圈100000萬次來看,效能整整差了快10倍。
static void Main(string[] args)
{
    //載入dll
    Assembly assembly = System.Reflection.Assembly.LoadFile(Application.StartupPath + "\\BaseMethods.dll");
    Type type = assembly.GetType("BaseMethods.CallMe");

    #region object呼叫方式

    object instance = Activator.CreateInstance(type);

    Console.WriteLine("====透過InvokeMember方式來呼叫====");
    string str = type.InvokeMember("GetName",
                BindingFlags.InvokeMethod,
                null,
                instance,
                    new object[] { "Lawrence" }
            ).ToString();
    Console.WriteLine("您呼叫的是有參數的方法,回傳值 : " + str);
    Console.WriteLine("");

    #endregion

    #region dynamic呼叫方式

    Console.WriteLine("====透過Dynamic方式來呼叫====");
    dynamic obj = Activator.CreateInstance(type);
    string str1 = obj.GetName("Lawrence");
    Console.WriteLine("您呼叫的是有參數的方法,回傳值 : " + str1);
    Console.WriteLine("");

    #endregion

    Stopwatch sw = new Stopwatch();

    #region object呼叫方式迴圈

    Console.WriteLine("====透過InvokeMember 迴圈呼叫100000次的時間====");
    sw.Start();
    for (int i = 0; i < 100000; i++)
    {
        type.InvokeMember("GetName",
                BindingFlags.InvokeMethod,
                null,
                instance,
                    new object[] { "Lawrence" }
            ).ToString();
    }
    sw.Stop();
    Console.WriteLine("InvokeMember所花費時間 {0}ms", sw.ElapsedMilliseconds);
    Console.WriteLine("");
    sw.Reset();

    #endregion

    #region dynamic呼叫方式迴圈

    Console.WriteLine("====透過Dynamic 迴圈呼叫100000次的時間====");
    sw.Start();
    for (int i = 0; i < 100000; i++)
    {
        obj.GetName("Lawrence");
    }
    sw.Stop();
    Console.WriteLine("Dynamic所花費時間 {0}ms", sw.ElapsedMilliseconds);
    Console.WriteLine("");
    sw.Reset();

    #endregion

    Console.Read();
}


本文範例 :
Dynamic.zip