rust 使用第三方庫構建mini命令行工具

来源:https://www.cnblogs.com/dreamHot/archive/2023/06/18/17488550.html
-Advertisement-
Play Games

這是上一篇 [rust 學習 - 構建 mini 命令行工具](https://www.cnblogs.com/dreamHot/p/17467837.html)的續作,擴展增加一些 crate 庫。這些基礎庫在以後的編程工作中會常用到,他們作為基架存在於項目中,解決項目中的某個問題。 項目示例還是 ...


這是上一篇 rust 學習 - 構建 mini 命令行工具的續作,擴展增加一些 crate 庫。這些基礎庫在以後的編程工作中會常用到,他們作為基架存在於項目中,解決項目中的某個問題。

項目示例還是以上一篇的工程為基礎做調整修改ifun-grep 倉庫地址

怎麼去使用已發佈的 crate 庫

在開發ifun-grep項目時,運行項目命令為cargo run -- hboot hello.txt,測試項目的邏輯正確。在發佈到crates.io要如何使用呢,

在項目中使用

作為項目的一個功能函數,邏輯事務調用。在crates.io 中找到需要的庫

安裝已經發佈的示例庫ifun-grep . 通過cargo add添加依賴項

這裡我們有一個測試示例項目rust-web,這是在另一篇rust 基礎中創建的示例項目。

$> cargo add ifun-grep

安裝成功後,可以在在項目的Cargo.toml看到依賴

[dependencies]
ifun-grep = "0.1.0"

main.rs導入庫使用,這個庫包括了一個結構體Config,三個方法find\find_insensitive\run

use ifun_grep;

fn main(){
    let search = String::from("let");
    let config = ifun_grep::Config {
        search,
        file_path: String::from("hello.txt"),
        ignore_case: true,
    };

    let result = ifun_grep::run(config);

    println!("{}", result.is_ok());
}

執行cargo run,可以看到輸出了false。因為文件hello.txt不存在,在上一篇文中我們把錯誤處理統一放到了main.rs文件中處理的。而我們這邊作為一個 lib 庫,直接調用的run函數,所以這邊沒有任何的錯誤輸出,只提示我們沒有執行成功。

我們可以在項目目錄下新建一個測試文件hello.txt

Let life be beautiful like summer flowers.

The world has kissed my soul with its pain.

Eyes are raining for her.

you also miss the stars.

再次運行,可以看到列印的輸出內容。Let life be beautiful like summer flowers.

可以通過cargo remove ifun-grepCargo.toml移除依賴

作為腳本命令執行

可以看到作為功能性函數調用時,只能手動去初始化函數調用。不能像執行命令一樣,傳遞參數調用,也就不能執行main.rs中的處理邏輯以及錯誤列印。

通過cargo install 安裝二進位可執行文件的庫

$> cargo install ifun-grep

安裝完成後,就可以在全局環境中使用命令ifun-grep了。

通過cargo uninstall ifun-grep 移除。

開發時如何測試使用

開發時只能cargo run去執行main.rs文件,不能直接使用ifun-grep命令

可以通過cargo build 構建編譯,在target/debug下生成二進位文件

ifun-grep-build.jpg

這樣可以通過相對目錄地址訪問可執行文件執行命令

$> target/debug/ifun-grep Let hello.txt

如果我們的代碼 存儲在 github 或者 gitee 上,就可以將編譯包壓縮發佈版本,這樣需要的人不需要 cargo 就可以下載安裝。

構建發佈版本

$> cargo build --release

我的代碼倉庫在 giteeifun-grep 基礎版本發佈

ifun-grep-release v0.1.0.png

下載壓縮包後,需要把可執行文件配置到系統環境中,全局可用。也可以不用配置,直接使用文件路徑地址執行命令。

還需要考慮一個問題,就是系統的相容性,mac、windows、linux 等等,想要發佈一個相容的庫,可能還需要針對性構建編譯包併發布

這裡演示的是 mac 系統下載發佈包後,通過路徑訪問執行命令

ifun-grep-release v0.1.0 -run.png

clap庫解析 cli 參數

clap庫包含了對子命令、shell 完成和更好的幫助信息。

安裝,參數--features表示啟動特定功能,

$> cargo add clap --features derive

clap除了提供基礎的功能之外,還可以通過--features開啟特定功能。derive啟動自定義派生功能,可以通過過程巨集處理參數。

src/main.rs 中使用

// use std::{env, process};
use clap::Parser;
use ifun_grep::{Config};

fn main() {
    // let args: Vec<String> = env::args().collect();

    let config = Config::parse();

    // let config = Config::build(&args).unwrap_or_else(|err| {
    //     // println!("error occurred parseing args:{err}");
    //     eprintln!("error occurred parseing args:{err}");
    //     process::exit(1);
    // });
}

結構體 Config 派生了一個內部函數parse,可以直接解析參數生成實例 config

還需要修改src/lib.rs,使得結構體 Config 用擁有這種能力

use clap::Parser;

#[derive(Parser, Debug)]
pub struct Config {
    #[arg(long)]
    pub search: String,
    #[arg(long)]
    pub file_path: String,
    #[arg(long)]
    pub ignore_case: bool,
}

首先不再使用std::env去解析 cli 參數,也不需要調用Config的 build 方法去實例化創建 config。

通過clap::Parser的過程式巨集 Parser 去解析 cli 參數,並返回結構體Config的實例 config

執行命令

$> cargo run

報錯了,如圖,首先這個錯誤信息很友好,告訴我們必填的參數信息

clap-run.png

增加參數配置,調用命令執行

$> cargo run --  --search Let --file-path hello.txt

可以看到結果成功了,對比之前調用方式cargo run -- Let hello.txt,多了一個參數名稱定義--search

#[arg(long)] 參數巨集是用來定義參數接受的標誌,arg還有許多其他的功能

移除掉#[arg(long)],執行命令 cargo run

use clap::Parser;

#[derive(Parser, Debug)]
pub struct Config {
    pub search: String,
    pub file_path: String,
    pub ignore_case: bool,
}

報錯了,thread 'main' panicked at 'Argument 'ignore_case is positional, it must take a value,意思是 ignore_case 必須要有一個值,ignore_case是一個布爾值,隱式啟動了#[arg(action = ArgAction::SetTrue)],所以需要設置接受標誌

布爾值只需要通過設置標誌,而不需要設置值,--ignore_case 就表示 true

use clap::Parser;

pub struct Config {
    pub search: String,
    pub file_path: String,
    #[arg(short, long)]
    pub ignore_case: bool,
}

再次執行命令cargo run,

clap-run-noArg.png

還需要必填的兩個參數,此時不需要--

$> cargo run --  Let hello.txt

要想開啟大小寫不敏感,則需要增加--ignore-case

$> cargo run --  let hello.txt --ignore-case

需要註意的是結構體定義的下劃線ignore_case,在 clap 接受參數的標誌為--ignore-case

增加命令的描述信息

通常 cli 的命令都有一個--help功能,這可以基本說明這個腳本是幹嘛的,以及怎麼去使用

而這些 clap 正好有。測試一下,代碼修改後需執行cargo build

$> target/debug/ifun-grep --help

ifun-grep-help.png

可以看到對於ifun-grep一個基本的使用方式,包括Usage、Arguments、Options。還展示了對於結構體Config的註釋說明、例子。
通過簡寫的-h可以讓描述更加緊湊一點。

clap 通過#[command()]可以從Cargo.toml獲取到一些基礎信息,生成 Command 實例

#[derive(Parser)]
#[command(author, version, about, long_about = None)]
pub struct Config {
    pub search: String,
    pub file_path: String,
    #[arg(short, long)]
    pub ignore_case: bool,
}

編譯後,執行--help

ifun-grep-clap-command.png

也可以自定義這些欄位的值。

#[derive(Parser)]
#[command(name = "ifun-grep")]
#[command(author = "hboot <[email protected]>")]
#[command(version = "0.2.0")]
#[command(about="A simple fake grep",long_about=None)]
pub struct Config {
    pub search: String,
    pub file_path: String,
    #[arg(short, long)]
    pub ignore_case: bool,
}

通過執行ifun-grep -V可以查看設置的name、version信息

定義參數非必須

通過Option定義欄位數據類型,使得這個欄位非必須

#[derive(Parser)]
pub struct Config {
    name: Option<String>,
}

通過--help查看參數是,必須的Arguments:參數是<SEARCH>使用尖括弧的;而非必須的是中括弧[name].

如果某個參數可以接受多個,則通過集合定義類型

#[derive(Parser)]
pub struct Config {
    name: Vec<String>,
}

在命令執行多餘的參數會解析到欄位 name 中。隱式的啟動了#[arg(action = ArgAction::Set)],處理多個值。

使用標誌命名參數

在之前的實例中,已經使用了#[arg(short, long)],它用來標識參數名稱,它可以:

  • 意圖表達更明確
  • 不用在意參數的順序
  • 使參數變的可選
#[derive(Parser)]
pub struct Config {
    #[arg(short, long)]
    pub search: String,
    #[arg(short, long)]
    pub file_path: String,
    #[arg(short, long)]
    pub ignore_case: bool,
}

可以通過--help查看變化,所有的參數都變成了Options

ifun-grep-arg.png

子命令

在執行ifun-grep時,攜帶子命令執行。通過#[derive(Subcommand)]標誌屬性,子命令也可以有自己的版本、作者信息、參數等等

use clap::{Args, Parser, Subcommand};

#[derive(Parser)]
pub struct Config {
    #[arg(short, long)]
    pub search: String,
    #[arg(short, long)]
    pub file_path: String,
    #[arg(short, long)]
    pub ignore_case: bool,
    #[command(subcommand)]
    pub command: Commands,
}

#[derive(Subcommand)]
pub enum Commands {
    Add(AddArgs),
}

#[derive(Args)]
pub struct AddArgs {
    name: Option<String>,
}

預設值

可以通過#[arg(default_value_t)]定義預設值,定義欄位file_path預設值

#[derive(Parser)]
pub struct Config {
    #[arg(short, long)]
    pub search: String,
    #[arg(short, long, default_value = "hello.txt")]
    pub file_path: String,
    #[arg(short, long)]
    pub ignore_case: bool,
}

調用命令執行時,可以不在設置該欄位

$> target/debug/ifun-grep -s Let

命令執行是查詢成功的.

其他的功能比如:數據校驗、自定義值解析邏輯、自定義校驗等等。

anyhow 處理錯誤

提供了一種錯誤類型anyhow::Error. 處理出現的錯誤。

之前處理文件讀取的邏輯,使用了?

pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
    let content = fs::read_to_string(config.file_path)?;

    Ok(())
}

