提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
Go语言学习笔记(十六)
一、使用全局变量加锁同步改进程序
未对全局变量m加锁,因此会出现资源争夺问题,代码出现错误
var(
//声明一个全局的互斥锁
//lock是一个全局互斥锁
//Mutex是互斥
myMap = make(map[int]int,10)
lock sync.Mutex
)
func test(n int){
res :=1
for i :=1;i<=n;i++{
res *=i
}
//将res放入myMap加锁
lock.Lock()
myMap[n]=res
lock.Unlock()
}
示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。
二、反射
go语言提供了一种机制在运行时更新和检查变量的值、调用变量的方法和变量支持的内在操作,但是在编译时并不知道这些变量的具体类型,这种机制被称为反射。反射也可以让我们将类型本身作为第一类的值类型处理。
反射是指在程序运行期对程序本身进行访问和修改的能力,程序在编译时变量被转换为内存地址,变量名不会被编译器写入到可执行部分,在运行程序时程序无法获取自身的信息。
支持反射的语言可以在程序编译期将变量的反射信息,如字段名称、类型信息、结构体信息等整合到可执行文件中,并给程序提供接口访问反射信息,这样就可以在程序运行期获取类型的反射信息,并且有能力修改它们。
利用reflect 包来访问程序的反射信息。
在Go语言程序中,使用 reflect.TypeOf() 函数可以获得任意值的类型对象(reflect.Type)
示例
package main
import (
"fmt"
"reflect"
)
func main() {
var a int
typeOfA := reflect.TypeOf(a)
fmt.Println(typeOfA.Name(), typeOfA.Kind())
}
得出结果为int
反射的应用情况
interface{}具体类型通过reflect.Type表示,对应值通过reflect.Value表示。 reflect.TypeOf() 和 reflect.ValueOf() 函数分别返回reflect.Type和reflect.Value,利用这两个类型我们可以实现通用逻辑。
使用反射来遍历结构体的字段,调用结构体的方法,修改结构体字段的值,并获取结构体标签的值
例
import (
"fmt"
"reflect"
)
type Student struct {
Name string `json:"name"`
Sex string `json:"sex"`
Age int `json:"age"`
Sal float64 `json:"sal"`
}
func (s Student) GetName() string {
fmt.Println("该结构体Name字段值为:",s.Name)
return s.Name
}
func (s *Student) Set(newName string,newAge int,newSal float64){
s.Name = newName
s.Age = newAge
s.Sal = newSal
s.Print()
}
func (s Student) Print() { //第1个方法
fmt.Println("调用 Print 函数输出结构体:",s)
}
//反射获取结构体字段、方法,并调用
func testReflect(b interface{}) {
rVal := reflect.ValueOf(b).Elem()
rType := reflect.TypeOf(b).Elem()
//判断是否是结构体在进行下一步操作
if rType.Kind() != reflect.Struct{
fmt.Println("该类型不是结构体。所以无法获取字段及其方法。")
}
//获取字段数量
numField := rVal.NumField()
fmt.Printf("该结构体有%d个字段\n",numField)
//遍历字段
for i := 0; i < numField; i++ {
//获取字段值、标签值
rFieldTag := rType.Field(i).Tag.Get("json")
if rFieldTag != "" {
fmt.Printf("结构体第 %v 个字段值:%v ," +
"Tagjson名为:%v\n",i,rVal.Field(i),rFieldTag)
}
}
//调用方法(方法顺序 按照ACSII码排序)
rVal.Method(0).Call(nil)
rVal.Method(1).Call(nil)
//参数也需要以 Value 的切片 传入
params := make([]reflect.Value ,3)
params[0] = reflect.ValueOf("hhhh")
params[1] = reflect.ValueOf(28)
params[2] = reflect.ValueOf(99.9)
rVal.Method(2).Call(params)
rVal.Method(1).Call(nil)
}
func main() {
stu := Student{
Name: "莉莉安",
Sex: "f",
Age: 19,
Sal: 98.5,
}
//调用编写的函数并输出
testReflect(&stu)
fmt.Println("主函数输出结构体 Student :",stu)
}
案例来源
案例2
请编写一个案例,演示对(基本数据类型/结构体类型、interface{}、reflect.Value)进行反射的基本操作
package main
import (
"reflect"
"fmt"
)
//专门演示反射
func reflectTest01(b interface{}) {
//通过反射获取的传入的变量的 type , kind, 值
//1. 先获取到 reflect.Type
rTyp := reflect.TypeOf(b)
fmt.Println("rType=", rTyp)
//2. 获取到 reflect.Value
rVal := reflect.ValueOf(b)
n2 := 2 + 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 reflectTest02(b interface{}) {
//通过反射获取的传入的变量的 type , kind, 值
//1. 先获取到 reflect.Type
rTyp := reflect.TypeOf(b)
fmt.Println("rType=", rTyp)
//2. 获取到 reflect.Value
rVal := reflect.ValueOf(b)
//下面我们将 rVal 转成 interface{}
iV := rVal.Interface()
fmt.Printf("iv=%v iv type=%T \n", iV, iV)
//将 interface{} 通过断言转成需要的类型
//这里,我们就简单使用了一带检测的类型断言.
//同学们可以使用 swtich 的断言形式来做的更加的灵活
stu, ok := iV.(Student)
if ok {
fmt.Printf("stu.Name=%v\n", stu.Name)
}
}
type Student struct {
Name string
Age int
}
type Monster struct {
Name string
Age int
}
func main() {
//请编写一个案例,
//演示对(基本数据类型、interface{}、reflect.Value)进行反射的基本操作
//1. 先定义一个 int
// var num int = 100
// reflectTest01(num)
//2. 定义一个 Student 的实例
stu := Student{
Name : "tom",
Age : 20,
}
reflectTest02(stu)
}