程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> C# 3.0 新特性:擴展方法初探

C# 3.0 新特性:擴展方法初探

編輯:關於C語言
C#3.0中一個激動人心的特性就是擴展方法:你可以使用實例方法的語法來調用靜態方法。本文仔細闡述了這一新特性並且給出了幾個相應的例子。

  聲明擴展方法

  擴展方法的行為和靜態方法是非常類似的,你只能在靜態類中聲明它們。為聲明一個擴展方法,你需要給該方法的第一個參數指定this關鍵字,如下例:

// Program.cs
public static class EMClass
{
 public static int ToInt32Ext(this string s)
 {
  return Int32.Parse(s);
 }
 public static int ToInt32Static(string s)
 {
  return Int32.Parse(s);
 }
}
class Program
{
 static void Main(string[] args)
 {
  string s = "9";
  int i = s.ToInt32Ext(); // LINE A
  Console.WriteLine(i);
  int j = EMClass.ToInt32Static(s); // LINE B
  Console.WriteLine(j);
  Console.ReadLine();
 }
}

  為編譯如上代碼,你需要安裝Visual Studio 2005和LINQ的預覽版。如果你已經安裝了VS2005,那麼你將在Visual C#的LINQ PrevIEw裡看到三個新的工程模板:LINQ命令行應用程序,LINQ窗口程序和LINQ庫。如下操作編譯代碼:

  1. 打開VS2005編輯器,創建一個新工程,在新建工程窗口中選擇LINQ Console作為工程模板。

  2. 將工程命名為ExtensionMethods,點擊Ok。

  3. 將如上代碼鍵入編輯器。

  4. 按下F5編譯工程並運行。

  如果你只是安裝了.Net 2.0,那麼你可以運行命令行編譯器:

Csc.exe /reference:"C:\Program Files\LINQ PrevIEw\Bin
\System.Data.DLINQ.dll"
/reference:C:\Windows\Microsoft.Net\Framework\v2.0.50727\System.dll
/reference:"C:\Program Files\LINQ PrevIEw\Bin\System.Query.dll"
/reference:"C:\Program Files\LINQ PrevIEw\Bin\System.XML.XLINQ.dll"
/target:exe Program.cs

  就像你在如上代碼裡所看到的那樣,擴展方法(ToInt32Ext)和普通的靜態方法(ToInt32Static)的不同在於:

  1. 擴展方法的第一個參數有一個this關鍵字,而靜態方法不會在它的參數聲明裡有this關鍵字。

  2. 當使用擴展方法的是哦戶,使用this關鍵字聲明的的參數沒有進行傳遞。在上面的例子裡,Line A就是一個使用擴展方法ToInt32Ext的例子。不需要將參數傳遞給它。當靜態方法在使用的時候,是不能忽略掉任何的參數的。所有的參數必須傳遞進入函數。Line B就是一個例子。

  3. 擴展方法只能在靜態類中定義。對於靜態方法,這並不成為一個要求,因為靜態方法可以在一個靜態類或普通類中存在。

  4. 擴展方法只能針對實例調用。

  擴展方法,盡管本質上還是靜態的,但是只能針對實例調用。如果在一個類中調用它們將會引發編譯錯誤。調用它們的類實例是由聲明中的第一個參數決定的,就是有關鍵字this修飾的那個。

  在IL內部

  如果你觀看IL裡對以上代碼的分析結果,你將會看到如下圖的結果:


  以下是IL對於擴展方法ToInt32Ext的分析:

.method public hidebysig static int32 ToInt32Ext(string s) cil managed
{
 .custom instance void [System.Query]System.Runtime
 .CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 )
 // Code size 12 (0xc)
 .maxstack 1
 .locals init ([0] int32 CSCODE_REPLACEMENT 200)
 IL_0000: nop
 IL_0001: ldarg.0
 IL_0002: call int32 [mscorlib]System.Int32::Parse(string)
 IL_0007: stloc.0
 IL_0008: br.s IL_000a
 IL_000a: ldloc.0
 IL_000b: ret
} // end of method EMClass::ToInt32Ext

  以下代碼是IL對靜態方法ToInt32Static的分析:

