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

類別存取限制

類別利用存取修飾詞控制內部成員的存取限制,當你定義一個類別成員,必須瞭解如何使用存取修飾詞,以商當的避免外部程式的存取。

C# 定義了4個不同層級的修飾詞,分別是 public 、 private 、 protected 以及 internal ,提供類別成員不同的存取限制。

存取修飾詞
限制範圍
public
類別成員的存取完全不受到限制,允任何外部類別對其進行存取。
private
public 相反, private 對類別成員的存取設定了最大的限制,只允類別內部的存取,當一個類別成員沒有定義存取修飾詞, private 為預設的存取限制。
protected
public private 兩種修飾詞剛好是存取限制的兩個極端,而 protected 則限制必須是繼承的衍生類別才可以存取基礎類別的成員。
internal
允許同一個組件裏的類別對其成員作存取,組件為應用程式的邏輯單位。

除了表列修飾詞本身的定義,另外可以透過合併修飾詞擴充控制範圍。

protected internal:存取限於目前組件或衍生自包含類別的型別。

private protected:存取限於目前組件內包含類別或衍生自包含類別的型別。

由於目前介紹單一類別,接下來僅針對 public 以及 private 作討論,剩下的兩個修飾詞- protected 與 Internal 牽涉繼承等相關物件導向理論,於後續討論。

類別隱藏內部資料成員與實作細節,而存取修飾詞提供類別內部成員不同程度的封裝等級,限制了外部程式碼引用類別時對其成員的能見度,透過限制有效範圍隱藏資料與方法成員。

修飾詞必須配置於類別成員一開始宣告的地方,例如以下的的程式片段宣告一個 public的類別函式成員myfunction:

public void myfunction
{
   …
}

一旦以特定的關鍵字宣告完成,此類別成員的引用必須符合關鍵字所規定的存取限制,否則將引發程式的錯誤。

public 與 private

public 以及 private 是兩種極端的限制,以 public 宣告的成員允許外部程式碼對其進行完整、沒有限制的存取,宣告為 private 的成員,除了本身所屬的類別之外,外部程式碼對其存取操作都是禁止的。

類別 ClassM 包含一個 public 成員 MethondA ,以及一個 private 成員 MethodB ,MethondA 可以無限制的被外部類別程式碼所存取,而 MethodB 則只允許內部成員對其進行存取,但是 MethodA 與 MethodB 可以彼此互相存取。

class Program
{
    static void Main(string[] args)
    {
        MathA matha = new MathA();
        int result = matha.Add(100, 200);
        Console.WriteLine(result);
        Console.ReadKey();
    }
}
class MathA
{
    public int Add(int a, int b)
    {
        int result = a + b;
        return result;
    }
}

在類別 MathA 中的函式 Add 是 public ,因此可以供外部存取,主程式中建立MathA 物件,然後引用 Add() 方法,執行兩個指定參數的加總運算,最後輸出的結果為 300 。
進一步擴充 MathA 類別,加入除法函式 Division()。

class MathA
{
    public int Add(int a, int b)
    {
        int result = a + b;
        return result;
    }
    public double Division(double a, double b)
    {
        double result;
        if (b == 0)
        {
            result = DInfinity();
        }
        else
        {
            result = a / b;
        }
        return result;
    }
    private double DInfinity()
    {
        return double.PositiveInfinity;
    }
}

在 Division() 函式中,如果作為除數的第二個參數 b 是零,則執行 DInfinity() 函式,這個函式回傳一個代表無限大的值。

在主程式中,執行Division() 方法的程式碼如下:

class Program
{
    static void Main(string[] args)
    {
        MathA matha = new MathA();
        int result = matha.Add(100, 200);
        Console.WriteLine(result);

        double dresult = matha.Division(100.2, 0);
        Console.WriteLine(dresult);

        Console.ReadKey();
    }
}

在呼叫Division() 方法時,針對第二個參數傳入 0 ,得到「正無窮大」,這是方法 DInfinity() 回傳的結果,由於這個方法只允許被除數是 0 的時候,於MathA 類別內部存取,因此宣告為 private ,現在嘗試於 Main 當中撰寫以下的程式碼,直接執行 DInfinity() 方法:

double dresult = matha.DInfinity();

這一行程式碼並沒有辦法執行,會出現以下的錯誤訊息:

'MathA.DInfinity()' 由於其保護層級之故,所以無法存取。

表示 DInfinity() 因為 private 的限制而無法成功執行。

除了方法函式, private 常見於類別欄位的設計,用以保護不想被外部程式碼直接存取的資料。

class Program
{
    static void Main(string[] args)
    {
        ProductInfo pinfo = new ProductInfo();
        string bookTitle = pinfo.BookTitle(false);
        string bookPrice = pinfo.BookPrice("NT$");
        Console.WriteLine("書名:{0}", bookTitle);
        Console.WriteLine("價格:{0}", bookPrice);
        Console.ReadKey();
    }
}
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;
    }
}

類別 ProductInfo 封裝了一本特定的圖書資訊,由於我們希望外部程式取出其中的書名與價格時,能保持一致的輸出格式,因此將價格與書名,分別設定為 private 欄位 price 與 title ,如此一來,外部的程式便無法直接存取,另外定義方法 BookPrice 支援價格的格式化輸出,而方法 BookTitle 則根據傳入的 bool 參數,如果是 false ,則加上未上市的標識。

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

以上是輸出結果,由於傳入了 false ,因此加上未上市的標示輸出,而價格則加上指定的 NT$ 字首。

如果嘗試直接存取欄位 price 與 title ,同樣會出現無法存取的錯誤訊息。

對於欄位資料的保護,比較正式的作法是透過屬性成員進行設定,後續的課程持續作討論。




沒有留言: