【公告】網站目前停止所有的課程訂閱服務,原有學員權益不受影響,造成不便還請見諒,我們正在打造更多課程以及圖書,包含 Python 為主的課程主題,未來將會合併且擴充目前的課程內容,提供全新課程訂閱服務,感謝學員的支持。

類別屬性成員

類別欄位處理資料時,經常設定為 private,另外再透過其它機制提供間接存取,避免外部程式碼任意存取以保護資料內容。

class ProductInfo
{
    private double price = 960.00;
    private string title = "ASP.NET MVC 商業應用開發";
    public string BookPrice(string doller)
    {
        string bookPrice = doller + " " + price;
        return bookPrice;
    }
    public string BookTitle(bool p)
    {
        string bookTitle;
        if (p == true)
            bookTitle = title;
        else
            bookTitle = title + " 〈未上市〉";
        return bookTitle;
    }
}

在「類別存取限制」講座中,討論存取限制的範例,這段程式利用方法成員,間接針對 private 欄位 price與 title 值,進行限制性的存取,同時於方法中進一步執行判斷式。

透過公開函式存取 private 欄位儘管可以達到欄位存取控管的目的,不過實務上運用屬性機制是比較好的作法,將欄位宣告為 private 層級限定為只能在定義它的類別內部作存取,然後藉由屬性公開存取。

設計屬性成員

屬性成員允許欄位以類別方法的方式作存取,針對欄位設計專屬的運算邏輯,控制外部程式碼的存取操作,如同類別資料的守門員,對於某些須要限制存取能力的欄位成員相當有用。

屬性在設計上與一般欄位成員不同的地方,在於其提供一組存取子,架構如下 :

T PropertyName
{
     get
     {  // 讀取欄位 … }
   
     set
     {  // 設定欄位 … }
}

其中 set 與 get 區塊支援私有欄位的存取程式碼, get 程式區塊裏面,須使用 return 關鍵字,將其對應的欄位值回傳給引用這屬性的程式碼,而 set 接受外部傳入的值,以名稱為 value 的變數儲存這個值,我們透過 value取得使用者設定的值。

無論 get 或 set 區塊,需要的時候,會同時撰寫其它的邏輯程式碼,以判斷是否回傳或設定指定的欄位值。

class ProductInfo
{
    private double price = 960.00;
    private string title = "ASP.NET MVC 商業應用開發";
 
    public double Price
    {
        get
        {
            return price;
        }
        set
        {
            if (value > price * 0.6)
            {
                price = value;
            }
        }
    }
    public string BookPrice(string doller)
    {
        string bookPrice = doller + " " + price;
        return bookPrice;
    }
    public string BookTitle(bool p)
    {
        string bookTitle;
        if (p == true)
            bookTitle = title;
        else
            bookTitle = title + " 〈未上市〉";
        return bookTitle;
    }
}

在 ProductInfo 類別中,加入名稱為 Price 的屬性,其中的 get 區塊,直接取得欄位 price 的值回傳,而 set 區塊, value 是外部程式碼傳送進來,嘗試重設 Price 屬的新值,先判斷其是否小於 price 乘上 0.6 的結果,這個判斷式會將標價小於六折的值排除,避免商品以小於六折水平定價。

class Program
{
    static void Main(string[] args)
    {
        ProductInfo pinfo = new ProductInfo();
        pinfo.Price = 770;
        string bookTitle = pinfo.BookTitle(false);
        string bookPrice = pinfo.BookPrice("NT$");
        Console.WriteLine("書名:{0}", bookTitle);
        Console.WriteLine("價格:{0}", bookPrice);
        Console.ReadKey();
    }
}

將 Price 屬性設定為 770 ,這個值會傳入 Price 屬性中的 set 區塊,以 value 作表示,由於其大於原始價格 price 欄位值乘上 0.6 (960*0.6 = 770),因此價格輸出為 770 。

書名:ASP.NET MVC 商業應用開發 〈未上市〉
價格:NT$ 770

在類別的設計中,有時只需要單純的提供屬性成員,以方便後續特定資料的內容存取,這種情形可以建立空的 get 與 set 進行支援,這在資料庫應用相當常見:

public string ShortName { get; set; }

除此之外,可以視需要僅設計 get 或是 set ,如果只有 get 則是一個唯讀屬性,只有 set則是唯寫屬性,接下來利用另外一個範例作說明。

class ABCompare
{
    private int a = 50;
    private int b = 0;
    public int A
    {
        get
        {
            return a;
        }
    }
    public int B
    {
        get
        {
            return b;
        }
        set
        {
            if (value < 0)
                b = 0;
            else
                b = value;
        }
    }
    public bool CompareResult()
    {
        if (b < a)
            return false;
        else
            return true;
    }
}

ABCompare 類別定義兩個欄位,分別是 a 與 b ,進行 a 與 b 兩個值的比較運算,a是比較的基準值不會改變, b 則為進行比較的值,這個值是變動的,由外部程式設定,不過只接受大於零的正值。

屬性 A 與 B 分別支援 a 與 b 欄位的存取。屬性 A 只定義 get 回傳 a 欄位值,因此是唯讀屬性,避免 a 欄位值被修改,而 B 則於 set 中,判斷 value 值以排除小於 0 的設值。

函式 CompareResult() 則回傳 a 與 b 的大小比較結果。

class Program
{
    static void Main(string[] args)
    {
        int b;
        bool result;
        ABCompare ab = new ABCompare();
        while (true)
        {
            Console.Write("輸入比較值(小於零一律視為零):");
            var s = Console.ReadLine();
            if (s.Length == 0) return;

            b = int.Parse(s);
            ab.B = b;
            result = ab.CompareResult();

            if (result == false)
                Console.WriteLine("{0} 小於 {1}", ab.B, ab.A);
            else
                Console.WriteLine("{0} 不小於 {1}", ab.B, ab.A);
        }
    }
}

建立無窮 while 迴圈以方便測試 ABCompare 比較方法,要求輸入作為比較的值,如果使用者輸入是 0 ,則跳出迴圈結束程式,接下來將輸入的值設定給 B 屬性,並且引用 CompareResult() 函式執行比較結果,最後將結果輸出。

輸入比較值(小於零一律視為零):100
100 不小於 50
輸入比較值(小於零一律視為零):-36
0 小於 50

輸出結果中第二個測試嘗試指定一個負值,被轉換為零回傳,並輸出比較的結果。

屬性限制

屬性同樣可以設定存取修飾詞,不過 set 與 get 兩種存取子兩者必須同時存在,而且當中只能選擇性的針對其中一個作設定。

另外必須注意的是, get 與 set 的存取範圍設定必須比屬性本身更嚴格。

回到上述 ABCompare 類別中嘗試將其中屬性 B 的 set 存取子設為 private :

    public int B
    {
        get
        {
            return b;
        }
        private set
        {
            if (value < 0)
                b = 0;
            else
                b = value;
        }
    }

重新執行將出現以下的錯誤訊息:

CS0272 無法在此內容中使用屬性或索引子 'ABCompare.B',因為無法存取 set 存取子。 …

最後,將其中的 get 與 set 同時進行 private 存取設定,如下式

    public int B
    {
        private get
        {
            return b;
        }
        private set
        {
            if (value < 0)
                b = 0;
            else
                b = value;
        }
    }

則會出現另外一段錯誤訊息:

CS0274 不可同時對屬性或索引子 'ABCompare.B' 的兩個存取子,指定存取範圍修飾詞。 …



沒有留言: