C++框架_之Qt的信號和槽的詳解

来源:https://www.cnblogs.com/wanghui1234/archive/2018/04/28/8964968.html
-Advertisement-
Play Games

1、信號和槽 2、自定義信號槽 3、自定義信號槽需要註意的事項 4、信號槽的更多用法 5、案例代碼........ ...


C++_之Qt的信號和槽的詳解

1、概述

  信號槽是 Qt 框架引以為豪的機制之一。所謂信號槽,實際就是觀察者模式。當某個事件發生之後,比如,按鈕檢測到自己被點擊了一下,它就會發出一個信號(signal)。這種發出是沒有目的的,類似廣播。如果有對象對這個信號感興趣,它就會使用連接(connect)函數,意思是,將想要處理的信號和自己的一個函數(稱為槽(slot))綁定來處理這個信號。也就是說,當信號發出時,被連接的槽函數會自動被回調。這就類似觀察者模式:當發生了感興趣的事件,某一個操作就會被自動觸發。(這裡提一句,Qt 的信號槽使用了額外的處理來實現,並不是 GoF 經典的觀察者模式的實現方式。)

  信號和槽是Qt特有的信息傳輸機制,是Qt設計程式的重要基礎,它可以讓互不幹擾的對象建立一種聯繫。

  槽的本質是類的成員函數,其參數可以是任意類型的。和普通C++成員函數幾乎沒有區別,它可以是虛函數;也可以被重載;可以是公有的、保護的、私有的、也可以被其他C++成員函數調用。唯一區別的是:槽可以與信號連接在一起,每當和槽連接的信號被髮射的時候,就會調用這個槽。

1.1對象樹(子對象動態分配空間不需要釋放)

參考連接:https://blog.csdn.net/fzu_dianzi/article/details/6949081

 

Qt提供了一種機制,能夠自動、有效的組織和管理繼承自QObject的Qt對象,這種機制就是對象樹。

Qt對象樹在用戶界面編程上是非常有用的。它能夠幫助程式員減輕記憶體泄露的壓力。

比如說當應用程式創建了一個具有父視窗部件的對象時,該對象將被加入父視窗部件的孩子列表。當應用程式銷毀父視窗部件時,其下的孩子列表中的對象將被一一刪除。這讓我們在編程時,能夠將主要精力放在系統的業務上,提高編程效率,同時也保證了系統的穩健性。

下麵筆者將簡單分析對象樹。

代碼驗證:

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QDialog *dlg = new QDialog(0);
    QPushButton *btn = new QPushButton(dlg);
    qDebug() << "dlg = " << dlg;
    qDebug() << "btn = " << btn;
    dlg->exec();
    delete btn;
    qDebug() << "dlg = " << dlg;
    return 0;
}
dlg = QDialog(0x3ea1a0) 
btn = QPushButton(0x3ea228)
/*關閉視窗後,dlg = QDialog(0x3ea1a0)
這說明關閉視窗,不會銷毀該視窗部件,而是將其隱藏起來。
我們在qDebug() << "dlg = " << dlg;
之後加上
qDebug() << "btn = " << btn;
明顯的,我們之前已經delete btn,btn指針沒有被賦值為0,這是編譯器決定的。
執行程式後,必然出現段錯誤。
2、
將程式稍微修改下。*/
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QDialog *dlg = new QDialog(0);
    QPushButton *btn = new QPushButton(dlg);
    qDebug() << "dlg = " << dlg;
    qDebug() << "btn = " << btn;
    dlg->exec();
    delete dlg;
    qDebug() << "btn = " << btn;
    return 0;
}

2、信號和槽

為了體驗一下信號槽的使用,我們以一段簡單的代碼說明:

Qt5 的書寫方式:(推薦的使用)★★★★★

#include <QApplication>
#include <QPushButton>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QPushButton button("Quit");
QObject::connect(&button, &QPushButton::clicked,
&app, &QApplication::quit);
    button.show();
    return app.exec();
}

