什麼是dynamic類型?微軟給出的官方文檔中這樣解釋:在經過 dynamic
類型實現的操做中,該類型的做用是繞過編譯時類型檢查。 改成在運行時解析這些操做。 dynamic
類型簡化了對 COM API(例如 Office Automation API)、動態 API(例如 IronPython 庫)和 HTML 文檔對象模型 (DOM) 的訪問。在大多數狀況下,dynamic
類型與 object
類型的行爲相似。 可是,若是操做包含 dynamic
類型的表達式,那麼不會經過編譯器對該操做進行解析或類型檢查。 編譯器將有關該操做信息打包在一塊兒,以後這些信息會用於在運行時評估操做。 在此過程當中,dynamic
類型的變量會編譯爲 object
類型的變量。 所以,dynamic
類型只在編譯時存在,在運行時則不存在。測試
下例中生成的類型是一致的:優化
dynamic dyn = "Fode";
Object obj = "Fode";this
// Rest the mouse pointer over dyn and obj to see their // types at compile time. System.Console.WriteLine(dyn.GetType()); System.Console.WriteLine(obj.GetType());
其輸出結果都是String類型,可知CLR能夠正確的識別出dynamic是哪一種類型,在反編譯看看其生成的IL代碼:編碼
IL_0000: nop IL_0001: ldstr "Fode" IL_0006: stloc.0 IL_0007: ldstr "Fode" IL_000c: stloc.1 IL_000d: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey() IL_0012: pop IL_0013: ret
JIT編譯器將dynamic識別爲String類型,並將其推算到運算棧中(IL代碼中 ldstr(將新對象引用推送到存儲在元數據中的字符串文字)、(stloc.*)從評估堆棧的頂部彈出當前值,並將其存儲在索引*處的本地變量列表中),不一樣IL代碼也不所謂,前文只是介紹dynamic這個類型關鍵字,只須要你知道他的類型是繞過編譯器就能夠,如如下操做,Object類型就會報編譯的錯誤。可是,對於 dyn + 3
,不會報告任何錯誤。 在編譯時不會檢查包含 dyn
的表達式,緣由是 dyn
的類型爲 dynamic
。spa
dynamic dyn = "Fode"; Object obj = "Fode"; dyn = dyn + 3; obj = obj + 3; //這句代碼編譯器會報錯
dynamic是Framework 4.0的新特性。dynamic的出現讓C#具備了若語言的特性。編譯器在編譯時候再也不對該類型進行檢查,編譯器默認dynamic對象支持開發者想要的任何特徵。好比,即便你對 GetStudent()方法返回的對象一無所知,也能夠像如下執行代碼的調用,編譯器不會報錯:code
static void Main(string[] args) { dynamic dyn = GetStudent(); //正確的操做 Console.WriteLine(dyn.Age); Console.WriteLine(dyn.Name); dyn.PrintName(); //錯誤的操做 //Console.WriteLine(dyn.Birthday); //該對象沒有包含該屬性 //dyn.PrintAge(); //這行代碼會報錯誤,訪問級別不夠 Console.ReadKey(); } static Student GetStudent() { Student student = new Student(); student.Age = 21; student.Name = "Fode"; return student; } class Student { public String Name { get; set; } public Int32 Age { get; set; } public void PrintName() { Console.WriteLine(this.Name); } private void PrintAge() { Console.WriteLine(this.Age); } }
若是運行時dyn對象不包含指定的特性(屬性、字段、方法等),運行時會拋出一個運行時的錯誤 RuntimeBinderException。對象
注意:有人可能會將var關鍵字與dynamic進行比較。實際上,var和dynamic徹底是兩回事,兩個不一樣的概念。var其實是編譯期間拋給我門的「語法糖」,一旦被編譯,編譯器會自動匹配var變量的實際類型,並用實際類型來替換給變量的聲明,這看上去就好像咱們在編碼的時候用實際類型進行聲明同樣,優勢也顯而易見,當【賦值方】類型發生變化時,【實現方】無需改變其類型,由於var會自動去適配。而dynamic被編譯後,其實是一個Object類型,只不過編譯器會對dynamic類型進行特殊處理,讓它在編譯期間不進行任何的類型檢查,而是將類型檢查放到了運行期。這從VS這個IDE就能看出,在編輯窗口中,var支持【智能感知】,由於vs能推斷出var類型的實際類型;而dynamic聲明的變量卻不支持【智能感知】,由於對其運行期的類型一無所知。對dynamic變量使用【智能感知】會提示"此操做將在運行時解析"。blog
BB了這麼久,重點來了,利用好了動態類型dynamic的這個特性,能夠簡化C#中的反射語法,更高深的優化將在之後的博客推出。在dynamic出現以前,咱們先用基礎反射獲取一個類中的方法,並執行它:索引
static void Main() { DynamicObj obj = new DynamicObj(); var fun = obj.GetType().GetMethod(nameof(obj.CallFun)); Int32 result = (Int32)fun.Invoke(obj, new Object[] { "Fode" }); Console.WriteLine(result); Console.ReadKey(); } public class DynamicObj { public Int32 CallFun(String str) { return str.Length; } }
其結果沒有什麼好講的,就是一個用反射調用 CallFun() 方法的例子,而用dynamic以後,代碼看上去更簡潔了,而且在可控制的範圍內減小了一次拆箱的操做,代碼以下:開發
dynamic dyn = new DynamicObj(); Int32 result = dyn.CallFun("Fode"); Console.WriteLine(result);
可能咱們會對這樣的簡化不覺得然,畢竟代碼看起來並無減小多少,可是,若是考慮到效率兼優美兩個特性,那麼dynamic的優點就顯現出來了。對上面的代碼個執行10000000次,在進行分析,以下所示:
CodeTimer.Time("使用dynamic", 10000000, () => { //執行裏面的代碼10000000次 dynamic dyn = new DynamicObj(); Int32 result = dyn.CallFun("Fode"); }); CodeTimer.Time("使用基礎反射", 10000000, () => { //執行裏面的代碼10000000次 DynamicObj obj = new DynamicObj(); var fun = obj.GetType().GetMethod(nameof(obj.CallFun)); Int32 result = (Int32)fun.Invoke(obj, new Object[] { "Fode" }); }); Console.ReadKey();
其運行結果以下所示:
從以上結果看出,使用dynamic使用時間爲481ms,基礎反射使用時間爲3063ms,CPU和時間上相差了5倍多,測試器 CodeTimer 的代碼隨後會貼出。
總結:能夠看到雖然用dynamic優化後的反射跟基礎反射的相比,效率雖然在同一個數量級上。但是基礎反射卻沒有dynamic代碼簡潔,所以建議:始終使用dynamic來簡化反射實現(前提你知道你要是實現的類型),在日後的隨筆,將會提出用ExpressionTree和Emit技術深度優化反射。
代碼下載鏈接: https://pan.baidu.com/s/174c9KCTvg4XpCjZFkrhTbQ