當文件不存在時,會列印出錯誤信息something error:No such file or directory (os error 2).但不知道具體哪個文件不存在。

可以通過自定義錯誤類型IfunError,來構建自己的錯誤信息

#[derive(Debug)]
pub struct IfunError(String);

pub fn run(config: Config) -> Result<(), IfunError> {
    let file_path = config.file_path.clone();
    let content = fs::read_to_string(config.file_path)
        .map_err(|err| IfunError(format!("could not read file {} - {}", file_path, err)))?;

    // ...
    Ok(())
}

再次執行訪問不存在的文件,報錯信息為something error:IfunError("could not read file 1.txt - No such file or directory (os error 2)")

anyhow正好做了事情,可以通過 anyhow 的特征context可以附加錯誤內容信息,也保留了原始錯誤。

安裝 crate anyhow

$> cargo add anyhow

調整處理讀取文件的函數run,在src/lib.rs文件中修改:

use anyhow::{Context, Result};

pub fn run(config: Config) -> Result<()> {
    let file_path = config.file_path.clone();
    let content = fs::read_to_string(config.file_path)
        .with_context(|| format!("could not read file {}", file_path))?;

    // ...
    Ok(())
}

還需要修改src/main.rs文件,將錯誤輸出方式改為{:?}

fn main() {
    // ...
    if let Err(e) = run(config) {
        // println!("something error:{e}");
        eprintln!("something error:{:?}", e);
        process::exit(1);
    }
}

