GoLang补充提升(五)

GoLang补充提升(五)

1.反射

TypeOf

(1)基本反射
func main() {
	var a int64 = 100
	var b float64 = 3.1415926
	reflectType(a)
	reflectType(b)
}

func reflectType(a any) {
	t := reflect.TypeOf(a)
	fmt.Printf("type:%v\n", t)
}
(2)type name和type kind

在反射中关于类型还划分为两种:类型(Type)和种类(Kind)。因为在Go语言中我们可以使用type关键字构造很多自定义类型,而种类(Kind)就是指底层的类型,但在反射中,当需要区分指针、结构体等大品种的类型时,就会用到种类(Kind)

  • Go语言的反射中像数组、切片、Map、指针等类型的变量,它们的.Name()都是返回空。
type myInt int64

func main() {
	var a *float64
	var b myInt
	var c rune
	reflectType(a) //Name:,Kind:ptr
	reflectType(b) //Name:myInt,Kind:int64
	reflectType(c) //Name:int32,Kind:int32

	type person struct {
		name string
		age  int
	}
	p := person{
		name: "zzs",
		age:  18,
	}
	reflectType(p) //Name:person,Kind:struct
}

func reflectType(a any) {
	t := reflect.TypeOf(a)
	fmt.Printf("Name:%v,Kind:%v\n", t.Name(), t.Kind())
}

ValueOf

(1)通过反射获取值
func main() {
	var a int64 = 100
	var b float64 = 3.14
	reflectValue(a)
	reflectValue(b)
}

func reflectValue(a any) {
	v := reflect.ValueOf(a)
	k := v.Kind()
	switch k {
	case reflect.Int64:
		fmt.Println("Int64:", v.Int())
	case reflect.Float64:
		fmt.Println("Float64:", v.Float())
	}
}
(2)通过反射设置值
  • 想要在函数中通过反射修改变量的值,需要注意函数参数传递的是值拷贝,必须传递变量地址才能修改变量值。而反射中使用专有的Elem()方法来获取指针对应的值。
func main() {
	var a float64 = 3.14
	reflectSetValue2(&a)
	fmt.Println(a)
}

func reflectSetValue1(a any) {
	v := reflect.ValueOf(a)
	if v.Kind() == reflect.Float64 {
		v.SetFloat(9.9999) //不能修改值,因为函数调用是值的拷贝,只有传递指针时才能修改对应的值
	}
}

func reflectSetValue2(a any) {
	v := reflect.ValueOf(a)
	if v.Elem().Kind() == reflect.Float64 {
		v.Elem().SetFloat(9.9999)
	}
}
(3)通过反射获取结构体标签和字段
type student struct {
	name string `json:"Name" zzs:"emmm"`
	age  int    `json:"Age" zzs:"hahaha"`
}

func main() {
	s := student{
		name: "zzs",
		age:  18,
	}
	t := reflect.TypeOf(s)
	//遍历所有字段
	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)
		fmt.Println(field.Name, field.Type, field.Index, field.Tag.Get("zzs"))
	}
	//通过名称获取字段
	if ageField, ok := t.FieldByName("age"); ok {
		fmt.Println(ageField.Name, ageField.Type, ageField.Index, ageField.Tag.Get("zzs"))
	}
}
(4)通过反射获取调用结构体方法
type myStudent struct {
	name string
	age  int
}

func (s *myStudent) Study(str string) {
	fmt.Println(str, "学习")
}

func (s *myStudent) Sleep(str string) {
	fmt.Println(str, "睡觉")
}

func main() {
	s := myStudent{
		name: "zzs",
		age:  18,
	}
	t := reflect.TypeOf(&s)  //要是值接收者就传递值,要是指针接收者就传递指针
	v := reflect.ValueOf(&s) //要是值接收者就传递值,要是指针接收者就传递指针
	fmt.Println(t.NumMethod())
	for i := 0; i < t.NumMethod(); i++ {
		fmt.Println(t.Method(i).Name)
		fmt.Println(t.Method(i).Type)
		a := []reflect.Value{reflect.ValueOf("zzs")}
		v.Method(i).Call(a)
	}
}

2.反射实现ini配置文件解析

  • ini配置文件
# mysqlConfig
[mysql]
address=10.20.30.40
port=3306
username=root
password=root1


; redisConfig
[redis]
host=11.22.33.44
port=4367
password=root123
database=666

解析实现

  • 先定义相关的结构体对象,添加标签方便反射进行使用
  • 读取ini文件信息,对文件进行解析
  • 通过反射对值进行设置
type mySqlConfig struct {
	Address  string `ini:"address"`
	Port     int    `ini:"port"`
	Password string `ini:"password"`
	UserName string `ini:"username"`
}
type redisConfig struct {
	Host     string `ini:"host"`
	Port     int    `ini:"port"`
	DataBase int    `ini:"database"`
	Password string `ini:"password"`
}
type myConfig struct {
	MysqlCfg mySqlConfig `ini:"mysql"`
	RedisCfg redisConfig `ini:"redis"`
}
func main() {
	var config myConfig
	err := loadIni("34/conf.ini", &config)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(config)
}

func loadIni(fileName string, data any) error {
	t := reflect.TypeOf(data)
	if t.Kind() != reflect.Ptr {
		return errors.New("error Data Must Prt")
	}
	if t.Elem().Kind() != reflect.Struct {
		return errors.New("error Data Must Struct")
	}
	buffer, err := ioutil.ReadFile(fileName)
	if err != nil {
		fmt.Println(err)
		return err
	}
	str := string(buffer)
	arr := strings.Split(str, "\r\n")
	setInfo(arr, data)
	return nil
}

func setInfo(arr []string, data any) error {
	t := reflect.TypeOf(data)
	v := reflect.ValueOf(data)
	var val reflect.Value
	for _, lineStr := range arr {
		if lineStr == "" || strings.HasPrefix(lineStr, "#") || strings.HasPrefix(lineStr, ";") {
			continue
		}
		if strings.HasPrefix(lineStr, "[") {
			//获取config名称
			configName := lineStr[1:strings.LastIndex(lineStr, "]")]
			for i := 0; i < t.Elem().NumField(); i++ {
				field := t.Elem().Field(i)
				if field.Tag.Get("ini") == configName {
					val = v.Elem().Field(i)
					break
				}
			}
			continue
		}
		item := strings.Split(lineStr, "=")
		if len(item) != 2 {
			return errors.New("error SetInfo Item Len Error")
		}
		for i := 0; i < val.NumField(); i++ {
			if val.Type().Field(i).Tag.Get("ini") == item[0] {
				if val.Field(i).Kind() == reflect.Int {
					num, err := strconv.Atoi(item[1])
					if err != nil {
						fmt.Println(err)
						break
					}
					val.Field(i).SetInt(int64(num))
				} else {
					val.Field(i).SetString(item[1])
				}
				break
			}
		}
	}
	return nil
}

3.反射说明

反射是一个强大并富有表现力的工具,能让我们写出更灵活的代码。但是反射不应该被滥用,原因有以下三个。

  • 基于反射的代码是极其脆弱的,反射中的类型错误会在真正运行的时候才会引发panic,那很可能是在代码写完的很长时间之后。
  • 大量使用反射的代码通常难以理解。
  • 反射的性能低下,基于反射实现的代码通常比正常代码运行速度慢一到两个数量级。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值