Go---反射

反射

1) 反射可以在运行时动态获取变量的各种信息,比如变量的类型,类别
2) 如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)

3) 通过反射,可以修改变量的值,可以调用关联的方法。
4) 使用反射,需要 import ("reflect")

5)反射的示意图

 

反射的应用场景
反射常见应用场景有以下两种
1) 不知道接口调用哪个函数,根据传入参数在运行时确定调用的具体接口,这种需要对函数或方法反射。

2)对结构体序列化时,如果结构体有指定Tag, 也会使用到反射生成对应的字符串。

反射重要的函数和概念

 1) reflect.TypeOf(变量名),获取变量的类型,返回reflect.Type类型
 2)  reflect.ValueOf(变量名),获取变量的值,返回reflect.Value类型reflect.Value是一个结构体类型。通过reflect.value,可以获取到关于该变量的很多信息。

3) 变量、interface{} 和reflect.Value是可以相互转操的,这点在实际开发中,会经常使用到。

for example

package main

import (

    "fmt"

    "reflect"

)

//专门演示反射

func reflectTest(b interface{}){

  //通过反射获取的传入的变量的type , kind, 值

  //1.先获取到reflect. Type

  rTyp := reflect.TypeOf(b)

  fmt.Println("rtyp = ",rTyp)

  //2.获取到reflect.Value

  rVal := reflect.ValueOf(b)

  n2 := 33+rVal.Int()

  fmt.Println("n2=",n2)

  fmt.Printf("rVal=%v , rVal type=%T \n",rVal,rVal)

  //下面将rVal 转成interface{}

  iV := rVal.Interface()

  //将interface{} 通过断言转成需要的类型

  num2 := iV.(int)

  fmt.Println("num2=",num2)

}

//结构体反射

func reflectStruct(b interface{}){

    //1.先获取到reflect. Type

    rTyp := reflect.TypeOf(b)

    fmt.Println("rtyp = ",rTyp)

    //2.获取到reflect.Value

    rVal := reflect.ValueOf(b)

   

    //下面将rVal 转成interface{}

    iV := rVal.Interface()

    fmt.Printf("iV=%v , iV type=%T \n",iV,iV)

    //将interface{} 通过断言转成需要的类型

    stu,ok := iV.(Student)

    if ok {

        fmt.Println("name= ",stu.Name)

    }

}

type Student struct{

    Name string

    Age int

}

func main(){

    //请编写一个案例,

    //演示对(基本数据类型、interface{}、 reflect. Value)进行反射的基本操作

    //1.先定义一个int

    var num int = 96

    reflectTest(num)

    //2.定义一个Student的实例

    stu := Student{

        Name: "mary",

        Age : 19,

    }

    reflectStruct(stu)

}

 

常量

1)常量使用const修改
2)常量在定义的时候,必须初始化
3)常量不能修改
4)常量只能修饰bool、数值类型(int, float系列)、 stringj类型
5)语法: const identifier [type] = value

常量使用注意事项
  1)比较简洁的写法 

    const (

        a =2

        b =3

    )

2)还有一种专业的写法

const (

        a =iota

        b

        c

        d

    )

表示给a 赋值为0
b在a的基础上+1
c在b的基础上+1
d在c的基础上+1

3)Golang中没有常量名必须字母大写的规范

 4)仍然通过首字母的大小写来控制常量的访问范围

反射注意事项和细节说明
1) reflect.Value.Kind, 获取变量的类别,返回的是一个常量 

 2) Type是类型, Kind是类别,Type 和Kind可能是相同的,也可能是不同的.
        比如: var num int = 10  ,num的Type是int , Kind也是int
        比如: var stu Student,  stu的Type是 包名.Student , Kind是struct

3)通过反射可以在让变量interface{}和Reflect.Value之,间相互转换。

4)使用反射的方式来获取变量的值(并近回对应的类型),要求数据类型匹配,比如x是int,那么就应该使用reflect.Value(x).Int(), 而不能使用其它的,否则报panic

5)通过反射的来修改变量,注意当使用SetXxx方法来设置需要通过对应的指针类型来完成,这样才能改变传入的变量的值,同时需要使用到eflect.Value. Elem()方法

//通过反射,修改 num int的值 修改studentd的值

func reflectUpdate(b interface{}){

    //2.获取到reflect.Value

    rVal := reflect.ValueOf(b)

    // look rVal of Kind is

    fmt.Printf("rVal kind=%v \n",rVal.Kind())

    //3.设置rVal

    //Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装。

    rVal.Elem().SetInt(233)

}

func main(){

    num := 10

    reflectUpdate(&num)

    fmt.Println(num)

}

练习

1)使用反射来遍历结构体的字段,调用结构体的方法,并获取结构体标签的值

使用的两个方法

 

 

package main

import (

    "fmt"

    "reflect"

)

//定义一个Monster结构体

type Monster struct {

    Name string `json:"name"`

    Age int `json:"monster_age"`

    Score float64

    Sex string

}

//显示s的值

func (s Monster) Print(){

    fmt.Println("----------hey--------")

    fmt.Println(s)

    fmt.Println("----------hey--------")

}

//求和

func (s Monster) GetSum(n1,n2 int) int {

    return n1+n2

}

//给Monster 赋值

func (s Monster) Set(name string,age int ,score float64,sex string){

    s.Name =name

    s.Age = age

    s.Score =score

    s.Sex = sex

}

func TestStruct(a interface{}){

    //获取reflect.Type类型

    typ := reflect.TypeOf(a)

    //获取reflect.Value

    val := reflect.ValueOf(a)

    //获取a的 类型

    kd := val.Kind()

   

    if kd != reflect.Struct {

        fmt.Println("expect struct")

        return

    }

    //NumField() : 返回v持有的结构体类型值的字段数,如果v的Kind不是Struct会panic

    //获得几个字段

    num := val.NumField()

    fmt.Printf("Struct has  %d field \n",num)

     //遍历结构体的所有字段

    for i:=0;i<num;i++{

         //Field(i) : 返回结构体的第i个字段(的Value封装)。如果v的Kind不是Struct或i出界会panic

        fmt.Printf("Field %d : 值=%v \n",i,val.Field(i))

        //获取到struct标签,注意需要通过reflect.Type来获取tag标签的值

        tagVal := typ.Field(i).Tag.Get("json")

        //如果改字段有标签就打印

        if tagVal != ""{

            fmt.Printf("Field %d : tag = %v \n",i,tagVal)

        }

    }

    //获取到该结构体有多少个方法

    numOfMethod := val.NumMethod()

    fmt.Printf("struct has %d methods \n",numOfMethod)

    //var params []reflect.Value

    //方法的排序默认是按照函数名的排序(ASCII码 )

    // G P S

    val.Method(1).Call(nil) //获取第二方法,并调用它

    //调用结构体的第一方法Method(0)

     var params []reflect.Value //声明了 []reflect.Vaule

     params = append(params,reflect.ValueOf(10))

     params = append(params,reflect.ValueOf(40))

     res := val.Method(0).Call(params) //传入参数是 []reflect.Value ,返回的[]reflect.Value

     fmt.Println("res=",res[0].Int())  //返回结果,返回的结果是[ ]reflect. value*/

}

func main(){

    var a Monster = Monster{

        Name : "牛魔王",

        Age : 600,

        Score :900.0,

        Sex:"male",

    }

    TestStruct(a)

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值