天天看點

3.5 C#的封裝和通路修飾符

C#的封裝和通路修飾符

封裝是面向對象程式設計的一個重要特性,它可以隐藏類的内部實作細節,隻暴露必要的接口給外部使用者。封裝可以保護類的資料不被随意修改,提高類的可維護性和安全性。

在C#中,封裝是通過使用通路修飾符來實作的。通路修飾符可以指定類、屬性、方法、字段等成員的可見性和通路權限。C#支援以下五種通路修飾符:

  • public:表示成員可以被任何對象通路,沒有任何限制。
  • private:表示成員隻能被同一個類中的其他成員通路,對外部不可見。
  • protected:表示成員隻能被同一個類或者其派生類中的其他成員通路,對外部不可見。
  • internal:表示成員隻能被同一個程式集中的其他對象通路,對其他程式集不可見。
  • protected internal:表示成員可以被同一個程式集中的任何對象通路,或者被其他程式集中派生自該類的對象通路。

如果沒有指定通路修飾符,則預設使用類成員的預設通路修飾符,即為private。

下面我們用一些代碼示例來說明不同通路修飾符的用法和效果。

public 通路修飾符

public 通路修飾符表示成員可以被任何對象通路,沒有任何限制。例如,我們定義一個公共的Person類,它有一個公共的屬性Name和一個公共的方法SayHello:

// 定義一個公共的Person類
public class Person
{
    // 定義一個公共的屬性Name
    public string Name { get; set; }

    // 定義一個公共的方法SayHello
    public void SayHello()
    {
        Console.WriteLine("Hello, I am " + Name);
    }
}
           

然後我們在另一個類中建立Person類的對象,并通路其屬性和方法:

// 在另一個類中建立Person類的對象
Person p = new Person();
// 通路其屬性Name,并指派
p.Name = "Alice";
// 通路其方法SayHello,并調用
p.SayHello();
           

輸出結果為:

Hello, I am Alice
           

可以看到,我們可以在任何地方通路Person類及其成員,因為它們都是公共的。

private 通路修飾符

private 通路修飾符表示成員隻能被同一個類中的其他成員通路,對外部不可見。例如,我們定義一個私有的字段_age來存儲Person類對象的年齡,并提供一個公共的屬性Age來擷取或設定該字段:

// 定義一個私有的字段_age
private int _age;

// 定義一個公共的屬性Age
public int Age 
{
    // get通路器用于擷取_age字段的值
    get 
    {
        return _age;
    }
    // set通路器用于設定_age字段的值,并進行驗證
    set 
    {
        // 如果value參數小于0或大于120,則抛出異常
        if (value < 0 || value > 120)
        {
            throw new ArgumentException("Invalid age");
        }
        // 否則将value參數指派給_age字段
        _age = value;
    }
}
           

上面的示例中,我們可以通過Age屬性來通路或修改_age字段,但是我們不能直接通路或修改_age字段,因為它是私有的。這樣可以保證_age字段的值始終在合理的範圍内,不會被随意修改。

protected 通路修飾符

protected 通路修飾符表示成員隻能被同一個類或者其派生類中的其他成員通路,對外部不可見。這樣有助于實作繼承,我們将在後面的章節詳細讨論這個。

例如,我們定義一個基類Animal,它有一個受保護的屬性Name和一個受保護的方法Eat:

// 定義一個基類Animal
public class Animal
{
    // 定義一個受保護的屬性Name
    protected string Name { get; set; }

    // 定義一個受保護的方法Eat
    protected void Eat()
    {
        Console.WriteLine(Name + " is eating");
    }
}
           

然後我們定義一個派生類Dog,它繼承自Animal類,并重寫了Name屬性和Eat方法:

// 定義一個派生類Dog,繼承自Animal類
public class Dog : Animal
{
    // 重寫Name屬性,添加了set通路器
    public override string Name 
    {
        get => base.Name;
        set => base.Name = value;
    }

    // 重寫Eat方法,添加了自己的邏輯
    public override void Eat()
    {
        // 調用基類的Eat方法
        base.Eat();
        // 添加自己的邏輯
        Console.WriteLine(Name + " likes bones");
    }
}
           

然後我們在另一個類中建立Dog類的對象,并通路其Name屬性和Eat方法:

// 在另一個類中建立Dog類的對象
Dog d = new Dog();
// 通路其Name屬性,并指派
d.Name = "Bob";
// 通路其Eat方法,并調用
d.Eat();
           

輸出結果為:

Bob is eating
Bob likes bones
           

