何为Reflection

Reflection是一种能够窥探类型和值信息的技术,利用该技术我们能在运行时了解,修改类型和值内部结构的信息

interface{}

interface{}是一种特殊的Interface,内部没有方法,就导致所有的类型都可以转化为interface{},从而使得go有了泛型的能力,interface{}在底层表示为(Type,Value)的集合,所以在go才会有nil type一说,所以如果我们在将任意值转成interface{}后,我们不会损失有关任意值原来类型和值的信息,reflect库就提供了reflect.Value,reflect.Type来帮助我们获得这一集合,当然使用这些会有一些额外开销,不过我们也有了一些超能力

reflect.TypeOf

reflect.TypeOf是reflect库提供的方法,我们能通过该方法获得有关值的类型信息,该方法返回reflect.Type,通过
    go doc reflect.Type
可知,reflect.Type是一个interface,我们可以看到里面有很多方法,它们便是窥探值类型信息的利器,其中便有一个通用的方法,Kind()能够返回类型的枚举
func explain_kind(variable interface{}) {
    t := reflect.TypeOf(variable)
    switch t.Kind() {
    case reflect.Int,reflect.Int8,reflect.Int16,
        reflect.Int32,reflect.Int64,
        reflect.Uint,reflect.Uint8,reflect.Uint16,reflect.Uint32,
        reflect.Uint64,reflect.Uintptr:
        // do something
    case reflect.Float32,reflect.Float64:
        // do something
    case reflect.String:
        // do something
    case reflect.Bool:
        // do something
    case reflect.Complex64,reflect.Complex128:
        // do something
    default:
        /* have reflect.Map,reflect.Slice
            reflect.Array,reflect.Func,
            reflect.Ptr,reflect.Interface,
            reflect.Chan,reflect.UnsafePointer,
            reflect.Invalid..etc
        */
    }
}
当然,我们还有更多简单易用的方法获得比如reflect.Func的函数名,参数信息,reflect.Struct的字段和方法等等我们想到和意想不到的信息

reflect.ValueOf

reflect.ValueOf是reflect的可以窥探和修改值信息的方法,它返回reflect.Value,查询文档可以知道它其实是一个结构体,它提供了一些方法

reflect.Set系

func (v Value) Set(x Value)

func (v Value) SetBool(x Value)

func (v Value) SetCap(n int)

func (v Value) SetComplex(x complex128)

func (v Value) Float(x float64)

...

Set系方法只能用于CanAddr可寻址的Value,因为改变副本值毫无意义,此外Set方法参数底层类型必须与调用者底层类型完全匹配,否则会panic,而SetInt,SetFloat有容错性,可以修改能隐式转换的类型比如int64类型的reflect.Value,可以传参int32,但是SetInt,SetFloat等不能修改指向interface{}的reflect.Value,否则会panic,此外,reflect也提供了reflect.CanSet方法来检测值是否能使用Set系方法

package main

import (
	"fmt"
	"reflect"
)

func main() {
	var f64 float64 = 12.343
	value := reflect.ValueOf(f64)
	// false
	fmt.Printf("%v\n",value.CanSet())
    value = reflect.ValueOf(&f64).Elem()
    // true
	fmt.Printf("%v\n",value.CanSet())
	// panic
	//value.Set(reflect.ValueOf(12))
	value.SetFloat(12)
	value.Set(reflect.ValueOf(12.1))
	var inter interface{}
	value = reflect.ValueOf(&inter).Elem()
	// true
	fmt.Printf("%v\n",value.CanSet())
	value.Set(reflect.ValueOf(12))
	// panic
	//value.SetInt(13)
}
注意下列代码失败和成功的原因,第一个传递的是副本,我们修改它对原来的值毫无影响,第二次传入的是地址,我们reflect.Value拥有了指向原来值地址的指针,当然我们通过reflect.Elem函数获取其指向的指向的值,否则只会获得其地址的reflect.Value
	var f64 float64 = 12.343
	value := reflect.ValueOf(f64)
	// false
	fmt.Printf("%v\n",value.CanSet())
	value = reflect.ValueOf(&f64).Elem()
	fmt.Printf("%v\n",value.CanSet())

