在 C# 4.0,增加了動態語言的特性。
簡單地說,使用 dynamic 宣告出來的變數,在編譯器編譯時期會被省略不編譯,直到執行期到動態地解譯所使用的 member field, member function 或 function。
舉例來說,下面的程式會發生編譯錯誤。原因是 object 這種類別沒有 Name 的屬性。
例1 1: class C1
2: {
3: public string Name { get; set; }
4: }
5: class Program
6: {
7: static void Main(string[] args)
8: {
9: C1 c1 = new C1() { Name = "a" };
10: WriteName(c1);
11: }
12: public static void WriteName(object o)
13: {
14: Console.WriteLine(o.Name);
15: }
16: }
想要讓這個程式碼 compile 通過當然是很容易的事,只需要將 WriteName 的參數由 object 型別改成 C1 型別即可。
以下這一段程式,想要寫一個能印出物件的 Name 屬性的共用程式 WriteName。
例2
1: class C1
2: {
3: public string Name { get; set; }
4: }
5: class C2
6: {
7: public string Name { get; set; }
8: public int Id { get; set; }
9: }
10: class Program
11: {
12: static void Main(string[] args)
13: {
14: C1 c1 = new C1() { Name = "a" };
15: C2 c2 = new C2() { Name = "b", Id = 1 };
16: WriteName(c1);
17: WriteName(c2);
18: }
19: public static void WriteName(C_What o)
20: {
21: Console.WriteLine(o.Name);
22: }
23: }
WriteName 的參數1,應該宣告 C1 還是 C2 呢?都不行,靜態語言是不會過的。精通物件導向的我們,當然知道此時最好使用 interface 來解決問題。如下。
例3
1: interface IName
2: {
3: string Name { get; set; }
4: }
5: class C1 : IName
6: {
7: public string Name { get; set; }
8: }
9: class C2 : IName
10: {
11: public string Name { get; set; }
12: public int Id { get; set; }
13: }
14: class Program
15: {
16: static void Main(string[] args)
17: {
18: C1 c1 = new C1() { Name = "a" };
19: C2 c2 = new C2() { Name = "b", Id = 1 };
20: WriteName(c1);
21: WriteName(c2);
22: }
23: public static void WriteName(IName o)
24: {
25: Console.WriteLine(o.Name);
26: }
27: }
這樣一來,解決了 C1, C2 的問題。「只」需要實作 IName 的介面,就可以使用 WriteName 這個函數了。
問題就在 IName 是一個自訂的介面,不是通用的介面。別人寫好的類別,絕對不會知道及實作我們自訂的 IName 介面。如果我們的WriteName 想要通吃呢?下面的程式就是使用 Reflection 機制寫出來的 solution
例4
1: class C1
2: {
3: public string Name { get; set; }
4: }
5: class C2
6: {
7: public string Name { get; set; }
8: public int Id { get; set; }
9: }
10: class Program
11: {
12: static void Main(string[] args)
13: {
14: C1 c1 = new C1() { Name = "a" };
15: C2 c2 = new C2() { Name = "b", Id = 1 };
16: WriteName(c1);
17: WriteName(c2);
18: }
19: public static void WriteName(object o)
20: {
21: Type type = o.GetType();
22: string str = (string)type.GetProperty("Name").GetValue(o, null);
23: Console.WriteLine(str);
24: }
25: }
這個方式,一律使用 object 來接參數,再使用 Reflection 的機制來動態的取得 Name 的 property,並當成字串輸出到 Console。棒吧!
可是,這段程式有點難寫耶!如果一次要取得三個property,並執行傳入參教的某個 function,就累翻了。
最後,則是使用 dynamic 的宣告,將例3的 C_What 改成 dynamic 即完成了
例5
1: class C1
2: {
3: public string Name { get; set; }
4: }
5: class C2
6: {
7: public string Name { get; set; }
8: public int Id { get; set; }
9: }
10: class Program
11: {
12: static void Main(string[] args)
13: {
14: C1 c1 = new C1() { Name = "a" };
15: C2 c2 = new C2() { Name = "b", Id = 1 };
16: WriteName(c1);
17: WriteName(c2);
18: }
19: public static void WriteName(dynamic o)
20: {
21: Console.WriteLine(o.Name);
22: }
23: }
例5 的語法與例3幾乎完全一樣,但事實上走的是例4的執行時間Reflection 機制。大大地節省了程式碼的撰寫。
由執行時期才決定資訊的機制,就是動態語言的特性。