reflect.Type 和 reflect.Value

reflect包定义了两个非常重要的类型,Type 和Value, 一个Type表示一个Go类型,它是一个接口。 函数reflect.TypeOf接受任意的interface{} 类型,并返回对应动态类型的reflect.Type

注意:如果我们把一个具体的值转换为接口类型会有一个隐式的接口转换操作,它会创建一个包含两个信息的接口值:操作数的动态类型和它的动态的值

reflect.ValueOf 接受任意的interface{} 类型, 并返回对应动态类型的reflect.Value 和reflect.TypeOf类似,reflect.ValueOf 返回的结果也都是具体值

reflect.ValueOf的逆操作是reflect.Value.Interface方法,它返回的是一个interface{}接口值

v := reflect.Value(3) x := v.interface() i := x.(int)

reflect.Value的Kind方法来区分不同的类型,尽管有无限种类型,但是类型的分类kind只有少数几种:基础类型Bool,String, 以及各种数字类型;聚合类型Array和struct, 引用类型chan Func, Ptr Slice 和Map,接口类型interface;最后还有Invalid类型,表示它们还没有任何值,reflect.Value的零值就属于Invalid类型

下面是一个reflect.TypeOf的使用代码例子:

// 在reflectEx01函数里,x 其实可以传任何数据类型,并且函数本身也不会关注x的类型
func reflectEx01(x interface{}) {
    t := reflect.TypeOf(x)
    fmt.Printf("type of is %v\n", t)
    k := t.Kind()
    switch k {
    case reflect.Int64:
        fmt.Println(" x is int64")
    case reflect.Float64:
        fmt.Println("x is float64")
    //  这里我们其实写非常多多的Kind类型
    default:
        fmt.Println("unknown")
    }
}

func main() {
    var x float64 = 3.44
    reflectEx01(x)
}

下面是一个reflect.ValueOf的使用代码例子:

func reflectValue(a interface{}) {
    v := reflect.ValueOf(a)
    // v.Type() 获取的内容 和 reflect.TypeOf() 的功能是一样的
    v.Type()
    k := v.Kind()
    switch k {
    case reflect.Int64:
        fmt.Printf("a is int64\n%d",v.Int())
    case reflect.Float64:
        fmt.Printf("a is float64\n%f",v.Float())
    case reflect.Int:
        fmt.Printf("a is int\n%d",v.Int())
    default:
        fmt.Printf("unknown\n")
    }
}

func main() {
    x := 43
    reflectValue(x)
}

通过反射设置值

func reflectSetValue(a interface{}) {
    v := reflect.ValueOf(a)
    k := v.Kind()

    switch k {
    case reflect.Int64:
        fmt.Printf("a is int64 sotre value is:%d\n", v.Int())
    case reflect.Float64:
        fmt.Printf("a is float64 store value is:%f\n",v.Float())
    case reflect.Ptr:
        res, ok := v.Elem().Interface().(int64)
        if ok{
            fmt.Println(res)
        } else {
            fmt.Println(ok)
        }

        v.Elem().SetInt(2323)
        fmt.Printf("a is ptr store value is:%v\n",v.Elem().Int())
    }
}

func main() {
    var x int64 = 23
    reflectSetValue(&x)
}

这里有几个问题需要注意下面代码中我们通过t.Filed(i)和v.Filed(i)返回的结果也是完全不一样的,

type Student struct {
    Name string
    Age int
    Sex int
    //xx int
}

func main() {
    var s Student
    t := reflect.TypeOf(s)
    v := reflect.ValueOf(s)
    k := t.Kind()
    switch k {
    case reflect.Struct:
        fmt.Println("s is struct")
        filedsNum := t.NumField()
        fmt.Printf("s have [%d] fileds\n", filedsNum)
        // 这里需要知道的是我们是没有办法类型来拿到值的信息的
        for i:= 0;i < filedsNum;i++ {
            fmt.Printf("filed name is %s filed type is %v\n",
                t.Field(i).Name, t.Field(i).Type)
        }
        fmt.Println()
        // 这里需要知道reflect.Value的interface()方法是不能对不可导出的值进行转换的
        // 如果我们想要获取值的信息
        for i:= 0; i< filedsNum;i++ {
            fmt.Printf("filed name is %s filed type is %v filed value is %v\n",
                t.Field(i).Name, v.Field(i).Type(), v.Field(i).Interface())
        }
    }

    //v := reflect.ValueOf(s)
    ////t := reflect.TypeOf(s)
    //t := v.Type()
    //k := t.Kind()
    //switch k {
    //case reflect.Struct:
    //  fmt.Println("s is struct")
    //
    //  fmt.Println(t.NumField()) // 可以获取字段数量
    //  for i := 0; i < t.NumField();i++ {
    //      filed := v.Field(i)
    //      fmt.Printf("name:%s type:%v value:%v\n", t.Field(i).Name,filed.Type().Kind(),filed.Interface())
    //  }
    //default:
    //  fmt.Println("default")
    //}

}

通过反射获取结构体信息并设置

type Student struct {
    Name string
    Age int
    Sex int
}


func main(){
    var s Student
    v := reflect.ValueOf(&s)
    v.Elem().Field(0).SetString("aaa")
    v.Elem().FieldByName("Name").SetString("bbb")
    v.Elem().FieldByName("Age").SetInt(23)
    v.Elem().FieldByName("Sex").SetInt(0)
    fmt.Println(s)
}

通过反射获取结构体的方法并调用方法

type Student struct {
    Name string
    Age int
}

func (s *Student) SetName(name string) {
    s.Name = name
}

func (s *Student)Hello() {
    fmt.Println("hello world")
}

func main() {
    s := Student{"aa",23}
    v := reflect.ValueOf(&s)
    t := v.Type()
    fmt.Println("method num:",v.NumMethod())
    for i:=0;i<v.NumMethod();i++ {
        f := t.Method(i) // 这里只能获得方法的信息,但是并不能执行方法

        fmt.Printf("%d method, name:%v, type:%v\n",i,f.Name,f.Type)
    }
    // 如果我们只是为了获取方法名的话可以通过t.Method来获取,但是如果是为了执行方法的需要v.Method
    m := v.MethodByName("Hello")
    var args []reflect.Value
    m.Call(args)
    m = v.MethodByName("SetName")
    var args2 []reflect.Value
    name := "abc"
    name2 := reflect.ValueOf(name)
    args2 = append(args2, name2)
    m.Call(args2)
    fmt.Println(s)
}

获取结构体中tag信息

结构体的tag信息是可以存在多个值的,就像下面例子一样,有json和ini两个,中间通过空格隔开

type Student struct {
    Name string `json:"name" ini:"ini_name"`
    Age string `json:"age" ini:"ini_age"`
}

func main() {
    var s Student
    v := reflect.ValueOf(s)
    t := v.Type()
    field := t.Field(1)
    ret := field.Tag.Get("json")
    ret2 := field.Tag.Get("ini")
    fmt.Println(ret)
    fmt.Println(ret2)
}

小结

反射主要用于在运行时动态获取一个变量的类型信息和值信息

应用场景: 1. 序列化和反序列化,如Json 2. 数据库的orm 3. 配置文件即系的相关库