The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

C# 4.0 新特性:動態型別、選用參數、具名參數

技術變化的可真快,C# 3.0 推出還不到兩年,估計不久 C# 4.0 可能就要出爐了,這幾天抽空看了一下關於 C# 4.0 的新特性,感覺未來寫 C# 會越來越有 "彈性",是好是壞見仁見智,但我還蠻期待的,今天我就簡單介紹幾個 C# 4.0 的新特性。

Dynamic Lookup ( 動態對應 )

C# 4.0 最大的強化即所謂的「動態編程」( dynamic programming ),他可以讓把既有的 "靜態物件" 標記為 "動態物件"。

C# 編譯器原本需要在編譯時期(Compile-time)就檢查程式碼中物件的型別(Type)是否一致,即便 C# 3.0 帶來一個所謂 var 變動型別,不過骨子裡還是一樣是屬於 "靜態型別",只是在編譯時 C# 編譯器會將 var 編譯成明確的型別(Type),因此在編譯時期一樣會檢查型別是否有問題。

不過 C# 4.0 新增的 dynamic 動態型別有點不太一樣,雖然骨子裡一樣是屬於 "靜態型別",不過被宣告為 dynamic 的型別,編譯器(Compiler)不會進行型別檢查,而是讓程式在執行時讓 .NET Framework Runtime 進行型別檢查,等於是「延後檢查」罷了,並不是「不檢查型別」喔!

New Features in C# 4.0 文件中提到了四個可善用動態特性的例子:

  • 從動態語言來的動態物件存取,例如:Python 或 Ruby 等
  • 透過 IDispatch 存取 COM 物件
  • 原本透過 Reflection (反映) 存取 .NET 型別的方式將可透過 dynamic 型別簡化操作
  • 處理會不斷改變結構的物件,例如:HTML DOM 物件

可標記 dynamic 型別的範圍很多,包括方法(method), 運算子(operator), 索引子呼叫(indexer call), 屬性(property), 欄位(field), 甚至於可動態物件叫用(object invocations),這樣講看不出威力,看幾個程式碼範例吧:

dynamic d = GetDynamicObject(…);
d.M(7);              // 呼叫「動態方法」 
d.f = d.P;           // 動態 取得/設定 屬性/欄位
d["one"] = d["two"]; // 動態透過索引子取得/設定資料
int i = d + 3;       // 動態呼叫運算子(operators)
string s = d(5,7);   // 把 d 當成委派(delegate)來使用

以上例子的 d 被宣告成 dynamic 型別,意思是說任何對 d 所做的操作都是 "動態的",所以當編譯器讀到 d.M 方法時不會進行任何型別檢查,且不管你呼叫什麼方法(method)、傳入幾個參數、或任意型別的參數都沒關係,所有的型別驗證都會留到 runtime 時才會進行。而其他的例子應該也淺顯易懂,總之全部都變動態了。

C# 編譯器在編譯這些程式時只會將該程式碼轉成一些 metadata 告訴 Runtime 這段程式「預期應該怎樣執行,請照我的規格來進行呼叫」,至於型別都是執行時期才決定的。

在更進一步的範例如下:

Foo foo = new Foo();
dynamic d = new Bar();
var result = foo.M(d);

即便 Foo 編譯時是「靜態型別」,只要傳入的參數有一個以上的 dynamic 型別參數,連 M 都可以是動態的,有沒有覺得這樣的設計實在是太有彈性了

Named and Optional Arguments ( 具名與選用參數 )

C# 也越來越像 VB.NET 了,具名與選用參數這兩個功能在 VB.NET 裡老早就有了,直到 C# 4.0 終於要加入了,我覺得這個特性蠻不錯的,以後在設計方法時就不用寫那麼多 overload 方法,且呼叫方法時也變的非常的方便,參數可以設定預設值,且不用每個參數都傳入了。如果你用 C# 寫過 Office Automation 的程式就知道為什麼選用參數有多棒了!

註:選用參數也是 JavaScript 的特性之一,所以 C# 與 JavaScript 也越來越像了。

看範例比較快:

// 宣告一個 M 方法,其中第二、第三個參數是包含預設值的選用參數
public void M(int x, int y = 5, int z = 7);
// 宣告方法與呼叫方法與 JavaScript 簡直如出一轍
M(1, 2, 3); // 標準呼叫法
M(1, 2);    // 忽略 z 參數,等同於呼叫 M(1, 2, 7) 因為 z 的預設值為 7
M(1);       // 忽略 y 與 z 參數,等同於呼叫 M(1, 5, 7)

不用我多說了把,想想 JavaScript 是是怎麼寫的吧,不過 C# 4.0 是不允許你這樣寫的:

M(1, , 3); // C# 4.0 不允許忽略參數傳入

由於 C# 4.0 不允許忽略參數傳入,所以要忽略參數的話,就必須要靠「具名參數」的寫法,這點跟 VB.NET 是一樣的:

M(1, z: 3);    // 想忽略第2的參數,那第三個參數就用具名參數的寫法
M(x: 1, z: 3); // 也可全部都用具名參數的寫法
M(z: 3, x: 1); // 傳入具名參數是不需考慮順序的

剩下還有一些 C# 4.0 的新特性像是 Features for COM interop、Variance、Relationship with Visual Basic 等各位有空可以自行查閱 New Features in C# 4.0 文件,另外像是程式碼範例也可以在 C# Future 網站下載的到。

我現在有點難以想像未來的程式都動態化之後,很有可能會造成除錯上的困擾,尤其是當程式上線之後,程式在客戶端會怎麼執行、客戶會怎麼使用都不知道,若真的遇到一些難以解決的錯誤時,恐怕也很難透過 Exception 訊息得知真正的錯誤來源與發生錯誤的地方,例如說若錯誤來自於其他的動態語言,不知道 Stack Trace 會不會一樣的完整?

相關連結

留言評論