C#學習筆記--面向對象三大特征

来源:https://www.cnblogs.com/TonyCode/archive/2023/10/12/17759266.html
-Advertisement-
Play Games

C#核心 面向對象--封裝 用程式來抽象現實世界,(萬物皆對象)來編程實現功能。 三大特性:封裝、繼承、多態。 類與對象 聲明位置:namespace中 樣式:class 類名{} 命名:帕斯卡命名法(首字母大寫) 實例化對象:根據類來新建一個對象。Person p=new Person(); 成員 ...


C#核心

面向對象--封裝

用程式來抽象現實世界,(萬物皆對象)來編程實現功能。

三大特性:封裝、繼承、多態。

類與對象

聲明位置:namespace

樣式:class 類名{}

命名:帕斯卡命名法(首字母大寫)

實例化對象:根據類來新建一個對象。Person p=new Person();

成員變數

  1. 聲明在類語句塊中
  2. 用來描述對象的特征
  3. 可以是任意變數類型
  4. 數量不做限制
  5. 是否賦值根據需求決定
enum E_SexType
{
    Man,
    Woman
}
struct Position{}//位置結構體
class Pet{}//寵物類
//類中的成員變數
class Person
{
    public string name="TonyChang";//區別於結構體--可以預設賦初始值
    public int age=21;
    public E_SexType sex;
    public Person bestFriend;//區別於結構體---類中可以有同類的成員類型(本質是因為類屬於引用類型,但不可以實例化,防止反覆new,陷入死迴圈)
    public Position pos;
    public Pet pet;
}

成員類型的預設值:

值類型的:數字的為0,bool類型的false

引用類型:null

查看(int類型)預設值:default(int)

補充:class屬於引用類型,其中的值類型也放置在中。

成員方法

  1. 聲明在類語句塊中
  2. 用來描述對象行為
  3. 其返回值參數不做限制
  4. 數量不做限制
  5. 帕斯卡命名法(首字母大寫)

成員方法只有在實例化之後才可以使用調用。具體的一個對象的行為(方法),必須具體的對象調用。

//成員方法
class Person
{
    public string name;
    public int age;
    public void Speak()
    {
        Console.WriteLine("你好!");
    }    
}

//成員方法的使用
Person p=new Person;
p.Speak();

構造函數和析構函數

預設有一個無參構造函數,而類中可以允許自己聲明無參構造函數,而結構體不行。

一旦有自定義的構造函數,預設的無參構造函數則失效!

構造函數:

  1. public修飾
  2. 無返回值,名字和類名相同
class Person
{
    public string name;
    public int age;
    //構造函數
    public Person()
    {
        name="TonyChang";
        age=21;
    }
    //此時先調用age參數的構造函數 然後再調用兩個參數的構造函數
    public Person(string name,int age):this(age)
    {
        this.name=name;
        this.age=age;
    }
    public Person(string name)
    {
       this.name=name;
    }
    public Person(int age)
    {
        this.age=age;
    }
}

特殊的構造函數,在調用該函數之前先調用this的無參構造函數。

public Person(int age):this()
{
this.age=age;
}

析構函數:

由於C#中有自動的垃圾回收機制,一般不使用析構函數。

析構函數是當垃圾真正被回收時候才會調用。

~Person(){}//析構函數

成員屬性:

用於保護成員變數,為成員屬性的獲取和賦值添加邏輯處理。

//成員屬性 帕斯卡命名法
class Person
{
    private string name;
    public string Name
    {
        get{
            return name;
        }
        set{
            name=value;
        }
    }
    private int age;
    public int Age
    {
        //不可以獲得年齡(或者刪除set設置則可表明也無法獲取age)
        private get=>age;
        //可以設置年齡
        set
        {
            age=value;
        }
    }
    //額外:
    //自動成員屬性 (對於沒有特殊需要的成員)
    public float Height
    {
        get;
        set;
    }
}

索引器

可以讓對象像數組一樣通過索引來訪問其中的元素。

註意:結構體中也支持索引器。

//索引器
class Person
{
    private string name;
    private int age;
    private Person[] friends;
    private int[,] arry;
    //索引器
    public Person this[int index]
    {
        get
        {
            return friends[index];
        }
        set
        {
            //此處可以寫一些控制邏輯
            friends[index]=value;
        }
    }
    //索引器的重載
    public int this[int i,int j]
    {
        get 
        {
            return array[i,j];
        }
        set
        {
            array[i,j]=value;
        }
    }
}
//使用
Person p=new Person();
p[0]=new Person();//可以像數組一樣進行訪問

靜態成員

static修飾的成員變數/方法為靜態成員。

靜態成員歸屬為類,即不用初始化就可以使用的類的成員。

靜態成員函數中不可以使用非靜態成員,非靜態成員函數中可以使用靜態成員函數。

本質:在程式運行開始時,檢查到靜態成員,會再特定的區域為其開闢記憶體空間來存儲。所以說靜態成員與程式共生死。因為生命周期不同,所以靜態成員函數中不可以使用非靜態成員。

使用:全局性,穩定不變的常量。例如固定的數值 Π,重力加速度g等包括固定的計算方法,可以供全局成員來訪問使用。但是靜態過多會占用記憶體,引發GC。

常量與靜態成員

相同點:都可以通過類名點來使用

不同點:

  1. const 必須初始化,不能修改,而static可以
  2. const只能修飾變數,而static可以修飾很多
  3. const一定是寫在訪問修飾符的後面,static則無此要求

靜態類與靜態構造函數

用 static修飾的類為靜態類,往往來作為工具類。例如System中的Console類。只能包含靜態成員,不能實例化。

靜態構造函數 :在靜態構造函數中初始化靜態成員變數。

  1. 靜態類和普通類中均可以有
  2. 不能使用訪問修飾符
  3. 不能有參數
  4. 只會自動調用一次
//靜態構造函數
static class Test
{
    public static int testInt=100;
    public static float testFloat=20.5f;
    static Test()
    {
        //靜態構造函數
        Console.WriteLine("自動調用了!");
    }
}
class NormalTest
{
    public static int i=5;
    //首次使用靜態成員時候 自動調用一次
    //靜態成員函數
    static NormalTest()
    {
        Console.WriteLine("靜態構造函數");
    }
    public NormalTest()
    {
        Console.WriteLine("非靜態成員函數");
    }
}

拓展方法

拓展方法為現有的非靜態變數類型添加新方法。

作用:

  1. 提升程式的拓展性
  2. 不需要再對對象中重新寫方法
  3. 不需要繼承來添加方法
  4. 為別人封裝的類型添加額外的方法

特點:

  1. 一定寫在靜態類中
  2. 一定是個靜態函數
  3. 第一個參數為拓展目標(為誰而拓展)
  4. 第一個參數用this修飾
//拓展方法
static class expendTool
{
    //為int添加拓展方法
    public static void SpeakValue(this int value)
    {
        Console.WriteLine("這是int的拓展方法,int的數值為{0}",value);
    }
    //為string拓展方法
    public static void SpeakStringInfo(this string str,string name,string info)
    {
         Console.WriteLine("這是string的拓展方法,string的數值為{0},該拓展方法由{1}編寫,拓展內容為{2}",str,name,info);
    }
}
class Program
{
    static void Main(string[] args)
    {
        int i = 5;
        i.SpeakValue();
        string ss = "原始字元串";
        ss.SpeakStringInfo("TonyChang", "附加字元串");
    }
}

註意:如果拓展方法名稱與自身現有的方法名稱相同,則只會調用自身的方法,不會調用拓展的方法。

運算符重載

關鍵字 operator

特點:1. 一定是一個公共的靜態成員方法

  1. 返回值寫在operator前
  2. 邏輯處理自定義

作用:可以使自定義的數據類型來完成後相同意義的運算。

註意:

  1. 條件運算符要成對實現(有>必須有<)
  2. 一個符號可以有多個重載
  3. 不能使用ref與out
//運算符重載
class Point
{
    public int x;
    public int y;
    public Point(int x,int y)
    {
        this.x=x;
        this.y=y;
    }
    
    //重載+運算符
    //參數列表中必須要有自己的類別出現
    public static Point operator +(Point p1,Point p2)
    {
        Point sum=new Point();
        sum.x=p1.x+p2.x;
        sum.y=p1.y+p2.y;
        return sum;
    }
}
class Program
{
    static void Main(string[] args)
    {
        Point p1=new Point(1,1);
        Point p2=new Point(2,2);
        Point p3=P1+p2;
        Console.WriteLine(p3.x);
    }
}

補充:大部運算符可以重載,邏輯運算符中只可以允許重載 邏輯非!

不能重載的運算符有:

&& || 索引符[] 強制轉換符號() 特殊運算符 點. 三目運算符?:

*內部類和分部類(瞭解)

//內部類
class Person
{
    public class Body
    {
        class Arm
        {
            
        }
    }
}
class Program
{
    static void Main(string[] args)
    {
       Person.Body body=new Person.Body();
    }
}
//分佈類
//分佈類可以分佈在不同的腳本文件中
//分佈類的訪問修飾符要一致
partial class Student
{
    public string name;
    public bool sex;
    partial void Speak();
}
partial class Student
{
    public int age;
    public string stuId;
    partial void Speak()
    {
        Console.WriteLine("分佈方法的具體實現");
    }
}

垃圾回收機制

垃圾回收,英文簡稱GC(Garbage Collector)

垃圾回收過程:遍歷堆(Heap)上的動態分配的所有對象

通過識別它們是否被引用來確定其是否是垃圾。垃圾是指沒有引用所指引的對象、變數,需要被回收釋放掉占用的記憶體空間。

垃圾回收演算法:

引用計數、標記清除、標記整理、複製集合。

註意:垃圾回收只回收heap堆上的 棧中的記憶體由系統管理

回收機制: 三代記憶體

0代記憶體 1代記憶體 2代記憶體

  1. 每一代記憶體滿掉之後便會清理垃圾

  2. 高代連鎖:1代清理會連帶0代清理,2代清理連帶0代和1代

  3. 清理完垃圾之後,非垃圾內容搬遷到下一代中(0代將非垃圾轉移到1代記憶體,

    1代記憶體將非垃圾轉移到2代記憶體)所以2代記憶體存儲的較為老的對象實例,還包括大的對象

    一般是85kb以上的對象

  4. 0代1代的讀取速度要高於1代,分配記憶體位置優先0代>1代>2代

手動GC:

GC.Collect();

一般在場景載入時候進行GC。


面向對象--繼承

繼承者(子類)繼承父類(基類、超類)的特性,同時也可以有自己獨特的方法性質。

只能單繼承。子類只能由一個父類。

繼承特性:單根性、傳遞性。

//繼承
//老師類
class Teacher
{
    //姓名
    public string name;
    //職工號
    protected int number;
    //介紹名字
    public void SpeakName()
    {
        number = 10;
        Console.WriteLine(name);
    }
} 
//教學老師繼承老師類
class TeachingTeacher : Teacher
{
    //科目
    public string subject;
    //介紹科目
    public void SpeakSubject()
    {
        number = 11;
        Console.WriteLine(subject + "老師");
    }
}
//語文老師繼承教學老師類
class ChineseTeacher:TeachingTeacher
{
    public void Skill()
    {
        Console.WriteLine("一行白鷺上青天");
    }
}
 class Program
{
    static void Main(string[] args)
    {
        TeachingTeacher tt = new TeachingTeacher();
        tt.name = "汪老師";
        //tt.number = 1;
        tt.SpeakName();

        tt.subject = "Unity";
        tt.SpeakSubject();

        ChineseTeacher ct = new ChineseTeacher();
        ct.name = "張老師";
        //ct.number = 2;
        ct.subject = "語文";
        ct.SpeakName();
        ct.SpeakSubject();
        ct.Skill();
    }
}

里氏替換原則

父類容器裝在子類對象。(任何父類出現的地方,子類都可以替代)

class GameObject
{

}
class Player:GameObject
{
    public void PlayerAtk()
    {
        Console.WriteLine("玩家攻擊");
    }
}

class Monster:GameObject
{
    public void MonsterAtk()
    {
        Console.WriteLine("怪物攻擊");
    }
}

class Boss:GameObject
{
    public void BossAtk()
    {
        Console.WriteLine("Boss攻擊");
    }
}
class Program
{
    static void Main(string[] args)
    {
        //里氏替換原則
        Gameobjet player=new Player();
        Gameobjet monster=new Monster();
        Gameobjet boss=new Boss();
        //is 和 as
        if(player is Player)
        {
            (player as Player).PlayerAtk();
        }
    }
}

is和as

is是判斷一個對象是否為指定類型對象,返回值為true則為真,不是則為false

as用來將一個對象轉換為指定類型對象,返回值為指定類型對象,若轉換失敗則返回null

繼承中的構造函數

子類構造函數調用之前,先執行父類的構造函數。(爺爺-->父親-->子類)

所以要保證父類的構造函數(尤其為無參構造函數)

  1. 保證父類的無參構造函數
  2. 通過base調用指定的有參構造函數
//繼承中的構造函數
class Father
{
    //父類的無參構造函數很重要!
    public Father()
    {
        
    }
    public Father(int i)
    {
        Console.WriteLine("Father的有參構造");
    }
}
class Son:Father
{
    public Son(int i):base(i)
    {
        //構造函數
    }
}