再次執行命令,可以看到錯誤更加的友好。

ifun-grep-anyhow.png

使用anyhow!()巨集,輸出錯誤信息

src/main.rs,讀取文件之前增加錯誤輸出

fn main(){
    // ...
    println!("{}", anyhow!("anyhow error {}", "running"));
    //...
}

使用bail!()巨集,中斷執行

調用執行返回錯誤,中斷程式執行

src/main.rs,讀取文件之前增加錯誤輸出

use anyhow::{anyhow, bail};

fn main() -> Result<(), anyhow::Error> {
    // ...
    println!("{}", anyhow!("anyhow error {}", "running"));

    bail!("permission denied for accessing {}", config.file_path)
    //...
}

調用bail!時,返回值必須是Result<(), anyhow::Error>類型

bail!同等於return Err(anyhow!())

跟蹤錯誤棧

列印出錯誤信息,我們可以知道發生了錯誤,想知道是哪個文件、那行代碼發生的錯誤,則需要開啟錯誤棧追蹤。

這是一個特性功能,需要指定特性啟用

$> cargo add anyhow --features backtrace

然後通過設置環境變數,

  • RUST_BACKTRACE=1 panics和 error 都有錯誤棧輸出
  • RUST_LIB_BACKTRACE=1 僅打開錯誤輸出
  • RUST_BACKTRACE=1RUST_LIB_BACKTRACE=0 僅 panic 時

