GO反射(reflect)

本文深入探讨了Go语言中的反射机制,包括如何通过`reflect`包获取变量的类型和值,设置变量值,以及对结构体和方法的反射操作。示例涵盖了`ValueOf`、`Kind`、`Interface`、`Set`方法,以及结构体属性、Tag信息的获取和方法调用。同时,展示了如何利用反射进行类型转换和函数调用,以及如何处理方法反射。

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

目录

reflect 

                获取变量类别

ValueOf

        Value.Set

结构体反射

                反射获取结构体中的属性值和类型

                更改结构体属性值

                反射实现Tag原信息

函数反射

        Value的Call()方法

方法反射


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{}) {
    //反射获取类型
    var t = reflect.TypeOf(i)
​
    fmt.Println("类型:", t)
​
    //反射数据值
    var v = reflect.ValueOf(i)
    fmt.Println("值:", v)
}
func main() {
    Test(`
        her
        his
        10
    `)
    Test(10)
}
​
运行结果

类型: string
值: 
        her
        his
        10
    
类型: int
值: 10

                获取变量类别

package main
​
import (
    "fmt"
    "reflect"
)
​
//定义结构体
type Student struct {
    Name string
    Age  int
}
​
//反射
func Test(i interface{}) {
    //类型
    t := reflect.TypeOf(i)
​
    fmt.Println("类型:", t)
​
    //类别
    v := reflect.ValueOf(i).Kind()
    // c := v.Kind()
    // fmt.Println("类别:",c)
    fmt.Println("类别:", v)
}
func main() {
    var stu = Student{
        Name: "张三",
        Age:  20,
    }
    Test(stu)
​
    fmt.Println("---------------------")
    var num = 10
    Test(num)
}
​
运行结果

类型: main.Student
类别: struct
---------------------
类型: int
类别: int

示例,断言处理类型转化

package main
​
import (
    "fmt"
    "reflect"
)
​
/*结合断言*/
​
//定义结构体
type Student struct {
    Name string
    Age  int
}
​
//反射
func Test(i interface{}) {
    //类型
    t := reflect.TypeOf(i)
​
    fmt.Println("类型:", t)
​
    //类别
    v := reflect.ValueOf(i)
    c := v.Kind()
    fmt.Println("类别:", c)
    fmt.Printf("v的类型:%T\n", v)
    fmt.Printf("c的类型:%T\n", c)
​
    //转化为接口
    c1 := v.Interface()
    fmt.Printf("c1的类型为:%T\n", c1)
    //断言处理
    stu, ok := c1.(Student)
    if ok {
        fmt.Printf("stu结构:%v,stu的类型:%T\n", stu, stu)
    }
}
​
func main() {
    var stu = Student{
        Name: "张三",
        Age:  20,
    }
    Test(stu)
​
    fmt.Println("---------------------")
}
​
运行结果

类型: main.Student
类别: struct
v的类型:reflect.Value
c的类型:reflect.Kind
c1的类型为:main.Student
stu结构:{张三 20},stu的类型:main.Student
---------------------

ValueOf

获取变量值
reflect.valueOf(x).Float()
reflect.valueOf(x).Int()
reflect.valueOf(x).String()
reflect.valueOf(x).Boo1()

示例:类型转换

package main
​
import (
    "fmt"
    "reflect"
)
​
func Test(i interface{}) {
    a := reflect.ValueOf(i)
    fmt.Printf("类型:%T\n", a)
    //转化指定类型
    t := a.String()
    fmt.Printf("t的类型:%T\n", t)
}
func main() {
​
    //类型不同会报错
    var num = "hello"
    Test(num)
}
​
运行结果

类型:reflect.Value
t的类型:string

        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)
    fmt.Printf("更改前的类型为:%T,值为:%d\n", v, v)
    reslut := v.Int()
    fmt.Printf("更改后的类型为%T,值为:%d", reslut, reslut)
​
}
func main() {
​
    var num = 10
    Test(num)
}
​

运行结果是,很明显是地址的原因,找不地址

panic: reflect: reflect.Value.SetInt using unaddressable value

goroutine 1 [running]:

 

正确应该这样写