我們按照前面文章中介紹的在 Qt Creator 中創建工程的方法創建好工程,然後將main()函數修改為上面的代碼。點擊運行,我們會看到一個按鈕,上面有“Quit”字樣。點擊按鈕,程式退出。

connect()函數最常用的一般形式:

connect(sender, signal, receiver, slot);

參數:

 sender:發出信號的對象

 signal:發送對象發出的信號

 receiver:接收信號的對象

 slot:接收對象在接收到信號之後所需要調用的函數

信號槽要求信號和槽的參數一致,所謂一致,是參數類型一致。如果不一致,允許的情況是,槽函數的參數可以比信號的少,即便如此,槽函數存在的那些參數的順序也必須和信號的前面幾個一致起來。這是因為,你可以在槽函數中選擇忽略信號傳來的數據(也就是槽函數的參數比信號的少),但是不能說信號根本沒有這個數據,你就要在槽函數中使用(就是槽函數的參數比信號的多,這是不允許的)。

如果信號槽不符合,或者根本找不到這個信號或者槽函數,比如我們改成:

connect(&button, &QPushButton::clicked, &QApplication::quit2);

由於 QApplication 沒有 quit2 這樣的函數,因此在編譯時會有編譯錯誤:

'quit2' is not a member of QApplication

這樣,使用成員函數指針我們就不會擔心在編寫信號槽的時候出現函數錯誤。

Qt4 的書寫方式:

int main(int argc, char *argv[]) 
{ 
        QApplication a(argc, argv); 
        QPushButton *button = new QPushButton("Quit"); 
        connect(button, SIGNAL(clicked()), &a, SLOT(quit())); 
        button->show(); 
        return a.exec(); 
}

這裡使用了SIGNAL和SLOT這兩個巨集,將兩個函數名轉換成了字元串。註意到connect()函數的 signal 和 slot 都是接受字元串,一旦出現連接不成功的情況,Qt4是沒有編譯錯誤的(因為一切都是字元串,編譯期是不檢查字元串是否匹配),而是在運行時給出錯誤。這無疑會增加程式的不穩定性。

Qt5在語法上完全相容Qt4

小總結: 

  1>. 格式: connect(信號發出者對象(指針), &className::clicked, 信號接收者對象(指針), &classB::slot);
  2>. 標準信號槽的使用:
    connect(sender, &Send::signal, receiver, &Receiver::slot)

3、自定義信號槽

使用connect()可以讓我們連接系統提供的信號和槽。但是,Qt 的信號槽機制並不僅僅是使用系統提供的那部分,還會允許我們自己設計自己的信號和槽。

下麵我們看看使用 Qt 的信號槽,實現一個報紙和訂閱者的例子:

有一個報紙類Newspaper,有一個訂閱者類Subscriber。Subscriber可以訂閱Newspaper。這樣,當Newspaper有了新的內容的時候,Subscriber可以立即得到通知。

 

#include <QObject>
 ////////// newspaper.h //////////
class Newspaper : public QObject
{
    Q_OBJECT
public:
    Newspaper(const QString & name) :
        m_name(name)
    {
    }
 
    void send()
    {
        emit newPaper(m_name);
    }
 
signals:
    void newPaper(const QString &name);
 
private:
    QString m_name;
};
 
////////// reader.h //////////
#include <QObject>
#include <QDebug>
 
class Reader : public QObject
{
    Q_OBJECT
public:
    Reader() {}
 
    void receiveNewspaper(const QString & name)
    {
        qDebug() << "Receives Newspaper: " << name;
    }
};
 
////////// main.cpp //////////
#include <QCoreApplication>
 
#include "newspaper.h"
#include "reader.h"
 
int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
 
    Newspaper newspaper("Newspaper A");
    Reader reader;
    QObject::connect(&newspaper, &Newspaper::newPaper,
                     &reader,    &Reader::receiveNewspaper);
    newspaper.send();
 
    return app.exec();
}

