分享
  1. 首页
  2. 文章

第十九章:Go语言反射

aside section._1OhGeD · · 1680 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

golang-gopher.png

1. 概述

Go语言提供了一种机制,能够在运行时更新变量和检查它们的值、调用它们的方法和它们支持的内在操作,而不需要在编译时就知道这些变量的具体类型。这种机制被称为反射。反射也可以让我们将类型本身作为第一类的值类型处理。

2. 反射类型对象

使用reflect.TypeOf() 函数获取任意变量的类型对象 reflect.Type

**函数的源码如下 : **

// TypeOf returns the reflection Type that represents the dynamic type of i.
// If i is a nil interface value, TypeOf returns nil.
func TypeOf(i interface{}) Type {
 eface := *(*emptyInterface)(unsafe.Pointer(&i))
 return toType(eface.typ)
}

通过获取的类型对象就能访问原变量的类型信息

在类型信息中我们需要知道 类型(Type)种类(Kind)的区别

类型 Type : 通常指的是系统中的原生数据类型和使用type 关键字定义的类型,通过类型对象 reflect.Type中的Name() 方法获取

种类Kind : 指定的对象的根本种类,是更高一层的概括,通过reflect.Type 中的 Kind() 函数获取

  • Type的值和Kind的值可能相同,也可能不相同
package main
import (
 "fmt"
 "reflect"
)
type char string
type dogs struct {
}
func main() {
 var c char
 Str := "golang go.."
 // 获取C的类型对象
 TypeOfC := reflect.TypeOf(c)
 // Name() 获取类型
 // Kind() 获取种类
 fmt.Println("Type = ", TypeOfC.Name(), "Kind = ", TypeOfC.Kind()) // Type = char Kind = string
 Golden := dogs{}
 TypeOfGolden := reflect.TypeOf(Golden)
 fmt.Println("Type = ", TypeOfGolden.Name(), "Kind = ", TypeOfGolden.Kind()) // Type = char Kind = string
 TypeOfStr := reflect.TypeOf(Str)
 fmt.Println("Type = ", TypeOfStr.Name(), "Kind = ", TypeOfStr.Kind()) //Type = string Kind = string
 huntaway := &dogs{} // 指针变量
 TypeOfHuntaway := reflect.TypeOf(huntaway)
 // Go语言中所有的指针变量种类都是 `ptr`
 // 指针变量的类型是此时是空
 fmt.Println("Type = ", TypeOfHuntaway.Name(), "Kind = ", TypeOfHuntaway.Kind()) // Type = Kind = ptr
 // 对指针获取反射对象时,可以通过 reflect.Elem() 方法获取这个指针指向的元素类
 TypeOfHuntaway = TypeOfHuntaway.Elem()
 fmt.Println("Type = ", TypeOfHuntaway.Name(), "Kind = ", TypeOfHuntaway.Kind()) // Type = dogs Kind = struct 
}

go run main.go

Type = char Kind = string
Type = dogs Kind = struct
Type = string Kind = string
Type = Kind = ptr
Type = dogs Kind = struct

当一个变量是结构体实例的时候,怎么通过反射获取类型信息呢?

通过 reflect 包中reflect.typeField() FieldByIndex FieldByName FieldByNameFunc 方法获取的 StructField 结构体有些什么内容呢?

// A StructField describes a single field in a struct.
type StructField struct {
 // Name is the field name.
 Name string
 // PkgPath is the package path that qualifies a lower case (unexported)
 // field name. It is empty for upper case (exported) field names.
 // See https://golang.org/ref/spec#Uniqueness_of_identifiers
 PkgPath string
 Type Type // field type
 Tag StructTag // field tag string
 Offset uintptr // offset within struct, in bytes
 Index []int // index sequence for Type.FieldByIndex
 Anonymous bool // is an embedded field 
}
package main
import (
 "fmt"
 "reflect"
)
type dogs struct {
 Name string `json:"name"`
 Age int8
 T int `json:"t" id:"99"`
}
func main(){
 // 创建实例
 Hachiko := dogs{Name:"Hachiko",Age:int8(2),T:66}
 //获取反射对象实例
 typeD := reflect.TypeOf(Hachiko)
 // NumField()函数,返回结构体成员的数量
 for i:=0;i<typeD.NumField();i++{
 // 获取结构体成员的类型
 // Field()函数 根据索引返回结构体对应field的信息
 typeOfField := typeD.Field(i)
 // 输出成员字段名称和tag(标签信息),成员类型
 fmt.Printf("%s , %v,%s\n",typeOfField.Name,typeOfField.Tag,typeOfField.Type)
 }
 // 通过结构体字段名获取其类型信息
 // FieldByName()函数,根据字段名返回字段信息
 if fieldType,ok := typeD.FieldByName("T");ok{
 fmt.Println(fieldType.Tag.Get("json"),fieldType.Tag.Get("id"))
 }
}

go run main.go

Name , json:"name",string
Age , ,int8
T , json:"t" id:"99",int
t 99

3. 反射的值对象

反射可以动态的获取或者设置变量的值
Go语言中使用reflect.Value获取和设置变量的值

