go語(yǔ)言反射實(shí)現(xiàn)原理是什么

這篇文章給大家分享的是有關(guān)go語(yǔ)言反射實(shí)現(xiàn)原理是什么的內(nèi)容。小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過(guò)來(lái)看看吧。

目前累計(jì)服務(wù)客戶上千余家,積累了豐富的產(chǎn)品開(kāi)發(fā)及服務(wù)經(jīng)驗(yàn)。以網(wǎng)站設(shè)計(jì)水平和技術(shù)實(shí)力,樹(shù)立企業(yè)形象,為客戶提供網(wǎng)站建設(shè)、做網(wǎng)站、網(wǎng)站策劃、網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)絡(luò)營(yíng)銷、VI設(shè)計(jì)、網(wǎng)站改版、漏洞修補(bǔ)等服務(wù)。創(chuàng)新互聯(lián)始終以務(wù)實(shí)、誠(chéng)信為根本,不斷創(chuàng)新和提高建站品質(zhì),通過(guò)對(duì)領(lǐng)先技術(shù)的掌握、對(duì)創(chuàng)意設(shè)計(jì)的研究、對(duì)客戶形象的視覺(jué)傳遞、對(duì)應(yīng)用系統(tǒng)的結(jié)合,為客戶提供更好的一站式互聯(lián)網(wǎng)解決方案,攜手廣大客戶,共同發(fā)展進(jìn)步。

Go反射的實(shí)現(xiàn)和 interfaceunsafe.Pointer 密切相關(guān)。如果對(duì)golang的 interface 底層實(shí)現(xiàn)還沒(méi)有理解,可以去看我之前的文章: Go語(yǔ)言interface底層實(shí)現(xiàn) , unsafe.Pointer 會(huì)在后續(xù)的文章中做介紹。

(本文目前使用的Go環(huán)境是Go 1.12.9)

interface回顧

首先我們簡(jiǎn)單的回顧一下interface的結(jié)構(gòu),總體上是:

go語(yǔ)言反射實(shí)現(xiàn)原理是什么

細(xì)分下來(lái)分為有函數(shù)的 iface 和無(wú)函數(shù)的 eface (就是 interface{} );

無(wú)函數(shù)的 eface

go語(yǔ)言反射實(shí)現(xiàn)原理是什么

有函數(shù)的 iface

go語(yǔ)言反射實(shí)現(xiàn)原理是什么

靜態(tài)類型(static interface type)和動(dòng)態(tài)混合類型(dynamic concrete type)

Go語(yǔ)言中,每個(gè)變量都有唯一個(gè) 靜態(tài)類型,這個(gè)類型是編譯階段就可以確定的。有的變量可能除了靜態(tài)類型之外,還會(huì)有 動(dòng)態(tài)混合類型。

例如以下例子:

//帶函數(shù)的interface
var r io.Reader 
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
 return nil, err
}
r = tty
//不帶函數(shù)的interface
var empty interface{}
empty = tty

有函數(shù)的 iface 的例子

我們一句一句來(lái)看:第1行, var r io.Reader

go語(yǔ)言反射實(shí)現(xiàn)原理是什么

第4行至第七行就是簡(jiǎn)單的賦值,得到一個(gè) *os.File 的實(shí)例,暫且不看了。最后一句第十句 r = tty

go語(yǔ)言反射實(shí)現(xiàn)原理是什么

無(wú)函數(shù)的 eface 的例子

我們接著往下看, var empty interface{}

go語(yǔ)言反射實(shí)現(xiàn)原理是什么

最后是 empty = tty

go語(yǔ)言反射實(shí)現(xiàn)原理是什么

但是記?。弘m然有 動(dòng)態(tài)混合類型,但是對(duì)外”表現(xiàn)”依然是靜態(tài)類型。

Go反射簡(jiǎn)介

Go反射有三大法則:

//接口數(shù)據(jù)  =====》 反射對(duì)象
1. Reflection goes from interface value to reflection object.
//反射對(duì)象 ===> 接口數(shù)據(jù)
2. Reflection goes from reflection object to interface value.
// 倘若數(shù)據(jù)可更改,可通過(guò)反射對(duì)象來(lái)修改它
3. To modify a reflection object, the value must be settable.

Go 的反射就是對(duì)以上三項(xiàng)法則的實(shí)現(xiàn)。

Go的反射主要由兩部分組成: TypeValue , TypeValue 是倆結(jié)構(gòu)體:(這倆結(jié)構(gòu)體具體內(nèi)容可以略過(guò)不看,知道有這回事兒就行了)

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
}

Value:

type Value struct {
 typ *rtype
 ptr unsafe.Pointer
 flag
}

你會(huì)發(fā)現(xiàn)反射的實(shí)現(xiàn)和interface的組成很相似,都是由“類型”和“數(shù)據(jù)值”構(gòu)成,但是值得注意的是:interface的“類型”和“數(shù)據(jù)值”是在“一起的”,而反射的“類型”和“數(shù)據(jù)值”是分開(kāi)的。