在執行命令時,設置環境變數,打開錯誤輸出時的錯誤追蹤

$> RUST_LIB_BACKTRACE=1 cargo run -- -s let -f 1.txt

thiserror 自定義自己的錯誤類型

anyhow不同,thiserror可以用來自定義錯誤類型。

通過過程式巨集#[derive(Error)],它是由 std::error::Error派生而來。

$> cargo add thiserror

定義一個文件不能存在的錯誤類型,並用於讀取文件時的邏輯

use thiserror::Error;

#[derive(Error, Debug)]
pub enum IfunError {
    #[error("the file is't exist")]
    FileNotExist(#[from] std::io::Error),
}

pub fn run(config: Config) -> Result<(), IfunError> {
    let content = fs::read_to_string(config.file_path)?;

    // ...

    Ok(())
}

執行命令,訪問不存在的文件。錯誤信息輸出會被自定義的類型包裹:

ifun-grep-thiserror.png

ansi_term 更好的列印輸出

ansi_term控制臺上的列印輸出,包括字體樣式、格式化。

安裝

$> cargo add ansi_term

包括對文本的字體顏色、背景色、是否加粗、是否閃爍等等。

通過ansi_term::Colour控制字體樣式

我們將ifun-grep的 參數列印使用顏色標記輸出

use ansi_term::Colour::{Green, Yellow};

fn main(){
    //...
    println!(
        "will search {} in {}",
        Green.paint(&config.search),
        Yellow.paint(&config.file_path)
    );
}

執行命令cargo run -- -s Let -f hello.txt

ifugn-grep-ansi_term.png

加粗bold()、加下劃線underline()、背景色on()

use ansi_term::Colour::{Green, Yellow};

fn main(){
    //...
    println!(
        "will search {} in {}",
        Green.bold().paint(&config.search),
        Yellow.underline().paint(&config.file_path)
    );
}

給程式查詢出的行數據加背景色、閃爍

use ansi_term::Colour::{Red, Yellow};

pub fn run(config: Config) -> Result<(), anyhow::Error> {
    //...
    for line in result {
        println!("{}", Red.on(Yellow).blink().paint(line));
    }

    Ok(())
}

通過ansi_term::Style控制樣式

Colour是一個枚舉類型,專門針對顏色樣式處理;Style是結構體類型,是字體樣式的集合。

設置字體顏色,結構體需要實例化一個實例對象,然後再調用對應的方法。

use ansi_term::Colour::{Green, Yellow};
use ansi_term::Style;

fn main(){
    //...

    println!(
        "will search {} in {}",
        Green.bold().paint(&config.search),
        // Yellow.underline().paint(&config.file_path)
        Style::new().fg(Yellow).paint(&config.file_path)
    );
}

顏色擴展ansi_term::Colour::Fixed

除了內置枚舉的顏色,還可以通過色碼值設置顏色。0-255

use ansi_term::Colour::Fixed;

Fixed(154).paint("other color");

也可以通過ansi_term::Colour::RGB,設置三個不同的值

use ansi_term::Colour::RGB;

RGB(154, 56, 178).paint("other color");

此外還有內置ANSIStrings類型,可以通過to_string()方法轉換為String;

支持格式化輸出\[u8]位元組字元串,對於不知道編碼的文本輸出很有用。會生成ANSIByteString類型,通過write_to方法寫入輸出流中。

Green.paint("ansi_term".as_bytes()).write_to(&mut std::io::stdout()).unwrap();

indicatif展示進度條

處理任務時,顯示任務的執行進度。會讓人感覺良好,更有耐心等待執行完畢

$> cargo add indicatif

手動創建一個進度條,為了看到進度條的進度效果,可以使用std::thred線程休眠一段時間。

use indicatif::ProgressBar;
use std::{thread, time};

fn main(){
    let bar = ProgressBar::new(100);
    let ten_millis = time::Duration::from_millis(10);
    for _ in 0..100 {
        bar.inc(1);
        thread::sleep(ten_millis);
        // ...
    }

    bar.finish();
}

通過ProgressBar類型創建了一個進度條的實例對象,然後通過實例bar.inc()逐步增加進度。完成後調用bar.finish()表示進度完成,並保留顯示進度信息。

ifun-grep-indicatif.jpg

也支持多進度條的MultiProgress

log日誌記錄

一個程式運行時期的日誌列印,非常重要,這對於運行監測喝解決有問題都有很到的幫助。

$> cargo add log

通常可以將日誌按照登記劃分,比如錯誤、警告、信息等。還需要一個日誌輸出的適配器 env_logger,可以將日誌寫入終端、日誌伺服器等。

$> cargo add env_logger

美化輸出,將接受到的參數作為信息info!()輸出,將產生的錯誤使用error!()輸出

env_logger預設輸出日誌到終端,

use log;

fn main() {
    env_logger::init();

    // ...
    log::info!(
        "will search {} in {}",
        Green.bold().paint(&config.search),
        Style::new().fg(Yellow).paint(&config.file_path)
    );

    // ...

    if let Err(e) = run(config) {
        log::error!("something error:{:?}", e);
        process::exit(1);
    }

}

必須在程式之前初始化完畢日誌環境變數配置。預設只展示error錯誤類型的日誌

執行cargo run -- -s Let -f 1.txt命令訪問不存在的文件,可以看到只有 error 錯誤輸出列印。

通過設置變數RUST_LOG=info,查看

$> RUST_LOG=info cargo run -- -s Let -f 1.txt

ifun-grep-log.png

初始化指定信息類型

在執行命令前加上RUST_LOG=info很麻煩,有遺忘的可能,可以通過初始化env_logger::init()調用時,設定一個預設值

use env_logger::Env;

fn mian(){
    env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();

    // ...
}

通過終端設置的變數優先順序比預設值高,可以通過執行時設置變數覆蓋預設值。

自定義輸出模板

可以看到預設的輸出列印包括了時間、類型以及模塊名。可以通過改變模板自定義輸出格式

use std::io::Write;

fn main(){
    env_logger::builder()
        .format(|buf, record| writeln!(buf, "{} - {}", record.level(), record.args()))
        .init();
}

輸出格式改變為信息類型 - 信息。使用預設的挺好,現在好多編輯器的日誌輸出都是這種格式。

測試

之前的單元測試示例都是和邏輯代碼放在一起的,並用#[test]註釋。可以將這些測試放在tests目錄中

新建tests/lib.rs用於存放單元測試用例。

use ifun_grep::{find, find_insensitive};

#[test]
fn case_sensitive() {
    let search = "rust";
    let content = "\
nice. rust
I'm hboot.
hello world.
Rust
";

    assert_eq!(vec!["nice. rust"], find(search, content));
}

#[test]
fn case_insensitive() {
    let search = "rust";
    let content = "\
nice. rust
I'm hboot.
hello world.
Rust
";

    assert_eq!(
        vec!["nice. rust", "Rust"],
        find_insensitive(search, content)
    );
}

通過藉助第三飯庫來使得測試更容易,

assert_cmd

可以處理結果進行斷言;也可以測試調用命令進行測試。一起配合使用的還有predicates用來斷言布爾值類型結果值

因為測試示例只在開發階段需要,則在安裝時加參數--dev

$> cargo add assert_cmd predicates --dev

新增一個處理文件不存在的的測試示例。日誌列印輸出時會包含有could not read file字元串。

use assert_cmd::prelude::*;
use ifun_grep::{find, find_insensitive};
use predicates::prelude::*;
use std::{error::Error, process::Command};

#[test]
fn file_doesnt_exist() -> Result<(), Box<dyn Error>> {
    let mut cmd = Command::cargo_bin("ifun-grep")?;

    cmd.arg("-s let").arg("-f 1.txt");
    cmd.assert()
        .failure()
        .stderr(predicate::str::contains("could not read file"));

    Ok(())
}

通過運行cargo test,測試示例是運行成功的。

assert_fs用於測試文件系統的斷言

剛纔測試了文件不存在的錯誤輸出,還需要增加文件存在的測試,並寫入內容。

$> cargo add assert_fs --dev

生成要測試的文件;斷言測試生成的文件。tests/lib.rs增加測試用例

use assert_cmd::prelude::*;
use assert_fs::prelude::*;
use ifun_grep::{find, find_insensitive};
use predicates::prelude::*;
use std::{error::Error, process::Command};

#[test]
fn file_content_exist() -> Result<(), Box<dyn Error>> {
    let file = assert_fs::NamedTempFile::new("1.txt")?;
    file.write_str("hello world \n Rust-web \n good luck for you!")?;

    let mut cmd = Command::cargo_bin("ifun-grep")?;

    cmd.arg("-s good").arg("-f").arg(file.path());
    cmd.assert()
        .success()
        .stderr(predicate::str::contains("good luck for you!"));

    Ok(())
}

這樣書寫的單元測試用例更能直接、明瞭。和實際使用ifun-grep時同樣的命令操作,而不是使用開發時運行cargo run

追逐的不應該是夢想,隨心所欲,隨遇而安!
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ## 一、問題一:不能正常開啟虛擬機 創建虛擬機後,我錯誤的使用了`shutdown now`的關機命令,每次關機不能正常啟動虛擬機,需要重啟VMware的五大服務,然後重啟電腦才能正常啟動虛擬機。 ## 二、問題二:啟動虛擬機黑屏的解決方案 偶爾啟動虛擬機時,會一直長時間的黑屏沒有反應,從網上查找 ...
  • 本文主要驗證 Elasticsearch 快照在 [Easysearch](http://www.infinilabs.com/docs/latest/easysearch/overview) 中進行數據恢復。 ## 準備測試數據 ### 索引 ![](https://www.infinilabs. ...
  • 在Android開發中,有時候出於安全,性能,代碼共用的考慮,需要使用C/C++編寫的庫。雖然在現代化工具鏈的支持下,這個工作的難度已經大大降低,但是畢竟萬事開頭難,初學者往往還是會遇到很多不可預測的問題。本篇就是基於此背景下寫的一份簡陋指南,希望能對剛開始編寫C/C++庫的讀者有所幫助。同時為了盡 ...
  • ### 什麼是JNI開發 JNI的全稱是Java Native Interface,顧名思義,這是一種解決Java和C/C++相互調用的編程方式。**它其實只解決兩個方面的問題,怎麼找到和怎麼訪問。** 弄清楚這兩個話題,我們就學會了JNI開發。**需要註意的是,JNI開發只涉及到一小部分C/C++ ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前言 在使用vue項目編寫的時候,不可避免的會碰到需要時js api來調用組件進行顯示的情況 例如餓了麽element ui 的 Notification 通知、Message 消息提示等組件 雖然已經提供了,但是由於api的限制,我們只 ...
  • ## 1. 對象的定義與賦值 經常使用的定義與賦值方法`obj.prop =value`或者`obj['prop']=value` ```js let Person = {}; Person.name = "Jack"; Person["gender"] = "female"; console.lo ...
  • 最近得空學習了下uniapp結合vue3搭建跨端項目。之前也有使用uniapp開發過幾款聊天/仿抖音/後臺管理等項目,但都是基於vue2開發。隨著vite.js破局出圈,越來越多的項目偏向於vue3開發,就想著uniapp搭配vite4.x構建項目效果會如何?經過一番嘗試果然真香~ 版本信息 HBu ...
  • ### 迭代器模式(Iterator Pattern) #### 一、定義 提供一種方法順序訪問一個聚合對象中各個元素,而又不需要暴露該對象的內部表示。 #### 二、優缺點 **優點:** 1、它支持以不同的方式遍歷一個聚合對象。 2、迭代器簡化了聚合類。 3、在同一個聚合上可以有多個遍歷。 4、 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...