在Go语言中,对于一般的代码,在经过编译成可执行文件以后,变量的名字,类型等信息是没有被携带到可执行程序的
例如 var a int=10
在编译成可执行程序以后,变量a的名字a以及类型int信息都没有了,转而编程了在内存中的某个地址以及占用内存的大小
对于反射而言,在编译为可执行程序并且在程序运行过程中,还保留变量的名字,类型等信息,而这一切的实现是依赖于reflect这个包的。
对于一个接口,它包含了两部分的内容,分别是类型和值,我们可以通过reflect.TypeOF和reflect.ValueOf这两个方法分别获取类型和值,值得一提的是,如果是想要修改指针对应的值,应该使用Elem()这个方法。
例如当前需要实现一个需求,读取一个ini文件的内容,并且将他赋值给一个结构体:
函数签名为:
loadIni(v interface{})
假设我们要使用的结构体为:
type MysqlConfig struct { Address string `ini:"address"` Port int `ini:"port"` Username string `ini:"username"` Password string `ini:"password"` }
当前目录下面有一个ini文件,mysql.ini,里面的内容如下:
[mysql] address=127.0.0.1 port=3306 username=gyy password=cyl
那么loadIni()这个方法的实现过程如下:
func loadIni(v interface{}) { m1 := make(map[string]string) iniFile, err := os.Open("./mysql.ini") if err != nil { fmt.Printf("oprn file failed,err:%v\n", err) return } reader := bufio.NewReader(iniFile) // 读取文件 for { // 每次读取内容读取到\n(包含了\n) line, err := reader.ReadString('\n') if err != nil { // 读取内容出错时,如果这个错误不是文件结束符,就返回 if err != io.EOF { fmt.Printf("oprn file failed,err:%v\n", err) return } } // 判断当前读取的内容中是否包含= if strings.Contains(line, "=") { // 按照=切割,前者为字段名称,后者为字段值 dealString := strings.Split(line, "=") name := dealString[0] value := dealString[1] // 在windows文件中,每一行内容结束的地方是\r\n,所以在获取值的时候,需要将\r\n去掉 if strings.Contains(value, "\r") && strings.Contains(value, "\n") { value = string(strings.TrimSuffix(value, "\n")[:len(value)-2]) } else if strings.Contains(value, "\n") { value = string(strings.TrimSuffix(value, "\n")[:len(value)-1]) } m1[name] = value } if err == io.EOF { break } } // 使用reflect包实现反射功能 // t获取类型信息 // s获取值信息 t := reflect.TypeOf(v) s := reflect.ValueOf(v) // 由于修改的是指针,所以此处使用了Elem()方法 // 函数传参使用的是值传递,所以为了让传入变量在函数内部修改后在外部生效,需要传递指针 // t.Elem().NumField()可以获取结构体字段数量 for i := 0; i < t.Elem().NumField(); i++ { // 获取该字段的标志信息Tag key := t.Elem().Field(i).Tag.Get("ini") // 获取该字段的类型信息,并且将他转化为string类型 fieldType := fmt.Sprintf("%s", t.Elem().Field(i).Type) switch fieldType { // 根据该字段的类型,写入相应的数据 case "int": // 现将string转化为int64,然后使用s向该字段的值写入内容 intValue, _ := strconv.ParseInt(m1[key], 10, 64) s.Elem().Field(i).SetInt(intValue) case "float64": float64Value, _ := strconv.ParseFloat(m1[key], 64) s.Elem().Field(i).SetFloat(float64Value) default: s.Elem().Field(i).SetString(m1[key]) } } return } func main() { var mc MysqlConfig loadIni(&mc) fmt.Printf("%v\n", mc.Address) fmt.Printf("%v\n", mc.Port) fmt.Printf("%v\n", mc.Username) fmt.Printf("%v\n", mc.Password) }