TypeValue 提供了非常多的方法:例如獲取對(duì)象的屬性列表、獲取和修改某個(gè)屬性的值、對(duì)象所屬結(jié)構(gòu)體的名字、對(duì)象的底層類型(underlying type)等等

Go中的反射,在使用中最核心的就兩個(gè)函數(shù):

  • reflect.TypeOf(x)

  • reflect.ValueOf(x)

這兩個(gè)函數(shù)可以分別將給定的數(shù)據(jù)對(duì)象轉(zhuǎn)化為以上的 TypeValue 。這兩個(gè)都叫做 反射對(duì)象

Reflection goes from interface value to reflection object(法則一)

給定一個(gè)數(shù)據(jù)對(duì)象,可以將數(shù)據(jù)對(duì)象轉(zhuǎn)化為反射對(duì)象 TypeValue

go語(yǔ)言反射實(shí)現(xiàn)原理是什么

事例代碼:

package main

import (
 "fmt"
 "reflect"
)

func main() {
 var x float64 = 3.4

 t := reflect.TypeOf(x)
 v := reflect.ValueOf(x)

 fmt.Println("type:", t) //type: float64

 fmt.Println("value:", v.String()) //value: <float64 Value>
 fmt.Println("type:", v.Type()) // type: float64
 fmt.Println("kind is float64:", v.Kind() == reflect.Float64) //kind is float64: true
 fmt.Println("value:", v.Float()) //value: 3.4

}

由代碼17行可以看出: Value 還可以獲取到當(dāng)前數(shù)據(jù)值的 Type 。

所以,法則一的圖應(yīng)為:

go語(yǔ)言反射實(shí)現(xiàn)原理是什么

Reflection goes from reflection object to interface value.(法則二)

給定的反射對(duì)象,可以轉(zhuǎn)化為某種類型的數(shù)據(jù)對(duì)象。即法則一的逆向。

go語(yǔ)言反射實(shí)現(xiàn)原理是什么

注意 Type 是沒(méi)法逆向轉(zhuǎn)換的,仔細(xì)想想也合理,如果可逆類型轉(zhuǎn)化成什么呢?(#^.^#)

承接法則一的代碼:

package main
import (
 "fmt"
 "reflect"
)
func main() {
 var x float64 = 3.4
 t := reflect.TypeOf(x)
 v := reflect.ValueOf(x)
 ...
 o := v.Interface().(float64) // 法則2代碼
 fmt.Println(o)
}

To modify a reflection object, the value must be settable.(法則三)

法則三是說(shuō):通過(guò)反射對(duì)象,可以修改原數(shù)據(jù)中的內(nèi)容。

這里說(shuō)的反射對(duì)象,是指 Value ,畢竟 Type 只是表示原數(shù)據(jù)的類型相關(guān)的內(nèi)容,而 Value 是對(duì)應(yīng)著原數(shù)據(jù)對(duì)象本身。

在目前以上的所有例子中,反射得到的 Value 對(duì)象的修改,都是無(wú)法直接修改原數(shù)據(jù)對(duì)象的。

package main
import (
 "fmt"
 "reflect"
)
func main() {
 var x float64 = 3.4
 t := reflect.TypeOf(x)
 v := reflect.ValueOf(&x)
 ....
 o := v.Interface().(float64)
 fmt.Println(o)
 v.SetFloat(5.4) //此行會(huì)報(bào)錯(cuò)
 fmt.Println(x)
}

這段代碼20行會(huì)報(bào)一個(gè)panic

reflect: reflect.Value.SetFloat using unaddressable value

這句話的意思并不是地址不可達(dá),而是:對(duì)象 v 不可設(shè)置( settable )。

我們可以通過(guò) Value 結(jié)構(gòu)體的 CanSet() 方法來(lái)查看是否可以設(shè)置修改新值。

通過(guò)以下代碼可以知道 CanSet() 返回值是false。

fmt.Println(v.CanSet()) // false

如何通過(guò)反射對(duì)象來(lái)修改原數(shù)據(jù)對(duì)象的值呢?

如何才能可以通過(guò)反射對(duì)象來(lái)修改原數(shù)據(jù)對(duì)象的值或者說(shuō)為什么不能設(shè)置呢?

原因簡(jiǎn)單且純粹:在Go中,任何函數(shù)的參數(shù)都是值的拷貝,而非原數(shù)據(jù)。

反射函數(shù) reflect.ValueOf() 也不例外。我們目前得到的反射對(duì)象,都是原對(duì)象的copy的反射對(duì)象,而非原對(duì)象本身,所以不可以修改到原對(duì)象;即使可以修改,修改一個(gè)傳參時(shí)候的副本,也毫無(wú)意義,不如報(bào)錯(cuò)兒。Go反射第三法則中的制定的 settable 屬性就由此而來(lái),還延伸出了類似于 CanSet() 的方法。

那如何修改呢?

首先,在Go中要想讓函數(shù)“有副作用“,傳值必須傳指針類型的。

...

var x float64 = 3.4
v := reflect.ValueOf(&x)

...

此時(shí)還不行,因?yàn)檫@樣反射對(duì)象對(duì)應(yīng)的是原數(shù)據(jù)對(duì)象的指針類型,必須要拿到當(dāng)前類型的值類型(*v),如何做?

