国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

腳本之家,腳本語言編程技術及教程分享平臺!
分類導航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務器之家 - 腳本之家 - Golang - 圖文詳解go語言反射實現(xiàn)原理

圖文詳解go語言反射實現(xiàn)原理

2020-06-04 10:46ryogou Golang

這篇文章主要介紹了圖文詳解go語言反射實現(xiàn)原理,本文圖文并茂給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友參考下吧,需要的朋友可以參考下

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

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

interface回顧

首先我們簡單的回顧一下interface的結構,總體上是:

圖文詳解go語言反射實現(xiàn)原理

細分下來分為有函數(shù)的 iface 和無函數(shù)的 eface (就是 interface{} );

無函數(shù)的 eface

圖文詳解go語言反射實現(xiàn)原理

有函數(shù)的 iface

圖文詳解go語言反射實現(xiàn)原理

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

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

例如以下例子:

?
1
2
3
4
5
6
7
8
9
10
//帶函數(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 的例子

我們一句一句來看:第1行, var r io.Reader

圖文詳解go語言反射實現(xiàn)原理

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

圖文詳解go語言反射實現(xiàn)原理

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

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

圖文詳解go語言反射實現(xiàn)原理

最后是 empty = tty

圖文詳解go語言反射實現(xiàn)原理

但是記住:雖然有 動態(tài)混合類型 ,但是對外”表現(xiàn)”依然是靜態(tài)類型。

Go反射簡介

Go反射有三大法則:

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

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

Go的反射主要由兩部分組成: TypeValueTypeValue 是倆結構體:(這倆結構體具體內容可以略過不看,知道有這回事兒就行了)

Type:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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:

?
1
2
3
4
5
type Value struct {
 typ *rtype
 ptr unsafe.Pointer
 flag
}

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

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

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

  • reflect.TypeOf(x)
  • reflect.ValueOf(x)

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

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

給定一個數(shù)據(jù)對象,可以將數(shù)據(jù)對象轉化為反射對象 TypeValue

圖文詳解go語言反射實現(xiàn)原理

事例代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 還可以獲取到當前數(shù)據(jù)值的 Type

所以,法則一的圖應為:

圖文詳解go語言反射實現(xiàn)原理

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

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

圖文詳解go語言反射實現(xiàn)原理

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

承接法則一的代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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.(法則三)

法則三是說:通過反射對象,可以修改原數(shù)據(jù)中的內容。

這里說的反射對象,是指 Value ,畢竟 Type 只是表示原數(shù)據(jù)的類型相關的內容,而 Value 是對應著原數(shù)據(jù)對象本身。

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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) //此行會報錯
 fmt.Println(x)
}

 

這段代碼20行會報一個panic

?
1
reflect: reflect.Value.SetFloat using unaddressable value

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

我們可以通過 Value 結構體的 CanSet() 方法來查看是否可以設置修改新值。

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

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

如何通過反射對象來修改原數(shù)據(jù)對象的值呢?

如何才能可以通過反射對象來修改原數(shù)據(jù)對象的值或者說為什么不能設置呢?

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

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

那如何修改呢?

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

?
1
2
3
4
5
6
...
 
var x float64 = 3.4
v := reflect.ValueOf(&x)
 
...

此時還不行,因為這樣反射對象對應的是原數(shù)據(jù)對象的指針類型,必須要拿到當前類型的值類型(*v),如何做?

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

?
1
2
3
4
5
6
7
...
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在結構上是如此的相近!都分為兩部分:一部分是 Type 一部分是 value

反射會不會是比著interface來實現(xiàn)的?

反射是什么意思?反射的意思是在運行時,能夠動態(tài)知道給定數(shù)據(jù)對象的類型和結構,并有機會修改它!

現(xiàn)在一個數(shù)據(jù)對象,如何判斷它是什么結構?

數(shù)據(jù)interface中保存有結構數(shù)據(jù)呀,只要想辦法拿到該數(shù)據(jù)對應的內存地址,然后把該數(shù)據(jù)轉成interface,通過查看interface中的類型結構,就可以知道該數(shù)據(jù)的結構了呀~

其實以上就是Go反射通俗的原理。

圖可以展示為:

圖文詳解go語言反射實現(xiàn)原理

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

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

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

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

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

?
1
2
3
4
5
6
// 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ù)結構,一個是 Type ,一個是 emptyInterface

