本篇文章带大家主要来聊聊Golang中反射,希望对你有新的认知。
虽然很多人使用 Go 语言有一定时间了,甚至有的使用了 1 年 2 年,然后对于 Go 语言中的反射还是模棱两可,使用起来的时候,心里也不是非常有底气。【相关推荐:Go视频教程、编程教学】
更有甚者,几乎不使用反射,当然,也不是什么错,在工作中能用最简单最高效,又可扩展,性能还好的方式来进行处理自然是最 nice ,没有必要去生搬硬套一些高级用法,毕竟工作不是我们的试炼场,可以自己下来多多实验,本次就来好好看看如何去玩反射
文章分别从如下五个方面来聊
- 反射是什么
- 反射的规则
- 使用案例并灵活运用
- 反射原理
- 总结
简单来看反射是什么
简单来看,反射就是在程序运行时期对程序本身进行访问和修改的能力,例如在程序运行时,可以修改程序的字段名称,字段值,还可以给程序提供接口访问的信息等等
这是 Go 语言中提供的一种机制,我们可以在 Go 语言公共库中可以看到很多关于 reflect 的使用位置
例如常用的 fmt 包,常用的 json 序列化和反序列化,自然前面我们说到的 gorm 库自然也是使用了反射的
可是我们一般为什么要使用反射呢?
根据反射的能力,自然是因为我们提供的接口并不知道传入的数据类型会是什么样的, 只有当程序运行的时候才知道具体的数据类型
但是我们编码的时候又期望去校验程序运行时传入的类型会是什么样的(例如 json 的序列化)并对其这种具体的数据进行操作,这个时候,咱们就需要用到反射的能力了
所以对于使用到反射的地方,你都能看到 interface{} 是不是就不奇怪了呢?
正是因为不确定传入的数据类型会是什么样的,所以才设计成 interface{} ,那么如果对于 interface 有还不清楚他的特点和使用方式的,可以查看历史文章:
先关注反射的规则
首先关注反射的三个重要的定律,知道规则之后,我们按照规则玩就不会有什么问题,只有当我们不清楚规则,总是触发条款的时候,才会出现奇奇怪怪的问题
反射是可以将 接口类型的变量 转换成 反射类型的对象
- 反射可以将 反射类型的对象 转换成 接口类型的变量
- 我们在运行时要去修改的 反射类型的对象 ,那么要求这个对象对应的值是要可写的
对于上述 3 个规则也是比较好理解,还记的之前我们说过的 unsafe 包里面的指针吗?
都是将我们常用的数据类型,转换成包(例如 unsafe包,或者 reflect 包)里面的指定数据类型,然后再按照包里面的规则进行修改数据
相当于,换个马甲,就可以进行不同的操作了
关注使用案例并灵活运用
一般咱们先会基本的应用,再去研究他的原理,研究他为什么可以这样用,慢慢的才能理解的更加深刻
对于定律一,将 接口类型的变量 转换成 反射类型的对象
实际上此处说的 接口类型的变量 我们可以传入任意数据类型的变量,例如 int, float, string ,map, slice, struct
等等
反射類型的對象這裡就可以理解成reflect 反射包中的reflect.Type
和 reflect.Value
對象,可以透過reflect 包中提供的TypeOf 和ValueOf 函數得到
其中reflect.Type
實際上是一個interface ,他裡麵包含了各種介面需要實現,它裡面提供了關於類型相關的資訊
其中如下圖可以查看到reflect.Type
的所有方法,其中
- 綠色的是所有數據類型都是可以呼叫的
- 紅色的是函數型別資料可以呼叫的
- 黑色的是Map,數組Array,通道Chan,指標Ptr 或切片Slice 可以呼叫的
- 藍色的是結構體呼叫的
- 黃色的是通道channel 類型呼叫的
reflect.Value
實際上是一個struct,根據這個struct 還關聯了一組方法,這裡面存放了數據類型和具體的數據,透過查看其數據結構就可以看出
type Value struct { typ *rtype ptr unsafe.Pointer flag }
看到此處的unsafe.Pointer是不是很熟悉,底層自然就可以將unsafe.Pointer
轉換成uintptr
,然後再修改其資料後,再轉換回來,對於Go 指標不太熟悉的可以檢視這文章:
寫一個簡單的demo 就可以簡單的取得到變數的資料型別和值
func main() { var demoStr string = "now reflect" fmt.Println("type:", reflect.TypeOf(demoStr)) fmt.Println("value:", reflect.ValueOf(demoStr)) }
對於定律二,將反射類型的物件轉換成介面類型的變數
我們可以透過將reflect.Value
類型轉換成我們特定的資料類型,因為reflect. Value
中有對應的typ *rtype
以及ptr unsafe.Pointer
例如我們可以透過reflect.Value
物件的interface () 方法來處理
func main() { var demoStr string = "now reflect" fmt.Println("type:", reflect.TypeOf(demoStr)) fmt.Println("value:", reflect.ValueOf(demoStr)) var res string res = reflect.ValueOf(demoStr).Interface().(string) fmt.Println("res == ",res) }
#對於定律三,修改反射類型的物件
首先我們看上書的demo 程式碼,傳入TypeOf 和ValueOf 的變數其實也是拷貝,那麼如果期望在反射型別的物件中修改其值,那麼就需要拿到具體變數的位址然後再進行修改,前提是這個變數是可寫的
舉例你就能明白
func main() { var demoStr string = "now reflect" v := reflect.ValueOf(demoStr) fmt.Println("is canset ", v.CanSet()) //v.SetString("hello world") // 会panic }
可以先呼叫reflect.Value
物件的CanSet
查看是否可寫,如果是可寫的,我們再寫,如果不可寫就不要寫了,否則會panic
那麼傳入變數的位址就可以修改了? ?
傳入位址的思路沒有毛病,但是我們去設定值的方式有問題,因此也會出現上述的panic 情況
此處仔細看能夠明白,反射的物件v 自然是不可修改的,我們應該找到reflect.Value
裡面具體具體的資料指針,那麼才是可以修改的,可以使用reflect.Value
的Elem 方法
稍微複雜一點的
看上了上述案例可能會覺得那麼簡單的案例,一演示就ok,但是工作中一用就崩潰,那自然還是沒有融會貫通,說明還沒有消化好,再來一個工作中的例子
- 一個結構體裡面有map ,map 中的key 是string,value 是[]string
- 需求是存取結構體中hobby 欄位對應的map key 為sport 的切片的第1 個元素,並將其修改為hellolworld
type RDemo struct { Name string Age int Money float32 Hobby map[string][]string } func main() { tmp := &RDemo{ Name: "xiaomiong", Age: 18, Money: 25.6, Hobby: map[string][]string{ "sport": {"basketball", "football"}, "food": {"beef"}, }, } v := reflect.ValueOf(tmp).Elem() // 拿到结构体对象 h := v.FieldByName("Hobby") // 拿到 Hobby 对象 h1 := h.MapKeys()[0] // 拿到 Hobby 的第 0 个key fmt.Println("key1 name == ",h1.Interface().(string)) sli := h.MapIndex(h1) // 拿到 Hobby 的第 0 个key对应的对象 str := sli.Index(1) // 拿到切片的第 1 个对象 fmt.Println(str.CanSet()) str.SetString("helloworld") fmt.Println("tmp == ",tmp) }
可以看到上述案例运行之后有时可以运行成功,有时会出现 panic 的情况,相信细心的 xdm 就可以看出来,是因为 map 中的 key 是 无序的导致的,此处也提醒一波,使用 map 的时候要注意这一点
看上述代码,是不是就能够明白咱们使用反射去找到对应的数据类型,然后按照数据类型进行处理数据的过程了呢
有需要的话,可以慢慢的去熟练反射包中涉及的函数,重点是要了解其三个规则,对象转换方式,访问方式,以及数据修改方式
反射原理
那么通过上述案例,可以知道关于反射中数据类型和数据指针对应的值是相当重要的,不同的数据类型能够用哪些函数这个需要注意,否则用错直接就会 panic
TypeOf
来看 TypeOf 的接口中涉及的数据结构
在 reflect 包中 rtype
是非常重要的,Go 中所有的类型都会包含这个结构,所以咱们反射可以应用起来,结构如下
// rtype must be kept in sync with ../runtime/type.go:/^type._type. type rtype struct { size uintptr ptrdata uintptr hash uint32 tflag tflag align uint8 fieldAlign uint8 kind uint8 equal func(unsafe.Pointer, unsafe.Pointer) bool gcdata *byte str nameOff ptrToThis typeOff }
其中可以看到此处的 rtype
的结构保持和 runtime/type.go
一致 ,都是关于数据类型的表示,以及对应的指针,关于这一块的说明和演示可以查看文末的 interface{} 处的内容
ValueOf
从 ValueOf 的源码中,我们可以看到,重要的是 emptyInterface 结构
// emptyInterface is the header for an interface{} value.type emptyInterface struct { typ *rtype word unsafe.Pointer }复制代码
emptyInterface 结构中有 rtype
类型的指针, word 自然是对应的数据的地址了
reflect.Value
对象中的方法也是非常的多,用起来和上述说到的 reflect.Type
接口中的功能类似
关于源码中涉及到的方法,就不再过多的赘述了,更多的还是需要自己多多实践才能体会的更好
殊不知,此处的 reflect.Value
也是可以转换成 reflect.Type
,可以查看源码中 reflect\value.go
的 func (v Value) Type() Type {
其中 reflect.Value
,reflect.Type
,和任意数据类型
可以相互这样来转换
如下图:
总结
至此,关于反射就聊到这里,一些关于源码的细节并没有详细说,更多的站在一个使用者的角度去看反射需要注意的点
关于反射,大多的人是建议少用,因为是会影响到性能,不过如果不太关注这一点,那么用起来还是非常方便的
高级功能自然也是双刃剑,你用不好就会 panic,如果你期望去使用他,那么就去更多的深入了解和一步一步的吃透他吧
大道至简,反射三定律,活学活用
更多编程相关知识,请访问:编程视频!!
以上是一文詳解Golang中的反射的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Golang更適合高並發任務,而Python在靈活性上更有優勢。 1.Golang通過goroutine和channel高效處理並發。 2.Python依賴threading和asyncio,受GIL影響,但提供多種並發方式。選擇應基於具體需求。

Golang和C 在性能上的差異主要體現在內存管理、編譯優化和運行時效率等方面。 1)Golang的垃圾回收機制方便但可能影響性能,2)C 的手動內存管理和編譯器優化在遞歸計算中表現更為高效。

selectgolangforhighpperformanceandcorrency,ifealforBackendServicesSandNetwork程序; selectpypypythonforrapiddevelopment,dataScience和machinelearningDuetoitsverserverserverserversator versator anderticality andextility andextentensivelibraries。

Golang和Python各有优势:Golang适合高性能和并发编程,Python适用于数据科学和Web开发。Golang以其并发模型和高效性能著称,Python则以简洁语法和丰富库生态系统著称。

Golang和Python分別在哪些方面更易用和學習曲線更平緩? Golang更適合高並發和高性能需求,學習曲線對有C語言背景的開發者較平緩。 Python更適合數據科學和快速原型設計,學習曲線對初學者非常平緩。

Golang和C 在性能競賽中的表現各有優勢:1)Golang適合高並發和快速開發,2)C 提供更高性能和細粒度控制。選擇應基於項目需求和團隊技術棧。

Golang適合快速開發和並發編程,而C 更適合需要極致性能和底層控制的項目。 1)Golang的並發模型通過goroutine和channel簡化並發編程。 2)C 的模板編程提供泛型代碼和性能優化。 3)Golang的垃圾回收方便但可能影響性能,C 的內存管理複雜但控制精細。

goimpactsdevelopmentpositationality throughspeed,效率和模擬性。 1)速度:gocompilesquicklyandrunseff,IdealforlargeProjects.2)效率:效率:ITScomprehenSevestAndardArdardArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdEcceSteral Depentencies,增強的Depleflovelmentimency.3)簡單性。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

Atom編輯器mac版下載
最受歡迎的的開源編輯器

SAP NetWeaver Server Adapter for Eclipse
將Eclipse與SAP NetWeaver應用伺服器整合。

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器