可以看到,我們可以在派生類中通路基類的受保護成員,并對其進行重寫或擴充。但是我們不能在其他類中直接通路基類的受保護成員,因為它們對外部不可見。

internal 通路修飾符

internal 通路修飾符表示成員隻能被同一個程式集中的其他對象通路,對其他程式集不可見。程式集是一個可執行的單元,通常是一個.dll或.exe檔案。

例如,我們定義一個類Library,它有一個内部的屬性Name和一個内部的方法Borrow:

// 定義一個類Library
public class Library
{
    // 定義一個内部的屬性Name
    internal string Name { get; set; }

    // 定義一個内部的方法Borrow
    internal void Borrow()
    {
        Console.WriteLine("You have borrowed a book from " + Name);
    }
}
           

然後我們在同一個程式集中的另一個類中建立Library類的對象,并通路其屬性和方法:

// 在同一個程式集中的另一個類中建立Library類的對象
Library l = new Library();
// 通路其屬性Name,并指派
l.Name = "CSDN Library";
// 通路其方法Borrow,并調用
l.Borrow();
           

輸出結果為:

You have borrowed a book from CSDN Library
           

可以看到,我們可以在同一個程式集中通路Library類及其成員,因為它們都是内部的。

但是如果我們在另一個程式集中嘗試通路Library類及其成員,就會出現編譯錯誤,因為它們對其他程式集不可見。

protected internal 通路修飾符

protected internal 通路修飾符表示成員可以被同一個程式集中的任何對象通路,或者被其他程式集中派生自該類的對象通路。這是一個比較靈活的通路修飾符,它結合了protected和internal的特點。

例如,我們定義一個類Book,它有一個受保護内部的屬性Title和一個受保護内部的方法Read:

// 定義一個類Book
public class Book
{
    // 定義一個受保護内部的屬性Title
    protected internal string Title { get; set; }

    // 定義一個受保護内部的方法Read
    protected internal void Read()
    {
        Console.WriteLine("You are reading " + Title);
    }
}
           

然後我們在同一個程式集中的另一個類中建立Book類的對象,并通路其屬性和方法:

// 在同一個程式集中的另一個類中建立Book類的對象
Book b = new Book();
// 通路其屬性Title,并指派
b.Title = "C# Tutorial";
// 通路其方法Read,并調用
b.Read();
           

輸出結果為:

You are reading C# Tutorial
           

可以看到,我們可以在同一個程式集中通路Book類及其成員,因為它們都是受保護内部的。

然後我們在另一個程式集中定義一個派生類Novel,它繼承自Book類,并重寫了Title屬性和Read方法:

// 在另一個程式集中定義一個派生類Novel,繼承自Book類
public class Novel : Book
{
    // 重寫Title屬性,添加了set通路器
    public override string Title 
    {
        get => base.Title;
        set => base.Title = value;
    }

    // 重寫Read方法,添加了自己的邏輯
    public override void Read()
    {
        // 調用基類的Read方法
        base.Read();
        // 添加自己的邏輯
        Console.WriteLine("You are enjoying the story of " + Title);
    }
}
           

然後我們在同一個程式集中的另一個類中建立Novel類的對象,并通路其屬性和方法:

// 在同一個程式集中的另一個類中建立Novel類的對象
Novel n = new Novel();
// 通路其屬性Title,并指派
n.Title = "The Three-Body Problem";
// 通路其方法Read,并調用
n.Read();
           

輸出結果為:

You are reading The Three-Body Problem
You are enjoying the story of The Three-Body Problem
           

可以看到,我們可以在其他程式集中通路Book類及其成員,因為我們是通過派生自該類的對象來通路的。

但是如果我們在其他程式集中直接建立Book類的對象,并嘗試通路其屬性和方法,就會出現編譯錯誤,因為它們對其他程式集中非派生類的對象不可見。

總結

本文介紹了C#的封裝和通路修飾符的概念和用法,通過一些代碼示例展示了不同通路修飾符的效果和差別。我們學習了以下五種通路修飾符:

  • public:表示成員可以被任何對象通路,沒有任何限制。
  • private:表示成員隻能被同一個類中的其他成員通路,對外部不可見。
  • protected:表示成員隻能被同一個類或者其派生類中的其他成員通路,對外部不可見。
  • internal:表示成員隻能被同一個程式集中的其他對象通路,對其他程式集不可見。
  • protected internal:表示成員可以被同一個程式集中的任何對象通路,或者被其他程式集中派生自該類的對象通路。

封裝和通路修飾符是面向對象程式設計的基礎知識,掌握它們可以幫助我們設計出更合理、更安全、更易維護的類和對象。希望本文對你有所幫助,謝謝閱讀!