package main
​
import (
    "fmt"
    "reflect"
)
​
func Test(i interface{}) {
    v := reflect.ValueOf(i)
​
    //更新值,需要value的地址,否则会报错 Elem()相当于指针
    v.Elem().SetInt(100)
​
    fmt.Printf("更改前的类型为:%T\n", v)
    reslut := v.Elem().Int()
    fmt.Printf("更改后的类型为%T,值为:%d", reslut, reslut)
​
}
func main() {
​
    var num = 10
    Test(&num)
}
​
运行结果

更改前的类型为:reflect.Value
更改后的类型为int64,值为:100

结构体反射

示例:反射出结构体数量和方法数量

package main
​
import (
    "fmt"
    "reflect"
)
​
//结构体反射
//定义结构体
type Students struct {
    Name string
    Age  int
    sex  string
}
​
//方法
func (s Students) Run() {
    fmt.Println("正在跑")
}
​
func (s Students) Sleep() {
    fmt.Println("正在休息")
}
func Test(i interface{}) {
    v := reflect.ValueOf(i)
​
    //类别判断
    if v.Kind() != reflect.Struct {
        fmt.Println("不是struc类型")
        return
    }
​
    //获取结构体字段
    stu_num := v.NumField()
    fmt.Printf("字段数量:%d\n", stu_num)
​
    //获取方法
    stu_men := v.NumMethod()
    fmt.Println("方法数量", stu_men)
​
}
func main() {
    //初始化结构体
    var stu = Students{
        Name: "张三",
        Age:  20,
        sex:  "men",
    }
    Test(stu)
}
​
运行结果

字段数量:3
方法数量 2

                反射获取结构体中的属性值和类型

package main
​
import (
    "fmt"
    "reflect"
)
​
type Students struct {
    Name string
    Age  int
    sex  string
}
​
//反射获取结构体中的属性值和类型
func Test(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 v = Students{
        Name: "张三",
        Age:  18,
        sex:  "男",
    }
​
    Test(v)
}
​
运行结果

索引:0,值:张三 类型: string
索引:1,值:18 类型: int
索引:2,值:男 类型: string

                更改结构体属性值

