C# 4.0 新功能:動態繫結

官方網站 C# Future 上面有一份 2009 年 2 月份剛出爐的 C# 4.0 技術文件:New Features in C# 4.0。雖然 C# 4.0 規格尚未正式發布,但從這份文件已經可以看出一些端倪。例如文中提到,C# 4.0 的新功能主要可分成四大塊:



1. 動態查閱(dynamic lookup)

簡單地說,就是讓 .NET Runtime 在執行時期(而非編譯時期)來幫你決定欲繫結的方法,這表示 C# 又向動態語言(dynamic language)邁進了一步。稍後我會用一個很小的範例簡單示範一下。

2. 具名參數與可選參數(named and optional parameters)


「具名參數」指的是可以像 VB 那樣,呼叫函式時指定參數的名稱,這樣一來,連參數傳遞的順序都可以不固定了。

關於 optional parameters,有此一說:這種寫法不好,因為它讓程式設計師在寫程式時方便,卻在日後閱讀程式碼時,不知道在呼叫函式時其實還傳遞了其他帶預設值的參數。無論如何,微軟(Anders Hejlsberg?)最終還是決定把這功能加進 C# 了。

3. 與 COM 物件互動的相關功能


存取 COM 物件的方法和屬性時更方便了,不用寫一堆轉型的程式碼。

4. Variance


文件中提到兩個術語:covariance 和 contravariance。若有興趣了解這兩個術語,除了閱讀文件中的說明,也可以參考這篇文章:C# 4.0 - Covariance and contra-variance。(這裡我就偷懶一下啦!)

一個簡單的動態繫結範例

看一下這個簡單範例:

   1:      class Foo
   2:      {
   3:          public void Hello()
   4:          {
   5:              Console.WriteLine("Hello World!");
   6:          }
   7:      }
   8:  
   9:      class DynamicDemo
  10:      {
  11:          public void Run()
  12:          {
  13:              dynamic foo = new Foo();
  14:              foo.Hello();
  15:              foo.HelloFailed();
  16:          }
  17:      }

類別 Foo 只定義了 Hello 方法,但第 15 行還是可以通過編譯,因為變數 foo 是宣告成 dynamic 變數,而這 dynamic 關鍵字,便是 C# 4.0 新增的語法。

使用 dynamic 宣告的變數,在存取物件成員時,無論該物件有沒有提供那個成員,編譯器都不管,而會一律將它編譯成動態繫結的 IL code,故範例程式的第 14 行當然也是等到執行時期才進行方法的解析和繫結。此外,由於是動態繫結,所以在 Visual Studio 2010(CTP 版)的程式編輯器中輸入 "foo." 時,IntelliSense 功能也不會出現任何提示。


此範例程式在執行到第 15 行時會因為 Foo 類別沒有提供 HelloFailed 方法而出現錯誤訊息:

Unhandled Exception: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Coul
d not find symbol with name HelloFailed.

那麼,這些透過 dynamic 變數來執行的動態繫結動作是怎麼做到的呢?編譯器實際上會編譯成哪些 IL code?

當然,我們可以用 ILDASM 來觀察編譯出來的 IL code,但是我發現這些 IL code 又臭又長,不易解讀,還是試試 Reflector 吧。雖然目前 Reflector v5.1.4.0 的官方文件宣稱只支援到 .NET Framework 3.5,但實驗結果顯示,它還是能夠反組譯 VS2010 編譯出來的 .NET 4.0 組件。以下便是利用 Reflector 將前面範例中的 DynamicDemo.Run 方法反組譯成 C# 的部分結果:

   1:  public void Run()
   2:  {
   3:    object foo = new Foo();
   4:    if (<Run>o__SiteContainer0.<>p__Site1 == null)
   5:    {
   6:      <Run>o__SiteContainer0.<>p__Site1 = CallSite<Action<CallSite, object>>.Create(
   7:        new CSharpCallPayload(
   8:          Microsoft.CSharp.RuntimeBinder.RuntimeBinder.GetInstance(), 
   9:          false, false, "Hello", typeof(object), null));
  10:    }
  11:  }

從反組譯的結果便可以發現,原來 dynamic 變數實際上會編譯成 object 型別(第 3 行),等到要存取物件成員時,才透過 CSharpCallPayload、RuntimeBinder 等類別來完成動態繫結的動作。

為什麼要有 dynamic 型別?

Dynamic 型別雖然方便,但由於是執行時期才進行繫結,所以程式碼的執行速度自然比一般靜態繫結的程式來得稍微慢一些。因此,如果物件型別能夠預先確知,還是應該使用明確的型別宣告。不過,就如 Anders Hejlsberg 在他的一段訪談影片中所說:「the dynamic stuff is mostly intended for interoperability, but also for cases where things are dynamically typed by nature...」在碰到需要和其他程式語言交換資訊的場合(如 Python、COM 物件),或者物件本身的資料特性就是動態的(不固定的、多樣的),此時就非常適合用 dynamic 型別。當你在適當的場合選擇了適當的技術,其生產力的提升所帶來的效益可能就遠大於動態繫結所需付出的成本了。

小結

這裡只是牛刀小試一下 C# 4.0 的動態繫結語法,如果你也有興趣嚐鮮,可到微軟網站下載 VS2010 CTP 的 Virtual PC 影像檔(解壓縮之後,整個影像檔約 24GB)。不過,要注意的是,這個版本的使用期限是 2008/12/31, 若現在要使用,請看這篇文章的解法:Visual Studio 2010 CTP VPC: Dealing with Activation Messages(主要是 "Visual Studio 2010 CTP Expiration" 這個小節)。
Copyright © 2012. Huan-Lin 學習筆記 - All Rights Reserved
Powered by Blogger
Template Design by Cool Blogger Tutorials
Published by Templates Doctor