您好,登錄后才能下訂單哦!
這篇文章主要介紹“go語言中反射三定律指的是什么”,在日常操作中,相信很多人在go語言中反射三定律指的是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”go語言中反射三定律指的是什么”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!
反射三定律:1、反射可以將“接口類型變量”轉(zhuǎn)換為“反射類型對象”,這里反射類型指“reflect.Type”和 “reflect.Value”;2、反射可以將“反射類型對象”轉(zhuǎn)換為“接口類型變量”;3、如果要修改“反射類型對象”其值必須是“可寫的”。
在反射的世界里,我們擁有了獲取一個對象的類型,屬性及方法的能力。
在 Go 反射的世界里,有兩種類型非常重要,是整個反射的核心,在學(xué)習(xí) reflect 包的使用時,先得學(xué)習(xí)下這兩種類型:
reflect.Type
reflect.Value
它們分別對應(yīng)著真實(shí)世界里的 type 和 value,只不過在反射對象里,它們擁有更多的內(nèi)容。
從源碼上來看,reflect.Type 是以一個接口的形式存在的
type Type interface {
Align() int
FieldAlign() int
Method(int) Method
MethodByName(string) (Method, bool)
NumMethod() int
Name() string
PkgPath() string
Size() uintptr
String() string
Kind() Kind
Implements(u Type) bool
AssignableTo(u Type) bool
ConvertibleTo(u Type) bool
Comparable() bool
Bits() int
ChanDir() ChanDir
IsVariadic() bool
Elem() Type
Field(i int) StructField
FieldByIndex(index []int) StructField
FieldByName(name string) (StructField, bool)
FieldByNameFunc(match func(string) bool) (StructField, bool)
In(i int) Type
Key() Type
Len() int
NumField() int
NumIn() int
NumOut() int
Out(i int) Type
common() *rtype
uncommon() *uncommonType
}
而 reflect.Value 是以一個結(jié)構(gòu)體的形式存在,
type Value struct {
typ *rtype
ptr unsafe.Pointer
flag
}
同時它接收了很多的方法(見下表),這里出于篇幅的限制這里也沒辦法一一介紹。
Addr
Bool
Bytes
runes
CanAddr
CanSet
Call
CallSlice
call
Cap
Close
Complex
Elem
Field
FieldByIndex
FieldByName
FieldByNameFunc
Float
Index
Int
CanInterface
Interface
InterfaceData
IsNil
IsValid
IsZero
Kind
Len
MapIndex
MapKeys
MapRange
Method
NumMethod
MethodByName
NumField
OverflowComplex
OverflowFloat
OverflowInt
OverflowUint
Pointer
Recv
recv
Send
send
Set
SetBool
SetBytes
setRunes
SetComplex
SetFloat
SetInt
SetLen
SetCap
SetMapIndex
SetUint
SetPointer
SetString
Slice
Slice3
String
TryRecv
TrySend
Type
Uint
UnsafeAddr
assignTo
Convert
通過上一節(jié)的內(nèi)容(),我們知道了一個接口變量,實(shí)際上都是由一 pair 對(type 和 data)組合而成,pair 對中記錄著實(shí)際變量的值和類型。也就是說在真實(shí)世界里,type 和 value 是合并在一起組成 接口變量的。
而在反射的世界里,type 和 data 卻是分開的,他們分別由 reflect.Type 和 reflect.Value 來表現(xiàn)。
Go 語言里有個反射三定律,是你在學(xué)習(xí)反射時,很重要的參考:
Reflection goes from interface value to reflection object.
Reflection goes from reflection object to interface value.
To modify a reflection object, the value must be settable.
翻譯一下,就是:
反射可以將“接口類型變量” 轉(zhuǎn)換為“反射類型對象”;
反射可以將 “反射類型對象”轉(zhuǎn)換為 “接口類型變量”;
如果要修改 “反射類型對象” 其類型必須是 “可寫的”;
Reflection goes from interface value to reflection object.
為了實(shí)現(xiàn)從接口變量到反射對象的轉(zhuǎn)換,需要提到 reflect 包里很重要的兩個方法:
reflect.TypeOf(i) :獲得接口值的類型
reflect.ValueOf(i):獲得接口值的值
這兩個方法返回的對象,我們稱之為反射對象:Type object 和 Value object。
舉個例子,看下這兩個方法是如何使用的?
package main
import (
"fmt"
"reflect"
)
func main() {
var age interface{} = 25
fmt.Printf("原始接口變量的類型為 %T,值為 %v \n", age, age)
t := reflect.TypeOf(age)
v := reflect.ValueOf(age)
// 從接口變量到反射對象
fmt.Printf("從接口變量到反射對象:Type對象的類型為 %T \n", t)
fmt.Printf("從接口變量到反射對象:Value對象的類型為 %T \n", v)
}
輸出如下
原始接口變量的類型為 int,值為 25 從接口變量到反射對象:Type對象的類型為 *reflect.rtype
從接口變量到反射對象:Value對象的類型為 reflect.Value 復(fù)制代碼
如此我們完成了從接口類型變量到反射對象的轉(zhuǎn)換。
等等,上面我們定義的 age 不是 int 類型的嗎?第一法則里怎么會說是接口類型的呢?
關(guān)于這點(diǎn),其實(shí)在上一節(jié)(關(guān)于接口的三個 『潛規(guī)則』)已經(jīng)提到過了,由于 TypeOf 和 ValueOf 兩個函數(shù)接收的是 interface{} 空接口類型,而 Go 語言函數(shù)都是值傳遞,因此Go語言會將我們的類型隱式地轉(zhuǎn)換成接口類型。
原始接口變量的類型為 int,值為 25
從接口變量到反射對象:Type對象的類型為 *reflect.rtype
從接口變量到反射對象:Value對象的類型為 reflect.Value
Reflection goes from reflection object to interface value.
和第一定律剛好相反,第二定律描述的是,從反射對象到接口變量的轉(zhuǎn)換。
通過源碼可知, reflect.Value 的結(jié)構(gòu)體會接收 Interface
方法,返回了一個 interface{}
類型的變量(注意:只有 Value 才能逆向轉(zhuǎn)換,而 Type 則不行,這也很容易理解,如果 Type 能逆向,那么逆向成什么呢?)
// Interface returns v's current value as an interface{}.
// It is equivalent to:
// var i interface{} = (v's underlying value)
// It panics if the Value was obtained by accessing
// unexported struct fields.
func (v Value) Interface() (i interface{}) {
return valueInterface(v, true)
}
這個函數(shù)就是我們用來實(shí)現(xiàn)將反射對象轉(zhuǎn)換成接口變量的一個橋梁。
例子如下
package main
import (
"fmt"
"reflect"
)
func main() {
var age interface{} = 25
fmt.Printf("原始接口變量的類型為 %T,值為 %v \n", age, age)
t := reflect.TypeOf(age)
v := reflect.ValueOf(age)
// 從接口變量到反射對象
fmt.Printf("從接口變量到反射對象:Type對象的類型為 %T \n", t)
fmt.Printf("從接口變量到反射對象:Value對象的類型為 %T \n", v)
// 從反射對象到接口變量
i := v.Interface()
fmt.Printf("從反射對象到接口變量:新對象的類型為 %T 值為 %v \n", i, i)
}
輸出如下
原始接口變量的類型為 int,值為 25
從接口變量到反射對象:Type對象的類型為 *reflect.rtype
從接口變量到反射對象:Value對象的類型為 reflect.Value
從反射對象到接口變量:新對象的類型為 int 值為 25
當(dāng)然了,最后轉(zhuǎn)換后的對象,靜態(tài)類型為 interface{}
,如果要轉(zhuǎn)成最初的原始類型,需要再類型斷言轉(zhuǎn)換一下,關(guān)于這點(diǎn),我已經(jīng)在上一節(jié)里講解過了,你可以點(diǎn)此前往復(fù)習(xí):()。
i := v.Interface().(int)
至此,我們已經(jīng)學(xué)習(xí)了反射的兩大定律,對這兩個定律的理解,我畫了一張圖,你可以用下面這張圖來加強(qiáng)理解,方便記憶。
To modify a reflection object, the value must be settable.
反射世界是真實(shí)世界的一個『映射』,是我的一個描述,但這并不嚴(yán)格,因?yàn)椴⒉皇悄阍诜瓷涫澜缋锼龅氖虑槎紩€原到真實(shí)世界里。
第三定律引出了一個 settable
(可設(shè)置性,或可寫性)的概念。
其實(shí)早在以前的文章中,我們就一直在說,Go 語言里的函數(shù)都是值傳遞,只要你傳遞的不是變量的指針,你在函數(shù)內(nèi)部對變量的修改是不會影響到原始的變量的。
回到反射上來,當(dāng)你使用 reflect.Typeof 和 reflect.Valueof 的時候,如果傳遞的不是接口變量的指針,反射世界里的變量值始終將只是真實(shí)世界里的一個拷貝,你對該反射對象進(jìn)行修改,并不能反映到真實(shí)世界里。
因此在反射的規(guī)則里
不是接收變量指針創(chuàng)建的反射對象,是不具備『可寫性』的
是否具備『可寫性』,可使用 CanSet()
來獲取得知
對不具備『可寫性』的對象進(jìn)行修改,是沒有意義的,也認(rèn)為是不合法的,因此會報錯。
package main
import (
"fmt"
"reflect"
)
func main() {
var name string = "Go編程時光"
v := reflect.ValueOf(name)
fmt.Println("可寫性為:", v.CanSet())
}
輸出如下
可寫性為: false
要讓反射對象具備可寫性,需要注意兩點(diǎn)
創(chuàng)建反射對象時傳入變量的指針
使用 Elem()
函數(shù)返回指針指向的數(shù)據(jù)
完整代碼如下
package main
import (
"fmt"
"reflect"
)
func main() {
var name string = "Go編程時光"
v1 := reflect.ValueOf(&name)
fmt.Println("v1 可寫性為:", v1.CanSet())
v2 := v1.Elem()
fmt.Println("v2 可寫性為:", v2.CanSet())
}
輸出如下
v1 可寫性為: false
v2 可寫性為: true
知道了如何使反射的世界里的對象具有可寫性后,接下來是時候了解一下如何對修改更新它。
反射對象,都會有如下幾個以 Set
單詞開頭的方法
這些方法就是我們修改值的入口。
來舉個例子
package main
import (
"fmt"
"reflect"
)
func main() {
var name string = "Go編程時光"
fmt.Println("真實(shí)世界里 name 的原始值為:", name)
v1 := reflect.ValueOf(&name)
v2 := v1.Elem()
v2.SetString("Python編程時光")
fmt.Println("通過反射對象進(jìn)行更新后,真實(shí)世界里 name 變?yōu)椋?quot;, name)
}
輸出如下
真實(shí)世界里 name 的原始值為: Go編程時光
通過反射對象進(jìn)行更新后,真實(shí)世界里 name 變?yōu)椋?nbsp;Python編程時光
到此,關(guān)于“go語言中反射三定律指的是什么”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。