Delphi高手突破(三) Delphi高級進階

来源:http://www.cnblogs.com/c001100/archive/2016/10/11/5948352.html
-Advertisement-
Play Games

第 3章 異常及錯誤處理 健壯的程式來自於正確的錯誤處理。 相信我,總會有意外的…… Delphi 高手突破 正如同現實生活中我們不可能事事如意,你所寫的代碼也不可能每一行都能得到正確的執行。生活中遇到不如意的事情,處理好了,雨過天晴;處理不好,情況會越變越糟,甚至一發而不可收拾,後果難料。程式設計 ...


第 3章  異常及錯誤處理

健壯的程式來自於正確的錯誤處理。
 
 
 
 
相信我,總會有意外的……

Delphi  高手突破     
正如同現實生活中我們不可能事事如意,你所寫的代碼也不可能每一行都能得到正確
的執行。生活中遇到不如意的事情,處理好了,雨過天晴;處理不好,情況會越變越糟,
甚至一發而不可收拾,後果難料。程式設計中同樣如此,所謂健壯的程式,並非不出錯的
程式,而是在出錯的情況下能很好地處理的程式。
因此,錯誤處理一直是程式設計領域的一個重要課題。而異常就是面向對象編程提供
的錯誤處理解決方案。它是一個非常好的工具,如果你選擇了 OOP,選擇了 Delphi,那麼
異常也就成為你的惟一選擇了。
要讓你信服地選擇異常,需要給出一些理由。在本章中會讓你清楚明白地瞭解異常所
帶來的好處。
3.1  異常的本質
什麼是異常?為什麼要用它?
在基於函數的結構中,一般使用函數返回值來標明函數是否成功執行,並給出錯誤類
型等信息。於是就會產生如下形式的代碼:
 
nRetVal := SomeFunctionToOpenFile();
 
if nRetVal = E_SUCCESSED then // 成功打開
begin
  ……
end
else if nRetVal = E_FILE_NOT_FOUND then // 沒有找到文件
begin
  ……
end
else if nRetVal = E_FILE_FORMAT_ERR then // 文件格式錯
begin
  ……
end
else then
begin
  ……
end
 
使用返回錯誤代碼的方法是非常普遍的,但是使用這樣的方法存在兩個問題:
(1)造成冗長、繁雜的分支結構(大量的 if 或 case 語句),使得程式流程式控制制變得
複雜,同時造成測試工作的複雜,因為測試需要走遍每個分支。
 
·50·

異常及錯誤處理
(2)可能會存在沒有被處理的錯誤(函數調用者如果不判斷返回值的話)。
異常可以很好地解決以上兩個問題。
所謂“異常”是指一個異常類的對象。Delphi 的 VCL 中,所有異常類都派生於 Exception
類。該類聲明瞭異常的一般行為、性質。最重要的是,它有一個 Message 屬性可以報告異
常發生的原因。
拋出一個異常即標誌一個錯誤的發生。使用 raise 保留字來拋出一個異常對象,如:
3
raise Exception.Create(′An error occurred!′);
但需要強調的是,異常用來標誌錯誤發生,卻並不因為錯誤發生而產生異常。產生異
常僅僅是因為遇到了 raise,在任何時候,即使沒有錯誤發生,raise 都將會導致異常的發生。 註意:異常的發生,僅僅是因為 raise,而非其他!
一旦拋出異常,函數的代碼就從異常拋出處立刻返回,從而保護其下麵的敏感代碼不
會得到執行。對於拋出異常的函數本身來說,通過異常從函數返回和正常從函數返回(執
行到函數末尾或遇到了 Exit)是沒有什麼區別的,函數代碼同樣會從堆棧彈出,局部簡單
對象(數組、記錄等)會自動被清理、回收。
採用拋出異常以處理意外情況,則可以保證程式主流程中的所有代碼可用,而不必加
入繁雜的判斷語句。
例如,函數 A拋出異常:
 
function A() : Integer;
vat
  pFile : textfile;
