C#面向對象核心-封裝

来源:https://www.cnblogs.com/tanyuyang/archive/2023/03/28/17264756.html
-Advertisement-
Play Games

封裝 封裝定義為"把一個或多個項目封閉在一個物理的或者邏輯的包中",這個包就是類。在面向對象程式設計方法論中,封裝可以防止對實現細節的訪問。 1 類和對象 1.1 什麼是類 具有相同特征、行為,是一類事物的抽象 類是對象的模板,通過類創建對象 1.2 類聲明語法 //聲明在namespace中 /* ...


封裝

封裝定義為"把一個或多個項目封閉在一個物理的或者邏輯的包中",這個包就是類。在面向對象程式設計方法論中,封裝可以防止對實現細節的訪問。

1 類和對象

1.1 什麼是類

具有相同特征、行為,是一類事物的抽象

類是對象的模板,通過類創建對象

1.2 類聲明語法

//聲明在namespace中
/*class 類名
{
    //成員變數 表示特征
    //成員方法 表示行為
    //成員屬性 保護成員變數
    //構造函數和析構函數 初始化和釋放
    //索引器 像數組一樣使用
    //運算符重載 自定義對象可計算
    //靜態成員 類名.點出成員使用
}*/
class Person
{

}

1.3 類對象

類聲明和類對象聲明是兩個概念:

  • 類聲明相當於定義了一個變數類型;
  • 類對象聲明相當於聲明一個類的變數,這個過程稱為實例化對象;
  • 類對象是引用類型。
//類名 變數名;
//類名 變數名 = null;
//類名 變數名 = new 類名();
Person p1;
Person p2 = null;//空,不分配堆的記憶體空間

//p3 p4雖然來自一個類的實例化對象,但它們的特征行為等都是分別獨有的,不會相互共用
Person p3 = new Person();
Person p4 = new Person();

2 成員變數和訪問修飾符

2.1 成員變數

聲明在類語句塊中,來描述對象的特征,可以是任意的變數類型,數量無限制,是否賦值根據需求確定

 enum E_SexType
{
    Man,
    Woman,
}

struct Position
{

}

class Pet//寵物類
{

}

class Person
{
    //成員變數
    public string name;
    public int age;
    public E_SexType sex;
    //如果在類中聲明和自己類型相同的成員變數時,不能對它實例化:Person grilFriend = new Person();
    public Person grilFriend;//女朋友 類可以用自己,但結構體不能,因為類在初始化時候才會分配記憶體,結構體則會一直死迴圈
    public Person[] friend;//朋友
    public Position pos;//位置
    public Pet pet = new Pet();//寵物
}

2.2 訪問修飾符

  • public 公共的,自己(內部)和別人(外部)都能訪問使用
  • private 私有的,自己才能訪問和使用,不寫預設為私有
  • protected 保護的,自己和子類才能訪問使用
  • internal 內部的,同一個程式集的對象可以訪問,程式集就是命名空間

2.3 成員變數的使用和初始值

Person p = new Person();
/* 成員變數的使用和初始值
 * 值類型、數字類型:預設為0
 * bool類型:預設為false
 * 引用類型:預設為null
 * 用default(變數類型) 關鍵字可以看預設值
 */
Console.WriteLine(default(bool));
p.age = 10;

3 成員方法

3.1 成員方法聲明

  • 聲明在類語句塊中,來描述對象的行為
  • 和函數聲明規則相同,受訪問限制符影響
  • 不需要加static關鍵字
class Person
{
    public string name;
    public int age;
    public Person[] friends;

    //成員方法
    public void Speak(string str)
    {
        Console.WriteLine($"{name}說{str}");
    }

    public bool isAudlt()
    {
        return age >= 18;
    }