●首先看Newspaper這個類。這個類繼承了QObject類。只有繼承了QObject類的類,才具有信號槽的能力所以,為了使用信號槽,必須繼承QObject。凡是QObject類(不管是直接子類還是間接子類),都應該在第一行代碼寫上Q_OBJECT不管是不是使用信號槽,都應該添加這個巨集。這個巨集的展開將為我們的類提供信號槽機制、國際化機制以及 Qt 提供的不基於 C++ RTTI 的反射能力。

 

● Newspaper類的 public 和 private 代碼塊都比較簡單,只不過它新加了一個 signals。signals 塊所列出的,就是該類的信號。信號就是一個個的函數名,返回值是 void(因為無法獲得信號的返回值,所以也就無需返回任何值),參數是該類需要讓外界知道的數據。信號作為函數名,不需要在 cpp 函數中添加任何實現。

 

●Newspaper類的send()函數比較簡單,只有一個語句emit newPaper(m_name);。emit 是 Qt 對 C++ 的擴展,是一個關鍵字(其實也是一個巨集)。emit 的含義是發出,也就是發出newPaper()信號。感興趣的接收者會關註這個信號,可能還需要知道是哪份報紙發出的信號?所以,我們將實際的報紙名字m_name當做參數傳給這個信號。當接收者連接這個信號時,就可以通過槽函數獲得實際值。這樣就完成了數據從發出者到接收者的一個轉移。

 

● Reader類更簡單。因為這個類需要接受信號,所以我們將其繼承了QObject,並且添加了Q_OBJECT巨集。後面則是預設構造函數和一個普通的成員函數。Qt 5 中,任何成員函數、static 函數、全局函數和 Lambda 表達式都可以作為槽函數。與信號函數不同,槽函數必須自己完成實現代碼。槽函數就是普通的成員函數,因此作為成員函數,也會受到 public、private 等訪問控制符的影響。(如果信號是 private 的,這個信號就不能在類的外面連接,也就沒有任何意義。)

3.1自定義信號槽需要註意的事項

●發送者和接收者都需要是QObject的子類(當然,槽函數是全局函數、Lambda 表達式等無需接收者的時候除外);

●使用 signals 標記信號函數,信號是一個函數聲明,返回 void,不需要實現函數代碼;

●槽函數是普通的成員函數,作為成員函數,會受到 public、private、protected 的影響;

●使用 emit 在恰當的位置發送信號;

●使用QObject::connect()函數連接信號和槽。

●任何成員函數、static 函數、全局函數和 Lambda 表達式都可以作為槽函數

3.2信號槽的更多用法

 一個信號可以和多個槽相連

  如果是這種情況,這些槽會一個接一個的被調用,但是它們的調用順序是不確定的。

多個信號可以連接到一個槽

  只要任意一個信號發出,這個槽就會被調用

●一個信號可以連接到另外的一個信號

  當第一個信號發出時,第二個信號被髮出。除此之外,這種信號-信號的形式和信號-槽的形式沒有什麼區別。

●槽可以被取消鏈接

  這種情況並不經常出現,因為當一個對象delete之後,Qt自動取消所有連接到這個對象上面的槽。

●使用Lambda 表達式

  在使用 Qt 5 的時候,能夠支持 Qt 5 的編譯器都是支持 Lambda 表達式的。

  我們的代碼可以寫成下麵這樣:

QObject::connect(&newspaper, static_cast<void (Newspaper:: *)
(const QString &)>(&Newspaper::newPaper),
[=](const QString &name) 
{ /* Your code here. */ }
);

在連接信號和槽的時候,槽函數可以使用Lambda表達式的方式進行處理。

4、Lambda表達式

