.NET 擴展方法 (一)


我還記得剛剛學編程的時候,老師經常會提到一句話:注意空指針。所以經常在某些“入口”位置,進行代碼校驗,空指針的判斷就是其中的一項工作。

string類型作為常用的數據類型,它在項目中出現的機率極高,所以往往會有如下的代碼片段:

            // str 是 string 類型
            if (str == null || str == string.Empty)
            {
                // ... 其他操作
                return;
            }

每次都寫兩個雙等號判斷,着實有點煩人,讓人苦惱不已。在接下來的一段時間內有幸發現了一種新的寫法:

            // str 是 string 類型
            if (string.IsNullOrEmpty(str))
            {
                // ... 其他操作
                return;
            }

可是有些地方還要判斷是否為空格...

            // str 是 string 類型
            if (String.IsNullOrEmpty(str) || str.Trim().Length == 0)
            {
                // ... 其他操作
                return;
            }

代碼長度又變長了...慢着,別急,.NET 4.0來了...

            // str 是 string 類型
            if (String.IsNullOrWhiteSpace(str))
            {
                // ... 其他操作
                return;
            }

該方法很強大,不僅可以判斷空格,而且可以判斷制表符 '\t' 等空白字符。

可是... 可是... 我還是覺得不舒服,如果能實現下面的效果該多好呢。

            string str = null;

            bool result = str.IsEmpty();

            Console.WriteLine(result); // 正常編譯通過,正常運行不發生異常,並且輸出 True

假設,我們已經通過某些手段使string對象擁有IsEmpty方法:

如果IsEmpty是實例化方法,str對象為null,此時調用方法必定會引發空指針異常。

如果IsEmpty是靜態方法,按照語法規則,對象是無法調用靜態方法的。

所以,這條路好像行不通啊,怎么辦呢?——讓擴展方法來解決吧。

 

C#編程指南——擴展方法,給出的說明:擴展方法使你能夠向現有類型“添加”方法,而無需創建新的派生類型、重新編譯或以其他方式修改原始類型。

擴展方法是一種特殊的靜態方法,但可以像擴展類型上的實例方法一樣進行調用。

看樣子擴展方法很強大啊,那我們該如何實現和調用自定義的擴展方法呢?

1、因為擴展方法是向現有類型“添加”方法,所以必須有一個類型,這里就是我們想要添加方法的string類型了。

2、擴展方法也是方法,方法總是需要“載體”的,所以我們需要一個類。對該類的要求有且僅有兩點:

1)是頂級非泛型靜態類,2)是對於客戶代碼而言要有足夠的訪問權限。

3、對於擴展方法的要求有四點,除了是靜態方法和對於客戶代碼而言擁有足夠的訪問權限之外,還要求:

1)擴展方法的第一個參數必須是將要擴展的類型,這里就是string類型了。2)有且僅有第一個參數必須以this修飾符開頭。

4、寫完擴展方法,引入相應的命名空間(如果需要),根據VS的智能敏感提示,然后寫代碼調用即可。 

 

Demo:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MethodDemo
{
    public static class StringExtentsion
    {
        public static bool IsEmpty(this string str)
        {
            return string.IsNullOrWhiteSpace(str);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            string[] arr = new string[] { null, string.Empty, " ", "  \t  ", "   \r\n   " };

            foreach (string str in arr)
            {
                bool result = str.IsEmpty();

                Console.WriteLine(result); // 編譯通過,運行無異常,並且全部輸出True
            }
            
            Console.Read();
        }
    }
}

  

讓我們仔細看一下這個demo吧,哇塞,對象竟然可以調用靜態方法了,這完全打破了以往的語法規則啊。是這樣嗎?讓我們以懷疑的心態一探究竟。

我們利用 VS自帶的IL反匯編程序,反編譯一下,看一看MSIL代碼。StringExtentsion類的IsEmpty方法簽名如下:

.method public hidebysig static bool  IsEmpty(string str) cil managed 
{
} 
我們可以從中找到熟悉的影子 public static bool IsEmpty(string str){},它和一個標准的靜態方法無異,this 修飾符在這時候已經 “消失” 了。

 再看一下客戶代碼吧:

  IL_003b:  call       bool MethodDemo.StringExtentsion::IsEmpty(string)

 即便你看不懂MSIL代碼,我想你也有可能推測出正確結果——沒錯,我們被編譯器“欺騙”了,以下兩種寫法是等效的:

     result = str.IsEmpty();

     result = StringExtentsion.IsEmpty(str); // 此時 擴展方法中的this修飾符是可有可無的

  

 看到這里,對擴展方法已經有了一個大概了解,我們再回頭看一下 C#編程指南——擴展方法,中給出的一句說明:

擴展方法使你能夠向現有類型“添加”方法,而無需創建新的派生類型、重新編譯或以其他方式修改原始類型。擴展方法是一

種特殊的靜態方法,但可以像擴展類型上的實例方法一樣進行調用。

1、在這里“添加”兩個字被加了雙引號,故名其意不是真正的添加,擴展方法還存在於其定義的靜態類中,編譯后的方法簽名和標准的靜態方法無差異,

this修飾符、在這時候已經消失了。

2、擴展方法是一種特殊的靜態方法,其特殊在於其語法規則可保證編譯器可以將“擴展類型的對象調用靜態方法(擴展方法)”這種語法表象 編譯通過。 

  可以想象一下,編譯器在編譯的時候,它要查找當前代碼文件域所引入的命名空間下的所有擴展方法、其工作量是很大的(更多參見:ExtensionAttribute)。

 

 

后記:

1、這次舉的這個例子很特殊,能將擴展方法的絕對威力展示出來:

1)string是.NET 類庫中的類,我們不能修改string類吧。

2)string是密封類,不能繼承。

3)避免空指針異常,按語法規則最便捷的方式就是通過靜態方法的參數去解決,就像 string.IsNullOrWhiteSpace 方法一樣,

可是想得到的形式卻是對象調用實例方法。

 

 2、如果給object添加一個擴展方法會出現什么效果呢?(未完待續...)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM