第十一單元 面向對象三:繼承與多態

来源:https://www.cnblogs.com/xuyubing/archive/2023/05/30/17443011.html
-Advertisement-
Play Games

假設老師類設計如下: class 老師類 { 屬性:姓名,性別,生日,工資 行為:吃飯,跑步,教學 } 學生類設計如下: class 老師類 { 屬性:姓名,性別,生日,班級 行為:吃飯,跑步,學習 } 我們秉承著,讓最簡潔的代碼,實現最最強大的功能原則,能否讓上述案例中的重覆代碼進行優化呢?我們能 ...


假設老師類設計如下:

class 老師類
{
    屬性:姓名,性別,生日,工資
    行為:吃飯,跑步,教學
}

學生類設計如下:

class 老師類
{
    屬性:姓名,性別,生日,班級
    行為:吃飯,跑步,學習
}

  

我們秉承著,讓最簡潔的代碼,實現最最強大的功能原則,能否讓上述案例中的重覆代碼進行優化呢?我們能否將學生類與老師類再進行抽象,得到一個人類?這章節學習繼承與多態。

1. 繼承

繼承是面向對象程式設計中最重要的概念之一。繼承允許我們根據一個類來定義另一個類,這使得創建和維護應用程式變得更容易。同時也有利於重用代碼和節省開發時間。

當創建一個類時,程式員不需要完全重新編寫新的數據成員和成員函數,只需要設計一個新的類,繼承了已有的類的成員即可。這個已有的類被稱為的基類,這個新的類被稱為派生類

繼承的思想實現了 屬於(IS-A) 關係。例如,哺乳動物 屬於(IS-A) 動物,狗 屬於(IS-A) 哺乳動物,因此狗 屬於(IS-A) 動物。

基類和派生類

一個類可以派生自多個類或介面,這意味著它可以從多個基類或介面繼承數據和函數。

C# 中創建派生類的語法如下:

<訪問修飾符> class <基類>
{
 ...
}
class <派生類> : <基類>
{
 ...
}

  

現在我們將上述學生類,老師類案例使用繼承,進行代碼優化:

// 把公共的屬性與方法提取出來,封裝成父類
public class Person
{
    public string Name { get; set; }
    public string Sex { get; set; }
    public DateTime Birthday { get; set; }
    
    public void Eat()
    {
        ....
    }
    
    public void Run()
    {
        
    }
}

// 老師類設計如下:
public class Teacher : Person // 繼承Person
{
    public int Salary { get; set;}
    
    // 教學方法
    public void Teach()
    {
        ...
    }
}

// 學生類設計如下
public class Student : Person
{
    public string ClassName { get; set; }
    
    // 學習方法
    public void Study()
    {
        ...
    }
}

 

基類的初始化

派生類繼承了基類的成員變數和成員方法。因此父類對象應在子類對象創建之前被創建。您可以在成員初始化列表中進行父類的初始化。

下麵的程式演示了這點:

class Rectangle
{
    // 成員變數
    protected double length;
    protected double width;
    public Rectangle(double l, double w)
    {
        length = l;
        width = w;
    }
    public double GetArea()
    {
        return length * width;
    }
    public void Display()
    {
        Console.WriteLine("長度: {0}", length);
        Console.WriteLine("寬度: {0}", width);
        Console.WriteLine("面積: {0}", GetArea());
    }
}//end class Rectangle  
class Tabletop : Rectangle
{
    private double cost;
    public Tabletop(double l, double w) : base(l, w)
    { }
    public double GetCost()
    {
        double cost;
        cost = GetArea() * 70;
        return cost;
    }
    public void Display()
    {
        base.Display();
        Console.WriteLine("成本: {0}", GetCost());
    }
}
class Program
{
    static void Main(string[] args)
    {
        Tabletop t = new Tabletop(4.5, 7.5);
        t.Display();
        Console.ReadLine();
    }
}

當上面的代碼被編譯和執行時,它會產生下列結果:

長度: 4.5
寬度: 7.5
面積: 33.75
成本: 2362.5

註意

  1. C# 中 不支持多繼承基類:一個類只能繼承一個直接父類。

  2. 繼承需要滿足什麼樣的設計規範?

    • 子類們相同特征(共性屬性,共性方法)放在父類中定義。

    • 子類獨有的的屬性和行為應該定義在子類自己裡面。

  3. 所有的類都是Object類的子類。

  4. 子類可以繼承父類的屬性和行為(除靜態屬性和靜態方法外),但是子類不能繼承父類的構造器(如果非要繼承,需要額外的寫代碼)。

2. 方法重寫

什麼是重寫?

“重寫”父類方法就是修改它的實現方式或者說在子類中對它進行重新編寫。

為什麼要重寫父類的方法

通常,子類繼承父類的方法,在調用對象繼承方法的時候,調用和執行的是父類的實現。但是,有時候需要對子類中的繼承方法有不同的實現方式。例如,假設動物存在“跑”的方法,從中繼承有狗類和馬類兩個子類,狗與馬的奔跑速度或者動作都不太一樣。

如何重寫

  • 重寫父類的方法要用到override關鍵字(具有override關鍵字修飾的方法是對父類中同名方法的新實現)

  • 要重寫父類的方法,前提是父類中該要被重寫的方法必須聲明為virtual或者是abstract類型。給父類中要被重寫的方法添加virtual關鍵字表示可以在子類中重寫它的實現。(註意:C#中的方法預設並不是virtual類型的因此要添加virtual關鍵字才能夠被重寫)

  • virtual關鍵字用於將方法定義為支持多態,有virtual關鍵字修飾的方法稱為“虛擬方法”

聲明虛方法

[訪問修飾符] virtual [返回類型] 方法名(參數列表)

{

	//虛擬方法的實現,該方法可以被子類重寫

}

 

class Employee
{
    public virtual void EmpInfo()
    {
        Console.WriteLine("用virtual關鍵字修飾的方法是虛擬方法");
    }
}
class DervEmployee : Employee
{
    public override void EmpInfo()
    {
        base.EmpInfo();//base關鍵字將在下麵拓展中提到
        Console.WriteLine("該方法重寫base方法");
    }
}
class Program
{
    static void Main(string[] args)
    {
        DervEmployee objDervEmployee = new DervEmployee();
        objDervEmployee.EmpInfo();
        //註意:objDervEmployee派生類的實例是賦給Employee類的objEmployee的引用,
        // 所以objEmployee引用調用EmpInfo()方法時 還是調用DervEmployee類的方法
        Employee objEmployee = objDervEmployee;
        objEmployee.EmpInfo();
    }
}

 

base關鍵字用於從子類中訪問父類成員。即使父類的方法在子類中重寫,仍可以使用base關鍵字調用。

而且,在創建子類實例時,可以使用base關鍵字調用父類的構造函數。使用base關鍵字只能訪問父類的構造函數、實例方法或實例屬性,而不能訪問基類的靜態方法。

隱藏父類方法

如果父類與子類具有相同的方法,然後父方法並沒有加virtual 關鍵字修飾。此時,子類需要用 new 關鍵字進行隱藏父類的方法.


 

3. 抽象類

使用 abstract 關鍵字可以創建不完整且必須在派生類中實現的類和 class 成員。

public abstract class A
{
    // Class members here.
}

 

抽象類不能實例化。 抽象類的用途是提供一個可供多個派生類共用的通用基類定義。 例如,類庫可以定義一個抽象類,將其用作多個類庫函數的參數,並要求使用該庫的程式員通過創建派生類來提供自己的類實現。

抽象類也可以定義抽象方法。 方法是將關鍵字 abstract 添加到方法的返回類型的前面。 例如:

public abstract class A
{
    public abstract void DoWork(int i);
}

 

抽象方法沒有實現,所以方法定義後面是分號,而不是常規的方法塊。 抽象類的派生類必須實現所有抽象方法。 當抽象類從基類繼承虛方法時,抽象類可以使用抽象方法重寫該虛方法。 例如:

public class D
{
    public virtual void DoWork(int i)
    {
        // Original implementation.
    }
}

public abstract class E : D
{
    public abstract override void DoWork(int i);
}

public class F : E
{
    public override void DoWork(int i)
    {
        // New implementation.
    }
}

 

如果將 virtual 方法聲明為 abstract,則該方法對於從抽象類繼承的所有類而言仍然是虛方法。 繼承抽象方法的類無法訪問方法的原始實現,因此在上一示例中,類 F 上的 DoWork 無法調用類 D 上的 DoWork。通過這種方式,抽象類可強制派生類向虛擬方法提供新的方法實現。

4. 密封類

使用 sealed 關鍵字可以防止繼承以前標記為 virtual 的類或某些類成員。

public sealed class D
{
    // Class members here.
}

  

密封類不能用作基類。 因此,它也不能是抽象類。 密封類禁止派生。 由於密封類從不用作基類,所以有些運行時優化可以略微提高密封類成員的調用速度。

在對基類的虛成員進行重寫的派生類上,方法、索引器、屬性或事件可以將該成員聲明為密封成員。 在用於以後的派生類時,這將取消成員的虛效果。 方法是在類成員聲明中將 sealed 關鍵字置於 sealed 關鍵字前面。 例如:

public class D : C
{
    public sealed override void DoWork() { }
}

 

5. 介面

介面定義了所有類繼承介面時應遵循的語法合同。介面定義了語法合同 "是什麼" 部分,派生類定義了語法合同 "怎麼做" 部分。

介面定義了屬性、方法和事件,這些都是介面的成員。介面只包含了成員的聲明(c#8.0以外,介面也可以有預設實現)。成員的定義是派生類的責任。介面提供了派生類應遵循的標準結構。

介面使得實現介面的類或結構在形式上保持一致。

抽象類在某種程度上與介面類似,但是,它們大多只是用在當只有少數方法由基類聲明由派生類實現時。

介面本身並不實現任何功能,它只是和聲明實現該介面的對象訂立一個必須實現哪些行為的契約。

抽象類不能直接實例化,但允許派生出具體的,具有實際功能的類。

 

高內聚,低耦合:儘量依賴於介面,不依賴於實現,正所謂面向介面編程。說白了,就是為瞭解耦。

定義介面

 

介面使用 interface 關鍵字聲明,它與類的聲明類似。介面聲明預設是 public 的。下麵是一個介面聲明的實例:

interface IMyInterface
{
    void MethodToImplement();
}

  

以上代碼定義了介面 IMyInterface。通常介面命令以 I 字母開頭,這個介面只有一個方MethodToImplement(),沒有參數和返回值,當然我們可以按照需求設置參數和返回值。值得註意的是,該方法並沒有具體的實現。

實現介面

class InterfaceImplementer : IMyInterface
{
    public void MethodToImplement()
    {
        Console.WriteLine("MethodToImplement() called.");
    }
}

class Program
{
     static void Main()
    {
        InterfaceImplementer iImp = new InterfaceImplementer();
        iImp.MethodToImplement();
    }
}

 

InterfaceImplementer類實現了 IMyInterface 介面,介面的實現與類的繼承語法格式類似:

class InterfaceImplementer : IMyInterface

繼承介面後,我們需要實現介面的方法 MethodToImplement(), 方法名必須與介面定義的方法名一致。

介面繼承

以下實例定義了兩個介面 IMyInterface 和 IParentInterface。

如果一個介面繼承其他介面,那麼實現類或結構就需要實現所有介面的成員。

以下實例IMyInterface 繼承了 IParentInterface 介面,因此介面實現類必須實現 MethodToImplement()ParentInterfaceMethod()方法:

interface IParentInterface
{
    void ParentInterfaceMethod();
}

interface IMyInterface : IParentInterface
{
    void MethodToImplement();
}

class InterfaceImplementer : IMyInterface
{
    public void MethodToImplement()
    {
        Console.WriteLine("MethodToImplement() called.");
    }

    public void ParentInterfaceMethod()
    {
        Console.WriteLine("ParentInterfaceMethod() called.");
    }
}

class Program
{
    static void Main()
    {
        InterfaceImplementer iImp = new InterfaceImplementer();
        iImp.MethodToImplement();
        iImp.ParentInterfaceMethod();
    }
}

 

顯式介面實現

如果一個實現的兩個介面包含簽名相同的成員,則在該類上實現此成員會導致這兩個介面將此成員用作其實現。 如下示例中,所有對 Paint 的調用皆調用同一方法。 第一個示例定義類型:

public interface IControl
{
    void Paint();
}
public interface ISurface
{
    void Paint();
}
public class SampleClass : IControl, ISurface
{
    // Both ISurface.Paint and IControl.Paint call this method.
    public void Paint()
    {
        Console.WriteLine("Paint method in SampleClass");
    }
}
class Program
{
    static void Main(string[] args)
    {
        SampleClass sample = new SampleClass();
        IControl control = sample;
        ISurface surface = sample;

        // The following lines all call the same method.
        sample.Paint();
        control.Paint();
        surface.Paint();
    }
}

輸入結果如下:

Paint method in SampleClass
Paint method in SampleClass
Paint method in SampleClass

但你可能不希望為這兩個介面都調用相同的實現。 若要調用不同的實現,根據所使用的介面,可以顯式實現介面成員。 顯式介面實現是一個類成員,只通過指定介面進行調用。 通過在類成員前面加上介面名稱和句點可命名該類成員。 例如:

public class SampleClass : IControl, ISurface
{
    void IControl.Paint()
    {
        System.Console.WriteLine("IControl.Paint");
    }
    void ISurface.Paint()
    {
        System.Console.WriteLine("ISurface.Paint");
    }
}

class Program
{
    static void Main(string[] args)
    {
        SampleClass sample = new SampleClass();
        IControl control = sample;
        ISurface surface = sample;

       
        // sample.Paint(); // 此行報錯,因為顯示介面實現的方法不能直接通過類調用
        control.Paint();
        surface.Paint(); // 顯示介面實現,只能通過介面調用方法
    }
}

輸入結果:

IControl.Paint
ISurface.Paint

  

C# 8.0 開始,你可以為在介面中聲明的成員定義一個實現。 如果類從介面繼承方法實現,則只能通過介面類型的引用訪問該方法。 繼承的成員不會顯示為公共介面的一部分。 下麵的示例定義介面方法的預設實現:

interface IAnimal
{
    void Roar()
    {
        Console.WriteLine("動物在叫");
    }
}
class Tigger : IAnimal
{
    // 因為Roar()已經有預設實現,則可以不必強制實現,如果實現了,則介面預設實現失效
    //public void Roar()
    //{
    //    Console.WriteLine("老虎在吼");
    //}
}
class Program
{
    static void Main(string[] args)
    {
        IAnimal animal = new Tigger();

        animal.Roar();
    }
}

6. 多態

多態是同一個行為具有多個不同表現形式或形態的能力。

多態性意味著有多重形式。在面向對象編程範式中,多態性往往表現為"一個介面,多個功能"。

多態性可以是靜態的或動態的。在靜態多態性中,函數的響應是在編譯時發生的。在動態多態性中,函數的響應是在運行時發生的。

在 C# 中,每個類型都是多態的,因為包括用戶定義類型在內的所有類型都繼承自 Object。

多態就是同一個介面,使用不同的實例而執行不同操作,如圖所示:

img

現實中,比如我們按下 F1 鍵這個動作:

  • 如果當前在 Flash 界面下彈出的就是 AS 3 的幫助文檔;

  • 如果當前在 Word 下彈出的就是 Word 幫助;

  • 在 Windows 下彈出的就是 Windows 幫助和支持。

同一個事件發生在不同的對象上會產生不同的結果。

靜態多態性

在編譯時,函數和對象的連接機制被稱為早期綁定,也被稱為靜態綁定。C# 提供了兩種技術來實現靜態多態性。分別為:

  • 方法重載

  • 運算符重載

運算符重載 基本上用不上,本教案中不給予講解。

方法重載

您可以在同一個範圍內對相同的函數名有多個定義。函數的定義必須彼此不同,可以是參數列表中的參數類型不同,也可以是參數個數不同。不能重載只有返回類型不同的函數聲明。

下麵的實例演示了幾個相同的函數 Add(),用於對不同個數參數進行相加處理:

public class MyMath  
{  
    public int Add(int a, int b, int c)  
    {  
        return a + b + c;  
    }  
    public int Add(int a, int b)  
    {  
        return a + b;  
    }  
}  
class Program  
{  
    static void Main(string[] args)  
    {  
        MyMath dataClass = new MyMath();
        int add1 = dataClass.Add(1, 2);  
        int add2 = dataClass.Add(1, 2, 3);

        Console.WriteLine("add1 :" + add1);
        Console.WriteLine("add2 :" + add2);  
    }  
}  

 

下麵的實例演示了幾個相同的函數 print(),用於列印不同的數據類型:

class Printdata
{
    void print(int i)
    {
        Console.WriteLine("輸出整型: {0}", i );
    }

    void print(double f)
    {
        Console.WriteLine("輸出浮點型: {0}" , f);
    }

    void print(string s)
    {
        Console.WriteLine("輸出字元串: {0}", s);
    }
}

class Program
{
    static void Main(string[] args)
    {
        Printdata p = new Printdata();
        // 調用 print 來列印整數
        p.print(1);
        // 調用 print 來列印浮點數
        p.print(1.23);
        // 調用 print 來列印字元串
        p.print("Hello Runoob");
        Console.ReadKey();
    }
}

 

當上面的代碼被編譯和執行時,它會產生下列結果:

輸出整型: 1
輸出浮點型: 1.23
輸出字元串: Hello Runoob

  

動態多態性

動態多態性是通過 抽象類 / 介面虛方法 實現的。

語法:

父類類型 對象名稱 = new 子類構造器;
介面    對象名稱 = new 實現類構造器;

多態中成員訪問特點

  • 方法調用:編譯看左邊,運行看右邊。

  • 變數調用:編譯看左邊,運行也看左邊。(多態側重行為多態)

多態的前提

  • 有繼承/實現關係;有父類引用指向子類對象;有方法重寫。

優勢

  • 在多態形式下,右邊對象可以實現解耦合,便於擴展和維護。

Animal a = new Dog();
a.run(); // 後續業務行為隨對象而變,後續代碼無需修改
  • 定義方法的時候,使用父類型作為參數,該方法就可以接收這父類的一切子類對象,體現出多態的擴展性與便利。

多態下會產生的一個問題:

  • 多態下不能使用子類的獨有功能

以下實例創建了 Shape 基類,並創建派生類 Circle、 Rectangle、Triangle, Shape 類提供一個名為 Draw 的虛擬方法,在每個派生類中重寫該方法以繪製該類的指定形狀。

public class Shape
{
    public int X { get; private set; }
    public int Y { get; private set; }
    public int Height { get; set; }
    public int Width { get; set; }
   
    // 虛方法
    public virtual void Draw()
    {
        Console.WriteLine("執行基類的畫圖任務");
    }
}
​
class Circle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("畫一個圓形");
        base.Draw();
    }
}
class Rectangle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("畫一個長方形");
        base.Draw();
    }
}
class Triangle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("畫一個三角形");
        base.Draw();
    }
}
​
class Program
{
    static void Main(string[] args)
    {
        // 創建一個 List<Shape> 對象,並向該對象添加 Circle、Triangle 和 Rectangle
        var shapes = new List<Shape>
        {
            new Rectangle(),
            new Triangle(),
            new Circle()
        };
​
        // 使用 foreach 迴圈對該列表的派生類進行迴圈訪問,並對其中的每個 Shape 對象調用 Draw 方法
        foreach (var shape in shapes)
        {
            shape.Draw();
        }
​
        Console.WriteLine("按下任意鍵退出。");
        Console.ReadKey();
    }
​
}