reflect.Can系

func (v Value) CanAddr() bool

func (v Value) CanInterface() bool

func (v Value) CanSet() bool

reflect.Can系很简单,就是检查是否可CanAddr(寻址--调用Addr()),CanInterface(转化为interface{}--调用Interface()),CanSet(修改值---调用Set系函数)
注意:reflect.Type和reflect.Value有很多相同的方法,但是二者语义是不同的(尽管有部分效果相同),所以使用时要明确自己是想要类型还是值的信息,当然,知道值自然可以窥探其类型,在reflect.Value上调用reflect.Type()可以获得reflect.Value

More Funcions

func MakeFunc(type Type,fn func(args []Value) (results []Value)) Value
顾名思义,这个函数可以帮助我们生成函数,第一个参数是返回(生成)的函数类型,第二个则是生成函数的模板,这个函数可以帮助我们动态的生成函数(它们有相同的原型),下面是官网的一个例子,用来生成int64,float64等的swap函数(与官网相比我有所改动)

// example fo reflect.MakeFunc
package main

import (
	"reflect"
	"fmt"
)
// swap func prototyope
func proto_swap(args []reflect.Value) (results []reflect.Value) {
	// swap
	return []reflect.Value {
		args[1],
		args[0],
	}
}
// case of swap instance
var int_swap func(int64,int64) (int64,int64)
var float_swap func(float64,float64) (float64,float64)

func init() {
	int_swap_fn := reflect.ValueOf(&int_swap).Elem()
	float_swap_fn := reflect.ValueOf(&float_swap).Elem()
	new_int_swap_fn := reflect.MakeFunc(int_swap_fn.Type(),proto_swap)
	new_float_swap_fn := reflect.MakeFunc(float_swap_fn.Type(),proto_swap)
	int_swap_fn.Set(new_int_swap_fn)
	float_swap_fn.Set(new_float_swap_fn)
}
func main() {
	i1,i2 := int_swap(1,2)
	fmt.Printf("first: %d,second: %d\n",i1,i2)
	f1,f2 := float_swap(12.2,13.4)
	fmt.Printf("first: %3.2f,second: %3.2f\n",f1,f2)
}
这个swap函数用interface{}也能做
// we can do that with this
package main

import (
	"fmt"
)

func swap(x interface{},y interface{}) (interface{},interface{}) {
	return y,x
}
func int_swap(x int64,y int64) (int64,int64) {
	yi,xi := swap(x,y)
	return yi.(int64),xi.(int64)
}
func float_swap(x float64,y float64) (float64,float64) {
	yf,xf := swap(x,y)
	return yf.(float64),xf.(float64)
}
func main() {
	i1,i2 := int_swap(1,2)
	fmt.Printf("first: %d,second: %d\n",i1,i2)
	f1,f2 := float_swap(12.2,13.4)
	fmt.Printf("first: %3.2f,second: %3.2f\n",f1,f2)
}

func (tag StructTag) Get(key string) string
go提供了结构体标签,比如json库使用的tag json,我们也可以定义自己的标签,该函数会查找结构体标签中的key,没找到就返回empty string,所以如果想要知道结构体标签是否明确指定了empty string,就使用Lookup函数
func (tag StructTag) Lookup(key string) (value string,ok bool)

package main

import (
	"fmt"
	"reflect"
)
type Author struct {
	Name	string		`number:"20163284" unique:"true"`
	Age		int64		`number:"20189881" unique:"false"`
}
func main() {
	author := Author {
		Name: "Marco Epsilon",
		Age: 22,
	}
	as := reflect.ValueOf(author)
	for i := 0; i < as.NumField(); i++ {
		number,ok := as.Type().Field(i).Tag.Lookup("number")
		//fmt.Printf("tag number: %v\n",as.Type().Field(i).Tag.Get("number"))
		if !ok {
			continue
		}
		unique,ok := as.Type().Field(i).Tag.Lookup("unique")
		if !ok {
			continue
		}
		fmt.Printf("%v %v tag number: %v,tag unique: %v\n",as.Type().Field(i).Name,as.Field(i).Interface(),number,unique)
	}
}
人生如逆旅,我亦是行人 @苏轼