欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go反射底層原理及數(shù)據(jù)結(jié)構(gòu)解析

 更新時間:2022年06月08日 10:00:35   作者:??樹獺叔叔????  
這篇文章主要介紹了Go反射底層原理及數(shù)據(jù)結(jié)構(gòu)解析,反射的實現(xiàn)和interface的組成很相似,都是由“類型”和“數(shù)據(jù)值”構(gòu)成,下面小編分享更多相關(guān)內(nèi)容需要的小伙伴可以參考一下

1. 反射的引入與介紹

在計算機科學(xué)中,反射是指計算機程序在運行時(Run time)可以訪問、檢測和修改它本身狀態(tài)或行為的一種能力。用比喻來說,反射就是程序在運行的時候能夠“觀察”并且修改自己的行為。

需要反射的 2 個常見場景:

  • 有時你需要編寫一個函數(shù),但是并不知道傳給你的參數(shù)類型是什么,可能是沒約定好;也可能是傳入的類型很多,這些類型并不能統(tǒng)一表示。這時反射就會用的上了。
  • 有時候需要根據(jù)某些條件決定調(diào)用哪個函數(shù),比如根據(jù)用戶的輸入來決定。這時就需要對函數(shù)和函數(shù)的參數(shù)進行反射,在運行期間動態(tài)地執(zhí)行函數(shù)。

Java中動態(tài)代理與AOP的實現(xiàn),就要借助這種操作。而對于我們Go語言來說,也提供了反射的機制。

有了前面對于interface{}底層數(shù)據(jù)結(jié)構(gòu)的了解,Go中的每個實例對象可以分為兩快,類型信息與數(shù)值信息。而我們Go中提供的反射機制,能分別拿到這兩塊信息。

image.png

數(shù)據(jù)interface中保存有結(jié)構(gòu)數(shù)據(jù),拿到該數(shù)據(jù)對應(yīng)的內(nèi)存地址,然后把該數(shù)據(jù)轉(zhuǎn)成interface,通過查看interface中的類型結(jié)構(gòu),就可以知道該數(shù)據(jù)的結(jié)構(gòu)了,其實以上就是Go反射通俗的原理。

image.png

2. 反射的數(shù)據(jù)結(jié)構(gòu)

type Type interface {
    // 所有的類型都可以調(diào)用下面這些函數(shù)
	// 此類型的變量對齊后所占用的字節(jié)數(shù)
	Align() int
	// 如果是 struct 的字段,對齊后占用的字節(jié)數(shù)
	FieldAlign() int
	// 返回類型方法集里的第 `i` (傳入的參數(shù))個方法
	Method(int) Method
	// 通過名稱獲取方法
	MethodByName(string) (Method, bool)
	// 獲取類型方法集里導(dǎo)出的方法個數(shù)
	NumMethod() int
	// 類型名稱
	Name() string
	// 返回類型所在的路徑,如:encoding/base64
	PkgPath() string
	// 返回類型的大小,和 unsafe.Sizeof 功能類似
	Size() uintptr
	// 返回類型的字符串表示形式
	String() string
	// 返回類型的類型值
	Kind() Kind
	// 類型是否實現(xiàn)了接口 u
	Implements(u Type) bool
	// 是否可以賦值給 u
	AssignableTo(u Type) bool
	// 是否可以類型轉(zhuǎn)換成 u
	ConvertibleTo(u Type) bool
	// 類型是否可以比較
	Comparable() bool
	// 下面這些函數(shù)只有特定類型可以調(diào)用
	// 如:Key, Elem 兩個方法就只能是 Map 類型才能調(diào)用
	// 類型所占據(jù)的位數(shù)
	Bits() int
	// 返回通道的方向,只能是 chan 類型調(diào)用
	ChanDir() ChanDir
	// 返回類型是否是可變參數(shù),只能是 func 類型調(diào)用
	// 比如 t 是類型 func(x int, y ... float64)
	// 那么 t.IsVariadic() == true
	IsVariadic() bool
	// 返回內(nèi)部子元素類型,只能由類型 Array, Chan, Map, Ptr, or Slice 調(diào)用
	Elem() Type
	// 返回結(jié)構(gòu)體類型的第 i 個字段,只能是結(jié)構(gòu)體類型調(diào)用
	// 如果 i 超過了總字段數(shù),就會 panic
	Field(i int) StructField
	// 返回嵌套的結(jié)構(gòu)體的字段
	FieldByIndex(index []int) StructField
	// 通過字段名稱獲取字段
	FieldByName(name string) (StructField, bool)
	// FieldByNameFunc returns the struct field with a name
	// 返回名稱符合 func 函數(shù)的字段
	FieldByNameFunc(match func(string) bool) (StructField, bool)
	// 獲取函數(shù)類型的第 i 個參數(shù)的類型
	In(i int) Type
	// 返回 map 的 key 類型,只能由類型 map 調(diào)用
	Key() Type
	// 返回 Array 的長度,只能由類型 Array 調(diào)用
	Len() int
	// 返回類型字段的數(shù)量,只能由類型 Struct 調(diào)用
	NumField() int
	// 返回函數(shù)類型的輸入?yún)?shù)個數(shù)
	NumIn() int
	// 返回函數(shù)類型的返回值個數(shù)
	NumOut() int
	// 返回函數(shù)類型的第 i 個值的類型
	Out(i int) Type
        // 返回類型結(jié)構(gòu)體的相同部分
	common() *rtype
	// 返回類型結(jié)構(gòu)體的不同部分
	uncommon() *uncommonType
}
type Value struct {
    typ *rtype
    ptr unsafe.Pointer
    flag
}

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

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

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

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

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

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

