Go——反射的使用

本文详细介绍了Go语言中的反射机制,包括reflect包的使用,如valueOf、value.set、结构体和函数的反射,以及方法反射的调用。通过示例展示了如何获取变量类型、设置变量值、反射结构体属性和方法,以及如何反射调用函数和方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1,reflect

2,valueOf

3,value.set

4,结构体的反射

5,函数的反射

6,方法反射


概念:运行时动态的获取变量的相关信息。

反射:对一些对象进行序列化处理

Import ("reflect)

1,reflect

  1. reflect. TypeOf,获取变量的类型,返回reflect Type类型

  2. reflect ValueOf,获取变量的值,返回reflect .Value类型

  3. reflect.Value Kind, 获取变量的类别,返回一个常量

  4. reflect Value .Interface(),转换成interface{}类型

示例:获取变量类型

package main
​
import (
    "fmt"
    "reflect"
)
​
//定义一个函数
func Test(i interface{}) {
    //反射数据类型
    t := reflect.TypeOf(i)
    fmt.Println("类型是:", t)
    //反射数据值
    y := reflect.ValueOf(i)
    fmt.Println("类型是:", y)
}
​
func main() {
    a := "hello"
    Test(a)
}
[Running] go run "f:\goProject\src\dev_code\day23\example1\main.go"
类型是: string
类型是: hello
​
[Done] exited with code=0 in 0.73 seconds

示例:类型和类别

package main
​
import (
    "fmt"
    "reflect"
)
​
//类型和类别
//定义结构体
type student struct {
    name  string
    age   int
    score float32
}
​
//函数传入
func Test(i interface{}) {
    //反射数据类型
    t := reflect.TypeOf(i)
    fmt.Println("类型是:", t)
    //类别
    y := reflect.ValueOf(i) //或者u:=reflect.ValueOf(i).Kind()
    u := y.Kind()
    fmt.Println("值是:", y)
    fmt.Println("类别是:", u)     //类别是一个常量
​
}
​
func main() {
    var stu student
    stu.name = "zhangsan"
    stu.age = 20
    stu.score = 50
    Test(stu)
    fmt.Println("<<<--------我是华丽的分割线--------->>>")
    var num int = 10
    Test(num)
}
[Running] go run "f:\goProject\src\dev_code\day23\example2\main\main.go"
类型是: main.student
值是: {zhangsan 20 50}
类别是: struct
<<<--------我是华丽的分割线--------->>>
类型是: int   
值是: 10
类别是: int
​
[Done] exited with code=0 in 0.6 seconds

示例:断言处理类型转换

package main
​
import (
    "fmt"
    "reflect"
)
​
//类型和类别
//定义结构体
type student struct {
    name  string
    age   int
    score float32
}
​
//函数传入
func Test(i interface{}) {
    //反射数据类型
    t := reflect.TypeOf(i)
    fmt.Println("类型是:", t)
    //类别
    y := reflect.ValueOf(i) //或者u:=reflect.ValueOf(i).Kind()
    u := y.Kind()
    fmt.Println("类别是:", u) //类别是一个常量
    fmt.Printf("y的类型是%T\n", y)
    fmt.Printf("u的类型是%T\n", u)
    //转化成接口
    iy := y.Interface()
    fmt.Printf("iy的类型是%T\n", iy)
    //断言处理iy是student类型是true就赋值给stu_iy
    stu_iy, ok := iy.(student)
    if ok {
        fmt.Printf("stu_iy的类型是%T\n", stu_iy)
    }
}
​
func main() {
    var stu student
    stu.name = "zhangsan"
    stu.age = 20
    stu.score = 50
    Test(stu)
​
}
[Running] go run "f:\goProject\src\dev_code\day23\example2\main\main.go"
类型是: main.student
类别是: struct
y的类型是reflect.Value
u的类型是reflect.Kind
iy的类型是main.student
stu_iy的类型是main.student
​
[Done] exited with code=0 in 0.474 seconds

2,valueOf

获取变量值

reflect. Value0f(x)- Float()
reflect. Value0f(x).Int( )
reflect . ValueOf(x) . String( )
reflect . ValueOf(x). Bool()

示例:类型转换

package main
​
import (
    "fmt"
    "reflect"
)
​
//定义函数传入
func Test(i interface{}) {
    fmt.Printf("i的类型:%T\n", i)
    v := reflect.ValueOf(i)
    fmt.Printf("v的类型:%T\n", v)
    //转换指定类型
    t := v.Int()
    fmt.Printf("t的类型:%T\n", t)
}
func main() {
    var num int = 10
    Test(num)
}
[Running] go run "f:\goProject\src\dev_code\day23\example3\main\main.go"
i的类型:int
v的类型:reflect.Value
t的类型:int64
​
[Done] exited with code=0 in 0.616 seconds

不同类型会出错

package main
​
import (
    "fmt"
    "reflect"
)
​
//定义函数传入
func Test(i interface{}) {
    fmt.Printf("i的类型:%T\n", i)
    v := reflect.ValueOf(i)
    fmt.Printf("v的类型:%T\n", v)
    //转换指定类型
    t := v.Int()
    fmt.Printf("t的类型:%T\n", t)
}
func main() {
    var num string = "10"
    Test(num)
}
​
[Running] go run "f:\goProject\src\dev_code\day23\example3\main\main.go"
i的类型:string
v的类型:reflect.Value
panic: reflect: call of reflect.Value.Int on string Value
​
goroutine 1 [running]:
reflect.Value.Int(...)
    C:/Program Files/Go/src/reflect/value.go:1347
main.Test({0x8b5b20, 0xc00005a230})
    f:/goProject/src/dev_code/day23/example3/main/main.go:14 +0x297
main.main()
    f:/goProject/src/dev_code/day23/example3/main/main.go:19 +0x34
exit status 2
​
[Done] exited with code=1 in 0.589 seconds

3,value.set

设置变量值

reflect. value . SetFloat(),设置浮点数
reflect. value . SetInt(),设置整数
reflect value . SetString(),设置字符串

示例:报错

package main
​
import (
    "fmt"
    "reflect"
)
​
//定义函数传入
func Test(i interface{}) {
    v := reflect.ValueOf(i)
    //更新值
    v.SetInt(100)
    result := v.Int()
    fmt.Printf("更改后的类型是:%T,值是:%d\n", result, result)
​
}
func main() {
    var num int = 10
    Test(num) 
}
​
[Running] go run "f:\goProject\src\dev_code\day23\example4\main\main.go"
panic: reflect: reflect.Value.SetInt using unaddressable value
​
goroutine 1 [running]:
reflect.flag.mustBeAssignableSlow(0xc000028000)
    C:/Program Files/Go/src/reflect/value.go:262 +0x85
reflect.flag.mustBeAssignable(...)
    C:/Program Files/Go/src/reflect/value.go:249
reflect.Value.SetInt({0x795460, 0x837870, 0x100000000000000}, 0x64)
    C:/Program Files/Go/src/reflect/value.go:1991 +0x48
main.Test({0x795460, 0x837870})
    f:/goProject/src/dev_code/day23/example4/main/main.go:12 +0xc5
main.main()
    f:/goProject/src/dev_code/day23/example4/main/main.go:19 +0x2d
exit status 
​
[Done] exited with code=1 in 0.563 seconds

传入更改需要传指针,示例:

package main
​
import (
    "fmt"
    "reflect"
)
​
//定义函数传入
func Test(i interface{}) {
    v := reflect.ValueOf(i)
    //更新值需要value的地址,否则会崩溃Elem()表示*  传入指针
    v.Elem().SetInt(100)
    result := v.Elem().Int()
    fmt.Printf("更改后的类型是:%T,值是:%d\n", result, result)
​
}
func main() {
    var num int = 10
    Test(&num)//传入地址
}
[Running] go run "f:\goProject\src\dev_code\day23\example4\main\main.go"
更改后的类型是:int64,值是:100
​
[Done] exited with code=0 in 0.588 seconds

4,结构体的反射

示例:反射出结构体属性和方法数量。注意:方法名需要大写,相当于跨包调用

package main
​
import (
    "fmt"
    "reflect"
)
​
//结构体反射
type student struct {
    name  string
    age   int
    score float32
}
​
//结构体方法
func (s student) Run() { //方法名需要大写,反射相当于跨包调用
    fmt.Println("正在跑步")
}
func (s student) Sleep() {
    fmt.Println("正在睡觉")
}
​
//定义函数使用反射查看结构体字段数量和方法数量
func Test(i interface{}) {
    v := reflect.ValueOf(i)
    //类别判断
    if v.Kind() != reflect.Struct {
        fmt.Println("this is not struct")
        return
    }
    //获取结构体字段数量
    stu_num := v.NumField()
    fmt.Println("字段数量是:", stu_num)
    //获取结构体方法数量
    stu_methom := v.NumMethod()
    fmt.Println("方法数量是:", stu_methom)
​
}
func main() {
    var stu student = student{
        name:  "zhangsan",
        age:   20,
        score: 50,
    }
    Test(stu)
}
[Running] go run "f:\goProject\src\dev_code\day23\example5\main\main.go"
字段数量是: 3
方法数量是: 2
​
[Done] exited with code=0 in 0.592 seconds

获取字段属性值

package main
​
import (
    "fmt"
    "reflect"
)
​
type student struct {
    name string
    age  int
}
​
//反射来获取结构体中定义的属性值和类型
func Testprint(i interface{}) {
    v := reflect.ValueOf(i)
    //遍历结构体中所有的属性
    for i := 0; i < v.NumField(); i++ {
        fmt.Printf("索引:%d,值:%v,类型:%v\n", i, v.Field(i), v.Field(i).Kind())
    }
}
func main() {
    //实例化
    var stu student
    stu.name = "zhangsan"
    stu.age = 25
    Testprint(stu)
}
[Running] go run "f:\goProject\src\dev_code\day23\example9\main\main.go"
索引:0,值:zhangsan,类型:string
索引:1,值:25,类型:int
​
[Done] exited with code=0 in 0.593 seconds
也可以在cmd命令行中输出
F:\goProject\src\dev_code\day23\example9\main>go run main.go
索引:0,值:zhangsan,类型:string
索引:1,值:25,类型:int

更改属性的值

package main
​
import (
    "fmt"
    "reflect"
)
​
type student struct {
    Name string   //字段名字首字母需要大写
    Age  int
}
​
//更新值
func SetValue(i interface{}, name string) {
    v := reflect.ValueOf(i)
    vk := v.Kind()
    //如果不是指针,而指针指向的类型是结构体,则异常退出
    if vk != reflect.Ptr && v.Elem().Kind() == reflect.Struct {
        fmt.Println("type err")
        return
    }
    //修改值
    v.Elem().Field(0).SetString(name)
    //遍历结构体重所有的属性
    for i := 0; i < v.Elem().NumField(); i++ {
        fmt.Printf("索引:%d,值:%v,类型:%v\n", i, v.Elem().Field(i), v.Elem().Field(i).Kind())
    }
​
}
func main() {
    //实例化
    var stu student
    stu.Name = "zhangsan"
    stu.Age = 25
    //Testprint(stu)
    SetValue(&stu, "lisi")
}
​
[Running] go run "f:\goProject\src\dev_code\day23\example9\main\main.go"
索引:0,值:lisi,类型:string
索引:1,值:25,类型:int
​
[Done] exited with code=0 in 0.576 seconds

反射获取原信息

package main
​
import (
    "encoding/json"
    "fmt"
    "reflect"
)
​
type student struct {
    Name string `json:"name"`
    age  int
}
​
//反射来获取结构体中定义的属性值和类型
func Testprint(i interface{}) {
    v := reflect.ValueOf(i)
    //遍历结构体中所有的属性
    for i := 0; i < v.NumField(); i++ {
        fmt.Printf("索引:%d,值:%v,类型:%v\n", i, v.Field(i), v.Field(i).Kind())
    }
}
func main() {
    //实例化
    var stu student
    stu.Name = "zhangsan"
    stu.age = 25
    Testprint(stu)
    fmt.Println("-----原信息------")
    result, _ := json.Marshal(stu)
    fmt.Println("json原信息:", string(result))
    fmt.Println("-------反射获取原信息----------")
    //反射获取属性
    st := reflect.TypeOf(stu)
    s := st.Field(0)
    fmt.Printf("Name原信息名称:%s\n", s.Tag.Get("json"))
​
}
[Running] go run "f:\goProject\src\dev_code\day24\example1\main\main.go"
索引:0,值:zhangsan,类型:string
索引:1,值:25,类型:int
-----原信息------
json原信息: {"name":"zhangsan"}
-------反射获取原信息----------
Name原信息名称:name
​
[Done] exited with code=0 in 0.599 seconds

5,函数的反射

示例: Go中函数可以赋值给变量。

package main
​
import "fmt"
​
//定义函数
func hello() {
    fmt.Println("hello world")
}
​
func main() {
    a := hello
    a()
}
[Running] go run "f:\goProject\src\dev_code\day23\example6\main\main.go"
hello world
​
[Done] exited with code=0 in 0.583 seconds

示例:既然函数可以像普通的类型变量样, 那么在反射机制中就和不同的变量是一样, 在反射中函数和方法的类型(Type)都是reflect.Func,如果要调用函数,通过ValueCall()方法

package main
​
import (
    "fmt"
    "reflect"
)
​
//定义函数
func hello() {
    fmt.Println("hello world")
}
​
func main() {
    // a := hello
    // a()
    //反射使用函数
    v := reflect.ValueOf(hello)
    //类型判断是否是reflect.func
    if v.Kind() == reflect.Func {
        fmt.Println("函数")
    }
    //反射调用函数
    v.Call(nil)
​
}
[Running] go run "f:\goProject\src\dev_code\day23\example6\main\main.go"
函数
hello world
​
[Done] exited with code=0 in 0.605 seconds

示例:反射调用传参和返回值函数,传参传的是切片返回值也是切片

package main
​
import (
    "fmt"
    "reflect"
    "strconv"
)
​
//反射调用传参和返回值函数
func Test(i int) string {
    return strconv.Itoa(i)
}
​
func main() {
    v := reflect.ValueOf(Test)
    //定义参数切片
    params := make([]reflect.Value, 1)
    //切片元素赋值20
    params[0] = reflect.ValueOf(20)
    //反射调函数
    result := v.Call(params)
    fmt.Printf("result的类型:%T\n", result)
    //进行类型的转换切片转换string.[]reflect.Value转换成string
    s := result[0].Interface().(string)
    fmt.Printf("s的类型:%T,值:%s\n", s, s)
}
​
[Running] go run "f:\goProject\src\dev_code\day23\example7\main\main.go"
result的类型:[]reflect.Value
s的类型:string,值:20
​
[Done] exited with code=0 in 0.597 seconds

6,方法反射

反射中方法的调用。函数和方法可以说其实本质上是相同的,只不过方法与一个对象”进行了 “绑定",方法是“对象”的一种行为,这种行为是对于这个对象”的一系列操作, 例如修改"对象的某个属性。 示例:

package main
​
import (
    "fmt"
    "reflect"
    "strconv"
)
​
//反射方法
type Student struct {
    Name string
    Age  int
}
​
//方法
func (s *Student) Setname(name string) {
    s.Name = name
}
func (s *Student) Setage(age int) {
    s.Age = age
}
func (s *Student) String() string {
    return fmt.Sprintf("%p", s) + " name:" + s.Name + " age:" + strconv.Itoa(s.Age)
}
func main() {
    //实例化检查student对象内容,用地址
    stu := &Student{"zhangsan", 25}
    //反射获取值:指针方式
    stuv := reflect.ValueOf(&stu).Elem()
    fmt.Println("修改前:", stuv.MethodByName("String").Call(nil)[0])
    //修改值
    p := make([]reflect.Value, 1)
    p[0] = reflect.ValueOf("lisi")
    //按名称来调用方法Setname
    stuv.MethodByName("Setname").Call(p)
    p[0] = reflect.ValueOf(35)
    stuv.MethodByName("Setage").Call(p)
    fmt.Println("修改后:", stuv.MethodByName("String").Call(nil)[0])
}
[Running] go run "f:\goProject\src\dev_code\day24\example2\main\main.go"
修改前: 0xc000004078 name:zhangsan age:25
修改后: 0xc000004078 name:lisi age:35
​
[Done] exited with code=0 in 0.62 seconds

反射中方法调用:按索引的方式调方法

PS:索引的排序是与方法名字的ASCII阿斯克码名字进行的排序与前后写方法的顺序无关

package main
​
import (
    "fmt"
    "reflect"
    "strconv"
)
​
//按索引的方式调用方法
//反射方法
type Student struct {
    Name string
    Age  int
}
​
//方法
func (s *Student) Setname(name string) {
    s.Name = name
}
func (s *Student) Setage(age int) {
    s.Age = age
}
func (s *Student) String() string {
    return fmt.Sprintf("%p", s) + " name:" + s.Name + " age:" + strconv.Itoa(s.Age)
}
func main() {
    //实例化检查student对象内容,用地址
    stu := &Student{"zhangsan", 25}
    //反射获取值:指针方式
    stuv := reflect.ValueOf(&stu).Elem()
    //反射获取值:值类型
    // stuv:=reflect.ValueOf(stu)
    //方式二,按方法的索引调用
    fmt.Println("修改前:", stuv.Method(2).Call(nil)[0])
    p := make([]reflect.Value, 1)
    p[0] = reflect.ValueOf("wangwu")
    stuv.Method(1).Call(p)
​
    p[0] = reflect.ValueOf(35)
    stuv.Method(0).Call(p)
    fmt.Println("修改后:", stuv.Method(2).Call(nil)[0])
}
​
//索引的排序是与方法名字的ASCII阿斯克码名字进行的排序与前后写方法的顺序无关
[Running] go run "f:\goProject\src\dev_code\day24\example3\main\main.go"
​

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值