    public void AddFriend(Person p)//添加朋友
    {
        if (friends == null) 
        { 
            friends = new Person[] { p }; 
        }
        else
        {
            Person[] newFriends = new Person[friends.Length + 1];
            for(int i = 0; i < friends.Length; i++)//老朋友複製到新數組
            {
                newFriends[i] = friends[i];
            }
            newFriends[newFriends.Length - 1] = p;//新加的朋友
            friends = newFriends;//地址重定向
        }
    }

3.2 成員方法的使用

必須實例化對象,再通過對象來使用,相當於對象執行了某個行為

Person p =  new Person();
p.name = "abc";
p.age = 18;
p.Speak("123");

if (p.isAudlt()) p.Speak("我成年了");//使用

Person p2 = new Person();
p2.name = "def";
p2.age = 24;

p.AddFriend(p2);

foreach(Person f in p.friends)
{
    Console.WriteLine(f.name);
}

4 構造函數、析構函數和垃圾回收

4.1 構造函數

  • 用處:在實例化對象時,會調用的用於初始化的函數,如果沒寫,預設存在一個無參的構造函數
  • 沒有返回值,函數名和類名相同,訪問許可權一般都是public
  • 如果實現了有參構造函數且沒有寫無參構造函數,那麼預設的無參構造函數就沒有了,實例化對象時只能有參實例化
class Person
{
    public string name;
    public int age;

    //類中允許無參構造函數,結構體中不行
    //無參構造函數
    public Person()
    {
        name = "tyy";
        age = 24;
    }

    //構造函數可以被重載,this代表當前調用該函數的對象本身
    public Person(string name, int age)
    {
        this.name = name; 
        this.age = age;
    }

    //構造函數特殊寫法:通過this 復用構造函數代碼
    //訪問修飾符 構造函數名(參數):this(參數1,參數2...)。先調用this()裡面的構造函數重載,再執行後面花括弧內容
    //先調用this()裡面的構造函數重載,再執行後面花括弧內容
    public Person(string name):this()
    {
        Console.WriteLine("兩個構造函數調用");
    }
}

4.2 析構函數

  • 與記憶體垃圾回收有關,C#有自動回收垃圾,幾乎不用析構函數
  • 析構函數只能在類中定義,不能用於結構體;
  • 一個類中只能定義一個析構函數;
  • 析構函數不能繼承或重載;
  • 析構函數沒有返回值;
  • 析構函數是自動調用的,不能手動調用;
  • 析構函數不能使用訪問許可權修飾符修飾,也不能包含參數。
class Person
{
    public Person()
    {
        
    }
	~Person()
    {

    }
}

4.3 垃圾回收

  • 垃圾就是記憶體中沒有被任何變數、對象引用的內容;
  • 垃圾回收(Garbage Collecyor GC)就是遍歷堆(Heap)上動態分配的所有對象,識別它們是否被引用來確定哪些對象是垃圾,哪些對象有用;
  • GC只負責Heap記憶體的垃圾回收,引用類型都是存放在Heap中,因此它們的分配釋放都通過GC來處理;
  • 系統自動管理棧(Stack)記憶體的垃圾回收,值類型通過Stack分配記憶體,它們有自己的生命周期,自動分配釋放;
  • 垃圾回收演算法:引用計數(Reference Counting)、標記清除(Mark Sweep)、標記整理(Mark Compact)、複製集合(Copy Collection)。

GC基本原理:

  • 分代演算法:把Heap記憶體分為 0代記憶體 1代記憶體 2代記憶體
  • 新分配的對象存儲在0代記憶體里,每次分配或(0 1 2代記憶體滿)可能進行垃圾回收釋放記憶體,執行下列兩步:
  • 1、標記對象:從根(靜態欄位、方法參數)開始檢測引用對象,檢測到的為可達對象,未檢測到的為不可達對象(垃圾)
  • 2、搬遷對象壓縮堆:釋放不可達對象,搬遷可達對象到下一代,修改它們的引用地址
  • 1代記憶體滿時會觸發GC釋放0 1代記憶體空間,2代滿了0 1 2代記憶體都釋放
//手動出發垃圾回收的方法,一般在Loading切換場景時才調用
GC.Collect();

5 成員屬性

  • 用於保護成員變數;
  • 為成員屬性的獲取和賦值添加邏輯處理;
  • 解決3P(public、private、protected)的局限性;
  • 成員屬性可以設置成員變數在外部的許可權為只能獲取不能修改 或 只能修改不能獲取。

5.1 成員屬性的基本語法

/*
訪問修飾符 屬性類型 屬性名
{
    get{}   讀,寫了get就一定要有一個返回值
    set{}   寫
}
*/

class Person
{
    private string name;
    private int age;
    private int money;
    private bool sex;

