Go 語(yǔ)言 幾點(diǎn)忠告

2023-03-14 17:00 更新

原文鏈接:https://gopl-zh.github.io/ch12/ch12-09.html


12.9. 幾點(diǎn)忠告

雖然反射提供的API遠(yuǎn)多于我們講到的,我們前面的例子主要是給出了一個(gè)方向,通過(guò)反射可以實(shí)現(xiàn)哪些功能。反射是一個(gè)強(qiáng)大并富有表達(dá)力的工具,但是它應(yīng)該被小心地使用,原因有三。

第一個(gè)原因是,基于反射的代碼是比較脆弱的。對(duì)于每一個(gè)會(huì)導(dǎo)致編譯器報(bào)告類(lèi)型錯(cuò)誤的問(wèn)題,在反射中都有與之相對(duì)應(yīng)的誤用問(wèn)題,不同的是編譯器會(huì)在構(gòu)建時(shí)馬上報(bào)告錯(cuò)誤,而反射則是在真正運(yùn)行到的時(shí)候才會(huì)拋出panic異常,可能是寫(xiě)完代碼很久之后了,而且程序也可能運(yùn)行了很長(zhǎng)的時(shí)間。

以前面的readList函數(shù)(§12.6)為例,為了從輸入讀取字符串并填充int類(lèi)型的變量而調(diào)用的reflect.Value.SetString方法可能導(dǎo)致panic異常。絕大多數(shù)使用反射的程序都有類(lèi)似的風(fēng)險(xiǎn),需要非常小心地檢查每個(gè)reflect.Value的對(duì)應(yīng)值的類(lèi)型、是否可取地址,還有是否可以被修改等。

避免這種因反射而導(dǎo)致的脆弱性的問(wèn)題的最好方法,是將所有的反射相關(guān)的使用控制在包的內(nèi)部,如果可能的話避免在包的API中直接暴露reflect.Value類(lèi)型,這樣可以限制一些非法輸入。如果無(wú)法做到這一點(diǎn),在每個(gè)有風(fēng)險(xiǎn)的操作前指向額外的類(lèi)型檢查。以標(biāo)準(zhǔn)庫(kù)中的代碼為例,當(dāng)fmt.Printf收到一個(gè)非法的操作數(shù)時(shí),它并不會(huì)拋出panic異常,而是打印相關(guān)的錯(cuò)誤信息。程序雖然還有BUG,但是會(huì)更加容易診斷。

fmt.Printf("%d %s\n", "hello", 42) // "%!d(string=hello) %!s(int=42)"

反射同樣降低了程序的安全性,還影響了自動(dòng)化重構(gòu)和分析工具的準(zhǔn)確性,因?yàn)樗鼈儫o(wú)法識(shí)別運(yùn)行時(shí)才能確認(rèn)的類(lèi)型信息。

避免使用反射的第二個(gè)原因是,即使對(duì)應(yīng)類(lèi)型提供了相同文檔,但是反射的操作不能做靜態(tài)類(lèi)型檢查,而且大量反射的代碼通常難以理解??偸切枰⌒囊硪淼貫槊總€(gè)導(dǎo)出的類(lèi)型和其它接受interface{}或reflect.Value類(lèi)型參數(shù)的函數(shù)維護(hù)說(shuō)明文檔。

第三個(gè)原因,基于反射的代碼通常比正常的代碼運(yùn)行速度慢一到兩個(gè)數(shù)量級(jí)。對(duì)于一個(gè)典型的項(xiàng)目,大部分函數(shù)的性能和程序的整體性能關(guān)系不大,所以當(dāng)反射能使程序更加清晰的時(shí)候可以考慮使用。測(cè)試是一個(gè)特別適合使用反射的場(chǎng)景,因?yàn)槊總€(gè)測(cè)試的數(shù)據(jù)集都很小。但是對(duì)于性能關(guān)鍵路徑的函數(shù),最好避免使用反射。



以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)