.method public hidebysig static int32 ToInt32Static(string s) cil managed
{
 // Code size 12 (0xc)
 .maxstack 1
 .locals init ([0] int32 CSCODE_REPLACEMENT 300)
 IL_0000: nop
 IL_0001: ldarg.0
 IL_0002: call int32 [mscorlib]System.Int32::Parse(string)
 IL_0007: stloc.0
 IL_0008: br.s IL_000a
 IL_000a: ldloc.0
 IL_000b: ret
} // end of method EMClass::ToInt32Static


  .custom instance void: 本行代碼說明本方法只能針對實例使用。

  [System.Query]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ):本行代碼說明擴展特性被使用了。

  擴展方法轉換

  下表顯示了在編譯時進行的方法轉換

  方法 代碼編譯為 1 expr . identifIEr ( ) identifIEr (expr) 2 expr . identifIEr ( args ) identifIEr (expr, args) 3 expr . identifIEr <typeargs> ( ) identifIEr <typeargs> (expr) 4 expr . identifIEr <typeargs> ( args ) identifIEr <typeargs> (expr, args)
  如果你在ILDASM中檢查main方法的代碼,它將會如下顯示:

.method private hidebysig static void Main(string[] args) cil managed
{
 .entrypoint
 // Code size 42 (0x2a)
 .maxstack 1
 .locals init ([0] string s,
 [1] int32 i,
 [2] int32 j)
 IL_0000: nop
 IL_0001: ldstr "9"
 IL_0006: stloc.0
 IL_0007: ldloc.0
 IL_0008: call int32 ExtensionMethods.EMClass::ToInt32Ext(string)
 IL_000d: stloc.1
 IL_000e: ldloc.1
 IL_000f: call void [mscorlib]System.Console::WriteLine(int32)
 IL_0014: nop
 IL_0015: ldloc.0
 IL_0016: call int32 ExtensionMethods.EMClass::
 ToInt32Static(string)
 IL_001b: stloc.2
 IL_001c: ldloc.2
 IL_001d: call void [mscorlib]System.Console::WriteLine(int32)
 IL_0022: nop
 IL_0023: call string [mscorlib]System.Console::ReadLine()
 IL_0028: pop
 IL_0029: ret
} // end of method Program::Main
IL_0008: call int32 ExtensionMethods.EMClass::
ToInt32Ext(string)

  這裡表明方法轉換(expr . identifier ( ) <--> identifIEr (expr) )發生.

  所以當你調用 int i = s.ToInt32Ext();, 編譯器內部進行操作int i = EMClass.ToInt32Ext(s);那麼,重寫的新生會當作一個靜態方法調用來處理

  標識符按照如下的順序解析:

  1. 最近的包含的命名空間聲明

  2. 每個後繼包含的命名空間聲明

  3. 包含的編譯單元

  下面是方法的從高到低的優先級:

  1. 實例方法

  2. 在同一個命名空間裡的擴展方法

  3. 在當前命名空間之外的擴展方法

  為什麼使用擴展方法?

  你也許會問:"為什麼有了普通的靜態和實例類還需要使用擴展方法呢?"其實,簡單來說就是為了方便。我來舉個例子吧。如果你在過去的一段時間內開發了很多函數形成了一個庫。那麼當某人要用這個函數庫的時候,他必須要知道定義了所需的靜態方法的類名。就像下面這個一樣:

a = MyLibraryClass.
  這裡,IntelliSense將會彈出並且告訴你可用函數的名字,你只需要挑選你所需要的。然後鍵入你需要的方法名和相關參數。

a = MyLibraryClass.DesiredFunction(strName)
  使用這種方法,你必須事先知道哪個庫包含了你所要的哪個函數和它的名稱,使用擴展方法就不一樣了:

a = strName.
  這裡,IntelliSense將會彈出,並且顯示可以使用哪些擴展方法。你只需要鍵入你需要的擴展方法:

a = strName.DesiredFunction()
  這裡無須給出所需的參數名來指定數據類型。

  在對象實例中調用靜態方法

  擴展方法提供了一個新的機制用來在對象實例上調用靜態方法。但和實例方法比較起來,它還是在功能上有諸多限制,因此你應該保守的使用它,主要將它哦能夠在實例方法能力所不及的地方。

  C# 3.0並不是一個正式的版本,所以它的標准也沒有最後定稿。因此,這樣的格式也很有可能變化。
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved