Go语言基础——反射

Go语言提供了一种机制,在编译时不知道类型的情况下,在运行时,可更新变量、查看值、调用方法以及直接对它的结构成员进行操作,这种机制称为反射(reflection)。

1、Go的反射程序包:reflect

反射功能由reflect包提供,包内定义了两个重要的类型:Type和Value。
Type表示Go语 言的一个类型,它是一个有很多方法的接口,这些方法可以用来识别类型以及透视类型的组 成部分,比如一个结构的各个字段或者一个函数的各个参数。reflect.Type接口只有一个实现,即类型描述符,接口值中的动态类型也是类型描述符。
reflect.Value可以包含一任意类型的值。

通过示例展示Type和Value的使用

package main 

import (
    "fmt"
    "reflect"   //引入反射包
)

// 定义用户结构
type User struct {
    Id int
    Name string
    Age int
}

// 定义方法
func (u User)Hello(){
    fmt.Println("Hello World!")
}

func main() {
    u := User{1,"Tom",12}
//  Info(&u) 传入指针类型,编译报错:panic: reflect: NumField of non-struct type
    Info(u) // 通过值拷贝输入对象
}

// 反射示例函数
func Info(o interface{}) {
    t := reflect.TypeOf(o)  // 获取类型数据
    fmt.Println("Type:", t.Name()) // 打印类型名称
    
    v := reflect.ValueOf(o) // 获取对象的数值
    fmt.Println("Fields:")
    
    for i :=0; i < t.NumField(); i++ {
        f := t.Field(i) // 获取指定索引结构成员的信息
        val := v.Field(i).Interface()   // 获取指定结构成员的值
        fmt.Printf("%6s: %v = %v\n", f.Name, f.Type, val) // 打印成员的名称,类型和值
    }
    
    for i :=0; i <t.NumMethod(); i++ {
        m := t.Method(i) // 获取指定索引的方法的信息
        fmt.Printf("%6s: %v\n", m.Name, m.Type) // 打印方法的名称,方法声明
    }
}

程序运行输出的结果如下图:


图片.png

2、匿名字段的反射

在Go语言中,可以通过索引来获取匿名字段的信息,参见程序代码:

package main 

import (
    "fmt"
    "reflect"   //引入反射包
)

// 定义用户结构
type User struct {
    Id int
    Name string
    Age int
}

type Manager struct {
    User    // 定义匿名字段
    title string
}

func main() {
    m := Manager{User: User{1, "Lisa", 28}, title: "CFO"}   // 对象定义及匿名User类型字段初始化
    t := reflect.TypeOf(m)  // 获取对象的类型信息
    
    fmt.Printf("%#v\n", t.Field(0)) // 通过索引获取匿名字段的信息
    fmt.Printf("%#v\n", t.Field(1)) // 获取title字段的信息
    
    // 获取匿名User类型字段成员的方法,通过传入slice来制定索引对应的成员,可以获取相应的信息
    fmt.Printf("%#v\n", t.FieldByIndex([]int{0,0})) //获取User中的Id成员
    fmt.Printf("%#v\n", t.FieldByIndex([]int{0,1})) //获取User中的Name成员
}

运行输出结果:


图片.png

上图中,可以看到,对于User类型的匿名成员的名称为:User,是否匿名Anonymous标记为:true,而对于User成员Id和Name来讲,匿名标记为false。

3、通过反射修改结构对象的值

package main 

import (
    "fmt"
    "reflect"   //引入反射包
)

// 定义用户结构
type User struct {
    Id int
    Name string
    Age int
}

func main() {
    u := User{1,"Tom", 28}
    fmt.Println("before:",u)
    Set(&u)
    fmt.Println("after:",u)
}

func Set(o interface{}) {
    v := reflect.ValueOf(o)
    
    if v.Kind() == reflect.Ptr && !v.Elem().CanSet() {
        fmt.Print("输入的对象是不可修改的")
    } else {
        v = v.Elem()
    }
     // 判断是否获取到制定名称的字段信息
    f := v.FieldByName("Name")
    if !f.IsValid() {
        fmt.Println("没有找到Name的字段")
        return
    }
    // 修改字段的数值
    if f.Kind() == reflect.String {
        f.SetString("Tom2")
    } 
    
}

运行结果如下:

图片.png

reflect 包中还有很多有用的方法,具体详见包的参考文档及源码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 136,272评论 19 139
  • 第一次知道反射的时候还是许多年前在学校里玩 C# 的时候。那时总是弄不清楚这个复杂的玩意能有什么实际用途……然后发...
    勿以浮沙筑高台阅读 4,822评论 0 9
  • 首先巴拉巴拉一下golang反射机制的三个定律 1.反射可以从接口类型到反射类型对象 2.反射可以从反射类型对象到...
    吃猫的鱼0阅读 8,148评论 0 1
  • 只要你下定决心,就过你想要的生活,不管有钱还是没钱。你可以继续看你的书,保持你的想法,只用对自己说“我是自由人。”...
    无邪说在简书阅读 2,454评论 0 1
  • 一、本期目标: 建立和谐的家庭关系,改善缓和父子关系。 愿景:拥有一个幸福和谐的家庭,一家人友好相处相互信...
    夏宁点点阅读 1,130评论 0 0

友情链接更多精彩内容