begin
  …… // 一些代碼
  pFile := SomeFunctionToOpenAnFile();
  if pFile = nil then
raise Exception.Create(′Open file failed!′); // 文件打開失敗拋出異常
 Read(pFile, ……); // 讀文件
  …… // 其他一些對文件的操作,此時可以保證文件指針有效
end;
 
函數 A的代碼使得對文件打開的出錯處理非常簡單。如果打開文件失敗,則拋出一個
Exception 類的異常對象,函數立刻返回,從而保護了以下對文件指針的操作不被執行。而
之後的代碼可以假設文件指針肯定有效,從而令代碼更加美觀。
生活中,我們每天扔掉的垃圾都會有清潔工人收拾、處理,否則生活環境中豈不到處
充斥著垃圾?同樣,拋出的異常也需要被捕獲和處理。假設函數 B 調用了函數 A,要捕獲
這個文件打開失敗的異常,就需要在調用 A 之前先預設一個陷阱,這個陷阱就是所謂的
“try…except 塊”。
 
·51·

Delphi  高手突破     
先看一下函數 B 的代碼:
 
procedure B();
begin
  …… // 一些代碼
 try
   A(); // 調用A
   SomeFunctionDependOnA(); // 依賴於A的結果的函數
 Except
   ShowMessage(′some error occured′); // 嘿嘿,掉進來了,發生異常
 End;
  …… // 繼續的代碼
end;
 
A拋出的異常,會被 B所設的 try…except 所捕獲。一旦捕獲到異常,就不再執行之後
的敏感代碼,而是立刻跳至 except 塊執行錯誤處理,處理完成後再繼續執行整個 try 塊之
後的代碼。程式流程的控制權被留在了函數 B。
如果不喜歡自己收拾垃圾,因而在 B 中並沒有預設 try…except 塊的話,則異常會被繼
續拋給 B 的調用者,而如果 B 的調用者同樣不負責任,則異常會被繼續像踢足球一樣被踢
給更上層的調用者,依此類推。不過,不用擔心,我們有一個大管家,大家都不要的燙手
山芋,它會幫我們收拾,那就是——VCL(Delphi 的應用程式框架)。
因為 VCL 的框架使得所編寫的整個應用程式被包在一個大的 try…except 中,無論什
麽沒有被處理的異常,最終都會被它所捕獲,並將程式流程返回到最外層的消息迴圈中,
決無遺漏!這也就是為什麼會看到很多用 Delphi 所編寫的但並不專業的小軟體有時會跳出
一個報告錯誤的對話框(如圖 3.1 所示)。發生這樣的情況應該責怪軟體的編寫者沒有很
好地處理錯誤,但有些不明白異常機制的程式員常常會責怪 Delphi 編寫的程式怎能會有這
樣的情況發生。其實出現這個提示,應該感謝 VCL的異常機制讓程式可以繼續運行而不是
“非法終止”。
 
圖3.1  異常被VCL所捕獲 註意:VCL 用一個大的 try…except 將代碼包裹起來!
因此,在 VCL 框架中不會有不被處理的異常,換句話說,也就是不會有不被處理的錯
誤(雖然筆者說過異常並不等於錯誤)。對異常的捕獲也非常簡單,不見了一大堆的 if 或
 
·52·

