Home>Article>Backend Development> An article explaining reflection in Golang in detail
This article will mainly talk about reflection inGolang, hoping to gain a new understanding of you.
Although many people have been using the Go language for a certain period of time, and some have even used it for 1 or 2 years, they are still ambiguous about reflection in the Go language. , I am not very confident in my heart. [Related recommendations:Go video tutorial,Programming teaching]
What’s more, reflection is almost never used. Of course, there is nothing wrong with it. You can It is naturally the nicest to handle it in the simplest, most efficient, scalable, and good-performance way. There is no need to mechanically copy some advanced usage. After all, work is not our testing ground. You can go down and experiment by yourself,this Let’s take a closer look at how to play reflection
The article will talk about it from the following five aspects
In a simple way,Reflection is the ability to access and modify the program itself while the program is running, for example, when the program is running, you can modify the field names and field values of the program, and you can also provide the program with interface access information, etc.
This is a mechanism provided in the Go language. We can You can see a lot about the use of reflect in the language public library
For example, the commonly used fmt package,Commonly usedjsonSerialization and deserialization, naturally the gorm library we mentioned earlier also uses reflection
But why do we generally use What about reflection?
According to the ability of reflection, naturally because the interface we provide does not know what the incoming data type will be, the specific data type is only known when the program is running
But when we are coding, we also hope to verify what the type passed in when the program is running (such as serialization of json) and operate on this specific data. At this time, we need to use The ability of reflection
So wherever reflection is used, you can seeinterface{}Isn’t it surprising?
It is precisely because we are not sure what the incoming data type will be, so we designed it as interface{}. If you are not sure about the characteristics and usage of interface, you can check the historical articles. :
First pay attention to the three important laws of reflection. After knowing the rules,we follow the rules There will be no problems when playing, only when we don’t know the rules and always trigger the clauses, strange problems will occur
Reflection can be used to change the interface Type variables are converted into reflection type objects
The above three rules are also relatively easy to understand. Do you still remember the pointers in the unsafe package we mentioned before?
We convert our commonly used data types into specified data types in packages (such as unsafe packages, or reflect packages), and then modify the data according to the rules in the packages
Quite equivalent So, by changing the vest, you can perform different operations
Generally, we will learn the basic applications first, and then study its principles , study why it can be used in this way, and slowly you will understand more deeply
Actual For the interface type variables mentioned here, we can pass in variables of any data type, such asint, float, string, map, slice, struct
, etc.
Reflection type objects can be understood here asreflect.Type
andreflect.Value
objects in the reflect reflection package, which can be used through theTypeOf# provided in the reflect package. ## andValueOffunctions get
reflect.Typeis actually an interface, which contains various interfaces that need to be implemented, and it provides information about Type-related information
reflect.Typeas shown below, where
Actual The above is a struct. According to this struct, a set of methods are also associated, which stores the data type and specific data. You can see by looking at its data structure
See the unsafe.Pointer here Are you familiar? The bottom layer can naturally converttype Value struct { typ *rtype ptr unsafe.Pointer flag }
touintptr
, then modify its data and then convert it back. If you are not familiar with Go pointers, you can check this Article:
func main() { var demoStr string = "now reflect" fmt.Println("type:", reflect.TypeOf(demoStr)) fmt.Println("value:", reflect.ValueOf(demoStr)) }For the law Second, convert the reflection type object into an interface type variable
type into our specific data type, becausereflect. There are corresponding
typ *rtypeand
ptr unsafe.Pointerin Value
. For example, we can pass the interface of
object () method to deal with
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) }
For Law 3, modify the reflection type object
andValueOfare actually copies. If you want to modify the value in a reflection type object, you need to get the address of the specific variable. Then modify it,The premise is that this variable is writableYou can understand it by giving an example
func main() { var demoStr string = "now reflect" v := reflect.ValueOf(demoStr) fmt.Println("is canset ", v.CanSet()) //v.SetString("hello world") // 会panic }
You can first Call the
reflect.Valueobject'sCanSet
to check whether it is writable. If it is writable, we will write it again. If it is not writable, don't write it, otherwise it will panic
Then the address of the variable passed in can be modified? ?
#There is nothing wrong with the idea of passing in the address, but there is something wrong with the way we set the value, so the above panic situation will also occur
Careful here It can be understood that the reflected object v is naturally unmodifiable. We should find the specific data pointer in
reflect.Value, then it can be modified. You can usereflect.Value
的Elem
Method
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的接口中涉及的数据结构
在 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 的源码中,我们可以看到,重要的是 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,如果你期望去使用他,那么就去更多的深入了解和一步一步的吃透他吧
大道至简,反射三定律,活学活用
更多编程相关知识,请访问:编程视频!!
The above is the detailed content of An article explaining reflection in Golang in detail. For more information, please follow other related articles on the PHP Chinese website!