国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

對(duì)比編程語言的四種錯(cuò)誤處理方法,哪種才是最優(yōu)方案?

這篇具有很好參考價(jià)值的文章主要介紹了對(duì)比編程語言的四種錯(cuò)誤處理方法,哪種才是最優(yōu)方案?。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

作者:Andrea Bergia

譯者:豌豆花下貓@Python貓

英文:Error handling patterns

轉(zhuǎn)載請(qǐng)保留作者及譯者信息!

錯(cuò)誤處理是編程的一個(gè)基本要素。除非你寫的是“hello world”,否則就必須處理代碼中的錯(cuò)誤。在本文中,我將討論各種編程語言在處理錯(cuò)誤時(shí)使用的最常見的四種方法,并分析它們的優(yōu)缺點(diǎn)。

關(guān)注不同設(shè)計(jì)方案的語法、代碼可讀性、演變過程、運(yùn)行效率,將有助于我們寫出更為優(yōu)雅和健壯的代碼。

返回錯(cuò)誤代碼

這是最古老的策略之一——如果一個(gè)函數(shù)可能會(huì)出錯(cuò),它可以簡單地返回一個(gè)錯(cuò)誤代碼——通常是負(fù)數(shù)或者null。例如,C 語言中經(jīng)常使用:

FILE* fp = fopen("file.txt" , "w");
if (!fp) {
  // 發(fā)生了錯(cuò)誤
}

這種方法非常簡單,既易于實(shí)現(xiàn),也易于理解。它的執(zhí)行效率也非常高,因?yàn)樗恍枰M(jìn)行標(biāo)準(zhǔn)的函數(shù)調(diào)用,并返回一個(gè)值,不需要有運(yùn)行時(shí)支持或分配內(nèi)存。但是,它也有一些缺點(diǎn):

  • 用戶很容易忘記處理函數(shù)的錯(cuò)誤。例如,在 C 中,printf 可能會(huì)出錯(cuò),但我?guī)缀鯖]有見過程序檢查它的返回值!
  • 如果代碼必須處理多個(gè)不同的錯(cuò)誤(打開文件,寫入文件,從另一個(gè)文件讀取等),那么傳遞錯(cuò)誤到調(diào)用堆棧會(huì)很麻煩。
  • 除非你的編程語言支持多個(gè)返回值,否則如果必須返回一個(gè)有效值或一個(gè)錯(cuò)誤,就很麻煩。這導(dǎo)致 C 和 C++ 中的許多函數(shù)必須通過指針來傳遞存儲(chǔ)了“成功”返回值的地址空間,再由函數(shù)填充,類似于:
my_struct *success_result;
int error_code = my_function(&success_result);
if (!error_code) {
  // can use success_result
}

眾所周知,Go 選擇了這種方法來處理錯(cuò)誤,而且,由于它允許一個(gè)函數(shù)返回多個(gè)值,因此這種模式變得更加人性化,并且非常常見:

user, err = FindUser(username)
if err != nil {
    return err
}

Go 采用的方式簡單而有效,會(huì)將錯(cuò)誤傳遞到調(diào)用方。但是,我覺得它會(huì)造成很多重復(fù),而且影響到了實(shí)際的業(yè)務(wù)邏輯。不過,我寫的 Go 還不夠多,不知道這種印象以后會(huì)不會(huì)改觀!??

異常

異??赡苁亲畛S玫腻e(cuò)誤處理模式。try/catch/finally 方法相當(dāng)有效,而且使用簡單。異常在上世紀(jì) 90 年代到 2000 年間非常流行,被許多語言所采用(例如 Java、C# 和 Python)。

與錯(cuò)誤處理相比,異常具有以下優(yōu)點(diǎn):

  • 它們自然地區(qū)分了“快樂路徑”和錯(cuò)誤處理路徑
  • 它們會(huì)自動(dòng)從調(diào)用堆棧中冒泡出來
  • 你不會(huì)忘記處理錯(cuò)誤!

然而,它們也有一些缺點(diǎn):需要一些特定的運(yùn)行時(shí)支持,通常會(huì)帶來相當(dāng)大的性能開銷。

此外,更重要的是,它們具有“深遠(yuǎn)”的影響——某些代碼可能會(huì)拋出異常,但被調(diào)用堆棧中非常遠(yuǎn)的異常處理程序捕獲,這會(huì)影響代碼的可讀性。

此外,僅憑查看函數(shù)的簽名,無法確定它是否會(huì)拋出異常。

C++ 試圖通過throws 關(guān)鍵字來解決這個(gè)問題,但它很少被使用,因此在 C++ 17 中已被棄用 ,并在 C++ 20 中被刪除。此后,它一直試圖引入noexcept 關(guān)鍵字,但我較少寫現(xiàn)代 C++,不知道它的流行程度。

(譯者注:throws 關(guān)鍵字很少使用,因?yàn)槭褂眠^于繁瑣,需要在函數(shù)簽名中指定拋出的異常類型,并且這種方法不能處理運(yùn)行時(shí)發(fā)生的異常,有因?yàn)椤拔粗惓!倍鴮?dǎo)致程序退出的風(fēng)險(xiǎn))

Java 曾試圖使用“受檢的異常(checked exceptions)”,即你必須將異常聲明為函數(shù)簽名的一部分——但是這種方法被認(rèn)為是失敗的,因此像 Spring 這種現(xiàn)代框架只使用“運(yùn)行時(shí)異?!?,而有些 JVM 語言(如 Kotlin)則完全拋棄了這個(gè)概念。這造成的結(jié)果是,你根本無法確定一個(gè)函數(shù)是否會(huì)拋出什么異常,最終只得到了一片混亂。

