The reflection of go is very fragile. The premise of ensuring that the reflection code runs correctly is that before calling the method of the reflection object, first ask whether the method you are calling is suitable for all the original types used to create the reflection object.Most of the errors in go reflection come from calling a method that does not fit the current type.(For example, call on an integer reflection objectField()
method). Moreover, these errors are usually exposed at runtime, not at compile time, and it's easy to do so if the type we pass is not overwritten in the reflected code.panic
。
This article introduces the errors that are likely to occur when using go reflection.
There is no type judged before getting the value of Value
for, we have many ways to get its value, such as
Int()
、String()
etc. However, these methods have a prerequisite, that is, the underlying layer of the reflection object must be the type corresponding to the method we call, otherwise it willpanic
, for example, the following example:
var f float32 = 1.0 v := (f) // Error: panic: reflect: call of on float32 Value(())
In the above example,f
It's onefloat32
Float of type, then we try to passInt()
Method to get an integer, but this method can only be used forint
Reflection object of type, so an error will be reported.
- Methods involved:
Addr
,Bool
,Bytes
,Complex
,Int
,Uint
,Float
,Interface
; When calling these methods, if the type is incorrect, it willpanic
。 - Methods to determine whether a reflective object can be converted to a certain type:
CanAddr
,CanInterface
,CanComplex
,CanFloat
,CanInt
,CanUint
。 - How to determine whether other types can be converted:
CanConvert
, it can be judged whether a reflective object can be converted to a certain type.
passCanConvert
Method to determine whether a reflective object can be converted to a certain type:
// true (((1.0)))
If we want to convert the reflective object to our custom type, we can useCanConvert
To determine whether it can be converted, then call itConvert
Method to convert:
type Person struct { Name string } func TestReflect(t *) { p := Person{Name: "foo"} v := (p) // v can be converted to Person type (t, ((Person{}))) // v can be converted to Person type p1 := ((Person{})) (t, "foo", ().(Person).Name) }
illustrate:
-
(Person{})
Can be obtainedPerson
Type of information -
Can
v
Convert to(Person{})
The specified type
No pointer passed to
If we want to modify the original variable by reflecting the object, we must pass a pointer, otherwise an error will be reported (not considered for the time being).slice
, map
, Special cases where the structure field contains pointer field):
func TestReflect(t *) { p := Person{Name: "foo"} v := (p) // Error: panic: reflect: using unaddressable value ("Name").SetString("bar") }
The reason for this error is thatv
It's onePerson
value of type, not pointers, so we cannot pass("Name")
to modify its fields.
For a reflective object, only a copy of p is obtained, not p itself, so we cannot modify p by reflective object.
Operation on an invalid Value
We have many ways to create, and this kind of method does not
error
Return value, which means that even if we createWhen an invalid value is passed, there will be no error, but an invalid value will be returned
:
func TestReflect(t *) { var p = Person{} v := (p) // Person does not exist foo method // FieldByName returns a reflective object representing Field v1 := ("foo") (t, ()) // v1 is invalid, only the String method can be called // Other method calls will be panic (t, func() { // panic: reflect: call of on zero Value (()) }) }
For this question, we canIsValid
Method to judgeWhether it is valid:
func TestReflect(t *) { var p = Person{} v := (p) v1 := ("foo") //Judge whether it is valid through IsValid if () { ("p has foo field") } else { ("p has no foo field") } }
The Field() method directly panics when the passed index is out of range, without returning an invalid .
IsValid
Report reflective objectsv
Whether it represents a value. ifv
It is a zero value, then returnfalse
. ifIsValid
returnfalse
, thenString
Everything else will happen outside ofpanic
. Most functions and methods never return invalid values.
When does IsValid return false
of
IsValid
The return value ofWhether it is valid, not whether the value it represents is valid. for example:
var b *int = nil v := (b) (()) // true (().IsValid()) // false ((v).IsValid()) // false
In the above example,v
is valid, it represents a pointer, the object pointed to by the pointer isnil
. but()
and(v)
All are invalid because they represent the object pointed to by the pointer, and the object pointed to by the pointer isnil
. We cannot use it basednil
To do any reflection operation.
In other cases IsValid returns false
Except for the above situation,IsValid
In other cases, it will returnfalse
:
- Empty reflective value object, obtained by
nil
The reflection object created, whichIsValid
Will returnfalse
。 - The structure reflects the object through
FieldByName
Gets a non-existent field, whichIsValid
Will returnfalse
。 - The structure reflects the object through
MethodByName
Obtained a method that does not exist,IsValid
Will returnfalse
。 -
map
Reflection object throughMapIndex
Obtained a non-existent key, whichIsValid
Will returnfalse
。
Example:
func TestReflect(t *) { // Empty reflective object ({}.IsValid()) // false // Reflective object created based on nil ((nil).IsValid()) // false s := struct{}{} // Get non-existent fields ((s).FieldByName("").IsValid()) // false // Get a method that does not exist ((s).MethodByName("").IsValid()) // false m := map[int]int{} // Get the non-existent key of the map ((m).MapIndex((3)).IsValid()) }
Note: There are other situations that can alsoIsValid
returnfalse
, here are only some of the situations listed. When using it, we need to pay attention to whether the reflective object we are using will be invalid.
Modify unmodified values through reflection
forWe can pass
CanSet
Method to determine whether it can be set:
func TestReflect(t *) { p := Person{Name: "foo"} // Pass the value to create the emit object, // Its value cannot be modified because it is a copy v := (p) (t, ()) (t, (0).CanSet()) // The following line of code will panic: // panic: reflect: using unaddressable value // (0).SetString("bar") // The pointer reflection object itself cannot be modified. // The object it points to (that is, ()) can be modified v1 := (&p) (t, ()) (t, ().CanSet()) }
CanSet
Reportv
Whether the value of ? Addressable only (addressable
) and cannot be changed by using values obtained by using unexported structure fields. ifCanSet
returnfalse
, callSet
or any type of specificsetter
(For exampleSetBool
、SetInt
)Willpanic
。The condition of CanSet is addressable.
For reflection objects created by passing values, we cannot modify the original variable through reflection objects.CanSet
Method returnfalse
。 The exception is that if this value contains a pointer, we can still use that pointer to modify the object it points to.
The object pointed to by the pointer can only be set by the return value of the Elem method.
Calling the Elem method on the wrong Value
of
Elem()
returninterface
The reflective object contains a value or a pointer reflects the value pointed to by the object. If the object's reflectionKind
noor
, it will happen
panic
. If the reflective object isnil
, then returns a zero value.
We know,interface
Types actually contain types and data. And we pass it toThe parameters are
interface
, so methods are also provided in the reflective object to obtaininterface
Types and data of types:
func TestReflect(t *) { p := Person{Name: "foo"} v := (p) // The following line will report an error: // panic: reflect: call of on struct Value // () (()) // v1 is a reflection object of type *Person, which is a pointer v1 := (&p) ((), ()) }
In the above example,v
It's onePerson
type reflective object, it is not a pointer, so we cannot pass()
to get the object it points to. andv1
is a pointer, so we can pass()
to get the object it points to.
Called a method whose type cannot be called
This is probably the most common type of error, because in go's reflection system, some methods we call return a reflective object of the same type, but this new reflective object may be of a different type. It is also unknown whether the reflected object returned at the same time is valid.
In go, reflection has two major objectsand
, they all have some methods that are only applicable to certain specific types, that is, in the reflection design of go, only fortypeandvalueTwo major categories. But there are many types in actual go, such as
int
、string
、struct
、interface
、slice
、map
、chan
、func
etc.
Let's not talk about it for now, we
From the perspective of the word, abstracting all values of so many types into
After that, how do we get information specific to certain types of values? For example, get the value of a certain field in the structure, or call a certain method. This problem is easy to solve. You need to get the structure field, right? Then provide you with a
Field()
Method, you need to call the method, so I will provide you with aCall()
method.
But in this way, another problem is, if oursIt's from one
int
The value of type is created, then we callField()
The method will happenpanic
,becauseint
The value of type is notField()
Method:
func TestReflect(t *) { p := Person{Name: "foo"} v := (p) // Get the Name field of the reflected object (t, "foo", (0).String()) var i = 1 v1 := (i) (t, func() { // The following rank will panic: // v1 does not have a Field method ((0).String()) }) }
As for what methods are specific to certain types, you can refer to the following two documents:
- Type-specific method
- Type-specific method
Summarize
- Calling
Int()
、Float()
When you are using the method, you need to make sure that the type of the reflected object is the correct type, otherwisepanic
, for example in aflaot
Called on a type of reflection objectInt()
The method willpanic
。 - If you want to modify the original variable, create
When you need to pass a pointer to the original variable.
- if
of
IsValid()
Method returnfalse
, then it is an invalid reflective object, and any method called willpanic
,Apart fromString
method. - For value-based
, if we want to modify its value, we cannot call this reflective object
Set*
Method, because modifying a copy of a variable has no meaning. - At the same time, we cannot pass
Modify unexported fields in the structure, even if we create them
When the pointer of the structure is passed in.
-
Elem()
Only in pointers orinterface
Called on a type of reflection object, otherwisepanic
, its function is to obtain the reflected object of the object pointed to by the pointer, or to obtain the interfacedata
reflective object. -
and
There are many types of methods, such as
Field()
、Call()
etc. These methods can only be called on certain types of reflective objects, otherwisepanic
。
This is the article about this article about a common error in reflect reflection in Golang. For more related Golang reflect reflection content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!