異常及錯誤處理
case,程式控制流程的走向也就十分清晰明瞭了,這是給測試人員帶來的好消息。
3.2  創建自己的異常類
異常機制是完全融入面向對象的體系的,所以異常類和一般類一樣具有繼承和多態的
3
性質。其實,異常類和普通類並沒有什麼區別。
Object Pascal的運行時異常基類是 Exception,VCL中所有異常類都應該從它派生。當
然,Object Pascal 語言並不規定如此,可以用 raise 拋出任何除簡單類型之外的類類型的對
象,try…except 同樣可以捕獲它,在異常處理後同樣會自動析構、回收它,只是 Exception
定義了異常的大多數特征。既然別人已經為我們準備了一個好用的、完備的 Exception,當
然沒有理由不用它。
也許讀者也已經註意到,所有 VCL 的異常發生時,彈出的警告對話框都帶有一段有價
值的對於異常的發生原因的描述(正如圖 3.1 中的“"is not a valid integer value”)。這段
描述對於 debug 工作是非常有用的。它正是來自於 Exception 類的 Message屬性,所有異常
類被創建時都必須給出一個出錯描述。因此,在定義、使用自己的異常類時,也要給出一
個不會令人迷惑的、明白說出錯誤原因的 Message 屬性。 註意:從 Exception派生自己的異常類!
下麵以一個示常式序來演示如何定義、使用自己的異常類,其代碼及可執行文件可在
配書光碟的 exception 目錄下找到。
程式運行後的界面如圖 3.2 所示。
 
圖3.2  自定義異常類演示程式界面
該程式的運行界面十分充分地體現了第 1 章所說的“簡單性”原則。界面上只有 3 個
按鈕,先看上面兩個(另一個“try…finally”按鈕先不說明,留待 3.3 節講解)。一個模擬
打開文件時發生“找不到文件”的錯誤,一個模擬發生“文件格式錯”的錯誤。所謂模擬
發生錯誤,就是在並沒有真正發生錯誤的情況下拋出異常,使得編譯器認為發生了錯誤,
即單擊這兩個按鈕後,程式會分別拋出相應的異常。
首先要定義兩種錯誤所對應的異常類。它們的定義和實現在 ExceptionClass.pas 單元
中。該單元代碼清單如下:
 
·53·

Delphi  高手突破     
unit ExceptionClass;
 
interface
 
uses SysUtils, Dialogs;
 
Type
 
  EFileOpenFailed = class(Exception) // 定義一個文件打開失敗的通用異常類
 public
    procedure Warning(); virtual; abstract;
 end;
 
  EFileNotFound = class(EFileOpenFailed) // 細化文件打開失敗的異常
 public
    procedure Warning(); override;
 end;
 
  EFileFormatErr = class(EFileOpenFailed) // 細化文件打開失敗的異常
 public
    procedure Warning(); override;
 end;
 
implementation
 
{ EFileNotFound }
 
procedure EFileNotFound.Warning;
begin
 ShowMessage('真是不可思議,竟然找不到文件!');
end;
 
{ EFileFormatErr }
 
procedure EFileFormatErr.Warning;
begin
 ShowMessage('更不可思議的是,文件格式不對!');
end;
 
end.
 
我們先定義了一個標誌打開文件失敗的異常基類 EFileOpenFailed,並給它聲明瞭一個
 
·54·

異常及錯誤處理
抽象方法 Warning。然後又細化了錯誤的原因,從而派生出兩個異常類——EFileNotFound、
EFileFormatErr,它們都具體實現了 Warning 方法。
在應用程式的主Form(Form1)中,定義一個模擬發生錯誤並拋出異常的SimulateError()
方法來模擬發生錯誤、拋出異常。
然後定義一個 ToDo()方法來調用會引發異常的 SimulateError(),並且用 Try 將其捕獲
進行異常處理。
3
最後在兩個按鈕的 OnClick()事件中,調用 ToDo()方法。
其代碼清單如下:
 
unit Unit1;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls;
 
type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Label1: TLabel;
    Button3: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
      procedure SimulateError(Button : TObject);
      procedure ToDo(Button : TObject);
  end;
 
var
  Form1: TForm1;
 
implementation
 
uses ExceptionClass;
 
 
·55·

Delphi  高手突破     
{$R *.dfm}
 
procedure TForm1.SimulateError(Button : TObject);
begin
    if Button = Button1 then
        raise EFileNotFound.Create('File Not Found')
    else if Button = Button2 then
        raise EFileFormatErr.Create('File Format Error')
    else // Button = Button3
        raise Exception.Create('Unknonw Error');
end;
 
procedure TForm1.Button1Click(Sender: TObject);
begin
    ToDo(Sender);
end;
 
procedure TForm1.Button2Click(Sender: TObject);
begin
    ToDo(Sender);
end;
 
procedure TForm1.ToDo(Button : TObject);
begin
    try
        SimulateError(Button)
    except
        on E : EFileOpenFailed do
            E.Warning();
        on E : Exception do
            ShowMessage(E.Message);
    end;
end;
 
procedure TForm1.Button3Click(Sender: TObject);
var
    AStream : TMemoryStream;
begin
    AStream := TMemoryStream.Create();
 
    try
        SimulateError(Sender);
 
·56·

異常及錯誤處理
    finally
        AStream.Free();
    end;
end;
 
end.
  3
程式運行後,當單擊界面上方的兩個按鈕之一時,都會調用 ToDo 方法。而在 ToDo
方法中,由於 SimulateError 被調用而引發一個異常,雖然並沒有真的發生打開文件錯誤,
但確實拋出了異常。這再次說明瞭,異常只是用來標誌錯誤,而並不等同於錯誤。
程式中,我們定義了一個標誌打開文件失敗的異常基類 EFileOpenFailed,以及兩個派
生的異常類——EFileNotFound、EfileFormatErr。這樣定義異常類框架,給錯誤處理部分帶
來了更多的靈活性。這是多態性給我們的又一個恩惠。可以自由選擇需要捕獲的異常的“精
度”。也就是說,如果用戶非常關心發生錯誤的具體原因,則可以捕獲每個最底層的異常
類;而如果只關心是否發生了打開文件的錯誤,那麼可以只捕獲 EFileOpenFailed類;若關
心的只是是否有錯誤發生,則只需捕獲 Exception 就行了。
在 SimulateError 的調用之外,設置了 try…except,那麼它所引發的異常都會被捕獲。
將“精度”更“細”的異常類的處理代碼放在前面,而把“精度”較“粗”的異常類的處
理代碼放在後面。如果相反,則所有異常都會被 Exception的處理代碼捕獲,而其他的異常
類的處理代碼則永遠都沒有機會執行了。
Exception 程式演示了一個很小的、自定義的異常類框架的定義、實現及使用。“麻雀
雖小,五臟俱全”,它給出了一種在自己程式中錯誤的捕獲、處理的思路。
3.3  try…finally
現在已經知道,在函數中引發異常將導致函數的正常返回,因此函數棧中的局部簡單
對象(數組、記錄等)會得到釋放。同時也知道了,在 Object Pascal 中所有的類對象都在
堆中被構造,編譯器不會在退出函數時自動調用它們的析構函數,那麼如何保證所有的局
部類對象也能被釋放呢?
Object Pascal引入了獨特的 try...finally 來解決這個問題。
try…finally 塊幫你保證一些重要的代碼在無論是否發生異常的情況下都能被執行,這
些代碼位於 finally和 end之間。
再次打開 Exception 程式,現在來看一下沒用過的第 3 個按鈕。為它的 Click 事件添加
如下的代碼:
 
procedure TForm1.Button3Click(Sender: TObject);
var
  AStream : TMemoryStream;
 
·57·

Delphi  高手突破     
begin
  AStream := TMemoryStream.Create();
 
 try
   SimulateError(Self);
 finally
   AStream.Free();
 end;
end;
 
它首先創建了一個記憶體流對象,以模擬該函數申請了一些系統資源。然後還是調用
了 SimulateError 方法,不過這次 SimulateError 拋出的是一個 Exception 異常。但在此把
記憶體流對象的銷毀工作放在了 finally 保護之中,由此保證該對象的釋放。可以自己單步
跟蹤試一下,無論在發生異常(即調用了 SimulateError)的情況下,還是正常退出(不
調用 SimulateError 或將 SimulateError 的調用改為 Exit)的情況下,AStream.Free()都會得
到執行。
同時擁有 try…except 和 try…finally,應該說是 Delphi 程式員的一種幸運,值得慶幸。
只是,我們想得到的會更多,會希望擁有
 
try
  ……
except
  ……
finally
 
這樣的結構,只是目前還得不到滿足。雖然可以用
 
try
 try
   ……
 except
   ……
 end
finally
  ……
end;
 
來取代,但顯然不如所希望的那樣結構美觀和優雅。這不能不說是一種遺憾,讓我們寄希
望於下一個 Delphi 版本吧!
 
·58·

異常及錯誤處理
3.4  構造函數與異常
這個話題在 C++社區中經常會被提起,而在 Delphi 社區中似乎從來沒有人註意過,也
許由於語言的特性而使得 Delphi 程式員不必關心這個問題。但我想,Delphi 程式員也應該
3
對該問題有所瞭解,知道語言為我們提供了什麼而使得我們如此輕鬆,不必理會它。正所
謂“身在福中須知福”。
我們知道,類的構造函數是沒有返回值的,因此如果構造函數構造對象失敗,則不可
能依靠返回錯誤代碼來解決。那麼,在程式中如何標識構造函數的失敗呢?最“標準”的
方法就是:拋出一個異常。
構造函數失敗,意味著對象的構造失敗。那麼拋出異常之後,這個“半死不活”的對
象會被如何處理呢?
在此,讀者有必要先對 C++對這種情況的處理方式有一個瞭解。
在 C++中,構造函數拋出異常後,析構函數不會被調用。這種做法是合理的,因為此
時對象並沒有被完整構造。
如果構造函數已經做了一些諸如分配記憶體、打開文件等操作,那麼 C++類需要有自己
的成員來記住做過哪些動作。當然,這樣做對於類的實現者來說非常麻煩。因此,一般 C++
類的實現者都避免在構造函數中拋出異常(可以提供一個諸如 Init 和 UnInit 的成員函數,
由構造函數或類的客戶去調用它們,以處理初始化失敗的情況)。而每一本 C++的經典著
作所提供的方案都是使用智能指針(STL 的標準類 auto_ptr)。
在 Object Pascal 中,這個問題變得非常簡單,程式員不必為此大費周折。如果 Object
Pascal 的類在構造函數中拋出異常,則編譯器會自動調用類的析構函數(由於析構函數不
允許被重載,可以保證只有惟一一個析構函數,因此編譯器不會迷惑於多個析構函數之中)。
析構函數中一般會析構成員對象,而 Free()方法保證了不會對 nil 對象(即尚未被創建的成
員對象)調用析構函數,因此在使得代碼簡潔優美的前提下,又保證了安全。
以下的程式演示了構造函數中拋出異常後,Object Pascal 編譯器所作的處理方法。
首先定義 TMyClass: 
 
type 
  TMyClass = class
 private
    FStr : PChar; // 字元串指針
 public
   constructor Create();
    destructor Destroy(); override;
 end;
 
然後實現 TMyClass,並讓它的構造函數中拋出異常:
 
·59·

Delphi  高手突破     
constructor TMyClass.Create();
begin
  FStr := StrAlloc(10); // 構造函數中為字元串指針分配記憶體
 StrCopy(FStr, 'ABCDEFGHI');
  raise Exception.Create('error'); // 拋出異常,沒有理由
end;
 
destructor TMyClass.Destroy();
begin
 StrDispose(FStr); // 析構函數中釋放記憶體
 WriteLn('Free Resource');
end;
 
最後,編寫程式主流程的代碼。主流程中首先創建 TMyClass 類的實例:
 
var
  Obj : TMyClass;
  i : integer;
begin
 try
    Obj := TMyClass.Create();
    // Obj.Free(); // 不調用析構函數,但發生異常時,編譯器自動調用了析構函數
   WriteLn('Succeeded');
 except
    Obj := nil;
   WriteLn('Failed');
 end;
 
 Read(i); // 暫停屏幕,以便觀察運行結果
end.
 
這段代碼中,創建 TMyClass 類的實例時遇到了麻煩,因為 TMyClass 的構造函數拋出
了異常,但這段代碼執行結果卻是:
 
Free Resource
Failed
 
出現了“Free Resource”,說明發生異常後,析構函數被調用了。而這正是在構造函
數拋出異常之後,編譯器自動調用析構函數的結果。
因此,如果類的說明文檔或類的作者告知你,類的構造函數可能會拋出異常,那就要
記得用 try…except 包住它!
 
·60·

異常及錯誤處理
C++與 Object Pascal 對於構造函數拋出異常後的不同處理方式,其實正是兩種語言的
設計思想的體現。C++秉承 C 語言的風格,註重效率,一切交給程式員來掌握,編譯器不
做多餘動作;Object Pascal 繼承 Pascal 的風格,註重程式的美學意義,編譯器幫助程式員
完成複雜的工作。
3.5  小    結  3
異常是面向對象編程帶來的非常好的工具,不加以利用是很可惜的。但是,正如萬事
都有個“度”,濫用異常也是不可取的。使用異常不是沒有代價,它會增加程式的負擔,
編寫若幹 try...except 和編寫數以千計的 try...except 之間是有很大區別的。
同時,也不必過分害怕由它所帶來的負擔。其實,既然已經使用了 Delphi,其實就已
經在使用異常了,也許只是自己還不知道。聽聽 Chalie Calverts 的忠告:“在似乎有用的
時候,就應該使用 try...except 塊。但是要試著讓自己對這種技術的熱情不要太過分”。

 


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

-Advertisement-
Play Games
更多相關文章
  • 背景 本地環境:Win7,Visual Studio 2013, IIS 7.5 WebForm 項目,添加一個http介面給別人調用。 我的做法是添加了一個Controller,Application_Start 裡面添加路由。 然後本地測試通過。 發佈 發佈機器環境: Window Server ...
  • 目錄 1. ASP.NET 路由 註冊路由 動態映射HttpHandler 2. WebAPI 路由 註冊路由 調用GetRouteData 3. 2個路由系統銜接 GlobalConfiguration HostedHttpRoute 4. 補充 路由是進入Web API的第一扇門.目的用於確定C ...
  • 一、博客系統進度回顧 目前已經完成了,前臺展示,以及後臺發佈的功能,最近都在做這個,其實我在國慶的時候就可以弄完的,但是每天自己弄,突然最後國慶2天,連電腦都不想碰,所以就一直拖著,上一篇寫了前端實現用到的一些WebUI框架以及一些系統中用到的js插件,其實寫了這麼久,還是第一次,有人留言,不要爛尾 ...
  • 這兩天看一個要離職同事交接的代碼,看到一個淺拷貝的方法感覺挺好,在這裡記錄一下。 一、方法體 是一個靜態方法CopyHelper,包含以下三個部分 1、給PropertyInfo[]類型加個擴展的方法,方便進行查詢是否存在相同類型,相同名稱的欄位。 2、淺拷貝的主體方法,即調用上面的方法基礎上,如果 ...
  • 最前面的話:Smobiler是一個在VS環境中使用.Net語言來開發APP的開發平臺,也許比Xamarin更方便 ...
  • 上一章講到關於C#語法的基礎部分。瞭解相關的基礎部分之後我們就要去瞭解一下C#是什麼樣子訪問數庫的。C#把訪問資料庫這一部分的知識點叫作ADO.NET。即是JAVA常常講到的JDBC這一部分的知識點了。筆者根據使用資料庫方式的不同又分為有線連接和無線連接(關於有線和無線的叫法是筆者個人定義的。因為看 ...
  • C# 中的datagridview是一個非常有用且強大的控制項,可以用來綁定資料庫、綁定LIST類型的變數等等。 這裡我們說一說綁定List類型並實時更新datagridview的情況。實時更新,指的是在我操作BindingList時(刪除/Add)等操作的時候,datagridview也會發生相應變 ...
  • ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...