package main
​
import (
    "fmt"
    "reflect"
)
​
type Students struct {
    Name string
    Age  int
    sex  string
}
​
//反射获取结构体中的属性值和类型
func Test(i interface{}, name string) {
    v := reflect.ValueOf(i)
​
    vk := v.Kind()
​
    //判断是否为指针,如果不是指针而是指向结构体,则返回异常
​
    if vk != reflect.Ptr && v.Elem().Kind() == reflect.Struct {
        fmt.Println("error message")
        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 v = Students{
        Name: "张三",
        Age:  18,
        sex:  "男",
    }
​
    Test(&v, "李四")
}
​
运行结果

索引:0,值:李四 类型: string
索引:1,值:18 类型: int
索引:2,值:男 类型: string

                反射实现Tag原信息

package main
​
import (
    "encoding/json"
    "fmt"
    "reflect"
)
​
type Students struct {
    Name string `json:"stu_name" `
    Age  int
    sex  string
}
​
//反射获取结构体中的属性值和类型
func Test(i interface{}, name string) {
    v := reflect.ValueOf(i)
​
    vk := v.Kind()
​
    //判断是否为指针,如果不是指针而是指向结构体,则返回异常
​
    if vk != reflect.Ptr && v.Elem().Kind() == reflect.Struct {
        fmt.Println("error message")
        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 v = Students{
        Name: "张三",
        Age:  18,
        sex:  "男",
    }
​
    Test(&v, "李四")
​
    fmt.Println("-----------------原信息------------------")
    //Tag原信息
    result, _ := json.Marshal(v)
​
    fmt.Println("JSON原信息", string(result))
    fmt.Println("----------------反射实现原信息---------------")
    //反射获取属性类型
    re := reflect.TypeOf(v)
​
    //取索引为0的属性赋予给s
    s := re.Field(0)
    fmt.Printf("Name原信息名称:%s\n", s.Tag.Get("json"))
}
​
运行结果

索引:0,值:李四 类型: string
索引:1,值:18 类型: int
索引:2,值:男 类型: string
-----------------原信息------------------
JSON原信息 {"stu_name":"李四","Age":18}
----------------反射实现原信息---------------
Name原信息名称:stu_name


函数反射

示例:GO函数可以赋值给变量

package main
​
import "fmt”
​
func Hello(){
​
    fmt.Printlnt"hello world")
}
func main(){
​
    //通数可以赋值给变量
    a :=Hello
​
    a()
}
​
运行结果

hello world

        Value的Call()方法

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

package main
​
import (
    "fmt"
    "reflect"
)
​
//函数反射
func Helle() {
    fmt.Println("hello world")
}
func main() {
    v := reflect.ValueOf(Helle)
    //判断类型是否为reflect.Func类型
    if v.Kind() == reflect.Func {
        fmt.Println("函数")
    }
    //反射调用函数
    v.Call(nil)
}
 

运行结果

函数
hello world

Value的Call()方法的参数是一个Value 的islice,对应的反射函数类型的参数,返回值也是一个Value的 slice,同样对应反射函数类型的返回值。

示例

package main
​
import (
    "fmt"
    "reflect"
    "strconv"
)
​
//反射调用函数带参和返回值
func Test(i int) string {
    return strconv.Itoa(i)
}
func main() {
    v := reflect.ValueOf(Test)
​
    //定义参数切片
    scilt := make([]reflect.Value, 1)
​
    //切片赋值
    scilt[0] = reflect.ValueOf(10)
​
    //反射调用函数
    reslut := v.Call(scilt)
​
    fmt.Printf("类型为:%T\n", reslut)
​
    //类型断言,[]reflect.Value切片转换string
​
    var s = reslut[0].Interface().(string)
    fmt.Printf("s类型为:%T\n值为:%s", s, s)
}
​
运行结果

类型为:[]reflect.Value
​
s类型为:string
​
值为:10

 

 

方法反射

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

package main
​
import (
    "fmt"
    "reflect"
    "strconv"
)
​
type Students struct {
    Name string
    Age  int
}
​
//方法
func (s *Students) SetName(name string) {
    s.Name = name
}
​
func (s *Students) SetAge(age int) {
    s.Age = age
}
​
func (s *Students) String() string {
    return fmt.Sprintf("地址%p", s) + ",Name:" + s.Name + ",Age:" + strconv.Itoa(s.Age)
}
func main() {
​
    //实例化
    stu := &Students{"zhangsan", 20}
​
    //反射获取值,指针方式
​
    stuV := reflect.ValueOf(&stu).Elem()
    //也可以使用非指针
    //stuV := reflect.ValueOf(stu)
​
    //修改前
    fmt.Println("修改前", stuV.MethodByName("String").Call(nil)[0])
​
    //修改值
​
    Slice := make([]reflect.Value, 1)
​
    Slice[0] = reflect.ValueOf("李四")
​
    stuV.MethodByName("SetName").Call(Slice)
​
    Slice[0] = reflect.ValueOf(23)
    stuV.MethodByName("SetAge").Call(Slice)
​
    fmt.Println("修改后", stuV.MethodByName("String").Call(nil)[0])
​
}
​
运行结果

修改前 地址0xc000004078,Name:zhangsan,Age:20
修改后 地址0xc000004078,Name:李四,Age:23

第二种方式,使用索引

package main
​
import (
    "fmt"
    "reflect"
    "strconv"
)
​
type Students struct {
    Name string
    Age  int
}
​
//方法
func (s *Students) SetName(name string) {
    s.Name = name
}
​
func (s *Students) SetAge(age int) {
    s.Age = age
}
​
func (s *Students) String() string {
    return fmt.Sprintf("地址%p", s) + ",Name:" + s.Name + ",Age:" + strconv.Itoa(s.Age)
}
func main() {
​
    //实例化
    stu := &Students{"zhangsan", 20}
​
    //反射获取值,指针方式
​
    stuV := reflect.ValueOf(&stu).Elem()
​
    //修改前
    fmt.Println("修改前", stuV.Method(2).Call(nil)[0])
​
    //修改值
​
    //索引的大小根据AScll码排序
    Slice := make([]reflect.Value, 1)
​
    Slice[0] = reflect.ValueOf("李四")
​
    stuV.Method(1).Call(Slice)
​
    Slice[0] = reflect.ValueOf(23)
    stuV.Method(0).Call(Slice)
​
    fmt.Println("修改后", stuV.MethodByName("String").Call(nil)[0])
​
}
​
运行结果

修改前 地址0xc000004078,Name:zhangsan,Age:20
修改后 地址0xc000004078,Name:李四,Age:23

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小柏ぁ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值