package main
import (
 "fmt"
 "reflect"
)
func main() {
 var a int = 99
 fmt.Printf("a ==> %T,%v\n", a, a)
 // 使用reflect.ValueOf()函数获取反射值对象
 valueOfA := reflect.ValueOf(a)
 // 获取的反射值对象,再通过值对象的Interface()方法获取原值
 var b int = valueOfA.Interface().(int)
 fmt.Printf("b ==> %T,%v\n", b, b)
 // 反射对象的Int()方法获取int64类型值,然后强制转换成int32位
 var c int32 = int32(valueOfA.Int())
 fmt.Printf("c ==> %T,%v\n", c, c)
}

go run main.go

a ==> int,99
b ==> int,99
c ==> int32,99
package main
import (
 "fmt"
 "reflect"
)
type demo struct {
 a int
 b string
 c bool
 float64
 d [5]int
}
func (d *demo) dF1() {
 fmt.Println(d.a)
}
func (d *demo) dF2() {
 fmt.Println(d.b)
}
func main() {
 t := demo{99, "golang", true, 98.90, [5]int{1, 2, 3, 5, 6}}
 // 获取值对象
 valueOfT := reflect.ValueOf(t)
 // NumField()是获取字段数量
 fmt.Println(valueOfT.NumField()) // 5
 // 获取索引为1的字段
 fieldOf1 := valueOfT.Field(1)
 // 打印该值对象的类型
 fmt.Println(fieldOf1.Type()) // string
 // 通过字段名查找
 fieldOfd := valueOfT.FieldByName("d")
 fmt.Println(fieldOfd.Type()) // [5]int
}

go run main.go

5
string
[5]int

4. 反射修改值

reflect.Value 也提供了修改版值的方法

package main
import (
 "fmt"
 "reflect"
)
func main() {
 var age int8 = 99
 // 获取一个值对象
 valueOfAge := reflect.ValueOf(&age)
 // Elem() 对可寻值的元素获取它的值
 // Addr() 对可寻址的元素获取它地址
 // CanSet() bool 返回元素(值对象)是否能被设置
 // CanAddr() bool 返回元素(值对象)是否能被寻址
 valueOfAge = valueOfAge.Elem()
 valueOfAgeAddr := valueOfAge.Addr()
 fmt.Println(valueOfAge) // 99
 fmt.Println(valueOfAgeAddr) // 0xc000054080
 fmt.Println(valueOfAge.CanSet()) // true
 fmt.Println(valueOfAge.CanAddr()) // true
 // SetInt() 使用int64设置值
 // SetUint() 使用uint64设置值
 // SetFloat() 使用float64设置值
 // SetBool() 使用bool设置值
 // SetBytes() 设置字节数组[]bytes值
 // SetString 设置字符串值
 valueOfAge.SetInt(1)
 fmt.Printf("age type= %T, value= %v",age,age)
}

go run main.go

99
0xc000054080
true
true
age type= int8, value= 15

通过类型创建类型实例

package main
import (
 "fmt"
 "reflect"
)
func main(){
 var i int = 99
 // 获取反射类型对象
 typeOfI := reflect.TypeOf(i)
 // 根据反射类型对象创建类型实例
 newI := reflect.New(typeOfI)
 // 答应类型和种类
 fmt.Println(newI.Type(),newI.Kind()) //*int ptr
}

5. 综合Demo

package main
import (
 "fmt"
 "log"
 "reflect"
)
type Person struct {
 Name string `json:"name"`
 Age int `json:"age"`
 Sex string `json:"sex"`
}
func (p Person) PersonSet(name string,age int,sex string){
 p.Name = name
 p.Age = age
 p.Sex = sex
 fmt.Println(p)
}
func (p Person) ShowPerson(){
 fmt.Println(p)
}
func reflectOfStruct(a interface{}){
 // 获取reflect.Type类型
 typeObj := reflect.TypeOf(a)
 // 获取reflect.Value类型
 valueObj := reflect.ValueOf(a)
 // 获取Kind类别,下面两种方法都能获取
 KindType := typeObj.Kind()
 //KindValue := valueObj.Kind()
 //fmt.Println(KindType)
 //fmt.Println(KindValue)
 if KindType != reflect.Struct{
 log.Fatal("Kind is not error")
 return
 }
 // 获取字段数量
 fieldsNum := valueObj.NumField()
 for i:=0;i<fieldsNum;i++{
 fmt.Printf("field %d %v\n",i,valueObj.Field(i))
 // 获取指定的标签值
 tagValue := typeObj.Field(i).Tag.Get("json")
 if tagValue != ""{
 fmt.Printf("field %d tag = %v\n",i,tagValue)
 }
 }
 // 获取方法数量
 MethodNum := valueObj.NumMethod()
 fmt.Printf("has %d methods\n",MethodNum)
 // 调用第一个方法
 valueObj.Method(1).Call(nil)
 // 对有参数的方法调用
 var params []reflect.Value
 params = append(params,reflect.ValueOf("lisi"))
 params = append(params,reflect.ValueOf(88))
 params = append(params,reflect.ValueOf("man"))
 // 传递参数,调用指定方法名的方法
 valueObj.MethodByName("PersonSet").Call(params)
}
func main() {
 var a Person = Person{
 Name:"zhangsan",
 Age:99,
 }
 // 调用函数
 reflectOfStruct(a)
}

go run main.go

field 0 zhangsan
field 0 tag = name
field 1 99
field 1 tag = age
field 2 
field 2 tag = sex
has 2 methods
{zhangsan 99 }
{lisi 88 man}

有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:aside section._1OhGeD

查看原文:第十九章:Go语言反射

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
1680 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