我們分析一下用Reflector看到的東西:
Code
1private static void Main(string[] args)
2{
3
object
foo1 = new Foo();
4 if (<Main>o__
SiteContainer0.<>p__
Site1 == null)
5 {
6 <Main>o__SiteContainer0.<>p__Site1 =
CallSite<Action<CallSite, object>>.
Create(new CSharpCallPayload(Microsoft.CSharp.RuntimeBinder.RuntimeBinder.GetInstance(), false, false, "
Do1",
typeof(object
), null));
7 }
8 <Main>o__SiteContainer0.<>p__Site1.
Target(<Main>o__SiteContainer0.<>p__
Site1,
foo1);
9 if (<Main>o__SiteContainer0.<>p__Site2 == null)
10 {
11 <Main>o__SiteContainer0.<>p__Site2 = CallSite<Action<CallSite, object>>.Create(new CSharpCallPayload(Microsoft.CSharp.RuntimeBinder.RuntimeBinder.GetInstance(), false, false, "Do2", typeof(object), null));
12 }
13 <Main>o__SiteContainer0.<>p__Site2.Target(<Main>o__SiteContainer0.<>p__Site2, foo1);
14 if (<Main>o__SiteContainer0.<>p__Site3 == null)
15 {
16 <Main>o__SiteContainer0.<>p__Site3 = CallSite<Action<CallSite, object>>.Create(new CSharpCallPayload(Microsoft.CSharp.RuntimeBinder.RuntimeBinder.GetInstance(), false, false, "KissFanweixiao", typeof(object), null));
17 }
18 <Main>o__SiteContainer0.<>p__Site3.Target(<Main>o__SiteContainer0.<>p__Site3, foo1);
19}
首先,編譯器產生了__SiteContainer0的本地變數來儲存我們的CallSite的內容,然後我們注意到test已經是object類型。由此看來這不能算是什麼真正的“動態類型”,只不過算是個“協助方法”罷了。
接下來代碼會檢查這個CallSite是否是null,如果是,就是用CallSite.Create並且傳遞一個CSharpCallPayload的對象作為參數,這個對象儲存了所有我們將要去調用的方法的資訊。如果這個CallSite不為null,就把這個方法的所有的所有內“附著”到foo這個對象上了。
編譯器就是做了這兩步,實現了這種動態功能。
以上內容來自Justin Etheredge的blog,該blog的後文還比較了dynamic的效率問題,經過他的測試,使用var和使用dynamic造成的天壤之別使其效能也有天壤之別,6ms:2106ms。但是我剛看到這測試的時候就覺得不太恰當,如果您自己運行一下dynamic的程式就知道一啟動的時候總以為程式出問題了一點反應都沒有,過了一會才出結果,也就是runtime上第一次handle這些東東是要有較高時間消耗的,但是這個情況只有一開始載入的時候才會出現,所以比較效率的這組數字不能說的太準確,更何況兩者不是幹同一個活的,一個是八路,一個是地下工作者,沒法比。不過Justin兄弟剛又更新了一篇blog,又重新分析了這個測試,並增加了測試的case,還是挺不錯的,值得一看。
Dynamic lookup特性使我們可以使用一種統一的方式動態invoke其他資源,通過它,我們的對象不需要關心是來在COM,IronPython還是reflection,我們只需要將需要的東西apply給它(熟悉javascript的朋友看到apply就明白dynamic的思想了吧),在runtime時.net自動幫我們搞定剩下的工作。
這給我們的開發給予了巨大的靈活性,但是dynamic object在編譯時間是閉著眼睛過去的,所以對於運行時能否正確通過,我們就比較麻煩了。
從上面的reflect後的代碼我們能看到dynamic類型可以被認為是一個特殊版本的object——只是標記了這個對象能被動態使用,任何對象都可以被顯時的轉換為dynamic類型。相反的,還有一種assignment conversion(賦值轉換)可以將dynamic類型轉換為其他類型:
dynamic d = 7; // implicit conversion
int i = d; // assignment conversion
當然,不只方法可以這麼用,欄位、屬性、索引器、操作符和委託都可以dynamic:
dynamic d = GetDynamicObject(…);
d.M(7); // calling methods
d.f = d.P; // getting and settings fields and properties
d["one"] = d["two"]; // getting and setting through indexers
int i = d + 3; // calling operators
string s = d(5,7); // invoking as a delegate
對於運行時的lookup,dynamic機制是根據它的目標對象來確定的,就是說new的是啥玩意,就通過這個東東來確定怎麼lookup,對待COM是通過IDispatch,對於Dynamic對象是通過IDynamicObject,對於標準的.net對象就是用反射,這些內容從微軟的官方文檔裡就能很容易看明白。
拿微軟的一個例子來說一下dynamic的一個應用:
我們寫一個IronPython的方法:
Code
1def welcome(name):
2 return "Hello '" + name + "' from fanweixiao"
存為helloworld.py
然後用ScriptRuntime py = Python.CreateRuntime();建立IronPython的運行環境,用dynamic helloworld = py.UseFile("helloworld.py");來建立動態類型helloworld,就可以使用helloworld.welcome("cnblogs guy");來列印出結果來了。
很爽吧?做silverlight的也不用死盯著一門語言不放了。
C#與Java的攀比從2.0的泛型開始逐漸佔據上風,到了3.0時代的LINQ,再到4.0時代的Dynamic,不得不說優勢的顯著。但是技術終歸是個工具,工具生來就是被利用的,我同意馮大輝的觀點,使用現有技術足夠做好現在市場需要的需求了。不管開發速度有多快,產品架構有多好,我們還是應該多離開code去看看我們的使用者究竟在想什麼。不要除了coding就是用“我在做伏地挺身”來回答使用者的問題。
c#4.0新特性之一: Dynamic Lookup (1)