程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> c#3.0系列:Extension Method

c#3.0系列:Extension Method

編輯:關於C#

我們說在C#3.0中,引入了一些列新的特性,但是個人認為Extension Method 這個特性是最爽的,最有創新的。

它真正的解決了:在保持現有Type原封不動的情況下對其進行擴展,你可以 在對Type的定義不做任何變動的情況下,為之添加所需的方法成員。下面我就來 講講。

C#3.X出來之前

大家都知道javascript有個特新Prototype,它就如同C#3.X中的Extension Method。這裡不多將了。

我們主要看看.NET的實現。在C#3.X出來之前我們可以做到對Type進行擴展。

interface的情況:

1public interface IEmployee
2{
3    string Name { get; set; }
4    int Age{get; set;}
5    int add(int n);
6}

對這個Interface進行擴展,為之添加一個Add方法執行相關的運算。我們唯 一的解決方案就是直接在這個Interface中添加一個Add成員。如上。實現了這個 Interface的Type必須實現該Interface的所有方法。所以,我們添加了Add這個 方法,將導致所有實現它的Type的重新定義和編譯,在很多情況下,我們根本不 需要這樣。

Class的情況:

如果我們將一個class作為基類,在基類中添加一個Add Method,所有的 Child Class都不會受到影響。但是在很多情況下,對於我們需要擴展的 Interface或者是class,我們是完全不能做任何改動。比如,我們要對datagrid 控件進行擴展。我們常用的方法就自定義一個Class去繼承這個datagrid,將需 要添加的成員定義在我們自己定義的Class中,這就是我們常說的自定義控件, 如果對於一個Sealed Class又該如何呢?我們要求的是對這個不能變動的Type進 行擴展,也就是使這個不能變動的Type的Instance具有我們添加的對象。

如果聽到這樣的要求:我們要對一個Type或者Interface進行擴展,卻不允許 我們修改它。這個要求確實有點苛刻。但是c#3.x 中我們可以選擇Extension Method。Extension Method本質上是在被擴展的對象實例上可以調用的靜態函數 ,不是繼承,所以不同於普通的成員函數,擴展函數不能直接訪問被擴展對象的 成員。只能通過該對象的實例來訪問。

C#3.X出來之後

簡單地說Extension Method是一個定義在Static Class的一個特殊的Static  Method。之所以說這個Static Method特別,是因為Extension Method不但能 按照Static Method的語法進行調用,還能按照Instance Method的語法進行調用 。

我們還是先來看例子:

public static class MyExtensionMethods
   {
     // this代表擴展方法應用於string類型上
     public static int ToInt32(this string s)
     {
       int i;
       Int32.TryParse(s, out i);
       return i;
     }
   }
public static void fnExtensionMethod()
     {
       string s = "27";
       // 使用string的ToInt32()擴展方法
       int i = s.ToInt32();
     }

我們可以看看上面的例子,我們知道net framework 裡string是個Sealed 類 型,我們只能使用Extension Method來對其進行擴展。我們可以看看它的定義方 式。ToInt32是一個Static方法。和一般的Static方法不同的是:在第一個參數 前添加了一個this 關鍵字。這是在C# 3.0中定義Extension Method而引入的關 鍵字。添加了這樣一個關鍵字就意味著在調用該方法的時候這個標記有this的參 數可以前置,從而允許我們向調用一般Instance Method的方式來調用這個 Static Method。注意:需要在(只需要在)第一個參數前面使用this修飾。

Extension Method IN CLR

C# 3.0的這些新的特性大都影響Source被C# Compiler編譯成Assembly這個階 段,換句話說,這些新特僅僅是Compiler的新特性而已。通過對Compiler進行修 正,促使他將C# 3.0引入的新的語法編譯成相對應的IL Code,從本質上看,這 些IL Code 和原來的IL並沒有本質的區別。所有當被編譯生成成Assembly被CLR 加載、執行的時候,CLR是意識不到這些新的特性的。

我們先看看Extension Method與傳統的Static Method有什麼區別。