萬物之父--裝箱和拆箱

object 是所有類型的基類,

作用:

  1. 可以利用里氏替換原則,用父類裝子類
  2. 可以用來表示不確定類型,作為函數的參數類型

裝箱:

用object存值類型。本該在棧中數值轉移到堆上

拆箱

將object轉換為值類型,將堆上的值類型轉移到棧上(配合 is和as 使用)

優點:統一對象類型(里氏替換原則),方便對不同類型對象數值的管理

缺點:消耗性能,

//裝箱拆箱
int i=5;
object obj=i;//裝箱
i=(int)obj;//拆箱

*密封類(瞭解)

使用sealed關鍵字修飾的類,不可以被派生。(結扎了!)

面向對象--多態

V: virtual(虛函數)

O: override(重寫)

B: base(父類)

讓繼承同一父類的子類們在執行相同方法有不同的表現與狀態。

就是說,繼承是一脈相承父類的品質,而多態是由自己的個性,儘管做的和父輩的事情相同。

解決的問題:

class Father
{
    public void SpeakName()
    {
        Console.WriteLine("Father的方法");
    }
}
class Son:Father
{
    public new void SpeakName()
    {
        Console.WriteLine("Son的方法");
    }
}
class Program
{
    static void Main(string[] args)
    {
        #region 解決的問題
        Father f = new Son();
        f.SpeakName();//調用的是父親的方法
        (f as Son).SpeakName();//調用的是兒子的方法
        #endregion
    }
}

使用多態來保證(繼承類)一個類方法的獨立性

class GameObject
{
    public string name;
    public GameObject(string name)
    {
        this.name = name;
    }

    //虛函數 可以被子類重寫
    public virtual void Atk()
    {
        Console.WriteLine("游戲對象進行攻擊");
    }
}

class Player:GameObject
{
    public Player(string name):base(name)
    {

    }

    //重寫虛函數
    public override void Atk()
    {
        //base的作用
        //代表父類 可以通過base來保留父類的行為
        base.Atk();
        Console.WriteLine("玩家對象進行攻擊");
    }
}

抽象類與抽象方法

抽象類不可以被實例化

abstract class Thing{

public string name;

}

抽象方法:沒有方法體的純虛方法,繼承的子類必須實現純虛方法。(子類必須重寫該方法,子類的子類不必強制實行,但也可以繼續重寫。)

抽象方法與virtual(虛函數)方法區別:

  1. 抽象方法只能在抽象類中出現,沒有方法體,子類必須重寫實現
  2. 虛函數則有方法體,可在普通類中出現,由子類選擇性的實現
abstract class Fruits
{
    public string name;

    //抽象方法 是一定不能有函數體的
    public abstract void Bad();

    public virtual void Test()
    {
        //可以選擇是否寫邏輯
    }
}
class Apple : Fruits
{
    public override void Bad()
    {

    }
    //虛方法是可以由我們子類選擇性來實現的
    //抽象方法必須要實現
}

介面(重要)

概念:介面是行為的抽象規範

關鍵字:interface

聲明規範:

  1. 不能包含成員變數
  2. 只能包含方法、屬性、索引器、事件
  3. 成員不能被實現
  4. 成員可以不用寫訪問修飾符,預設為public,不能是private
  5. 介面不能繼承類,但是可以繼承另一個介面

使用規範:

  1. 類可以繼承多個介面
  2. 類繼承介面,必須實現介面中所有成員

特點:

  1. 和類的聲明相似
  2. 介面就是用來繼承的
  3. 介面不能被實例化,可以作為容器存儲對象(里氏替換原則,父類裝子類)

介面是抽象行為的”基類“

//介面的聲明
//命名規範 I+帕斯卡命名法
interface IFly
{
    void Fly();//方法
    string Name//屬性
    {
        get;
        set;
    }
    int this[int index]//索引器
    {
        get;
        set;
    }
    event Action doSomthing;//事件委托
}

介面的使用---類的繼承

  1. 一個類只能繼承一個基類,但是可以繼承多個介面
  2. 繼承介面之後,必須實現其中的內容
//介面的使用
class Animal
{
    
}
class Person:Animal,IFly
{
    //實現介面方法也可以加virtual來實現
    public virtual void Fly()
    {
        
    }
    public string Name
    {
        set;
        get;
    }
    public int this[int index]
    {
        get
        {
            return 0;
        }
        set
        {
            
        }
    }
    public event Action doSomething;
}

介面的使用---介面的繼承

介面繼承基類介面之後,不需要實現介面中的內容(抽象繼承抽象,還是抽象)

等到最後類來具體實現

//介面繼承介面
interface IWalk
{
    void Walk();
}
interface IMove:IFly,IMove
{
    
}
//必須實現所有相關的
//繼承來的抽象內容(介面,介面的父介面中的成員)
class Test:IMove
{
    
       public int this[int index] { 
           get => throw new NotImplementedException(); 
           set => throw new NotImplementedException(); 
       }

        public string Name { 
            get => throw new NotImplementedException(); 
            set => throw new NotImplementedException();
        }

        public event Action doSomthing;

        public void Fly()
        {
            throw new NotImplementedException();
        }

        public void Walk()
        {
            throw new NotImplementedException();
        }
}

顯示實現介面

//介面的使用--當作容器 父類裝子類
 interface IAtk
{
    void Atk();
}

interface ISuperAtk
{
    void Atk();
}
//顯示實現介面
class Player : IAtk, ISuperAtk
{
    //遇到相同方法名字
    //顯示實現介面 就是用 介面名.行為名 去實現
    void IAtk.Atk()
    {

    }

    void ISuperAtk.Atk()
    {

    }

    public void Atk()
    {

    }
}
class Progarm
{
    static void Main(string[] args)
    {
        IFly f = new Person();
		//里氏替換原則
        IMove im = new Test();
        IFly ifly = new Test();
        IWalk iw = new Test();

        IAtk ia = new Player();
        ISuperAtk isa = new Player();
        ia.Atk();
        isa.Atk();

        Player p = new Player();
        (p as IAtk).Atk();//IAtk的
        (p as ISuperAtk).Atk();//ISuperAtk的Atk
         p.Atk();//自己的Atk
    }
}

*密封方法(瞭解)

sealed 修飾的重寫方法,子類不會被重寫方法


其它關聯知識點

命名空間namespace

  1. 命名空間是個工具包,用來管理類
  2. 不同命名空間中允許由同名類
  3. 不同命名空間相互使用時,using引用命名空間 或者指明出處
  4. 命名空間可以包裹命名空間

萬物之父Object中的方法

string

  1. string 本質是char[]數組 可以有

    string ss="Tony",char[0]='T'

  2. 字元串的拼接

  3. 正向查找字元的位置 IndexOf()

  4. 反向查找字元串的位置 LastIndexOf()

  5. 移除指定位置後的字元 Remove(index)//註意接受返回值

  6. 字元串的替換

  7. 大小寫轉換

  8. 字元串的截取

  9. 字元串的切割 str.Split(',');按照,切割

StringBuilder

字元串頻繁拼接使用StringBuilder,不會再次頻繁的創建新的對象,減少垃圾產生。

容量問題:初始時候本身有一定容量,在容量允許範圍內,直接存儲。

超過容量之後,會以2倍大小擴容。相對於string每一次更改便會新建對象,可減少垃圾產生。

//StringBuilder
StringBuilder str=new StringBuilder("My Name is Tony");
//獲取容量
str.Capacity;
//增加
str.Append("Chang");
str.AppendFormat("{0}{1}",123,456);
//插入
str.Insert(0,"Hello");
//刪除
str.Remove(0,10);
//清空
str.Clear();
//查
str[1];
//替換
str.Replace("Name"."name");
//重新賦值
str.Clear();
str.Append("Hello World");
//equals
if(str.Equals("123456"))
{
    
}

String 還是StringBuilder?

String的方法種類較多,使用更加方便和靈活,但性能上不如StringBuilder,不如StringBuilder產生垃圾少

需要頻繁修改的字元串選用StringBuilder更好些。

如何優化記憶體?

  1. 節約記憶體
    1. 少new對象 少產生垃圾
    2. 合理使用static
    3. 合理使用String與StringBuilder
  2. 減少GC產生

結構體與類的區別

  1. 存儲位置 結構體是值類型,存儲在棧中 類是引用類型,存儲在堆中
  2. 結構體中的成員變數不可以賦初始值,類中可以
  3. 結構體具備封裝特性但是不具備繼承和多態 而類都具有
  4. 結構體不具備繼承特性,所以不可以使用protected保護修飾符修飾
  5. 結構體聲明有參構造之後,無參構造不會被頂掉
  6. 結構體不能聲明析構函數,而類可以
  7. 結構體需要在構造函數中初始化所有成員變數,而類隨意
  8. 結構體不能被static修飾,不存在靜態的結構體,而類隨意
  9. 結構體不能在內部聲明與自己一樣的結構體變數(會無限創建...棧溢出),類可以(因為是引用)
  10. 結構體可以繼承介面(不可以繼承類、結構體)