    //屬性的命名用帕斯卡命名法
    public string Name
    {
        get
        {
            //可以在返回之前添加一些邏輯條件
            return name;//雖然name是私有的,但是通過成員屬性也能得到它
        }
        set
        {
            //可以在設置之前添加一些邏輯條件
            //value關鍵字表示外部傳入的值
            name = value;
        }
    }

    public int Money
    {
        get
        {
            //解密處理
            return money - 5;
        }
        set
        {
            //可以進行額外的加密處理 起到安全和保密作用
            if(value < 0)
            {
                value = 0;
                Console.WriteLine("錢不能為負數");
            }
            money = value + 5;
        }
    }
}

5.2 成員屬性的使用

Person p = new Person();
p.Name = "abc";//執行set{}
Console.WriteLine(p.Name);//p.Name執行get{}

p.Money = 1000;
Console.WriteLine(p.Money);//外部看是1000元,但在記憶體裡面是1005元 起到了加密的作用

5.3 get和set的訪問修飾符

  • 預設會使用屬性的訪問修飾符;
  • 如果要加,許可權要低於屬性的訪問許可權;
  • 不能讓get和set的許可權都低於屬性的許可權,要滿足 只讀不寫 或 只寫不讀,解決3P的局限性。

5.4 get和set可以只有一個

class Person
{
    private string name;
    private int age;
    private int money;
    private bool sex;
    
    public bool Sex
    {
        get { return sex; }//創造只讀屬性,private類型的成員也能讀
    }
}

5.5 自動屬性

  • 作用:外部只讀不寫的特征
  • 如果類中有一個特征是只希望外部只讀不寫的,又沒什麼特殊處理,那麼可以直接使用自動屬性
class Person
{
    private string name;
    private int age;
    private int money;
    private bool sex;
    
    public float Height
    {
        get;
        private set;
    }
}

6 索引器

  • 索引器(Indexer) 允許一個對象可以像數組一樣使用下標的方式來訪問;
  • 當為類定義一個索引器時,該類的行為就會像一個 虛擬數組(virtual array) 一樣。可以使用數組訪問運算符 [ ] 來訪問該類的的成員。

6.1 基本語法

/*
 element-type this[int index]
{
    // get 訪問器
    get
    {
        // 返回 index 指定的值
    }

    // set 訪問器
    set
    {
        // 設置 index 指定的值
    }
}
*/
class Person
{
    private string name;
    private int age;
    private Person[] friends;

    public Person this[int index]
    {
        //可以寫邏輯條件來處理
        get
        {
            if(friends == null || friends.Length - 1 < index)
            {
                return null;
            }
            return friends[index];
        }
        set
        {
            //和成員屬性類似,value代表傳入的值
            if(friends == null)
            {
                friends = new Person[] { value };
            }else if(index > friends.Length - 1)
            {
                friends[friends.Length - 1] = value;//舉例 如果索引越界就把最後一個朋友頂掉
            }
            else friends[index] = value;
        }
    }
}

6.2 使用

Person p = new Person();
p[0] = new Person();//調用set 得到1個朋友
Console.WriteLine(p[0]);//調用get

6.3 索引器重載

class Person
{
    public int[,] array;
    
    //函數名相同,但參數類型、個數或順序不同
    public int this[int i, int j]
    {
        get
        {
            return array[i, j];
        }
        set
        {
            array[i, j] = value;
        }
    }
}

7 靜態成員

  • 關鍵字 static
  • 用static修飾的 成員變數、方法、屬性等 稱為靜態成員
  • 特點:直接用類名+點 使用,如Console.XX

7.1 自定義靜態成員

class Test
{
    //靜態成員變數
    public static float PI = 3.121592654f;
    //成員變數
    public int testInt = 100;

    //靜態成員方法
    //靜態方法不能使用非靜態成員變數,與靜態和非靜態的生命周期有關
    public static float CalcCircle(float r)
    {
        //πr²
        return PI * r * r;
    }

    //普通成員方法
    //非靜態方法可以使用靜態成員變數
    public void testFun()
    {
        Console.WriteLine("123");
        Console.WriteLine(PI);
    }
}

7.2 靜態成員使用

Console.WriteLine(Test.PI);
Console.WriteLine(Test.CalcCircle(2));

//普通的只有new了對象之後才能使用那些變數方法等
Test t = new Test();
Console.WriteLine(t.testInt);
t.testFun();

7.3 為什麼能夠不實例化對象直接使用

  • 靜態成員在程式開始運行時就會分配記憶體空間,和程式同生共死,只要使用了靜態成員,直到程式結束了它的記憶體才會被釋放;
  • 每個靜態成員在記憶體里都有唯一的一塊空間,因此類中只有一個該靜態成員的實例,在任何地方使用都是改變那一個。

7.4 常量const和靜態變數static

相同點:都可以使用類名+點使用
不同點:

  • const必須初始化且不能修改;
  • const只修飾變數;
  • const一定寫在訪問修飾符後面,static可前可後。

8 靜態類和靜態構造函數

8.1 靜態類

用 static 修飾的類,只能包含靜態成員,不能被實例化
作用:

  • 將常用的靜態成員寫在靜態類中,方便使用;
  • 靜態類不能被實例化,體現工具類的唯一性 如Console就是一個靜態類,它寫在命名空間System裡面。
static class TestClass
{
    public static int testIndex = 0;
    public static void testFun()
    {

    }
}

8.2 靜態構造函數

  • 在構造函數加上static修飾;
  • 不能使用訪問修飾符,不能有參數,只會自動調用一次;
  • 作用:在靜態構造函數中初始化靜態變數。

8.2.1 靜態類 中的 靜態構造函數

static class StaticClass
{
    //第一次使用類時,類裡面的靜態成員自動調用一次
    public static int testInt = 100;
    public static int testInt2 = 100;

    static StaticClass()
    {
        Console.WriteLine("靜態構造函數");
    }
}

8.2.2 普通類 中的 靜態構造函數

class Test
{
    public static int testInt = 200;
    static Test()
    {
        Console.WriteLine("靜態構造");
    }
    public Test()
    {
        Console.WriteLine("普通構造");
    }
}

8.3 使用

Console.WriteLine(StaticClass.testInt);

//第一次使用類時,類裡面的靜態成員自動調用一次
Console.WriteLine(Test.testInt);
//普通構造在new的時候調用
Test t = new Test();

9 拓展方法

為現有 非靜態 的 <變數類型> 添加新方法

作用:

  • 提升程式的拓展性;
  • 不需要在對象中重寫方法或通過繼承來添加方法;
  • 為別人封裝的類型寫額外的方法。

特點:

  • 一定寫在靜態類中
  • 一定是個靜態函數
  • 第一個參數為拓展目標
  • 第一個參數用this修飾

9.1 基本語法

訪問修飾符 static 返回值 函數名(this 要拓展的類名 參數名,參數類型 參數名,......)

static class Tools
{
    //為int拓展了一個成員方法,int裡面是沒有SpeakValue方法的
    //value 代表使用該方法的 實例化對象
    public static void SpeakValue(this int value)
    {
        //拓展方法的邏輯
        Console.WriteLine("為int拓展的方法" + value);
    }

    public static void SpeakStringInfo(this string str, string str2, string str3)
    {
        Console.WriteLine("為string拓展的方法,調用方法的對象是:" + str);
        Console.WriteLine("傳的參數:" + str2 + str3);
    }

    //為自定義的類型Test拓展方法
    public static void Fun3(this Test t)
    {
        Console.WriteLine("為Test拓展的方法");
    }
}

class Test
{
    public int i = 10;
    public void Fun1()
    {
        Console.WriteLine("123");
    }
    public void Fun2()
    {
        Console.WriteLine("456");
    }
}

9.2 使用

int i = 10;
i.SpeakValue();

string s = "ABC";
s.SpeakStringInfo("a ", "b");

Test t = new Test();
t.Fun3();

10 運算符重載

10.1 基本概念

作用:讓自定義的類和結構體能夠使用運算符進行運算,關鍵字 operator

特點:

  • 一定是一個公共的靜態方法;
  • 返回值寫在operator前;
  • 邏輯處理自定義。

註意:

  • 條件運算符需要成對實現;
  • 一個符號可以多個重載;
  • 不能使用ref和out。

10.2 可重載和不可重載的運算符

註意:運算符需要兩個參數還是一個參數

可重載的:

  • 算數運算符:+ - * / % ++ --
  • 邏輯運算符:!
  • 位運算符:| & ^ ~ << >>
  • 條件運算符:> < >= <= == != ,需要成對出現:(大於,小於)(大於等於,小於等於)(等於,不等於)

不可重載的:

  • 邏輯與(&&) 邏輯或(||)
  • 索引符 []
  • 強轉運算符 ()
  • 特殊運算符
  • 點. 三目運算符

10.3 基本語法

public static 返回類型 operator 運算符(參數列表)

class Point
{
    public int x;
    public int y;

    //重載‘+’成為類Point的加法
    public static Point operator +(Point p1, Point p2)
    {
        Point p = new Point();
        p.x = p1.x + p2.x;
        p.y = p1.y + p2.y;
        return p;
    }

    //可以有多個重載
    public static Point operator +(Point p1, int value)
    {
        Point p = new Point();
        p.x = p1.x + value;
        p.y = p1.y + value;
        return p;
    }
}

10.4 使用

Point p1 = new Point();
p1.x = 1;
p1.y = 1;
Point p2 = new Point();
p2.x = 2;
p2.y = 2;

Point p3 = p1 + p2;

Point p4 = p3 + 2;

11 內部類和分部類

11.1 內部類

  • 在一個類中再申明一個類
  • 使用時要用包裹者點出內部類
  • 實現類之間的親密關係
class Person
{
    public int age;
    public string name;
    public Body body;
    public class Body
    {
        Arm leftArm;
        Arm rightArm;
        class Arm
        {

        }
    }
}

//使用
Person p = new Person();
Person.Body body = new Person.Body();

11.2 分部類

把一個類分成幾部分申明。
作用:分部描述一個類,增加程式的拓展性,關鍵字 partial

11.3 分部方法

將方法的聲明和實現分離。

特點:

  • 不能加訪問修飾符,預設私有
  • 只能在分部類中聲明
  • 返回值只能是void
  • 可以有參數,但不用out關鍵字
//分部類
partial class Student
{
    public string name;
    public bool sex;

    //分部方法
    partial void Move();
}

partial class Student
{
    public int number;

    partial void Move()
    {
        //實現分部方法邏輯
        throw new NotImplementedException();
    }

    public void Speak(string str)
    {

    }
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • PowerPlume是PowerBuilder深度創新的擴展開發框架(免費商用)。 它的三個主要特色是一、原創功能;二、零改動相容(非侵入性);三、極簡介面設計。 ...
  • 記錄人生第一次重裝系統之後的數據恢復過程,包括桌面恢復、常用軟體下載和屬性修改、vscode插件、zotero數據恢復、onenote筆記數據恢復,讓重裝系統的你不用慌。 ...
  • 發文原因 很多初學者都使用 cargo new [project_name] 來創建項目,並直接在 main.rs 文件中實現所有功能。 這樣是不合理的,並不符合我們 cargo 的開發規範。 下麵將簡單的介紹一下 rust project 中的文件結構。 cargo new [project_na ...
  • 其他 1 命名空間 命名空間用來組織和重用代碼的,命名空間就像一個工具包,類就像工具。 1.1 使用 namespace MyGame { class GameObject { } } namespace MyGame//命名空間可以分開寫 { class Player : GameObject { ...
  • 多態 1 認識多態 1.1 基本概念 多態是同一個行為具有多個不同表現形式或形態的能力,意味著有多重形式。在面向對象編程範式中,多態性往往表現為"一個介面,多個功能"。 在 C# 中,每個類型都是多態的,因為包括用戶定義類型在內的所有類型都繼承自 Object。 多態性分為靜態的和動態多態。在靜態多 ...
  • 微軟發佈 C# async/await 非同步語法功能已經好久了,但是目前來看使用並不廣泛。本人經過實踐在開發過程中使用 async/await 一路到底確實很爽,而且也沒有啥問題。但是在面對舊項目變更要使用些功能的時候可能會遇到同步方法調用非同步方法的情況,本人在這種情況就發生調用沒有響應的問題,並作 ...
  • 繼承 繼承主要實現重用代碼,來節省開發時間。 1 繼承基本概念 一個類B繼承一個類A,被繼承的類A稱為 父類、基類、超類,繼承的類B稱為 子類、派生類。 子類會繼承父類的所有成員 子類擁有父類的所有特征和行為 子類可以有自己的特征行為 C#中允許子類和父類存在同名的成員,但不建議使用 特點: 單根性 ...
  • 本文主要介紹了.net7簡單使用NPOI讀取Excel表格。NPOI是指構建在POI 3.x版本之上的一個程式,NPOI可以在沒有安裝Office的情況下對Word或Excel文檔進行讀寫操作。NPOI這個老牌控制項不錯,只需要很少的代碼就可以實現,下麵是一步一步實現,希望對你有參考價值。 一、環境準 ...
一周排行
    -Advertisement-
    Play Games
  • 下麵是一個標準的IDistributedCache用例: public class SomeService(IDistributedCache cache) { public async Task<SomeInformation> GetSomeInformationAsync (string na ...
  • 這個庫提供了在啟動期間實例化已註冊的單例,而不是在首次使用它時實例化。 單例通常在首次使用時創建,這可能會導致響應傳入請求的延遲高於平時。在註冊時創建實例有助於防止第一次Request請求的SLA 以往我們要在註冊的時候實例單例可能會這樣寫: //註冊: services.AddSingleton< ...
  • 最近公司的很多項目都要改單點登錄了,不過大部分都還沒敲定,目前立刻要做的就只有一個比較老的項目 先改一個試試手,主要目標就是最短最快實現功能 首先因為要保留原登錄方式,所以頁面上的改動就是在原來登錄頁面下加一個SSO登錄入口 用超鏈接寫的入口,頁面改造後如下圖: 其中超鏈接的 href="Staff ...
  • Like運算符很好用,特別是它所提供的其中*、?這兩種通配符,在Windows文件系統和各類項目中運用非常廣泛。 但Like運算符僅在VB中支持,在C#中,如何實現呢? 以下是關於LikeString的四種實現方式,其中第四種為Regex正則表達式實現,且在.NET Standard 2.0及以上平... ...
  • 一:背景 1. 講故事 前些天有位朋友找到我,說他們的程式記憶體會偶發性暴漲,自己分析了下是非托管記憶體問題,讓我幫忙看下怎麼回事?哈哈,看到這個dump我還是非常有興趣的,居然還有這種游戲幣自助機類型的程式,下次去大玩家看看他們出幣的機器後端是不是C#寫的?由於dump是linux上的程式,剛好win ...
  • 前言 大家好,我是老馬。很高興遇到你。 我們為 java 開發者實現了 java 版本的 nginx https://github.com/houbb/nginx4j 如果你想知道 servlet 如何處理的,可以參考我的另一個項目: 手寫從零實現簡易版 tomcat minicat 手寫 ngin ...
  • 上一次的介紹,主要圍繞如何統一去捕獲異常,以及為每一種異常添加自己的Mapper實現,並且我們知道,當在ExceptionMapper中返回非200的Response,不支持application/json的響應類型,而是寫死的text/plain類型。 Filter為二方包異常手動捕獲 參考:ht ...
  • 大家好,我是R哥。 今天分享一個爽飛了的面試輔導 case: 這個杭州兄弟空窗期 1 個月+,面試了 6 家公司 0 Offer,不知道問題出在哪,難道是杭州的 IT 崩盤了麽? 報名面試輔導後,經過一個多月的輔導打磨,現在成功入職某上市公司,漲薪 30%+,955 工作制,不咋加班,還不捲。 其他 ...
  • 引入依賴 <!--Freemarker wls--> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.30</version> </dependency> ...
  • 你應如何運行程式 互動式命令模式 開始一個互動式會話 一般是在操作系統命令行下輸入python,且不帶任何參數 系統路徑 如果沒有設置系統的PATH環境變數來包括Python的安裝路徑,可能需要機器上Python可執行文件的完整路徑來代替python 運行的位置:代碼位置 不要輸入的內容:提示符和註 ...