1.method public hidebysig static int32 ToInt32(class ConsoleApplication1.person s) cil managed
2{
3  .custom instance void [System.Core] System.Runtime.CompilerServices.ExtensionAttribute::.ctor()
4  .maxstack 2
5  .locals init (
6    [0] int32 i,
7    [1] int32 CS$1$0000,
8    [2] int32 CS$0$0001)
9  L_0000: nop
10  L_0001: ldarg.0
11  L_0002: callvirt instance int32 ConsoleApplication1.person::get_Age()
12  L_0007: stloc.2
13  L_0008: ldloca.s CS$0$0001
14  L_000a: call instance string [mscorlib]System.Int32::ToString ()
15  L_000f: ldloca.s i
16  L_0011: call bool [mscorlib]System.Int32::TryParse(string, int32&)
17  L_0016: pop
18  L_0017: ldloc.0
19  L_0018: stloc.1
20  L_0019: br.s L_001b
21  L_001b: ldloc.1
22  L_001c: ret
23}
24
25
26
27
28

大家注意,這是Extension Method的IL,它與傳統的Static Method最大的區 別事在Extension Method多了這句:

.custom instance void [System.Core] System.Runtime.CompilerServices.ExtensionAttribute::.ctor()

就是在ToInt32方法上添加一個Customer Attribute: System.Runtime.CompilerServices.ExtensionAttribute。這個

屬性是為了Extension Method的而定義的。

我們再來看看,IL事如何實現Instance Method方式調用Extension Method的 。

1.method public hidebysig static void fnExtensionMethod() cil managed
2{
3  .maxstack 1
4  .locals init (
5    [0] string s,
6    [1] int32 i,
7    [2] int32 j)
8  L_0000: nop
9  L_0001: ldstr "27"
10  L_0006: stloc.0
11  L_0007: ldloc.0
12  L_0008: call int32 ConsoleApplication1.MyExtensionMethods::ToInt32(string)
13  L_000d: stloc.1
14  L_000e: ldstr "28"
15  L_0013: call int32 ConsoleApplication1.MyExtensionMethods::ToInt32(string)
16  L_0018: stloc.2
17  L_0019: ldloca.s i
18  L_001b: call instance string [mscorlib]System.Int32::ToString ()
19  L_0020: call void [mscorlib]System.Console::WriteLine(string)
20  L_0025: nop
21  L_0026: ret
22}
23
24

通過上面的IL,我們看到調用的是 ConsoleApplication1.MyExtensionMethods的ToInt32方法。

我們基本上看出了Extension Method的本質。當Compiler對Adds方法的調用 進行編譯的過程的時候,它必須判斷這個ToInt32方式是String已有的成員還是 以Extension Method的方式定義。Extension Method的優先級是最低的,只有確 定String中沒有定義相應的ToInt32方法的時候,Compiler才會在引用的 Namespace中查看這些Namespace中是否定義有對應的ToInt32 Extension Method 的Static Class。找到後作進行相應的編譯,否則出現編譯錯誤。

擴展接口類型

看下面的例子


1public static void fnIntExtensionMethod()
2    {
3
4      Console.WriteLine("***** Extending an interface *****\n");
5      // Call IMath members from MyCalc object.
6      MyCalc c = new MyCalc();
7      Console.WriteLine("1 + 2 = {0}", c.Add(1, 2));
8      Console.WriteLine("1 - 2 = {0}", c.Subtract(1, 2));
9      // Can also cast into IBasicMath to invoke extension.
10      Console.WriteLine("30 - 9 = {0}", ((IMath)c).Subtract (30, 9));
11      // This would NOT work!
12       //IMath itfBM = new IMath();
13       //itfBM.Subtract(10, 10);
14      Console.ReadLine();
15
16    }
17
18public static class MyExtensionMethods
19  {
20    // this代表擴展方法應用於string類型上
21    // ToInt32()是將string類型轉換為int類型的擴展方法
22    public static int ToInt32(this string s)
23    {
24      int i;
25      Int32.TryParse(s, out i);
26      return i;
27    }
28
29    public static int Subtract(this IMath itf ,int x, int y)
30    {
31      return x - y;
32    }
33
34  }
35public interface IMath
36  {
37    int Add(int x, int y);
38  }
39
40class MyCalc : IMath
41  {
42    public int Add(int x, int y)
43    {
44      return x + y;
45    }
46  }

注意這裡擴展時必須給出函數的實現,擴展接口後,顯然不能直接在接口上調 用這些擴展函數,只能理解為,所有繼承該接口的對象新增加了這些擴展函數功 能。

注意事項:

引用擴展函數

必須引用定義擴展函數的命名空間,否則擴展函數不可用。

智能提示

Visual studio 的智能提示將擴展函數標記為向下的藍色箭頭。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved