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 

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


图片.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 包中还有很多有用的方法,具体详见包的参考文档及源码

你可能感兴趣的:(Go语言基础——反射)