Go提供了另外一個(gè)方法 Elem()

...
var x float64 = 3.4
v := reflect.ValueOf(&x)
p := v.Elem()
fmt.Println(p.CanSet()) // true
p.SetFloat(7.1)
fmt.Println(x) // 7.1

看以上代碼,就可以修改原數(shù)據(jù)了。

反射原理

不難發(fā)現(xiàn),go的反射和interface在結(jié)構(gòu)上是如此的相近!都分為兩部分:一部分是 Type 一部分是 value 。

反射會(huì)不會(huì)是比著interface來(lái)實(shí)現(xiàn)的?

反射是什么意思?反射的意思是在運(yùn)行時(shí),能夠動(dòng)態(tài)知道給定數(shù)據(jù)對(duì)象的類型和結(jié)構(gòu),并有機(jī)會(huì)修改它!

現(xiàn)在一個(gè)數(shù)據(jù)對(duì)象,如何判斷它是什么結(jié)構(gòu)?

數(shù)據(jù)interface中保存有結(jié)構(gòu)數(shù)據(jù)呀,只要想辦法拿到該數(shù)據(jù)對(duì)應(yīng)的內(nèi)存地址,然后把該數(shù)據(jù)轉(zhuǎn)成interface,通過(guò)查看interface中的類型結(jié)構(gòu),就可以知道該數(shù)據(jù)的結(jié)構(gòu)了呀~

其實(shí)以上就是Go反射通俗的原理。

圖可以展示為:

go語(yǔ)言反射實(shí)現(xiàn)原理是什么

圖中結(jié)構(gòu)中牽扯到的指針,都是 unsafe.Pointer 指針,具體這是個(gè)什么指針,后續(xù)的文章中會(huì)有介紹,在此,你就姑且認(rèn)為是可以指向Go系統(tǒng)中任意數(shù)據(jù)的指針就可以。

源碼部分 (以下部分可以忽略,是我在查閱代碼時(shí)候遇到的一點(diǎn)點(diǎn)坑。)

我們來(lái)看看具體的源碼:源碼在”GO SDK/src/refelct“包中,具體主要是包中的”type.go”和”value.go”這兩個(gè)文件。

可以簡(jiǎn)單的認(rèn)為,反射的核心代碼,主要是 reflect.ValueOf()reflect.TypeOf() 這兩個(gè)函數(shù)。

先看類型轉(zhuǎn)換: reflect.TypeOf()

// TypeOf returns the reflection Type that represents the dynamic type of i.
// If i is a nil interface value, TypeOf returns nil.
func TypeOf(i interface{}) Type {
 eface := *(*emptyInterface)(unsafe.Pointer(&i))
 return toType(eface.typ)
}

其中出現(xiàn)了兩種數(shù)據(jù)結(jié)構(gòu),一個(gè)是 Type ,一個(gè)是 emptyInterface

分別看看這兩者的代碼:

emptyInterface 在 ”GO SDK/src/reflect/value.go“文件中

// emptyInterface is the header for an interface{} value.
type emptyInterface struct {
 typ *rtype
 word unsafe.Pointer
}

// nonEmptyInterface is the header for an interface value with methods.
type nonEmptyInterface struct {
 // see ../runtime/iface.go:/Itab
 itab *struct {
  ityp *rtype // static interface type
  typ *rtype // dynamic concrete type
  hash uint32 // copy of typ.hash
  _ [4]byte
  fun [100000]unsafe.Pointer // method table
 }
 word unsafe.Pointer
}

什么是go

go是golang的簡(jiǎn)稱,golang 是Google開(kāi)發(fā)的一種靜態(tài)強(qiáng)類型、編譯型、并發(fā)型,并具有垃圾回收功能的編程語(yǔ)言,其語(yǔ)法與 C語(yǔ)言相近,但并不包括如枚舉、異常處理、繼承、泛型、斷言、虛函數(shù)等功能。

感謝各位的閱讀!關(guān)于“go語(yǔ)言反射實(shí)現(xiàn)原理是什么”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,讓大家可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!

網(wǎng)頁(yè)標(biāo)題:go語(yǔ)言反射實(shí)現(xiàn)原理是什么
網(wǎng)站鏈接:http://bm7419.com/article42/goeehc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供關(guān)鍵詞優(yōu)化、網(wǎng)站維護(hù)網(wǎng)站營(yíng)銷、網(wǎng)站設(shè)計(jì)做網(wǎng)站、動(dòng)態(tài)網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

外貿(mào)網(wǎng)站建設(shè)