reflect包
在Go语言的反射机制中,任何接口值都由是一个具体类型
和具体类型的值
两部分组成的(我们在上一篇接口的博客中有介绍相关概念)。 在Go语言中反射的相关功能由内置的reflect包提供,任意接口值在反射中都可以理解为由reflect.Type
和reflect.Value
两部分组成,并且reflect包提供了reflect.TypeOf
和reflect.ValueOf
两个函数来获取任意对象的Value和Type。
在反射中关于类型还划分为两种:类型(Type)
和种类(Kind)
。因为在Go语言中我们可以使用type关键字构造很多自定义类型,而种类(Kind)
就是指底层的类型,但在反射中,当需要区分指针、结构体等大品种的类型时,就会用到种类(Kind)
。 举个例子,我们定义了两个指针类型和两个结构体类型,通过反射查看它们的类型和种类。
func reflectType(e interface {}) { tpe := reflect.TypeOf(e) fmt.Println( "type:" , tpe) fmt.Printf( "name:%v kind:%v \n" , tpe.Name(), tpe.Kind()) }
func main(){
k := 1204.58 reflectType(k) //type: float64 //name:float64 kind:float64 g := "zhansan" reflectType(g) //type: string //name:string kind:string }
Go语言的反射中像数组、切片、Map、指针等类型的变量,它们的.Name()
都是返回空
。
在reflect
包中定义的Kind类型如下:
1 type Kind uint 2 const ( 3 Invalid Kind = iota // 非法类型 4 Bool // 布尔型 5 Int // 有符号整型 6 Int8 // 有符号8位整型 7 Int16 // 有符号16位整型 8 Int32 // 有符号32位整型 9 Int64 // 有符号64位整型 10 Uint // 无符号整型 11 Uint8 // 无符号8位整型 12 Uint16 // 无符号16位整型 13 Uint32 // 无符号32位整型 14 Uint64 // 无符号64位整型 15 Uintptr // 指针 16 Float32 // 单精度浮点数 17 Float64 // 双精度浮点数 18 Complex64 // 64位复数类型 19 Complex128 // 128位复数类型 20 Array // 数组 21 Chan // 通道 22 Func // 函数 23 Interface // 接口 24 Map // 映射 25 Ptr // 指针 26 Slice // 切片 27 String // 字符串 28 Struct // 结构体 29 UnsafePointer // 底层指针 30 )
通过 reflect.ValueOf 获取值
func getreflect(e interface{}) { tpe := reflect.ValueOf(e) k := tpe.Kind() switch k { case reflect.String: fmt.Printf("value:%v\n", string(tpe.String())) case reflect.Int: fmt.Printf("value:%v\n", int(tpe.Int())) case reflect.Float64: fmt.Printf("value:%v\n", float64(tpe.Float())) case reflect.Bool: fmt.Printf("value:%v\n", bool(tpe.Bool())) } }
想要在函数中通过反射修改变量的值,需要注意函数参数传递的是值拷贝,必须传递变量地址才能修改变量值。而反射中使用专有的Elem()
方法来获取指针对应的值。
func setvalue(e interface{}) { k := reflect.ValueOf(e) // 反射中使用 Elem()方法获取指针对应的值 if k.Elem().Kind() == reflect.Float64 { k.Elem().SetFloat(float64(200.5)) } } func main(){ k := 1204.58 fmt.Println(k) // 1204.58 setvalue(&k) fmt.Println(k) // 200.5 }
isNil()和isValid()
func (v Value) IsNil() bool
IsNil()
报告v持有的值是否为nil。v持有的值的分类必须是通道、函数、接口、映射、指针、切片之一;否则IsNil函数会导致panic。
func (v Value) IsValid() bool
IsValid()
返回v是否持有一个值。如果v是Value零值会返回假,此时v除了IsValid、String、Kind之外的方法都会导致panic。
func main() {
// *int类型空指针
var a *int
fmt.Println("var a *int IsNil:", reflect.ValueOf(a).IsNil())
// nil值
fmt.Println("nil IsValid:", reflect.ValueOf(nil).IsValid())
// 实例化一个匿名结构体
b := struct{}{}
// 尝试从结构体中查找"abc"字段
fmt.Println("不存在的结构体成员:", reflect.ValueOf(b).FieldByName("abc").IsValid())
// 尝试从结构体中查找"abc"方法
fmt.Println("不存在的结构体方法:", reflect.ValueOf(b).MethodByName("abc").IsValid())
// map
c := map[string]int{}
// 尝试从map中查找一个不存在的键
fmt.Println("map中不存在的键:", reflect.ValueOf(c).MapIndex(reflect.ValueOf("娜扎")).IsValid())
}
结构体反射
任意值通过reflect.TypeOf()
获得反射对象信息后,如果它的类型是结构体,可以通过反射值对象(reflect.Type
)的NumField()
和Field()
方法获得结构体成员的详细信息。
reflect.Type
中与获取结构体成员相关的的方法如下表所示。
方法 | 说明 |
---|---|
Field(i int) StructField | 根据索引,返回索引对应的结构体字段的信息。 |
NumField() int | 返回结构体成员字段数量。 |
FieldByName(name string) (StructField, bool) | 根据给定字符串返回字符串对应的结构体字段的信息。 |
FieldByIndex(index []int) StructField | 多层成员访问时,根据 []int 提供的每个结构体的字段索引,返回字段的信息。 |
FieldByNameFunc(match func(string) bool) (StructField,bool) | 根据传入的匹配函数匹配需要的字段。 |
NumMethod() int | 返回该类型的方法集中方法的数目 |
Method(int) Method | 返回该类型方法集中的第i个方法 |
MethodByName(string)(Method, bool) | 根据方法名返回该类型方法集中的方法 |
StructField
的定义如下:
type StructField struct {
// Name是字段的名字。PkgPath是非导出字段的包路径,对导出字段该字段为""。
// 参见http://golang.org/ref/spec#Uniqueness_of_identifiers
Name string
PkgPath string
Type Type // 字段的类型
Tag StructTag // 字段的标签
Offset uintptr // 字段在结构体中的字节偏移量
Index []int // 用于Type.FieldByIndex时的索引切片
Anonymous bool // 是否匿名字段
}
实战 :
type User struct { //创建一个user 类型的结构体
// gorm.Model
Name string `json:"name"`
Age int `json:"age"`
Username string `json:"username"`
Password string `json:"password"`
Gender string `json:"gender"`
}
func (user User) Setmyname() {
fmt.Println("我的名字叫", user.Name)
}
func (user User) Setmyage() {
fmt.Println("我的名字叫", user.Age)
}
func reflectType(e interface{}) {
tpe := reflect.TypeOf(e)
for i := 0; i < tpe.NumField(); i++ {
field := tpe.Field(i)
fmt.Printf("name:%v index:%d type:%v tag:%v\n", field.Name, field.Index[i], field.Type, field.Tag.Get("json"))
}
}
func main(){
user := User{
Name: "zhansan",
Age: 25,
Username: "root",
Password: "admin",
Gender: "nan",
}
reflectType(user)
//name:Name index:[0] type:8 tag:name
//name:Age index:[1] type:8 tag:age
//name:Username index:[2] type:8 tag:username
//name:Password index:[3] type:8 tag:password
//name:Gender index:[4] type:8 tag:gender
}
v := reflect.ValueOf(user)
t := reflect.TypeOf(user)
for index := 0; index < v.NumMethod(); index++ {
methodType := v.Method(index).Type()
fmt.Printf("method name:%s\n", t.Method(index).Name)
fmt.Printf("method:%s\n", methodType)
var ref = []reflect.Value{}
v.Method(index).Call(ref)
}