如何選擇結構體和類:

  1. 如果想要用繼承和多態時,直接淘汰結構體,如玩家、怪物
  2. 存儲的對象是數據的集合時候,優先考慮結構體,如向量、坐標等
  3. 從本質上考慮,如果經常要改變賦值對象,原有的數值不想跟著改變的,選用結構體(值類型,複製拷貝,不影響本身),如坐標、向量、旋轉角等

抽象類與介面的區別

相同點:

  1. 都可以被繼承
  2. 都不可以直接實例化
  3. 都可以包含方法的聲明
  4. 子類必須實現未實現的方法
  5. 都遵循里氏替換原則(父類裝子類)

區別:

  1. 抽象類中可以有構造函數,而介面不可以
  2. 抽象類只能被單一繼承,介面可以被繼承多個
  3. 抽象類中可以有成員變數,介面中不能
  4. 抽象類中可以有聲明成員方法,虛方法,抽象方法,靜態方法而介面中只能聲明沒有實現的抽象方法
  5. 抽象類方法可以使用訪問修飾符;介面中建議不寫,預設為public

如何選擇抽象類與介面

表示對象的選用抽象類,表示行為拓展的用介面。不同對象的相同行為,我們可以抽象出行為,用介面來實現。

面向對象的七大原則

總目標:高內聚、低耦合

減少類內部對其它類的調用,減少模塊與模塊之間的交互複雜度。

  1. 單一職責原則(一個類專註於一個功能)
  2. 里氏替換原則(父類可以裝子類)
  3. 開閉原則(對拓展開放,對修改關閉,要保持開放和擴展,減少修改)
  4. 依賴倒轉原則(依賴於抽象,不依賴於抽象具體)
  5. 迪米特原則(最少知識原則,不要和陌生人說話)
  6. 介面隔離原則(一個介面不應該提供過多的功能,)
  7. 合成復用原則(儘量使用組合復用實現功能,減少繼承高耦合行為)

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

-Advertisement-
Play Games
更多相關文章
  • BeanPostProcessor後置處理器是Spring提供的一個擴展點,可以在Bean初始化前後做一些事情,註意這裡是bean的初始化,不是實例化,BeanPostProcessor是一個介面,裡面提供了兩個方法,分別為postProcessBeforeInitialization(初始化之前) ...
  • 之前,我們通過一系列文章,介紹瞭如何在Spring Boot中發送郵件: 發送郵件 添加附件 引用靜態資源 郵件模版 已經包含了大部分的應用場景。但最近DD在做YouTube中文配音的時候,碰到一個問題: 如上圖所示,收件人在客戶端收到的時候,顯示的名稱是郵箱的首碼,而不是我們的產品名稱,也就是郵箱 ...
  • 1. 依賴傳遞 在Maven中,依賴是會傳遞的,假如在業務項目中引入了spring-boot-starter-web依賴: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter ...
  • 百分比堆疊式柱狀圖是一種特殊的柱狀圖,它的每根柱子是等長的,總額為100%。柱子內部被分割為多個部分,高度由該部分占總體的百分比決定。 百分比堆疊式柱狀圖不顯示數據的“絕對數值”,而是顯示“相對比例”。但同時,它也仍然具有柱狀圖的固有功能,即“比較”——我們可以通過比較多個柱子的構成,分析數值之間的 ...
  • 基於java流浪動物領養系統設計與實現,可適用於流浪動物救助及領養管理系統,寵物教學、領養寵物、寵物認領、領養申請、動物認領信息,動物申請認領等等,流浪寵物救助系統; ...
  • 但近期部署系統和自己的開發項目時,發現debian系統安裝框架還是非常方便,我就把自己整理的常規部署命令貼出來,希望對大家有用處,基本對.NET CORE的開發者足夠用了 ...
  • 不知道大家有沒有瞭解FreeSSL,我近期發展這個網站 https://freessl.cn,如果你們都是個人站點,對證書沒什麼特別要求,可以考慮用他們家生成的證書,並且能自動續費,完全不用操心。 ...
  • 一 .netframework程式遷移到.netcore5.0對於.netframwork程式想要升級為.netcore5.0的方法,微軟官方也給出了方法見 https://docs.microsoft.com/en-us/dotnet/desktop/winforms/migration/?vie ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...