C++11中的Lambda表達式用於定義並創建匿名的函數對象,以簡化編程工作。首先看一下Lambda表達式的基本構成:

                                              [函數對象參數](操作符重載函數參數)mutable或exception ->返回值{函數體}

 ①函數對象參數;

  [],標識一個Lambda的開始,這部分必須存在,不能省略。函數對象參數是傳遞給編譯器自動生成的函數對象類的構造函數的。函數對象參數只能使用那些到定義Lambda為止時Lambda所在作用範圍內可見的局部變數(包括Lambda所在類的this)。函數對象參數有以下形式:

     ▲空。沒有使用任何函數對象參數。

     ▲=。函數體內可以使用Lambda所在作用範圍內所有可見的局部變數(包括Lambda所在類的this),並且是值傳遞方式(相當於編譯器自動為我們按值傳遞了所有局部變數)。

     ▲&。函數體內可以使用Lambda所在作用範圍內所有可見的局部變數(包括Lambda所在類的this),並且是引用傳遞方式(相當於編譯器自動為我們按引用傳遞了所有局部變數)。

     ▲ this。函數體內可以使用Lambda所在類中的成員變數。

     ▲ a。將a按值進行傳遞。按值進行傳遞時,函數體內不能修改傳遞進來的a的拷貝,因為預設情況下函數是const的。要修改傳遞進來的a的拷貝,可以添加mutable修飾符。

     ▲ &a。將a按引用進行傳遞。

       ▲ a, &b。將a按值進行傳遞,b按引用進行傳遞。

     ▲ =,&a, &b。除a和b按引用進行傳遞外,其他參數都按值進行傳遞。

     ▲ &, a, b。除a和b按值進行傳遞外,其他參數都按引用進行傳遞。

int m = 0, n = 0;
[=] (int a) mutable { m = ++n + a; }(4);
      [&] (int a) { m = ++n + a; }(4);

      [=,&m] (int a) mutable { m = ++n + a; }(4);
      [&,m] (int a) mutable { m = ++n + a; }(4);

      [m,n] (int a) mutable { m = ++n + a; }(4);
      [&m,&n] (int a) { m = ++n + a; }(4);

② 操作符重載函數參數;

標識重載的()操作符的參數,沒有參數時,這部分可以省略。參數可以通過按值(如:(a,b))和按引用(如:(&a,&b))兩種方式進行傳遞。

③ 可修改標示符;

mutable聲明,這部分可以省略。按值傳遞函數對象參數時,加上mutable修飾符後,可以修改按值傳遞進來的拷貝(註意是能修改拷貝,而不是值本身)。

④ 錯誤拋出標示符;

exception聲明,這部分也可以省略。exception聲明用於指定函數拋出的異常,如拋出整數類型的異常,可以使用throw(int)

⑤ 函數返回值;

->返回值類型,標識函數返回值的類型,當返回值為void,或者函數體中只有一處return的地方(此時編譯器可以自動推斷出返回值類型)時,這部分可以省略。

⑥ 是函數體;

{},標識函數的實現,這部分不能省略,但函數體可以為空。

總結:

 

 

案例代碼:

mainwidget.h

#ifndef MAINWIDGET_H
#define MAINWIDGET_H

#include <QWidget>
#include <QPushButton>
#include "subwidget.h" //子視窗頭文件

class MainWidget : public QWidget
{
    Q_OBJECT

public:
    MainWidget(QWidget *parent = 0);
    ~MainWidget();

public slots:
    void mySlot();
    void changeWin();
    void dealSub();
    void dealSlot(int, QString);

private:
    QPushButton b1;
    QPushButton *b2;
    QPushButton b3;

    SubWidget subWin;
};

#endif // MAINWIDGET_H

subwidget.h

#ifndef SUBWIDGET_H
#define SUBWIDGET_H

#include <QWidget>
#include <QPushButton>

class SubWidget : public QWidget
{
    Q_OBJECT
public:
    explicit SubWidget(QWidget *parent = 0);

