Home>Article>Backend Development> An article explaining reflection in Golang in detail

An article explaining reflection in Golang in detail

青灯夜游
青灯夜游 forward
2022-12-14 20:26:34 6022browse

This article will mainly talk about reflection inGolang, hoping to gain a new understanding of you.

An article explaining reflection in Golang in detail

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

  • What is reflection
  • Rules of reflection
  • Use cases and flexible application
  • Reflection principle
  • Summary

A simple look at what reflection is

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 rules of reflection

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

  • Reflection can convert reflection type objects into interface type variables
  • The reflection type objects we want to modify at runtime, Then it is required that the value corresponding to this object must be writable

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

Pay attention to use cases and use them flexibly

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

For Law 1, convert interface type variables into reflection type objects

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.Typeandreflect.Valueobjects in the reflect reflection package, which can be used through theTypeOf# provided in the reflect package. ## andValueOffunctions get

where

reflect.Typeis actually an interface, which contains various interfaces that need to be implemented, and it provides information about Type-related information

You can see all the methods of

reflect.Typeas shown below, where

  • greenis all the data The types are all callable
  • Redis the function type data that can be called
  • Blackis Map, array Array, channel Chan, pointer Ptr or slice Slice that can be called
  • The blueis the# that can be called by the structure
    ##Yellow
  • is the channel type call

reflect.Value

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

type Value struct { typ *rtype ptr unsafe.Pointer flag }
See the unsafe.Pointer here Are you familiar? The bottom layer can naturally convert

unsafe.Pointer

touintptr, then modify its data and then convert it back. If you are not familiar with Go pointers, you can check this Article:

    #Pointers in GO?
  • Write a simple demo to easily obtain the data type and value of the variable
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

We can convert the

reflect.Value

type into our specific data type, becausereflect. There are correspondingtyp *rtypeandptr unsafe.Pointerin Value. For example, we can pass the interface of

reflect.Value

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

First we read the book In the demo code, the variables passed in

TypeOf

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.Value

object'sCanSetto 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.ValueElemMethod

A slightly more complicated one

You may feel so after taking a fancy to the above case A simple case is ok as soon as it is demonstrated, but it crashes as soon as it is used at work. Naturally, it still has not been fully understood, indicating that it has not been digested well. Here is another example from work

A structure has a map in it , the key in the map is string, and the value is []string
The requirement is to access the #th slice of the map key corresponding to the hobby field in the structure for
    sport
  • ##1elements and modify them tohellolworld
    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.gofunc (v Value) Type() Type {

其中reflect.Valuereflect.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!

Statement:
This article is reproduced at:juejin.cn. If there is any infringement, please contact admin@php.cn delete