分別看看這兩者的代碼:

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 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
}

仔細一看,是空接口和包含方法的interface的兩個結構體。且和 efaceiface 內容字段一致!不是有 efaceiface 了嗎?這兩者有什么不同??

經(jīng)過查閱代碼,發(fā)現(xiàn):

interface源碼(位于”Go SDK/src/runtime/runtime2.go“)中的 efaceiface 會和 反射源碼(位于”GO SDK/src/reflect/value.go“)中的 emptyInterfacenonEmptyInterface 保持數(shù)據(jù)同步!

總結

以上所述是小編給大家介紹的圖文詳解go語言反射實現(xiàn)原理,希望對大家有所幫助!

原文鏈接:https://i6448038.github.io/2020/02/15/golang-reflection/

延伸 · 閱讀

精彩推薦
  • GolangGolang中Bit數(shù)組的實現(xiàn)方式

    Golang中Bit數(shù)組的實現(xiàn)方式

    這篇文章主要介紹了Golang中Bit數(shù)組的實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    天易獨尊11682021-06-09
  • Golanggo語言制作端口掃描器

    go語言制作端口掃描器

    本文給大家分享的是使用go語言編寫的TCP端口掃描器,可以選擇IP范圍,掃描的端口,以及多線程,有需要的小伙伴可以參考下。 ...

    腳本之家3642020-04-25
  • Golanggolang 通過ssh代理連接mysql的操作

    golang 通過ssh代理連接mysql的操作

    這篇文章主要介紹了golang 通過ssh代理連接mysql的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    a165861639710342021-03-08
  • Golanggolang的httpserver優(yōu)雅重啟方法詳解

    golang的httpserver優(yōu)雅重啟方法詳解

    這篇文章主要給大家介紹了關于golang的httpserver優(yōu)雅重啟的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,...

    helight2992020-05-14
  • GolangGolang通脈之數(shù)據(jù)類型詳情

    Golang通脈之數(shù)據(jù)類型詳情

    這篇文章主要介紹了Golang通脈之數(shù)據(jù)類型,在編程語言中標識符就是定義的具有某種意義的詞,比如變量名、常量名、函數(shù)名等等,Go語言中標識符允許由...

    4272021-11-24
  • Golanggolang如何使用struct的tag屬性的詳細介紹

    golang如何使用struct的tag屬性的詳細介紹

    這篇文章主要介紹了golang如何使用struct的tag屬性的詳細介紹,從例子說起,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看...

    Go語言中文網(wǎng)11352020-05-21
  • Golanggo日志系統(tǒng)logrus顯示文件和行號的操作

    go日志系統(tǒng)logrus顯示文件和行號的操作

    這篇文章主要介紹了go日志系統(tǒng)logrus顯示文件和行號的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    SmallQinYan12302021-02-02
  • Golanggolang json.Marshal 特殊html字符被轉義的解決方法

    golang json.Marshal 特殊html字符被轉義的解決方法

    今天小編就為大家分享一篇golang json.Marshal 特殊html字符被轉義的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧 ...

    李浩的life12792020-05-27
主站蜘蛛池模板: 日韩和的一区二在线 | 青青草在线视频免费观看 | 久久久国产精品入口麻豆 | 中文字幕日韩在线 | 国内精品视频 | 国产欧美综合一区二区三区 | 欧美激情五月 | 欧美激情一区 | 欧洲成人| 高清精品一区二区 | 欧美精品在线一区 | 欧美黑人一级爽快片淫片高清 | 国产精品久久国产精品 | 亚洲视频中文字幕 | 中文av一区 | 精品无码久久久久久久动漫 | 精品福利一区二区三区 | 激情五月婷 | 在线观看亚洲a | 亚洲精品电影网在线观看 | 国产精品免费久久久久久久久久中文 | 国产精品福利在线 | 日韩欧美视频一区二区 | 久久九精品 | 91伊人| 中文字幕一二三区 | 日韩中文字幕 | 国产精品网站在线观看 | 国产成人一区二区 | 久久视频精品 | 中文字幕免费在线 | a在线免费观看 | 精品日韩一区二区 | 中文字幕在线精品 | 综合久久99 | 久久久久久免费毛片精品 | 亚洲国产一区二区三区精品 | 亚洲激情一区 | 久久久久久久久99精品 | 色五月激情五月 | av短片在线观看 |