動態類型
事實上,duck typing是動態類型概念的一種。C#4.0已經通過dynamic關鍵字來實現動態類型,讓我們先來看看下面的示例:
string JSon = @"{ ""FirstName"": ""John"", ""LastName"": ""Smith"", ""Age"": 21 }";
dynamic person = CreateFromJson(JSon);
Console.WriteLine("{0} will be {1} next year", person.FirstName, person.Age + 1);
Console.WriteLine(person.ToJSon());
person.Play("basketball");//不存在的方法,可以通過編譯,但會拋出運行時異常
通過dynamic關鍵字,我們不需要在編譯時為person對象指定類型,編譯器不會進行類型檢查,而是將對象的屬性訪問和方法調用轉換為反射調用,所以,只要對象的運行時類型能通過反射找到匹配的屬性或方法即可。
上面的例子通過JSon創建了一個dynamic對象,就像Javascript中操作json一樣方便。在運行 時,person.FirstName和person.Age能通過反射正確地進行屬性訪問,person.ToJSon()也可以正確地執行,但 person.Play( "basketball")由於運行時類型不存在該方法而拋出異常。
C#4.0的味道如何?很爽嗎?不過,說實在的,我覺得有點兒不太舒服了!仔細想想,它像接口,像委托,還是更像HTTP + XML? 對於dynamic對象,編譯器不進行對象類型檢查,不進行屬性類型檢查,也不進行方法簽名檢查。很明顯,它像HTTP+XML,完全基於運行時協議,沒有一點兒靜態的東西。如果類比委托的話,更理想的方式應該是,不進行對象類型檢查,但進行屬性類型和方法簽名檢查,就像下面這樣:
string JSon = @"{ ""FirstName"": ""John"", ""LastName"": ""Smith"", ""Age"": 21 }";
dynamic person = CreateFromJson(JSon);
Console.WriteLine("{0} will be {1} next year", person.FirstName<string>, person.Age<int> + 1);
Console.WriteLine(person.ToJSon<string>());
person.Play<string>("basketball");
string firstName = person.FirstName<string>;
int age = person.Age<int>;
Func<string> toJson = person.ToJSon<Func<string>>;
Action<string> play = person.Play<Action<string>>;
這樣,除了屬性和方法的名稱是動態的外,屬性的類型和方法的簽名都是靜態的,把運行時錯誤的可能降到最低,同時享受靜態檢查的好處。其實,沿著這個思路,我們大可不必等著C#4.0的dynamic才開始動態類型,在C#2.0時代也可以這樣:
object jsonObj = CreateFromJSon(@"{ ""FirstName"": ""John"", ""LastName"": ""Smith"", ""Age"": 21 }");
Dynamic person = new Dynamic(JSonObject);
string firstName = person.Property<string>("FirstName");
int age = person.Age<int>("Age");
Func<string> toJson = person.Method<Func<string>>("ToJSon");
Action<string> play = person.Method<Action<string>>("Play");
看到這裡,相信您一定明白該如何實現Dynamic類了吧?如果覺得有用,就自己嘗試實現一下吧!
後續
下一篇打算繼續探討在C#模擬實現動態類型,敬請關注!