    void sendSlot();

signals:
     /* 信號必須有signals關鍵字來聲明
      * 信號沒有返回值,但可以有參數
      * 信號就是函數的聲明,只需聲明,無需定義
      * 使用:emit mySignal();
      * 信號可以重載
     */

    void mySignal();
    void mySignal(int, QString);

public slots:

private:
    QPushButton b;
};

#endif // SUBWIDGET_H

main.cpp

#include "mainwidget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWidget w;//執行MainWidget的構造函數
    w.show();

    return a.exec();
}

mainwidget.cpp

#include "mainwidget.h"
#include <QPushButton>
#include <QDebug> //列印

MainWidget::MainWidget(QWidget *parent)
    : QWidget(parent)
{
    b1.setParent(this);
    b1.setText("close");
    b1.move(100, 100);

    b2 = new QPushButton(this);
    b2->setText("abc");

    connect(&b1, &QPushButton::pressed, this, &MainWidget::close);
    /* &b1: 信號發出者,指針類型
     * &QPushButton::pressed:處理的信號,  &發送者的類名::信號名字
     * this: 信號接收者
     * &MainWidget::close: 槽函數,信號處理函數  &接收的類名::槽函數名字
     * 發送-處理-接收-處理
    */

    /* 自定義槽,普通函數的用法
     * Qt5:任意的成員函數,普通全局函數,靜態函數
     * 槽函數需要和信號一致(參數,返回值)
     * 由於信號都是沒有返回值,所以,槽函數一定沒有返回值
     */
    connect(b2, &QPushButton::released, this, &MainWidget::mySlot);

    connect(b2, &QPushButton::released, &b1, &QPushButton::hide);

    /* 信號:簡訊
     * 槽函數:接收簡訊的手機
     */

    setWindowTitle("老大");
    //this->setWindowTitle("老大");//等價同上

    b3.setParent(this);
    b3.setText("切換到子視窗");
    b3.move(50, 50);

    //顯示子視窗
    //subWin.show();

    connect(&b3, &QPushButton::released, this, &MainWidget::changeWin);


    //處理子視窗的信號
//    void (SubWidget::*funSignal)() = &SubWidget::mySignal;
//    connect(&subWin, funSignal, this, &MainWidget::dealSub);

//     void (SubWidget::*testSignal)(int, QString) = &SubWidget::mySignal;
//    connect(&subWin, testSignal, this, &MainWidget::dealSlot);

    //Qt4信號連接
    //Qt4槽函數必須有slots關鍵字來修飾
    connect(&subWin, SIGNAL(mySignal()), this, SLOT(dealSub()) );

    connect(&subWin, SIGNAL(mySignal(int,QString)),
            this, SLOT(dealSlot(int,QString)) );
    //缺點: SIGNAL SLOT 將函數名字 -> 字元串  不進行錯誤檢查

    //Lambda表達式, 匿名函數對象
    //C++11增加的新特性, 項目文件: CONFIG += C++11
    //Qt配合信號一起使用,非常方便

    QPushButton *b4 = new QPushButton(this);
    b4->setText("Lambda表達式");
    b4->move(150, 150);
    int a = 10, b = 100;
    connect(b4, &QPushButton::clicked,
            // = :把外部所有局部變數、類中所有成員以值傳遞方式
            // this: 類中所有成員以值傳遞方式
            // & : 把外部所有局部變數, 引用符號
            [=](bool isCheck)
            {
                qDebug() << isCheck;
            }


            );


    resize(400, 300);
}

void MainWidget::dealSlot(int a, QString str)
{
    // str.toUtf8() -> 位元組數組QByteArray
    // ……data()  -> QByteArray -> char *
    qDebug() << a << str.toUtf8().data();
}

void MainWidget::mySlot()
{
    b2->setText("123");
}

void MainWidget::changeWin()
{
    //子視窗顯示
    subWin.show();
    //本視窗隱藏
    this->hide();
}


void MainWidget::dealSub()
{
    //子視窗隱藏
    subWin.hide();
    //本視窗顯示
    show();
}

MainWidget::~MainWidget()
{

}

subwidget.cpp

#include "subwidget.h"

SubWidget::SubWidget(QWidget *parent) : QWidget(parent)
{
    this->setWindowTitle("小弟");
    b.setParent(this);
    b.setText("切換到主視窗");

    connect(&b, &QPushButton::clicked, this, &SubWidget::sendSlot);

    resize(400, 300);
}

void SubWidget::sendSlot()
{
    emit mySignal();
    emit mySignal(250, "我是子視窗");
}

SingnalAndSlot.pro

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = 03_SignalAndSlot
TEMPLATE = app


SOURCES += main.cpp\
        mainwidget.cpp \
    subwidget.cpp

HEADERS  += mainwidget.h \
    subwidget.h

CONFIG += C++11

 以上資料來源於互聯網和自己的見解。如有雷同,不勝榮幸。

 


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

-Advertisement-
Play Games
更多相關文章
  • 區塊鏈無疑是現在最火熱的技術,自從比特幣火了,其背後的技術——區塊鏈也得到了史無前例的追捧,這些年,各個行業都在實踐落地以搶占先機。 今天推薦一本區塊鏈技術的學習書籍—— ,除了原理之外知識之外,更多的是如何實戰把區塊鏈技術真正落地應用,也能幫助打開區塊鏈誤區,拓展知識面。 本書介紹 • 國內區塊鏈 ...
  • 中介者模式是關於數據交互的設計模式,該模式的核心是一個中介者對象,負責協調一系列對象之間的不同的數據請求,這一系列對象成為同事類。如房產中介(簡直不想提它),買房的賣房的,租房的放租的都到房產中介那裡去登記。如果有賣房的就會通知買房的去買房,如果有放租的就會通知租房的去租房。所有的事物都是通過中介進 ...
  • 閱讀目錄: 準備工作 搭建 RabbitMQ Server 單機版 RabbitMQ Server 高可用集群相關概念 搭建 RabbitMQ Server 高可用集群 搭建 HAProxy 負載均衡 因為公司測試伺服器暫不能用,只能在自己電腦上重新搭建一下 RabbitMQ Server 高可用集 ...
  • 雖然Java培訓已經有很久的年頭了,但是現在想學java的人數卻沒有減少的跡象。其中許多人對java的學費感到很好奇,不知道參加java培訓要花多少錢 參加java培訓學費基本上是10000到30000左右,有的肯能比這還高,不同的機構價錢會有略微的差別。不過你可千萬不要認為說學費貴的就一定最好,你 ...
  • 一、SQL註入與防範 使用PreparedStatement替代Statement對象,它提供了參數化SQL的方式 二、事務 定義 事務是併發控制的基本單位,滿足ACID特征 原子性:atomicity 一致性:consistency 隔離性:isolation 持久性:durability 事務控 ...
  • http://https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3102410964,4206562405&fm=27&gp=0.jpg ...
  • 題目描述 如圖:有n個重物,每個重物系在一條足夠長的繩子上。每條繩子自上而下穿過桌面上的洞,然後系在一起。圖中X處就是公共的繩結。假設繩子是完全彈性的(不會造成能量損失),桌子足夠高(因而重物不會垂到地上),且忽略所有的摩擦。 問繩結X最終平衡於何處。 註意:桌面上的洞都比繩結X小得多,所以即使某個 ...
  • 1、VO:value object值對象。通常用於業務層之間的數據傳遞,和PO一樣也是僅僅包含數據而已。但應是抽象出的業務對象,可以和表對應,也可以不,這根據業務的需要. 2、 String[]類型列印tosring時顯示為一塊記憶體空間,這時候根據鍵可以判斷值已經成功接收 3、 在對條件進行判斷的時 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...