當上面的代碼被編譯和執行時,它會產生下列結果:

畫一個長方形
執行基類的畫圖任務
畫一個三角形
執行基類的畫圖任務
畫一個圓形
執行基類的畫圖任務
按下任意鍵退出。

7. 作業

  1. 定義一個介面或者抽象類 MyMath(計算器類), 聲明一個方法 Calculator()

  2. 分別創建五個子類(加、減、乘,除,求餘 五個子類),用於實現MyMath 介面

  3. 在Main方法 定義兩個變數,並提示從控制台輸入運算符(+,-,*,/,%),實現輸入不同的運算符調用不能的實現子類。

   

視頻教程:

譽尚學教育_譽尚學教育騰訊課堂官網 (qq.com)
或者:
C# 最強入門編程(.Net 學習系列開山巨作)_嗶哩嗶哩_bilibili

 

海闊平魚躍,天高任我行,給我一片藍天,讓我自由翱翔。
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 咱換工作啦! 新工作這邊需要用到的開發語言是 Haxe,最近大概會寫幾篇筆記。Haxe 的介紹就不寫了,打算記錄點有用的學習內容,先從搭建開發環境開始吧! 當前適用版本: VSCode:Current Latest Version Haxe 版本:4.3.1 文章最近更新日期:2023.05. ...
  • 在最近的互聯網項目開發中,需要獲取用戶的訪問ip信息,併進行後續統計分析。 這些ip信息是在第三方的服務中分組存放的,且每個分組都都是分頁(1頁10條)存放的,如果一次性訪問大量的數據,API很有可能會報錯。 怎樣通過HTTP的方式去獲取到信息,並且模擬瀏覽器每頁每頁獲取10條的信息,且持久到資料庫... ...
  • 如前所述,在前幾章內容中筆者簡單介紹了`記憶體讀寫`的基本實現方式,這其中包括了`CR3切換`讀寫,`MDL映射`讀寫,`記憶體拷貝`讀寫,本章將在如前所述的讀寫函數進一步封裝,並以此來實現驅動讀寫記憶體浮點數的目的。記憶體`浮點數`的讀寫依賴於`讀寫記憶體位元組`的實現,因為浮點數本質上也可以看作是一個位元組集... ...
  • # 還在愁個人博客沒有圖片放?🥱 # 前言 之前將爬取的圖片下載後,我也是放到我了我的博客上面 [ZY知識庫 · ZY - Home Page (pljzy.top)](https://pljzy.top/) 然後順便寫了一個隨機返回圖片的介面,現在我就來說一下如何使用這個介面,以便在自己的博客上 ...
  • 在 WPF 中,CanContentScroll 是 ScrollViewer 控制項的一個附加屬性,它控制滾動視圖中的內容是否按項或像素來滾動。 當 CanContentScroll 設置為 false 時,表示 ScrollViewer 控制項使用逐像素的滾動方式,這意味著滾動視圖中的內容會以像素為 ...
  • 1. 泛型 泛指某種類型。 1、使用參數形式定義 2、使用時傳入具體類型 3、編譯時檢查類型安全 4、邏輯上是多個不同類型 泛型與非泛型之間的區別 性能高:可以避免裝箱和拆箱操作 類型安全 :在進行類型轉換的時候不會拋出異常 代碼重用:定義一次,用許多種不同類型實例化 代碼擴展性好 ArrayLis ...
  • 編寫包含多個 `csproj` 的程式時,隨著項目數量的持續增加,可能涉及一些文件夾的變動,手動添加項目或者變動會變得非常麻煩,這個時候,可以利用 `dotnet cli` 幫助我們完成。 如果從零開始,我們可以新建一個解決方案。 ```powershell dotnet new sln -n to ...
  • # 依賴註入的使用 ## 構造方法註入 這是將服務註入類的最常用方法,是將依賴項註入類的首選方式,也是微軟推崇的模式。這樣,除非提供了所有構造方法註入的依賴項,否則無法構造類,顯示的聲明瞭類必需的服務,使開發人員一目瞭然。 ```csharp public class BookAppService ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...