反射与select学习笔记|实践记录以及工具使用

82 阅读3分钟

反射

静态编程语言都是先编译器编译代码再执行,但是很多场景需要动态获取和执行;因此如Java、C#、Python等编程语言提供了反射机制,反射机制可以让程序在运行时获取和检查字段名字、值,还可以动态修改值,Java中还可以动态获取私有方法;反射功能非常强大,可以增加程序执行中的功能,比如说Spring依靠反射机制让Java焕发新春;Golang提供了反射机制,主要是涉及了Type和Struct;Type在Golang中以接口存在,Type以结构体形式存在。

反射结构体

提供reclect提供的TypeOf方法可以获取Type,根据Type可以获取对应的字段数量、名称,ValueOf方法可以获取Type并通过索引获取字段的值。

type person struct {
    name string `json:"name"`
    age  int    `json:"age"`
}
func main() {
    var p *person = &person{"tom", 15}
    var t reflect.Type = reflect.TypeOf(*p) //反射获取Type
    var v reflect.Value = reflect.ValueOf(*p) //反射获取Value
    fmt.Println(t.Kind(), t.Name(), t.NumField())
    for i := 0; i < t.NumField(); i++ {
       fmt.Println(t.Field(i).Name, t.Field(i).Type)//打印每个字段的名字 类型
       fmt.Println(v.Field(i))//打印每个字段的值
    }
}

反射修改值

通过反射获取的struct的Value,可以对每个字段调用CanSet和CanAddr获取字段是否能寻址和修改,与Java反射能修改私有字段不同,Golang的反射具有一定局限性不能修改不可导出字段即名字以小写字母开头的字段。

type person struct {
    name string `json:"name"`
    Age  int    `json:"age"`
}
func main() {
    var p *person = &person{"tom", 15}
    var t reflect.Value = reflect.ValueOf(*p)
    for i := 0; i < t.NumField(); i++ {
       fmt.Println(t.Field(i).Kind(), t.Field(i).CanAddr(), t.Field(i).CanSet())
    }//打印每个字段的类型 是否可寻址 是否能被修改
}

结构体标签

在实际应用中常常会使用Json、Toml等序列化格式,Golang支持我们在定义结构体时用反引号给成员加标签,方便序列化时反射获取。提供反射获取结构体标签信息的方式如下所示。

type person struct {
  name string `json:"name"`//对字段定义的标签
  age  int    `json:"age"`
}
func main() {
  var p *person = &person{"tom", 15}
  var t reflect.Type = reflect.TypeOf(p).Elem()
  for i := 0; i < t.NumField(); i++ {
     fmt.Println(t.Field(i).Tag)//打印每个字段的标签
  }
}

select

在实际生产应用中常常会频繁使用IO,IO会涉及用户态与内核态的切换具有较大的开销;因此诸如Linux等现代计算机操作系统提供了IO多路复用机制,如Linux中提供的select、poll、epoll,提供IO多路复用机制可以让一个线程或者少量线程管理大量IO处理,减少了从用户态陷入内核态的次数。Golang语言提供了select,不过与IO操作不同,Golang中的select主要用于多管道通信。
select在编程操作上类似于swicth操作,仅需一个case满足即执行且不会执行后续case中的语句,但每个case中的条件必须涉及对chan变量的写入或写出;若case中语句为从chan中读取数据,当chan中有数据写入则执行case后的语句;若case条件为写入chan,当该chan被其他读出则执行case后的语句;在使用select因注意极端情况,如select后无case语句,或有case语句但无法满足且无default会造成永久阻塞,程序会提示造成deadlock;当多个case同时满足时,select会随机选择一个case执行。

func main() {
    var ch1 chan int = make(chan int)//创建通信管道
    var ch2 chan int = make(chan int)
    go func() {
       time.Sleep(time.Second)
       ch1 <- 1
    }()
    go func() {
       time.Sleep(2 * time.Second)
       num, ok := <-ch2
       fmt.Println(num, ok)
    }()
    for i := 0; i < 2; i++ {
       select {
       case msg1 := <-ch1:
          fmt.Println(msg1)//当成功从chan1中读到数据时执行
       case ch2 <- 3://当ch2中的数据被取走时执行
       }
    }
    //select {}   会造成永久阻塞
}