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…