(譯者注:Spring 不使用“受檢的異?!保?yàn)檫@需要在函數(shù)簽名及調(diào)用函數(shù)中顯式處理,會(huì)使得代碼過于冗長而且造成不必要的耦合。使用“運(yùn)行時(shí)異?!?,代碼間的依賴性降低了,也便于重構(gòu),但也造成了“異常源頭”的混亂)

回調(diào)函數(shù)

另一種方法是在 JavaScript 領(lǐng)域非常常見的方法——使用回調(diào),回調(diào)函數(shù)會(huì)在一個(gè)函數(shù)成功或失敗時(shí)調(diào)用。這通常會(huì)與異步編程結(jié)合使用,其中 I/O 操作在后臺(tái)進(jìn)行,不會(huì)阻塞執(zhí)行流。

例如,Node.JS 的 I/O 函數(shù)通常加上一個(gè)回調(diào)函數(shù),后者使用兩個(gè)參數(shù)(error,result),例如:

const fs = require('fs');
fs.readFile('some_file.txt', (err, result) => {
  if (err) {
    console.error(err);
    return;
  }

  console.log(result);
});

但是,這種方法經(jīng)常會(huì)導(dǎo)致所謂的“回調(diào)地獄”問題,因?yàn)橐粋€(gè)回調(diào)可能需要調(diào)用其它的異步 I/O,這可能又需要更多的回調(diào),最終導(dǎo)致混亂且難以跟蹤的代碼。

現(xiàn)代的 JavaScript 版本試圖通過引入promise 來提升代碼的可讀性:

fetch("https://example.com/profile", {
      method: "POST", // or 'PUT'
})
  .then(response => response.json())
  .then(data => data['some_key'])
  .catch(error => console.error("Error:", error));

promise 模式并不是最終方案,JavaScript 最后采用了由 C#推廣開的 async/await 模式,它使異步 I/O 看起來非常像帶有經(jīng)典異常的同步代碼:

async function fetchData() {
  try {
    const response = await fetch("my-url");
    if (!response.ok) {
      throw new Error("Network response was not OK");
    }
    return response.json()['some_property'];
  } catch (error) {
    console.error("There has been a problem with your fetch operation:", error);
  }
}

使用回調(diào)進(jìn)行錯(cuò)誤處理是一種值得了解的重要模式,不僅僅在 JavaScript 中如此,人們在 C 語言中也使用了很多年。但是,它現(xiàn)在已經(jīng)不太常見了,你很可能會(huì)用的是某種形式的async/await。

函數(shù)式語言的 Result

我最后想要討論的一種模式起源于函數(shù)式語言,比如 Haskell,但是由于 Rust 的流行,它已經(jīng)變得非常主流了。

它的創(chuàng)意是提供一個(gè)Result類型,例如:

enum Result<S, E> {
  Ok(S),
  Err(E)
}

這是一個(gè)具有兩種結(jié)果的類型,一種表示成功,另一種表示失敗。返回結(jié)果的函數(shù)要么返回一個(gè)Ok 對(duì)象(可能包含有一些數(shù)據(jù)),要么返回一個(gè)Err 對(duì)象(包含一些錯(cuò)誤詳情)。函數(shù)的調(diào)用者通常會(huì)使用模式匹配來處理這兩種情況。

為了在調(diào)用堆棧中拋出錯(cuò)誤,通常會(huì)編寫如下的代碼:

let result = match my_fallible_function() {
  Err(e) => return Err(e),
  Ok(some_data) => some_data,
};

由于這種模式非常常見,Rust 專門引入了一個(gè)操作符(即問號(hào) ?) 來簡化上面的代碼:

let result = my_fallible_function()?;   // 注意有個(gè)"?"號(hào)

這種方法的優(yōu)點(diǎn)是它使錯(cuò)誤處理既明顯又類型安全,因?yàn)榫幾g器會(huì)確保處理每個(gè)可能的結(jié)果。

在支持這種模式的編程語言中,Result 通常是一個(gè) monad,它允許將可能失敗的函數(shù)組合起來,而無需使用 try/catch 塊或嵌套的 if 語句。

(譯者注:函數(shù)式編程認(rèn)為函數(shù)的輸入和輸出應(yīng)該是純粹的,不應(yīng)該有任何副作用或狀態(tài)變化。monad 是一個(gè)函數(shù)式編程的概念,它通過隔離副作用和狀態(tài)來提高代碼的可讀性和可維護(hù)性,并允許組合多個(gè)操作來構(gòu)建更復(fù)雜的操作)

根據(jù)你使用的編程語言和項(xiàng)目,你可能主要或僅僅使用其中一種錯(cuò)誤處理的模式。

不過,我最喜歡的還是 Result 模式。當(dāng)然,不僅是函數(shù)式語言采用了它,例如,在我的雇主 lastminute.com 中,我們在 Kotlin 中使用了 Arrow 庫,它包含一個(gè)受 Haskell 強(qiáng)烈影響的類型Either。我有計(jì)劃寫一篇關(guān)于它的文章,最后感謝你閱讀這篇文章,敬請(qǐng)保持關(guān)注??。

譯注:還有一篇《Musings about error handling mechanisms in programming languages》文章,同樣分析了不同編程語言在錯(cuò)誤處理時(shí)的方案。它還介紹了 Zig 編程語言的做法、Go 語言的 defer 關(guān)鍵字等內(nèi)容,可以豐富大家對(duì)這個(gè)話題的理解,推薦一讀。文章來源地址http://www.zghlxwxcb.cn/news/detail-436780.html

到了這里,關(guān)于對(duì)比編程語言的四種錯(cuò)誤處理方法,哪種才是最優(yōu)方案?的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包