This is a creation in Article, where the information may have evolved or changed.
GitHub:https://github.com/ZhangzheBJUT/blog/blob/master/reflect.md
A reflection of the rules
Reflection is the ability of a program to check its own structure, especially the type, when it runs, which is a form of meta-programming. It is also an important source of confusion.
The reflection models are different for each language (and many languages do not support reflection at all). This section will try to clearly explain how reflection works in Go.
1. Reflection from the interface value to the reflected object
at a basic level, reflection is just an algorithm that examines the types and values stored in an interface variable. There are two types in the reflect package that need to know:type and Value. These two types make it possible to access the contents of interface variables, and there are two simple functions,reflect. TypeOf and reflect. ValueOf, obtain the reflect from the interface value separately. Type and reflect. Value. ( Note: from reflect. Value is also easy to get reflect. Type, but here the Value and Type are conceptually separated )
Starting from TypeOf:
package mainimport ( "fmt" "reflect")func main() { var x float64 = 3.4 fmt.Println("type:", reflect.TypeOf(x))}
This program prints type: float64
Where the interface is, the reader may have doubts about it, and it seems that the program passed a variable x of type float64, not an interface value, to reflect. TypeOf. But it was there: as Godoc reported, reflect. The TypeOf declaration contains an empty interface:
// TypeOf 返回 interface{} 中的值反射的类型。 func TypeOf(i interface{}) Type
When calling reflect. TypeOf (x), x is first stored in an empty interface that is passed as a parameter; reflect. TypeOf unpack this empty interface to restore type information.
Reflect. The ValueOf function, of course, is to restore that value (starting from here we will skip over those conceptual examples and focus on the executable code):
var x float64 = 3.4fmt.Println("value:", reflect.ValueOf(x))
Print
value: <float64 Value>
except reflect. Type and reflect. Value, there are many ways to check and manipulate them. An important example is that Value has a Type method that returns reflect. The Type of Value. The other is that both type and Value have a Kind method that returns a constant to represent the type: Uint, Float64, Slice, and so on. The same value has a method called Int and Float that can fetch values stored internally (as with Int64 and float64):
var x float64 = 3.4v := reflect.ValueOf(x)fmt.Println("type:", v.Type())fmt.Println("kind is float64:", v.Kind() == reflect.Float64)fmt.Println("value:", v.Float())
Print
type: float64 kind is float64: true value: 3.4
There are also methods like Setint and setfloat, but you need to understand the setting before using them, and this part of the topic is discussed in the third rule below.
The Reflection library has a number of features that deserve special clarification.
To maintain the simplicity of the API, the "Get" and "set up" processes values with the broadest type of value: For example, Int64 can be used with all signed integers. That is, the Int method of value returns a int64, while the Setint value accepts a int64, so it may have to be converted to the actual type:
var x uint8 = 'x' v := reflect.ValueOf(x) fmt.Println("type:", v.Type()) // uint8. fmt.Println("kind is uint8: ", v.Kind() == reflect.Uint8) // true. x = uint8(v.Uint()) // v.Uint 返回一个 uint64.
The Kind of the reflected object describes the underlying type, not the static type. If a reflection object contains a value of a user-defined integer type, it is like
type MyInt int var x MyInt = 7 v := reflect.ValueOf(x)‘
The Kind of V is still reflect. int, although the static type of x is MyInt, not int. In other words, Kind cannot differentiate int from MyInt, and Type can.
2. Reflection from reflected object to interface value
Like reflection in physics, reflection in Go also has its own image.
from reflect. Value can be used to restore interface values using the Interface method, which can efficiently package type and value information into an interface expression and return this result:
// Interface 以 interface{} 返回 v 的值。func (v Value) Interface() interface{}
You can do this as a result
y := v.Interface().(float64) // y 将为类型 float64。fmt.Println(y)
The float64 expression value can be printed by reflecting the object v.
However, it can be done better. fmt.Println
, and fmt.Printf
so on all other functions that pass an empty interface value as a parameter, the way the package is unpacked inside the FMT package is like the previous example. So the correct print reflect. The content of Value is formatted by printing the results of the Interface method (formatted print routine).
fmt.Println(v.Interface())
Why not FMT. Println (v)? Because V is a reflect. value; This is what you want to get is the actual value it holds.
Because the value is float64, you can even use floating-point formatting if needed:
fmt.Printf("value is %7.1e\n", v.Interface())
Output: 3.4e+00
Again, for V.interface () No type is asserted as float64, and the null interface value internally has the actual value of the type information, and Printf will find it.
Simply put, the Interface method is the mirror of the ValueOf function, except that the return value is always static type interface{}.
Review: reflection can be from an interface value to a reflection object, or vice versa.
3. In order to modify the reflected object, its value must be set
var x float64 = 3.4v := reflect.ValueOf(x)v.SetFloat(7.1) // Error: will panic.
If you run this code, it reports a mysterious panic message.
panic: reflect.Value.SetFloat using unaddressable value
The problem is not that the value 7.1 cannot be addressed, but that V is not set. The setting is a property of the reflected value, and not all reflection values have this attribute.
The Canset method of value provides a set of values, in this case,
var x float64 = 3.4v := reflect.ValueOf(x)fmt.Println("settability of v:" , v.CanSet())
Print
settability of v: false
Calling the Set method on a non-set value will have an error.
But what is setup?
The setting is a little bit like address, but more stringent. This is the ability to modify the actual stored properties when creating a reflection object. Settings are used to determine whether the reflected object holds the original item. When this
var x float64 = 3.4v := reflect.ValueOf(x)
A copy of x is passed to reflect. ValueOf, so the interface value as reflect. The ValueOf parameter creates a copy of x, not the x itself. Therefore, if the statement
v.SetFloat(7.1)
Allowed to execute, although v appears to have been created from X, it cannot update x. Conversely, if a copy of x is allowed to be updated inside the reflected value, x itself will not get affected. This can be confusing and meaningless, so this is illegal, and setup is the attribute used to solve the problem.
This is amazing? Not really. This is actually a common and unusual situation. Consider passing x to the function:
F (x) does not expect F to modify X because it passes a copy of the value of x rather than the x itself. If you want F to modify x directly, you must pass the address of X to the function (that is, the pointer to x):
F (&x) This is clear and familiar, and reflection works in the same way. If you want to modify X by reflection, you must provide a pointer to the reflection library for the value you want to modify.
Let's try it. First initialize x as usual, and then create a reflection value that points to it, called P.
var x float64 = 3.4p := reflect.ValueOf(&x) // 注意:获取 X 的地址。fmt.Println("type of p:", p.Type())fmt.Println("settability of p:" , p.CanSet())
This output is
type of p: *float64settability of p: false
The Reflection object P is not configurable, and we do not want to set p, which is actually *p. To get the contents of P pointing, call the Elem method on the value, point to it indirectly from the pointer, and then save the reflected value as the result is called V:
v := p.Elem()fmt.Println("settability of v:" , v.CanSet())
Now V is a reflective object that can be set, like the output of the example,
settability of v: true
And since it comes from X, you can eventually use V.setfloat to modify the value of x:
v.SetFloat(7.1)fmt.Println(v.Interface())fmt.Println(x)
Get the desired output
7.17.1
Reflection can be difficult to understand, but the language does what it should do, although the underlying implementation is reflected by the Type and Value hidden. It is important to remember that the reflection value requires the address of some content to modify what it points to.
Two structural bodies
In the previous example, v itself is not a pointer, it is only obtained from a pointer. This is more common when using reflection to modify a struct's field. That is, when you have the address of a struct, you can modify its fields.
Here is a simple example of analyzing the structure value T. Because you want to modify a struct, a reflection object is created from its address. Set the Typeoft as its type, and then use a straightforward method to traverse its fields (refer to the reflect package for more information). Note that the field name is parsed from the struct type, but the field itself is the original reflect. The Value object.
type T struct { A int B string}t := T{23, "skidoo"}s := reflect.ValueOf(&t).Elem()typeOfT := s.Type()for i := 0; i < s.NumField(); i++ { f := s.Field(i) fmt.Printf("%d: %s %s = %v\n", i, typeOfT.Field(i).Name, f.Type(), f.Interface())}
Program output:
0: A int = 231: B string = skidoo
There is also a point about setup:the field name of T is capitalized (exportable), because only exportable fields are configurable.
Because s contains a reflective object that can be set, you can modify the fields of the struct body.
s.Field(0).SetInt(77)s.Field(1).SetString("Sunset Strip")fmt.Println("t is now", t)
Here is the result:
t is now {77 Sunset Strip}
If you modify the program so that s is created in t instead of &t, calling Setint and SetString will fail because the field T is not set.
Three summary
The rules for reflection are as follows:
Reflection from an interface value to a reflected object
Reflection from reflected object to interface value
In order to modify the reflected object, its value must be set
Once you understand the rules of reflection in Go, it becomes easy to use, although it is still very subtle. This is a powerful tool that should be avoided or used with caution unless it is really necessary.
There is also a lot of content about reflection that doesn't involve sending and receiving on--channel, allocating memory, using slice and maps, calling methods, and functions.
Case code: Https://github.com/ZhangzheBJUT/GoProject/blob/master/reflect/main.go
reference: http://blog.golang.org/laws-of-reflection