程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 使類的擴展更簡單——擴展方法,擴展方法

使類的擴展更簡單——擴展方法,擴展方法

編輯:C#入門知識

使類的擴展更簡單——擴展方法,擴展方法


1、什麼是擴展方法?

    擴展方法,首先是一種方法,它可以用來擴展已定義類型中的方法成員。

    在擴展方法誕生之前,如果想為一個已有類型自定義含有特殊邏輯的新方法時,你必須重新定義一個類型來繼承已有類型,以這種方式來添加方法。如果基類有抽象方法,則還要重新去實現這個抽象方法。

    這樣,為了擴展一個方法,需要承擔更多的因繼承而產生的開銷。使用繼承來擴展現有類型總有點大材小用的感覺,並且值類型或密封類(不能被繼承的類)等也不能被繼承,不能由此獲得擴展。

    於是,C#3.0提出了擴展方法。

 

2、擴展方法的使用

    2.1 定義擴展方法

 1  public static class ListExtern
 2     {
 3         public static int JSum(this IEnumerable<int> source)
 4         {
 5             if (source == null)
 6             {
 7                 throw new ArgumentException("輸入數組為空");
 8             }
 9             int jsum = 0;
10             bool flag = false;
11 
12             foreach (var i in source)
13             {
14                 if (!flag)
15                 {
16                     jsum += i;
17                     flag = true;
18                 }
19                 else
20                 {
21                     flag = false;
22                 }
23             }
24             return jsum;
25         }
26     }

    在以上代碼中,JSum方法就是一個擴展方法,它的功能是計算數組中小標為奇數的數組成員之和。並不是所有的方法都可以用作擴展方法。下列是符合擴展方法的定義規則:

(1)擴展方法必須在一個非嵌套、非泛型的靜態類中定義;

(2)它至少要有一個參數;

(3)第一個參數必須加上this關鍵字作為前綴(第一個參數類型也稱為擴展類型,即指方法對這個類型進行擴展);

(4)第一個參數不能使用任何其他的修飾符(如不能使用ref、out等修飾符);

(5)第一個參數的類型不能是指針類型。

    這些規則都是硬性規定,無論方法違反了哪一條,編譯器都可能會報錯,或認為它不是一個擴展方法。

     

    2.2 調用擴展方法

          成功定義了一個擴展方法後,接下來就該去調用它。

         

1  static void Main(string[] args)
2         {
3             List<int> source=new List<int>() {1,2,3,4,5,6,3};
4             int jsum = source.JSum();
5             Console.WriteLine("數組的奇數和為:"+jsum);
6             Console.ReadKey();
7         }

    成功調用,說明了擴展方法調用的獨特性,即這裡可以直接通過List<int>類型來調用擴展方法。

 

3、編譯器如何發現擴展方法

     對於C# 3.0編譯器而言,當它看到某個類型的變量在調用方法時,它會首先去該對象的實例方法中進行查找,如果沒有找到與調用方法同名並參數一致的實例方法,編譯器就回去查找存在合適的擴展方法。

     編譯器會檢查所有導入的命名控件和當前命名控件中的擴展方法,並將變量類型匹配到擴展類型,這裡存在一個隱式轉換的擴展方法。如在前面代碼中,從List<T>到我們擴展的類型IEnumerable<int>就存在一個隱式轉換。

     從編譯器發現擴展方法的過程來看,方法調用的優先級順序應為:類型實例方法-當前命名空間下的擴展方法-導入命名控件的擴展方法。下面就用代碼來演示一下編譯器發現方法的過程:

     

 1 namespace 擴展方法2
 2 {
 3     using 擴展方法3;
 4     class Program
 5     {
 6         static void Main(string[] args)
 7         {
 8             Person p = new Person() {Name = "哈哈"};
 9             p.Print();
10             p.Print("Hello");
11         }
12     }
13 
14     public class Person
15     {
16         public string Name { get; set; }
17     }
18 
19     public static class Extensionclass
20     {
21         public static void Print(this Person per)
22         {
23             Console.WriteLine($"調用的是當前命名空間下的擴展方法輸出,姓名為:{per.Name}");
24         }
25     }
26 }
27 
28 namespace 擴展方法3
29 {
30     using 擴展方法2;
31 
32     public static class CustomExtensionClass
33     {
34         public static void Print(this Person per)
35         {
36             Console.WriteLine($"調用的是CustomNamaspace命名空間下的擴展方法暑促:姓名為:{per.Name}");
37         }
38 
39         public static void Print(this Person per,string s)
40         {
41             Console.WriteLine($"調用的是CustomNamaspace命名空間下的擴展方法暑促:姓名為:{per.Name},附加字符串{s}");
42         }
43     }
44 
45 }

    在以上代碼中,存在兩個不同的命名控件,她們都定義了帶一個參數的擴展方法Print。根據前面對編譯器調用方法的優先級的分析,編譯器首先查看Person類型中是否定義了無參的Print實例方法。如果有,則停止查找;否則繼續查找當前命名空間下,即CurrentNamespace下是否定義了帶一個參數的擴展方法Print。

    注意:(1)如果擴展的類型中定義了無參數的Print的實例方法,則在p後面鍵入“.”運算符時,VS的智能提示將不會給出擴展方法。

             (2)如果同一個命名空間下的兩個類中含有擴展類型相同的方法,編譯器便不知道該調用哪個方法了,就會出現編譯錯誤。

 

4、空引用也可調用擴展方法

     4.1 拿例子說話

           

 1 namespace 擴展方法3
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             Console.WriteLine("空引用上調用擴展方法演示:");
 8             string s = null;
 9             Console.WriteLine($"字符串S為空字符串:{s.IsNull()}");
10             Console.ReadKey();
11         }
12     }
13 
14     public static class NullExtern
15     {
16         public static bool IsNull(this object obj)
17         {
18             return obj == null;
19         }
20     }
21 }

    以上的代碼沒有報異常,可以正常運行。不過在上面的代碼中,代碼擴展了object類型,所有繼承於object的類型都將具有該擴展方法,這就對其他子類型產生了“污染”。

更好的實現方式應該是:

1 public static bool isNull(this string str)
2 {
3      return str==null;  
4 }

    所以當我們為某一個類型定義擴展方法時,應盡量擴展具體的類型,而不要擴展其基類。在空引用上調用擴展方法之所以不會出現NullReferenceException異常,是因為對於編譯器而言,這個過程只是把空引用"S"當成參數傳入靜態方法而已,即s.IsNull的調用等效於下面代碼:Console.WriteLine($"字符串s為空字符串{NullExten.IsNull(s)}");這並不是真正地在空引用上調用方法,所以也就不存在異常的問題。

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