反射

使用反射机制,编写函数的适配器,桥连接
test1 := func(v1 int, v2 int){
    t.Log(v1,v2)
}
基本介绍
  1. 反射可以在运行时动态获取变量的各种信息,比如变量的类型,类别
  2. 如果是结构体变量,还可以获取到结构体本身的信息(包括字段,方法)
  3. 通过反射可以修改变量的值可以调用关联的方法
  4. 使用反射,需要import("reflect")

变量 interface{}和reflect.Value是可以相互转换

func test(b interface{}){
    //如何将interface{}转成reflect.Value
        rval:=reflect.ValueOf(b)l:=reflect.ValueOf(b)
    //如何将reflect.Value ->interface{}
    iVal:=rVal.Interface()
    //如何将interface{}转成原来的变量类型,使用类型断言
    v:=iVal.(Stu)
}
反射的基本类型
func reflectTest01(b interface{})  {
    rTyp := reflect.TypeOf(b)
    rVal := reflect.ValueOf(b)
    fmt.Println(rTyp,rVal) //通过输出的可以看到rVal的值,但是这个值不可以用于运算,因为是relect.Value类型
    n :=  rVal.Int() //通过relect包提供的各种接口可以取到真正的值Int Float String等等
    fmt.Println(n+2)
    iv :=rVal.Interface() //将rVal转成interface{}

    num2 := iv.(int) //将interface{}通过断言转成需要的类型
     fmt.Println(num2)
}

对结构体的反射

反射是运行时反射,所以转成接口时在运行时是我们想用的数据,但是如果不断言转换,编译会报错,因为编译时不知道它是什么样的数据

package main

import (
    "fmt"
    "reflect"
)

type Student struct {
    Name string
    Age  int
}

type Monster struct {
    Name string
    Age  int
}

func reflectTest01(b interface{}) {
    rTyp := reflect.TypeOf(b)
    rVal := reflect.ValueOf(b)
       //获取变量对应的kind的两种方法
    //(1) rVal.Kind()
    //(2) rTyp.kind()
    kind1 := rVal.Kind() //kind= struct
    kind2 := rVal.Kind() // kind=struct
    fmt.Printf("kind= %v kind=%v\n",kind1,kind2)
    fmt.Println(rTyp, rVal) //main.Student {Tom 24} 这里虽然输出是这样,但实际是reflect.Type和reflect.Val类型
    fmt.Printf("%T\n", rVal) //reflect.Value
    rInter := rVal.Interface()
    //运行时反射,下面语句的输出可以看到这里在运行时就是我们的student实例
    fmt.Printf("%T", rInter) //main.Studenti am student type
    switch rInter.(type) { //可以用switch type来判断是哪种类型
    case Student:
        fmt.Println("i am student type")
    case Monster:
        fmt.Println("i am monster type")
    }
    stu, ok := rInter.(Student) //使用ok模型来防止类型断言出现错误
    if ok {
        fmt.Println(stu.Name) //Tom
    }
}
func main() {
    stu := Student{"Tom", 24}
    reflectTest01(stu)
}
反射的注意事项和细节
  1. reflect.Value.Kind获取变量的类别,返回的是一个常量
  2. Type是类型,kind是类别,Type和Kind可能是相同的,也可能是不同的
    • var sum int = 10 num的Type是int,Kind也是int
    • var stu Student stu的Type是包名.Student, Kind是struct
通过反射来修改变量
func reflectTest01(b interface{}) {
    rVal := reflect.ValueOf(b)
    fmt.Printf("rVal kind=%v\n", rVal.Kind())
    rVal.Elem().SetInt(20) //Elem返回v持有的接口保管的值的Value封装,或者v持有的指针的值得Value封装
}
func main() {
    var num int = 10
    reflectTest01(&num)
    fmt.Println("num=", num)
}
使用反射来遍历结构体的字段,调用结构体的方法,并获取结构体的值
package main

import "reflect"
import "fmt"

type Monster struct {
    Name  string `json:"name"`
    Age   int    `json:"age"`
    Score int
}

func (s Monster) Print() {
    fmt.Println("--start--")
}
func (s Monster) Hello() {
    fmt.Println("--Hello--")
}
func (s Monster) GetSum(m, n int) int {
    return m + n
}

func TestStruct(a interface{}) {
    typ := reflect.TypeOf(a)
    val := reflect.ValueOf(a)
    kd := val.Kind() //获取到a对应的类型
    if kd != reflect.Struct { //如果传入的不是struct,就退出
        fmt.Println("expect struct")
        return
    }
    num := val.NumField() //获取到该结构体有几个字段
    fmt.Printf("Struct has %d fileds\n", num)
    for i := 0; i < num; i++ {
        //获取到struct标签,注意需要通过reflect.Type来获取tag标签的值
        tagVal := typ.Field(i).Tag.Get("json")
        //如果该字段有标签就显示否则不显示,这里的Score没有做转换,所以打印出来没有它
        if tagVal != "" {
            fmt.Printf("filed %d: tag为%v\n", i, tagVal)
        }
    }
    //获取到结构体有几个方法
    numOfMethod := val.NumMethod()
    fmt.Printf("Struct has %d methods\n", numOfMethod)
    var params []reflect.Value //声明了[]reflect.Value
    params = append(params, reflect.ValueOf(10))
    params = append(params, reflect.ValueOf(40))
    res := val.Method(0).Call(params) //获取第一个方法,反射的排序是根据函数名来排序的。H在P前面所以hello是第一个方法}
    fmt.Print(res[0])
}
func main() {
    monster := Monster{"牛魔王", 1000, 100}
    TestStruct(monster)
}
反射调用函数必须传入[]reflect.value{}类型的参数
vp.MethodByName("Introduce").Call([]reflect.Value{})
反射的type和value是两个对象,一个保存对象类型信息,一个保存值信息
反射作为函数的适配器
package main

import (
    "fmt"
    "reflect"
)

func test1(args ...int) {
    fmt.Println(args)
}
func test2(v1, v2 int, s string) {
    fmt.Println(v1, v2, s)
}

func t(call interface{}, args ...interface{}) {
    f := reflect.ValueOf(call)
    s := make([]reflect.Value, len(args))
    for i, v := range args {
        s[i] = reflect.ValueOf(v)
    }
    f.Call(s)
}

func main() {
    var a, b int
    var s string
    a = 1
    b = 2
    s = "hello"
    t(test1, a, b)
    t(test2, a, b, s)
}
反射调用方法时需要将方法名大写,否则会报错,因为不大写reflect包读不到方法
package main

import (
    "fmt"
    "reflect"
)

type Cal struct {
    Num1 int `json:"n1"`
    Num2 int `json:"n2"`
}

func (c Cal) Sum() {
    fmt.Println(c.Num1 + c.Num2)
}

func main() {
    var c = Cal{1, 2}
    rc := reflect.ValueOf(c)
    tc := reflect.TypeOf(c)
    for i := 0; i < rc.NumField(); i++ {
        fmt.Println(tc.Field(i))
    }
    v := rc.MethodByName("Sum")
    v.Call(nil)
}

方法绑定指针对象val使用val.Call(nil)是读取不到的,可以通过转换成interface再类型断言去执行

绑定在结构体实例上的方法是可以用val.Call()去执行的





posted @ 2019-12-19 19:35  离地最远的星  阅读(143)  评论(0编辑  收藏  举报