Go Reflection
何为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)
...
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.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)
}
}
人生如逆旅,我亦是行人 @苏轼
Next Post