2010/1/20

善用擴充方法

雖然我個人認為自從 .Net 2.0 以後引進的 Lambda 運算式在某種程度上破壞了 C# 的嚴謹度, 但是對於一個程式設計師而言, 我們也不能否定它對於程式撰寫所能提供的方便性。擴充方法 (Extension Method) 也是一樣; 如果你還不知道擴充方法是什麼, 那麼當你看完本文之後, 我相信你會感謝微軟提供了這麼好用的功能。

擴充方法是 .Net 3.5 才提供的, 所以在之前的版本中並沒有這東西。它的實作很簡單 (相對於其功能), 絕對超乎所有人的想像。請先看看以下的程式碼:

public static class cls
{
    public static string keep10chars(this string str)
    {
        return (str.Length > 10) ? str.Substring(0, 10) : str;
    }
}

撰寫擴充方法就是這麼簡單而已, 以下幾個重點要記住:

  1. 它必須包在一個宣告為 public static 的類別裡面 (如上例中的 public static class cls); 類別名稱可以隨便取, 並不影響執行及引用。 
  2. 方法必須以 public static [type] 宣告 (如上例中的 public static string)。
  3. 方法不能沒有參數, 而且其型別必須以 this [type] 宣告 (如上例中的 this string)。

如果你寫在網頁裡, 那麼完整的網頁程式如下:

using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {    
        Response.Write("12345678901234567890".keep10chars());
    }
}

public static class cls
{
    public static string keep10chars(this string str)
    {
        return (str.Length > 10) ? str.Substring(0, 10) : str;
    }

如上例, 當你把這個擴充方法 keep10chars 寫好之後, 你的字串物件就自動多了一個 .keep10chars() 方法可以使用了。在這個例子中, "12345678901234567890".keep10chars() 的輸出是 "1234567890"。意思就是說, 你已經為 string 型別建立了一個擴充方法 keep10chars(), 可以隨時引用。

擴充方法的用法就是這麼簡單。你不需要另外建立一個繼承自 string 的新型別, 而是直接套用在現有的系統型別上面; 也沒有什麼繁瑣的宣告或語法。你說, 這是不是非常方便呢?

擴充方法對於 ASP.NET 程式而言, 還有另一個極為友善的功能, 那就是它可以毫無問題的用在繫結控制項的 tag 裡面。舉個例子, 假設你的 GridView 中有一個 TemplateField, 你可以把擴充方法直接運用在從 Eval 函式撈出來的資料上:

<asp:TemplateField HeaderText="OperatorName" SortExpression="OperatorName">
    <EditItemTemplate>
        <asp:TextBox ID="TextBox1" runat="server" Text='<%# Bind("OperatorName") %>'></asp:TextBox>
    </EditItemTemplate>
    <ItemTemplate>
        <asp:Label ID="Label1" runat="server" Text='<%# Eval("OperatorName").ToString().keep10chars() %>'></asp:Label>
    </ItemTemplate>
</asp:TemplateField>

運用擴充方法, 我們就可以對所繫結的資料進行後處理, 而且是用非常簡單而輕鬆的做法。

不過, 請注意, 我發現我在 .Net 4.5 上建立的 ASP.NET 專案上建立的擴充方法, 總是發生未定義的錯誤。這個網頁有套用 Master Page, 我不知道這是否是引發錯誤的原因; 不過解法很簡單。

我的擴充方法是這樣定的:

namespace Johnny
{
    public static class abc
    {
        public static string Gender(this string isMale)
        {
            return (1)? "男" : "女";
        }
    }

    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
                ...

換句話說, 這個擴充方法 Gender() 是定義在和網頁相同的命名空間之下的。但是, 我還是必須以手動方式在網頁上加上明確的 IMPORT 指示詞, 這個擴充方法才能被找到:

<%@ Page Title="" Language="C#" MasterPageFile="~/..." %>
<%@ Import Namespace="Johnny" %>

<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
    <h2>...
    ...
<asp:TemplateField ...>
    <ItemTemplate>
        <asp:Label ID="lbIsMale" runat="server" Text='<%# Eval("IsMale").Gender() %>' />
    </ItemTemplate>
</asp:TemplateField>

我不知道為什麼這種問題會發生, 我只知道加上 IMPORT 指示詞就能解決這個問題。

如果你想更進一步了解擴充方法, 你可以參考 C# 程式設計手冊 (也有 VB 的版本)。

沒有留言:

張貼留言