In fact, Type and Value are essentially a encapsulation of Golang’s underlying data, which can be developed programmably based on iFace and eface, because those two objects are black boxes for developers. Why do I take Field and Method more in order to reflect the importance of these two, I will also focus on them
A few highlights of reflection: 3. Understand the usage of Type and Value 4. Master the Call function proficiently and conduct safe operation 5. 6. Learn to use reflection to operate IOC
Type
Type refers to meta information, which is similar to Java Class objects. If you get Type, you can basically do everything, including structure information, field information, method information, etc., so this is the key point. Java basically has all the functions of Go, the only difference is the bytecode dynamic modification and injection, which is certainly missing in compiled languages. Because the so-called types are determined before the program starts and cannot be modified, this part is the most important.
It should also be added that, using the Reflect package, the core needs to focus on the boundary points, which must be noticed, and is the most core concern, because some method calls are conditional.
Reflect.typeof () can be passed a null pointer when retrieved, as long as the null pointer is typed!
The structure of the body
Core concerns and boundaries, the official notes are very detailed, if you want to use which method you need to see the use of boundaries
type Type interface {
// Methods applicable to all types., corresponding to the unsafe align, is a method to calculate the size
Align() int
// The size of the field must be type.kind= structure type
FieldAlign() int
// It panics if i is not in the range [0, NumMethod()).
Value.Method() {Value.Method() {Value
Method(int) Method
MethodByName(string) (Method, bool)
NumMethod() int
// Name returns the type's name within its package for a defined type.
// For other (non-defined) types it returns the empty string.
Name() string
PkgPath() string
// Size, no care required
Size() uintptr
// Similar to Java ToString
String() string
// Kind returns the specific kind of this type.
// It is important to distinguish the boundary by kind, which returns the object type, such as pointer, slice, structure...
Kind() Kind
// Whether an interface is implemented
// Implements reports whether the type implements the interface type u.
Implements(u Type) bool
// AssignableTo reports whether a value of the type is assignable to type u.
AssignableTo(u Type) bool
// Whether to convert
// ConvertibleTo reports whether a value of the type is convertible to type u.
ConvertibleTo(u Type) bool
// Comparable reports whether values of this type are comparable.
Comparable() bool
// It panics if the type's Kind is not one of the
// sized or unsized Int, Uint, Float, or Complex kinds.
Bits() int
// ChanDir returns a channel type's direction.
// It panics if the type's Kind is not Chan.
ChanDir() ChanDir
// Start with a fun to determine if it is a mutable argument
// IsVariadic panics if the type's Kind is not Func.
IsVariadic() bool
// Elem returns a type of element type.
// It panics if the type's Kind is not Array, Chan, Map, Ptr, or Slice.
Elem() Type
// Field information
// It panics if the type's Kind is not Struct.
// It panics if i is not in the range [0, NumField()).
Field(i int) StructField
Field (1,1) = field (1,1) = field (1,1)
FieldByIndex(index []int) StructField
FieldByName(name string) (StructField, bool)
// return, filter
FieldByNameFunc(match func(string) bool) (StructField, bool)// Function argument type //It panics if the type's Kind is not Func. / /It panics if i is not in the range [0, NumIn(a)).
In(i int) Type/ / returnmapThe object'skeyType / /It panics if the type's Kind is not Map.
Key(a) Type// Return the array length //It panics if the type's Kind is not Array.
Len(a) int// Returns the number of fields in the structure //It panics if the type's Kind is not Struct.
NumField(a) int// The number of arguments to the function //It panics if the type's Kind is not Func.
NumIn(a) int// The number of values returned by the function //It panics if the type's Kind is not Func.
NumOut(a) int// The output type of the function //It panics if the type's Kind is not Func. / /It panics if i is not in the range [0, NumOut(a)).
Out(i int) Type
common(a) *rtype
uncommon(a) *uncommonType
}
Copy the code
How to use
A brief introduction:
Three test objects, which will be useful later
type UserService interface {
Service(str string) string
}
type userService struct {
ServerName string
Info map[string]interface{}
List [10]int
}
func (*userService) Service(str string) string {
return "name"
}
Copy the code
How to use it safely
func TestUserServer(t *testing.T) {
var in = (*UserService)(nil) / / 1, the interface
inter := reflect.TypeOf(in)
if inter.Kind() == reflect.Ptr { // 2, check whether it is a pointer to the inner element
inter = inter.Elem()
}
ifinter.Kind() ! = reflect.Interface {panic("this service not interface")
}
service := new(userService)
tp := reflect.TypeOf(service)
if tp.Kind() == reflect.Ptr {
method := tp.NumMethod() // Get method
for x := 0; x < method; x++ {
fmt.Printf("%+v\n", tp.Method(x))
}
if tp.Implements(inter) { // Determine whether an interface is implemented
fmt.Printf("%s implatement %s\n", tp, inter)
}
tp = tp.Elem()
}
if tp.Kind() == reflect.Struct { //
fieldN := tp.NumField()
for x := 0; x < fieldN; x++ { // Get the field information
fmt.Printf("%+v\n", tp.Field(x))
if tp.Field(x).Type.Kind() == reflect.Map { // If map is used, the key element can be obtained
fmt.Printf("FieldName: %s, key: %s.\n", tp.Field(x).Name, tp.Field(x).Type.Key().Kind())
}
if tp.Field(x).Type.Kind() == reflect.Array { // If it is an array, it can get the length information
fmt.Printf("FieldName: %s, len: %d.\n", tp.Field(x).Name, tp.Field(x).Type.Len())
}
}
}
}
Copy the code
Value
The structure of the body
Value can be said to bridge our data and meta-information, but because of the bridge, Value is mainly the use of understanding methods
type Value struct {
// typ holds the type of the value represented by a Value.
typ *rtype // can be understood as iface type
// Pointer-valued data or, if flagIndir is set, pointer to data.
// Valid when either flagIndir is set or typ.pointers() is true.
ptr unsafe.Pointer // data for iface
// flag holds metadata about the value.
flag
}
Copy the code
The main operating
Addr method
For example, if our object is of a non-pointer type and we want a pointer, we need to use Addr(). Honestly, it doesn’t feel much use.
fmt.Println(reflect.ValueOf(&userService{}).CanAddr()) // All reflect.valueof () can't get addr() directly
fmt.Println(reflect.ValueOf(userService{}).CanAddr())
// The function of addr
func TestAddr(t *testing.T) {
x := 2
d := reflect.ValueOf(&x)
value := d.Elem().Addr().Interface().(*int) // Can be converted directly to a pointer
*value = 1000
fmt.Println(x) / / "3"
}
Copy the code
Set method
This method is useful, but it needs to be called with reflect.canset () to make sure I’m not doing it right.
func TestDemos(t *testing.T) {
x := 2
d := reflect.ValueOf(&x)
d.Elem().SetInt(1000)
fmt.Println(x) / / 1000
}
Copy the code
Elem
Elem returns the value that the interface v contains or that the pointer v points to.
Returns what the interface actually contains or where the pointer is pointing. So when you use it, it’s best to use type judgment
func TestElem(t *testing.T) {
x := 2
d := reflect.ValueOf(&x)
if d.Kind() == reflect.Ptr {
d.Elem() // Call elem to get the object to which the pointer actually points
}
// Alternatively, the method can be called safely
d=reflect.Indirect(d)
}
Copy the code
New & Set (very important)
Sometimes when we get a type and want to instantiate an object, we need to use this. There are many such methods, newslice, newarr, etc. Note that reflect.new () returns a pointer to a type, such as type=string, The generated object is type=*string
When calling a Set, you must first call CanSet() to see if it can be Set. Almost every Value object is initialized without CanSet.
func TestElem(t *testing.T) {
value := reflect.New(reflect.TypeOf("111")) // Initialize a value of type string, but note that after initialization it is *string, any type is *. After the New() method is called, it is a pointer to the original type, that is, an extra *
fmt.Println(value.Kind()) // So the output is *string, PTR
value = reflect.Indirect(value) // Get the real type,string,
fmt.Println(value.Kind()) //
if value.CanSet() {
value.SetString("hello world") // set string, the type must be string, and can set,
}
fmt.Println(value.Interface().(string)) // "hello world"
}
Copy the code
Pay attention to point a
The reflect.new () method should never New a pointer type
Take initializing a structure as an example:
// Error
func main(a) {
// reflect.typeof (new(api.user)) is of type * api.user
// reflect.new (reflect.typeof (New(api.user))) initializes a data x=(* api.user)(nil) &x, so the final return type is ** api.user, value nil
value := reflect.New(reflect.TypeOf(new(api.User)))
fmt.Println(value.String()) //<**api.User Value>
// value.elem () is of type * api.user
fmt.Printf("%+v", value.Elem().Interface().(*api.User)) // nil
}
// The correct way
func main(a) {
// reflect.typeof (new(api.user)) is of type * api.user
// reflect.typeof (new(api.user)).elem () type is api.user
// reflect.new (reflect.typeof (New(api.user)).elem ())) initializes data of type api.user and returns & api.user, so the final type is * api.user
value := reflect.New(reflect.TypeOf(new(api.User)).Elem())
fmt.Println(value.String()) // <*api.User Value>
user := value.Interface().(*api.User) // So the type is * api.user, ok
user.Name = "tom" //
nuser := value.Elem().Interface().(api.User) // Get the api.user type and set it
nuser.Age = 10
fmt.Printf("%+v", value.Interface().(*api.User)) //&{Name: Tom Age:0}
}
Copy the code
Type = PTR; type= PTR; type= PTR;
Note 2
When value.set () is used to Set data, the type of value cannot be a pointer. Although value.canset () can be used to determine whether a field can be Set, but after all, we need to get the value to Set the data, not necessarily the field.
// Error
func main(a) {
value := reflect.New(reflect.TypeOf(new(api.User)).Elem()) // The value type is * api.user
if value.Kind()==reflect.Ptr { // The pointer type is ok, let's set a pointer type of data
value.Set(reflect.ValueOf(&api.User{})) // Set a *api.User data, found panic}}// panic: reflect: reflect.flag.mustBeAssignable using unaddressable value
// Write it correctly
func main(a) {
value := reflect.New(reflect.TypeOf(new(api.User)).Elem())
if value.Kind() == reflect.Ptr {
value = value.Elem()
}
if value.CanSet() {
value.Set(reflect.ValueOf(api.User{}))
}
fmt.Printf("%+v",value.Interface().(api.User))
}
{Name: Age:0}
Copy the code
So it’s often done
type User struct {
Name string
Age int
birthday time.Time // This is not visible
}
func test(a) {
user := api.User{}
value := reflect.ValueOf(&user)
fmt.Println(value.String())
for value.Kind() == reflect.Ptr { // If it is a pointer type, go to the real type
value = value.Elem()
}
if value.Kind() == reflect.Struct {// If it is a struct, you can go to the field
birthDay := value.FieldByName("birthday") // This cannot be set
if birthDay.CanSet() {
birthDay.Set(reflect.ValueOf(time.Now()))
}
name := value.FieldByName("Name") // This will do
if name.CanSet() {
name.Set(reflect.ValueOf("tom"))
}
}
fmt.Printf("%+v", user) // {Name:tom Age:0 birthday:{wall:0 ext:0 loc:<nil>}}
}
Copy the code
There is also a point :(how to assign interface value)
How to safely assign
// inter must a ptr
func SetInterface(dest interface{}, src interface{}) error {
if dest == nil || src == nil {
return nil
}
value := reflect.ValueOf(dest)
kind := value.Kind()
_, isExist := isNilKind[kind]
if isExist && value.IsNil() {
return errors.New("the dest is nil")}ifkind ! = reflect.Ptr {return errors.New("the dest must a ptr")
}
srcValue := reflect.ValueOf(src)
// If SRC is also a pointer, take elem
if value.Type() == srcValue.Type() {
srcValue = srcValue.Elem()
}
elem := value.Elem()
if elem.CanSet() {
if elem.Type() == srcValue.Type() {
elem.Set(srcValue)
}
}
return nil
}
Copy the code
Call(very important)
Golang, as we know, is very random about methods. Methods of various types can be defined. Therefore, the mainstream RPC language uses interface constraints on Method information to obtain the type. Later I’ll look at GO-RPC, which comes with an internal implementation of the RPC framework.
Reflect.valueof ().call () and reflect.typeof ().method ().func.call () do not need to pass receiver; the first argument of the latter must be receiver
func TestCall(t *testing.T) {
value := reflect.ValueOf(new(userService))
if value.NumMethod() > 0 {
fmt.Println(value.NumMethod()) / / 1
method := value.MethodByName("Service")
fmt.Println(method.Kind()) // "func"
method.Call([]reflect.Value{reflect.ValueOf("hello world")}) // hello world}}Copy the code
IsValid
// isValid checks whether a value has a value
func TestIsValid(t *testing.T) {
of := reflect.ValueOf(nil)
fmt.Println(of.IsValid()) // false
}
// IsValid reports whether v represents a value. Represents v represents a value (nil displays not)
// It returns false if V is the zero Value.
Copy the code
IsNil
The main thing is to determine the type of wrapper, whether it’s an object wrapped by a pointer, whether it’s empty. But easier because of the type panic
// Ok
func TestIsNil(t *testing.T) {
i := (*error)(nil)
value := reflect.ValueOf(i)
fmt.Println(value.IsValid()) // true, display is not null
fmt.Println(value.IsNil()) // true, because it really is nil
}
// IsNil reports whether its argument v is nil. The argument must be
// a chan, func, interface, map, pointer, or slice value; if it is
// not, IsNil panics.
// Correct usage
func TestIsNil2(t *testing.T) {
var (
isNilKind = map[reflect.Kind]interface{}{
reflect.Chan: nil,
reflect.Func: nil,
reflect.Map: nil,
reflect.Ptr: nil,
reflect.UnsafePointer: nil,
reflect.Interface: nil,
reflect.Slice: nil,
}
)
value := reflect.ValueOf(1234)
_, isExist := isNilKind[value.Kind()]
if isExist {
fmt.Println(value.IsNil()) // Call this judgment condition, which is kind}}Copy the code
Field
Meta information about some fields, such as tag information, field name, field type, etc
func TestDemos(t *testing.T) {
tp := reflect.TypeOf(new(userService))
if tp.Kind() == reflect.Ptr {
tp = tp.Elem()
}
iftp.Kind() ! = reflect.Struct { t.Fatal("not support")
}
field, _ := tp.FieldByName("ServerName") // Struct is not allowed
fmt.Printf("FieldTag json=%s\n",field.Tag.Get("json"))
fmt.Printf("FieldName=%s\n",field.Name)
}
Copy the code
Method
MethodByName(); Func (); Func (); Func (); Func (); And the latter is of type reflect.value.
type Method struct {
Name string / / the method name
PkgPath string
Type Type // method type Method type
Func Value // func with receiver as first argument
Index int // index for Type.Method
}
Copy the code
Interface oriented Development
type MethodMeta struct {
obj reflect.Value / / the caller
Method reflect.Method/ / method
InType []reflect.Type // Input type
OutType [] reflect.Type// Accept the type
}
Copy the code
This is done, what if we want to call this method
To explain why parameter passing is an interface, the advantage of interface is the type convention, go can implement methods of any type, so for this problem, it is very headache, the mainstream RPC framework this part of the implementation is based on the interface level.
func Proxy(service UserService) *MethodMeta {
val := reflect.TypeOf(service)
method, _ := val.MethodByName("Service") // Get method
meta := MethodMeta{}
meta.Method = method // The original information for the method
tt := method.Type // Method type
{
in := tt.NumIn()
meta.InType = make([]reflect.Type, in)
for x := 1; x < in; x++ { // element 0 is the caller, so only parameters need to be recorded, so it needs to be changed
meta.InType[x- 1] = tt.In(x)
}
}
{
in := tt.NumOut()
meta.OutType = make([]reflect.Type, in)
for x := 0; x < in; x++ {
meta.OutType[x] = tt.Out(x)
}
}
meta.obj = reflect.ValueOf(service)
return &meta
}
Copy the code
Demo
func BenchmarkCall(b *testing.B) {
b.SetParallelism(1)
proxy := Proxy(new(userService))
for i := 0; i < b.N; i++ {
value := reflect.New(proxy.InType[0]).Elem() // new incoming type
if value.CanSet() {
value.SetString("11111")
}
call := proxy.Method.Func.Call([]reflect.Value{proxy.obj, value}) // Call the function and return the type
_ = call[0].Interface() // Get the real type}}// goos: darwin
// goarch: amd64
// pkg: go-src-demo/insafe/test
//BenchmarkCall-8 2672127 442 ns/op
//PASS
func BenchmarkInvoke(b *testing.B) {
b.SetParallelism(1)
proxy :=new(userService)
for i := 0; i < b.N; i++ {
proxy.Service("111")}}// benchmarkinvoke8 1000000000 0.338 ns/op
Copy the code
And you can see how long does the reflection call take? It’s tens of thousands of times less efficient, maybe even more. Ns to be honest, this is acceptable.
Gob && Json serialization
At present, the mainstream RPC frameworks adopt the custom codec mechanism and rely on interface implementation to implement it. For example, the unified implementation of Request and Response interfaces provides the codec entry.
So Go also provides a variety of codec mechanisms. Golang’s underlying GOB supports codec in the way of Value, similar to the built-in serialization of Java’s Serializable interface, which is not suitable for cross-language. But Json, XML, and so on can be used across languages.
Gob codec
func TestGob(t *testing.T) {
user := User{
Name: "tom",
Age: 1,}// encoding, this encoding is not normal JSON encoding, but has special meaning
buffer := bytes.Buffer{}
err := gob.NewEncoder(&buffer).Encode(user) / / code
iferr ! =nil {
t.Fatal(err)
}
// Decoding, also, supports decoding using Value
of := reflect.TypeOf(new(User))
var value reflect.Value
if of.Kind() == reflect.Ptr {
value = reflect.New(of.Elem()) // reflection instantiates an object
}
err = gob.NewDecoder(&buffer).DecodeValue(value)
iferr ! =nil {
t.Fatal(err)
}
fmt.Println(value.Interface().(*User)) // Decoded successfully
}
Copy the code
Json codec
func TestJson(t *testing.T) {
user := User{
Name: "tom",
Age: 1,
}
buffer := bytes.Buffer{}
err := json.NewEncoder(&buffer).Encode(user) / / json encoding
iferr ! =nil {
t.Fatal(err)
}
of := reflect.TypeOf(new(User))
var value reflect.Value
if of.Kind() == reflect.Ptr {
value = reflect.New(of.Elem())
}
err = json.NewDecoder(&buffer).Decode(value.Interface()) / / json decode
iferr ! =nil {
t.Fatal(err)
}
fmt.Println(value.Interface().(*User)) // Prints data
}
Copy the code
The efficiency of JSON is much higher than that of GOB
BenchmarkName- 8 - 530700 1984 ns/op //json
BenchmarkName- 8 - 44244 26257 ns/op //gob
Copy the code
MakeFunc
The reflect.MakeFunc function remembers that the first Type of Type must be Func, and the second Type of Func only needs its method name.
type Model interface {
TableName() string
}
func TestEcho(t *testing.T) {
fun := (*Model)(nil)
tp := reflect.TypeOf(fun).Elem()
if tp.NumMethod() > 0 {
method, _ := tp.MethodByName("TableName")
if method.Type.Kind() == reflect.Func {
makeFunc := reflect.MakeFunc(method.Type, func(args []reflect.Value) (results []reflect.Value) {
return []reflect.Value{reflect.ValueOf("student")}
})
fmt.Println(makeFunc.Call([]reflect.Value{}))
}
}
}
Copy the code
To be honest, this thing is useless, nobody uses it, no method calls are efficient, and no method calls really do anything.
reference
Golang. Google. Cn/PKG/reflect…