方法和类型的反射
reflectreflectreflect.TypeOf()reflect.ValueOf()
func TypeOf(i interface{}) Type
type Type interface
func ValueOf(i interface{}) Value
type Value struct
interface<值,类型>
(value, type)
valuetypereflect.TypeOfreflect.ValueOf
xvar x float64 = 3.4reflect.TypeOf(x)float64reflect.ValueOf(x)3.4空接口
func TypeOf(i interface{}) Type
func ValueOf(i interface{}) Value
reflect.Typereflect.ValueTypeKind()Elem()panicValueType()reflect.TypeKind()Elem()
动态
由于反射是一个强大的工具,但反射对性能有一定的影响,除非有必要,否则应当避免使用或小心使用。下面代码针对int、数组以及结构体分别使用反射机制,其中的差异请看注释。
package main
import (
"fmt"
"reflect"
)
type Student struct {
name string
}
func main() {
var a int = 50
v := reflect.ValueOf(a) // 返回Value类型对象,值为50
t := reflect.TypeOf(a) // 返回Type类型对象,值为int
fmt.Println(v, t, v.Type(), t.Kind()) // 50 int int int
var b [5]int = [5]int{5, 6, 7, 8}
fmt.Println(reflect.TypeOf(b), reflect.TypeOf(b).Kind(),reflect.TypeOf(b).Elem()) // [5]int array int
var Pupil Student
p := reflect.ValueOf(Pupil) // 使用ValueOf()获取到结构体的Value对象
fmt.Println(p.Type()) // 输出:main.Student
fmt.Println(p.Kind()) // 输出:struct
}
intstring
Type()Kind()intkind()type()
通过反射可以修改原对象
CanAddr()CanSet()
Value
package main
import (
"fmt"
"reflect"
)
type Student struct {
name string
Age int
}
func main() {
var a int = 50
v := reflect.ValueOf(a) // 返回Value类型对象,值为50
t := reflect.TypeOf(a) // 返回Type类型对象,值为int
fmt.Println(v, t, v.Type(), t.Kind(), reflect.ValueOf(&a).Elem()) // 50 int int int 50
seta := reflect.ValueOf(&a).Elem() // 这样才能让seta保存a的值
fmt.Println(seta, seta.CanSet()) // 50 true
seta.SetInt(1000)
fmt.Println(seta) // 1000
var b [5]int = [5]int{5, 6, 7, 8}
fmt.Println(reflect.TypeOf(b), reflect.TypeOf(b).Kind(), reflect.TypeOf(b).Elem()) // [5]int array int
var Pupil Student = Student{"joke", 18}
p := reflect.ValueOf(Pupil) // 使用ValueOf()获取到结构体的Value对象
fmt.Println(p.Type()) // 输出:main.Student
fmt.Println(p.Kind()) // 输出:struct
setStudent := reflect.ValueOf(&Pupil).Elem()
//setStudent.Field(0).SetString("Mike") // 未导出字段,不能修改,panic会发生
setStudent.Field(1).SetInt(19)
fmt.Println(setStudent) // {joke 19}
}
虽然反射可以越过Go语言的导出规则的限制读取结构体中未导出的成员,但不能修改这些未导出的成员。因为一个结构体中只有被导出的字段才是可修改的(就是对外可见的才可以修改)。
v := reflect.ValueOf(x)xvvxvxxv = reflect.ValueOf(&x)Elem()setx = reflect.ValueOf(&x).Elem()xintsetx.SetInt(111)
tagtag
package main
import (
"fmt"
"reflect"
)
type Student struct {
name string
Age int `json:"years"`
}
func main() {
var Pupil Student = Student{"joke", 18}
setStudent := reflect.ValueOf(&Pupil).Elem()
sSAge, _ := setStudent.Type().FieldByName("Age")
fmt.Println(sSAge.Tag.Get("json")) // years
}
反射结构体
为了完整说明反射的情况,通过反射一个结构体类型,综合来说明。下面例子较为系统地利用一个结构体,来充分举例说明反射:
package main
import (
"fmt"
"reflect"
)
// 结构体
type ss struct {
int
string
bool
float64
}
func (s ss) Method1(i int) string { return "结构体方法1" }
func (s *ss) Method2(i int) string { return "结构体方法2" }
var (
structValue = ss{ // 结构体
20,
"结构体",
false,
64.0,
}
)
// 复杂类型
var complexTypes = []interface{}{
structValue, &structValue, // 结构体
structValue.Method1, structValue.Method2, // 方法
}
func main() {
// 测试复杂类型
for i := 0; i < len(complexTypes); i++ {
PrintInfo(complexTypes[i])
}
}
func PrintInfo(i interface{}) {
if i == nil {
fmt.Println("--------------------")
fmt.Printf("无效接口值:%v
", i)
fmt.Println("--------------------")
return
}
v := reflect.ValueOf(i)
PrintValue(v)
}
func PrintValue(v reflect.Value) {
fmt.Println("--------------------")
// ----- 通用方法 -----
fmt.Println("String :", v.String()) // 反射值的字符串形式
fmt.Println("Type :", v.Type()) // 反射值的类型
fmt.Println("Kind :", v.Kind()) // 反射值的类别
fmt.Println("CanAddr :", v.CanAddr()) // 是否可以获取地址
fmt.Println("CanSet :", v.CanSet()) // 是否可以修改
if v.CanAddr() {
fmt.Println("Addr :", v.Addr()) // 获取地址
fmt.Println("UnsafeAddr :", v.UnsafeAddr()) // 获取自由地址
}
// 获取方法数量
fmt.Println("NumMethod :", v.NumMethod())
if v.NumMethod() > 0 {
// 遍历方法
i := 0
for ; i < v.NumMethod()-1; i++ {
fmt.Printf(" ┣ %v
", v.Method(i).String())
}
fmt.Printf(" ┗ %v
", v.Method(i).String())
// 通过名称获取方法
fmt.Println("MethodByName :", v.MethodByName("String").String())
}
switch v.Kind() {
// 结构体:
case reflect.Struct:
fmt.Println("=== 结构体 ===")
// 获取字段个数
fmt.Println("NumField :", v.NumField())
if v.NumField() > 0 {
var i int
// 遍历结构体字段
for i = 0; i < v.NumField()-1; i++ {
field := v.Field(i) // 获取结构体字段
fmt.Printf(" ├ %-8v %v
", field.Type(), field.String())
}
field := v.Field(i) // 获取结构体字段
fmt.Printf(" └ %-8v %v
", field.Type(), field.String())
// 通过名称查找字段
if v := v.FieldByName("ptr"); v.IsValid() {
fmt.Println("FieldByName(ptr) :", v.Type().Name())
}
// 通过函数查找字段
v := v.FieldByNameFunc(func(s string) bool { return len(s) > 3 })
if v.IsValid() {
fmt.Println("FieldByNameFunc :", v.Type().Name())
}
}
}
}
输出结果:
--------------------
String : <main.ss Value>
Type : main.ss
Kind : struct
CanAddr : false
CanSet : false
NumMethod : 1
┗ <func(int) string Value>
MethodByName : <invalid Value>
=== 结构体 ===
NumField : 4
├ int <int Value>
├ string 结构体
├ bool <bool Value>
└ float64 <float64 Value>
--------------------
String : <*main.ss Value>
Type : *main.ss
Kind : ptr
CanAddr : false
CanSet : false
NumMethod : 2
┣ <func(int) string Value>
┗ <func(int) string Value>
MethodByName : <invalid Value>
--------------------
String : <func(int) string Value>
Type : func(int) string
Kind : func
CanAddr : false
CanSet : false
NumMethod : 0
--------------------
String : <func(int) string Value>
Type : func(int) string
Kind : func
CanAddr : false
CanSet : false
NumMethod : 0
structValue&structValueMethod2()
nMethod(n).Call(XXXX)