golang 反射
?
參考:|--http://blog.51cto.com/speakingbaicai/1707637
|--https://studygolang.com/articles/6324
反射是在golang程序運行時檢查變量所具有類型的一種機制。由于反射可以得出關(guān)于變量結(jié)構(gòu)的數(shù)據(jù)(即“關(guān)于數(shù)據(jù)的數(shù)據(jù)”),所以這也被認為是golang元編程的基礎(chǔ)。我們由反射三法則入手:
從類型和方法理解反射內(nèi)涵
在基本的層面上,反射只是一個檢查存儲在接口變量中的類型和值的算法。使用反射機制,首先需要導入reflect包,reflect包中有兩個重要類型需要了解,reflect.Type和reflect.Value,這兩個類型使得可以訪問變量的內(nèi)容。與此相關(guān)的,還有兩個簡單的函數(shù),reflect.TypeOf和reflect.ValueOf,可以從接口值中分別獲取reflect.Type和reflect.Value。
? ? 初學可能會認為reflect.Type和reflect.Value是一種并列關(guān)系,但其實它們是一種包含關(guān)系,我們結(jié)合一段代碼來理解這段話。
import ("fmt""reflect" )func main() {var x float64 = 1.1fmt.Println("reflect.Value:", reflect.ValueOf(x))fmt.Println("reflect.Type:", reflect.TypeOf(x))v := reflect.ValueOf(x)fmt.Println("reflect.Type:",v.Type())fmt.Println("actual value:", v.Float())fmt.Println("kind is float64?", v.Kind() == reflect.Float64) }根據(jù)程序及其結(jié)果,我們可以發(fā)現(xiàn):在go語言中,每個值都包含兩個內(nèi)容:類型和實際的值。從類型角度來看,reflect.Value是一個關(guān)于<類型,?實際的值>的二元組,而reflect.Type是值的類型,二者是包含關(guān)系。從方法角度來看,reflect.TypeOf?和?(reflect.ValueOf(x)).Type都可以返回reflect.Type;(reflect.ValueOf(x)).Float可以返回實際的值(類似的方法還包括(reflect.ValueOf(x)).Int、(reflect.ValueOf(x)).Bool等);(reflect.ValueOf(x)).Kind可以返回一個常量定義的類型。
? ? 根據(jù)上述分析,我們可以得出一個示意圖,更為直觀形象的表明值、類型、實際的值的關(guān)系。
此外,golang采用靜態(tài)類型機制,TypeOf返回靜態(tài)類型;但是,Kind返回底層類型。我們同樣以一段代碼來驗證這段話。
import ("fmt""reflect" )type MyInt intfunc main() {var x MyInt = 1v := reflect.ValueOf(x)fmt.Println("reflect.Type:", v.Type())fmt.Println("kind is int?", v.Kind() == reflect.Int) }反射三法則:
1?法則一:從接口值到反射對象的反射(Reflection goes from interface value toreflection object)
? ? 前文所述內(nèi)容其實就是從接口值到反射對象的反射,代表方法為reflect.ValueOf和reflect.TypeOf。可能有人會問,接口?接口在哪呢?我們來看一些前文提到這兩個函數(shù)的聲明,函數(shù)的參數(shù)是空接口,其實接口就在那里。
func ValueOf(i interface{}) Value func TypeOf(i interface{}) Type2?法則二:從反射對象到接口值的反射(Reflection goes from reflection object to interface value)
? ??從reflect.Value可以使用Interface方法還原接口值;此方法可以高效地打包類型和值信息到接口表達中,并返回這個結(jié)果。方法聲明:
func (v Value) Interface() interface{}? ? 通過反射對象?v?可以打印?float64?的表達值。
y :=v.Interface().(float64) // y 將為類型 float64。 fmt.Println(y)? ? 還有更為簡潔的實現(xiàn)。fmt.Println,fmt.Printf等其他所有傳遞一個空接口值作為參數(shù)的函數(shù),在?fmt包內(nèi)部解包的方式就像之前的例子這樣。因此正確的打印reflect.Value的內(nèi)容的方法就是將Interface方法的結(jié)果進行格式化打印(formatted print routine).?
fmt.Println(v.Interface())? ? 為什么不是fmt.Println(v)?因為v是一個?reflect.Value;這里希望獲得的是它保存的實際的值。
? ? 我們修改前文代碼還進行驗證:
func main() {var x float64 = 1.1fmt.Println("reflect.Value:", reflect.ValueOf(x))fmt.Println("reflect.Type:", reflect.TypeOf(x))v := (reflect.ValueOf(x))fmt.Println("reflect.Type:", v.Type())fmt.Println("actual value(interface):", v.Interface())fmt.Println("kind is float64?", v.Kind() == reflect.Float64) }其輸出:
進一步地,我們可以修改上述關(guān)系示意圖,新圖更為簡潔優(yōu)雅:
3.?為了修改反射對象,其值必須可設(shè)置(To modify a reflectionobject, the value must be settable)
? ? 反射對象可以通過SetFloat等方法設(shè)置值,通過CanSet判斷可設(shè)置性。但是這里面有坑,有些值是不可設(shè)置的。我們還是通過一段代碼來看。
import ("fmt""reflect" )func main() {var x float64 = 1.1v := reflect.ValueOf(x)fmt.Println("settability of v:",v.CanSet())v.SetFloat(1.2) }
其結(jié)果表明,反射對象v是不可設(shè)置的,如果硬要設(shè)置的話,會有panic異常。
? ? 為什么不能設(shè)置呢?我們可以從函數(shù)傳參的角度來思考這個問題。V := reflect.ValueOf(x),這個函數(shù)是值傳遞,即傳遞了一個x的副本到函數(shù)中,而非x本身。我們都知道,值傳遞的參數(shù)是不能被真正修改的。
? ? 我最初還有過這樣的想法:v和x又不是一個變量,x不能被修改,但是v應(yīng)該可以被修改啊。完全從形式上考慮,這樣似乎有道理。但是再多想一層,如果允許執(zhí)行,雖然v可以被修改,但是卻無法更新x。也就是說,在反射值內(nèi)部允許修改x的副本,但是x本身卻不會受到這個影響。這會造成混亂,并且毫無意義,因此在golang中這樣操作是非法的。
? ? 讓我們重新用函數(shù)傳參的角度思考這個問題。如果傳遞副本不能修改,那我們就通過就傳遞指針好了。我們來試試:
func main() {var x float64 = 1.1p := reflect.ValueOf(&x)fmt.Println("type of p:",p.Type())fmt.Println("settability of p:",p.CanSet()) }
還是不行。因為p的實際類型是*float64,而非float64,這樣修改相當于要直接修改地址了。
我們可以借助Elem方法,通過指針來修改指針指向的具體值。
func (v Value)Elem() Value func main() {var x float64 = 1.1p := reflect.ValueOf(&x)fmt.Println("type of p:",p.Type())v := p.Elem()fmt.Println("type of v:",v.Type())fmt.Println("settability of v:",v.CanSet()) }
這樣就可以進行修改了。雖然p是不可修改的,但是v可以修改。這種方法思路上類似引用傳參,傳入地址,修改地址所指向的具體值。
?
轉(zhuǎn)載于:https://www.cnblogs.com/K-artorias/p/8962901.html
總結(jié)
- 上一篇: 车机常用adb 命令总结
- 下一篇: 1H413000工业机电工程安装技术——