go的反射reflect
我们项目中反射一般是在特定场合获取对象的信息, 然后动态调用对象方法 或者修改对象的属性,但是go里面还有指针【地址】一说法, 来看看是怎么用的
package main import ( "fmt" "reflect" ) func main() { var x float64 = 3.4 p := reflect.ValueOf(&x) // Note: take the address of x. /*为了得到 p 指向的数据,可以调用 Value 类型的 Elem 方法。 Elem 方法能够对指针进行“解引用”,然后将结果存储到反射 Value 类型对象 v 中*/ v := p.Elem() v.SetFloat(7.1) fmt.Println(x) }
如果是常见的类型 struct又该如何了:
package mainimport ( "fmt" "reflect" )
func main() { type T struct { A int B string `json:"b"` } t := T{23, "skidoo"} s := reflect.ValueOf(&t).Elem() ///获取反射信息 typeOfT := s.Type() for i := 0; i < s.NumField(); i++ { f := s.Field(i) fmt.Printf("%d: %s %s = %v\n", i, typeOfT.Field(i).Name, f.Type(), f.Interface()) } // 通过字段名, 找到字段类型信息 if bType, ok := typeOfT.FieldByName("B"); ok { // 从tag中取出需要的tag fmt.Printf("B has tag %s\n", bType.Tag.Get("json")) } //修改对象数据 s.Field(0).SetInt(77) s.Field(1).SetString("Sunset Strip") fmt.Println("t is now", t) }
运行结果:
0: A int = 23
1: B string = skidoo
B has tag b
t is now {77 Sunset Strip}
如果我们修改了程序让 s 由 t(而不是 &t)创建,程序就会在调用 SetInt 和 SetString 的地方失败,因为 t 的字段是不可设置的。
普通方法的调用:
package main import ( "fmt" "reflect" ) // 普通函数 func add(a, b int) int { return a + b } func main() { // 将函数包装为反射值对象 funcValue := reflect.ValueOf(add) // 构造函数参数, 传入两个整型值 paramList := []reflect.Value{reflect.ValueOf(10), reflect.ValueOf(20)} // 反射调用函数 retList := funcValue.Call(paramList) // 获取第一个返回值, 取整数值 fmt.Println(retList[0].Int()) }
接口方法
package main import ( "fmt" "reflect" ) type Table interface { TableName() string } type Cal interface { Add(a, b int) int } type model struct{} func (m model) TableName() string { return "table_name" } func (m model) Add(a, b int) int { return a + b } func main() { var mod model getTableName(mod) } /* reflect.New()函数的声明 func New(typ Type) Value 根据传入的tpye,可以获取到对应的value */ func getTableName(v interface{}) { rt := reflect.TypeOf(v) rv := reflect.ValueOf(v) if tabler, ok := rv.Interface().(Table); ok { fmt.Println(tabler.TableName()) } if cal, ok := rv.Interface().(Cal); ok { fmt.Println(cal.Add(10, 20)) } /// if tabler, ok := reflect.New(rt).Interface().(Table); ok { fmt.Println(tabler.TableName()) } if cal, ok := reflect.New(rt).Interface().(Cal); ok { fmt.Println(cal.Add(20, 30)) } }
对象方法:
package mainimport ( "fmt" "reflect" )
type Person struct { Name string Age int Sex string }
//如果Tool小写后面就没办法反射 type Tool struct { Cap string Key string }
func (t Tool) Print() { fmt.Println("tool:" + t.Cap + t.Key) }
func (p Person) PrintInfo(t *Tool) { t.Cap = "green" t.Key = "long" fmt.Printf("姓名:%s, 年龄:%s, 性别:%s, 参数tool内容:%s %s\n", p.Name, p.Age, p.Sex, t.Key, t.Cap) }
func main() { p1 := Person{"Rbuy", 20, "男"} ///调用person的PrintInfo 方法 rtyp := reflect.TypeOf(p1) rcvr := reflect.ValueOf(p1) rmethod, ok := rtyp.MethodByName("PrintInfo") if ok { // 得到第一个此method第1参数的Type,第零个当然就是结构体本身了 replyType := rmethod.Type.In(1).Elem() //实例化返回值的地址 *tool replyv := reflect.New(replyType) //rcvr 可以理解为对象实例 replyv 理解为返回值 rmethod.Func.Call([]reflect.Value{rcvr, replyv})
//调用tool的print var t Tool rtyp = reflect.TypeOf(t) rmethod, ok = rtyp.MethodByName("Print") if ok { rmethod.Func.Call([]reflect.Value{replyv.Elem()}) } }
}