在Go中,任何函數(shù)的參數(shù)都是值的拷貝,而非原數(shù)據(jù)。

反射函數(shù)reflect.ValueOf()也不例外。我們目前得到的反射對象,都是原對象的copy的反射對象,而非原對象本身,所以不可以修改到原對象.

那如何修改呢?

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

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

時還不行,因為這樣反射對象對應(yīng)的是原數(shù)據(jù)對象的指針類型,必須要拿到當前類型的值類型(*v)。 Go提供了另外一個方法Elem()

p := v.Elem()
fmt.Println(p.CanSet()) // true

p.SetFloat(6.6)
fmt.Println(x) // 6.6

經(jīng)過以上操作,就可以修改原數(shù)據(jù)了,完整過程如下:

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

到此這篇關(guān)于Go反射底層原理及數(shù)據(jù)結(jié)構(gòu)解析的文章就介紹到這了,更多相關(guān) Go反射原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 基于Java實現(xiàn)音樂播放器的示例代碼

    基于Java實現(xiàn)音樂播放器的示例代碼

    這篇文章主要為大家詳細介紹了如何利用Java編寫一個簡單的音樂播放器,文中的示例代碼講解詳細,具有一定的學(xué)習(xí)價值,感興趣的小伙伴可以了解一下
    2023-07-07
  • java IO流 之 輸入流 InputString()的使用

    java IO流 之 輸入流 InputString()的使用

    這篇文章主要介紹了java IO流 之 輸入流 InputString()的使用,以及讀取數(shù)據(jù)的三種方式詳解,非常不錯,需要的朋友可以參考下
    2016-12-12
  • 解析Java和Eclipse中加載本地庫(.dll文件)的詳細說明

    解析Java和Eclipse中加載本地庫(.dll文件)的詳細說明

    本篇文章是對Java和Eclipse中加載本地庫(.dll文件)進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • 一文詳解Java線程中的安全策略

    一文詳解Java線程中的安全策略

    Java中的線程到底有哪些安全策略呢?這篇文章將詳細為大家分析一下。文中的示例代碼講解詳細,對我們學(xué)習(xí)或工作有一定幫助,需要的可以參考一下
    2022-05-05
  • java實現(xiàn)動態(tài)代理方法淺析

    java實現(xiàn)動態(tài)代理方法淺析

    這篇文章主要介紹了java實現(xiàn)動態(tài)代理方法淺析,很實用的功能,需要的朋友可以參考下
    2014-08-08
  • Dubbo?retries?超時重試機制的問題原因分析及解決方案

    Dubbo?retries?超時重試機制的問題原因分析及解決方案

    這篇文章主要介紹了Dubbo?retries?超時重試機制的問題,解決方案是通過修改dubbo服務(wù)提供方,將timeout超時設(shè)為20000ms或者設(shè)置retries=“0”,禁用超時重試機制,感興趣的朋友跟隨小編一起看看吧
    2022-04-04
  • Java?Maven?Settings配置參考教程

    Java?Maven?Settings配置參考教程

    這篇文章主要介紹了Java?Maven?Settings配置參考,本文通過示例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-09-09
  • Springboot AOP對指定敏感字段數(shù)據(jù)加密存儲的實現(xiàn)

    Springboot AOP對指定敏感字段數(shù)據(jù)加密存儲的實現(xiàn)

    本篇文章主要介紹了利用Springboot+AOP對指定的敏感數(shù)據(jù)進行加密存儲以及對數(shù)據(jù)中加密的數(shù)據(jù)的解密的方法,代碼詳細,具有一定的價值,感興趣的小伙伴可以了解一下
    2021-11-11
  • Java實現(xiàn)堆排序(大根堆)的示例代碼

    Java實現(xiàn)堆排序(大根堆)的示例代碼

    這篇文章主要介紹了Java實現(xiàn)堆排序(大根堆)的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • Java中如何利用Set判斷List集合中是否有重復(fù)元素

    Java中如何利用Set判斷List集合中是否有重復(fù)元素

    在開發(fā)工作中,我們有時需要去判斷List集合中是否含有重復(fù)的元素,這時候我們不需要找出重復(fù)的元素,我們只需要返回一個?Boolean?類型就可以了,下面通過本文給大家介紹Java中利用Set判斷List集合中是否有重復(fù)元素,需要的朋友可以參考下
    2023-05-05

最新評論