Go语言反射

在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)
}

  

上一篇:由一个“bug”到鲜为人知的jQuery.cssHooks


下一篇:【第八周】编程——线性表