1、defer語句
defer?語句的用途是:含有?defer?語句的函數(shù)或類型方法,會(huì)在該函數(shù)或方法將要返回之前(return之前),調(diào)用另一個(gè)函數(shù)。所以,****我們可以在函數(shù)開始時(shí),將一些容易忘記的結(jié)束操作用defer聲明好,這樣保證這些結(jié)束操作能在函數(shù)結(jié)束后正確執(zhí)行。當(dāng)defer要調(diào)用的函數(shù)有參數(shù)時(shí),執(zhí)行?defer?語句的時(shí)候,就會(huì)對(duì)延遲函數(shù)的實(shí)參進(jìn)行求值。舉例如下
func?printA(a?int)?{??
????fmt.Println("value?of?a?in?deferred?function",?a) } func?main()?{?? ????a?:=?5 ????defer?printA(a)?//此時(shí)a=5,所以實(shí)際傳到printA中的參數(shù)值為5,輸出為5 ????a?=?10 ????fmt.Println("value?of?a?before?deferred?function?call",?a) }
當(dāng)一個(gè)函數(shù)內(nèi)多次調(diào)用?defer?時(shí),Go 會(huì)把?defer?調(diào)用放入到一個(gè)棧中,隨后按照后進(jìn)先出(Last In First Out, LIFO)的順序執(zhí)行。
2、錯(cuò)誤處理
2.1、error類型
在 Go 中,錯(cuò)誤一直是很常見的。錯(cuò)誤用內(nèi)建的?error?類型來表示。就像其他的內(nèi)建類型(如?int、float64?等),錯(cuò)誤值可以存儲(chǔ)在變量里、作為函數(shù)的返回值等等。error類型定義如下:
type?error?interface?{?//是一個(gè)接口,實(shí)現(xiàn)該接口的類型都可以當(dāng)作錯(cuò)誤類型 ????Error()?string }
使用示例:
func?main()?{?? ????f,?err?:=?os.Open("/test.txt") ????if?err?!=?nil?{//error類型 ????????fmt.Println(err) ????????return ????} ????fmt.Println(f.Name(),?"opened?successfully")//println會(huì)自動(dòng)調(diào)用Error()輸出錯(cuò)誤信息 }
2.2、提取錯(cuò)誤信息的方法
Error()返回的描述不是很詳細(xì),我們可以用下面的方法獲取更詳細(xì)的錯(cuò)誤信息:
判斷底層結(jié)構(gòu)體類型(即實(shí)現(xiàn)error接口的結(jié)構(gòu)體),使用結(jié)構(gòu)體字段獲取更多信息
判斷底層結(jié)構(gòu)體類型(即實(shí)現(xiàn)error接口的結(jié)構(gòu)體),調(diào)用類型的方法獲取更多信息
與error類型變量直接比較
func?main()?{??
????files,?err?:=?filepath.Glob("[") ????if?error?!=?nil?&&?err?==?filepath.ErrBadPattern?{//比較,判斷是不是某個(gè)指定的錯(cuò)誤類型 ????????fmt.Println(err) ????????return ????} ????fmt.Println("matched?files",?files) }
2.3、fmt.Errorf
fmt?包中的?Errorf?函數(shù)會(huì)根據(jù)格式說明符,規(guī)定錯(cuò)誤的格式,并返回一個(gè)符合該錯(cuò)誤的字符串。示例:
func?circleArea(radius?float64)?(float64,?error)?{??
????if?radius?0?{ ????????//返回錯(cuò)誤類型 ????????return?0,?fmt.Errorf("Area?calculation?failed,?radius?%0.2f?is?less?than?zero",?radius) ????} ????return?math.Pi?*?radius?*?radius,?nil } func?main()?{?? ????radius?:=?-20.0 ????area,?err?:=?circleArea(radius) ????if?err?!=?nil?{ ????????fmt.Println(err) ????????return ????} ????fmt.Printf("Area?of?circle?%0.2f",?area) }
3 panic和recover
有些情況,當(dāng)程序發(fā)生異常時(shí),無法繼續(xù)運(yùn)行。在這種情況下,我們會(huì)使用?panic?來終止程序。當(dāng)函數(shù)發(fā)生 panic 時(shí),它會(huì)終止運(yùn)行,在執(zhí)行完所有的defer延遲函數(shù)后,程序控制返回到該函數(shù)的調(diào)用方。這樣的過程會(huì)一直持續(xù)下去,直到當(dāng)前協(xié)程的所有函數(shù)都返回退出,然后程序會(huì)打印出panic信息,接著打印出堆棧跟蹤(Stack Trace),最后程序終止。當(dāng)程序發(fā)生 panic 時(shí),使用?recover?可以重新獲得對(duì)該程序的控制。 需要注意:
你應(yīng)該盡可能地使用錯(cuò)誤,而不是使用 panic 和 recover。只有當(dāng)程序不能繼續(xù)運(yùn)行的時(shí)候,才應(yīng)該使用 panic 和 recover 機(jī)制。
**必須在defer函數(shù)中直接調(diào)用recover**。在延遲函數(shù)內(nèi)調(diào)用?recover,可以取到?panic?的錯(cuò)誤信息,并且停止panic續(xù)發(fā)事件(Panicking Sequence),程序運(yùn)行恢復(fù)正常。如果在延遲函數(shù)的外部調(diào)用?recover,就不能停止panic續(xù)發(fā)事件。
只有在相同的Go協(xié)程中調(diào)用 recover 才管用。recover?不能恢復(fù)一個(gè)不同協(xié)程的 panic
使用示例
func?recoverName()?{?? ????if?r?:=?recover();?r!=?nil?{//恢復(fù)程序運(yùn)行 ????????fmt.Println("recovered?from?",?r) ????????debug.PrintStack()//打印panic異常的堆棧信息 ????} } func?fullName(firstName?*string,?lastName?*string)?{?? ????defer?recoverName()?//延遲函數(shù) ????if?firstName?==?nil?{ ????????panic("runtime?error:?first?name?cannot?be?nil")//觸發(fā)程序異常 ????} ????if?lastName?==?nil?{ ????????panic("runtime?error:?last?name?cannot?be?nil") ????} ????fmt.Printf("%s?%s ",?*firstName,?*lastName) ????fmt.Println("returned?normally?from?fullName") } func?main()?{?? ????defer?fmt.Println("deferred?call?in?main") ????firstName?:=?"Elon" ????fullName(&firstName,?nil) ????fmt.Println("returned?normally?from?main") }
一個(gè)通用的recover defer使用的模板,可供參考:
func?foo()?(err?error)?{ ????defer?func()?{ ????????if?r?:=?recover();?r?!=?nil?{ ????????????switch?x?:=?r.(type)?{ ????????????case?string: ????????????????err?=?errors.New(x) ????????????case?error: ????????????????err?=?x ????????????default: ????????????????err?=?fmt.Errorf("Unknown?panic:?%v",?r) ????????????} ????????} ????}() ????panic("TODO") }
編輯:黃飛
?
評(píng)論