Golang——reflect(反射)

news2024/12/24 11:27:05

        反射是指在程序运行期间对程序本身进行访问和修改的能力。

        一. 变量的内在机制

  • 变量包含类型信息和值信息
  • 类型信息:是静态的元信息,是预先定义好的
  • 值信息:是程序运行过程中动态改变的

        二. 反射的使用

  • reflect包封装了反射相关的方法
  • 获取类型信息:reflect.TypeOf,是静态的
  • 获取值信息:reflect.ValueOf,是动态的

        三. 空接口和反射

  • 反射可以在运行时动态获取程序的各种详细信息
  • 反射获取interface类型信息

  • 反射获取interface值信息

  • 反射修改值信息

 四. 结构体与反射

  • 查看类型,字段和方法

        注意:结构体方法名和属性名必须大写,否则获取不到方法或报错。

package main

import (
	"fmt"
	"reflect"
)

type User struct {
	Id   int
	Name string
	Age  int
}

func (u User) SayHello() {
	fmt.Println("hello")
}

func Poni(x interface{}) {
	//获取类型
	t := reflect.TypeOf(x)
	fmt.Println(t)
	fmt.Println("字符串类型:", t.Name())

	//获取值
	v := reflect.ValueOf(x)
	fmt.Println("值:", v)

	//获取所有属性
	//通过类型获取结构体字段个数
	for i := 0; i < t.NumField(); i++ {
		//通过类型取每一个字段,包括类型和字段变量名
		f := t.Field(i)
		fmt.Printf("%s, %v\n", f.Name, f.Type)

		//通过值获取字段的值信息
		//interface():获取字段对应的值
		val := v.Field(i).Interface()
		fmt.Println("val : ", val)
	}

	fmt.Println("---------方法------------")
	//通过类型获取方法数
	for i := 0; i < t.NumMethod(); i++ {
		m := t.Method(i)
		fmt.Println(m.Name)
		fmt.Println(m.Type)

	}
}

func main() {
	u := User{
		1,
		"wy",
		20,
	}
	Poni(u)
}

  •  查看匿名字段

  • 修改结构体的值

  • 调用结构体方法

  • 获取字段的tag

        标签保存在类型的reflect.StructField结构体里。

 五.反射练习

  • 任务:解析如下配置文件
    • 序列化:将结构体序列化为配置文件数据并保存到硬盘
    • 反序列化:将配置文件内容反序列化到程序的结构体
  • 配置文件有servermysql相关配置
#this is comment
;this a comment
;[]表示一个section
[server]
ip = 10.238.2.2
port = 8080

[mysql]
username = root
passwd = admin
database = test
host = 192.168.10.10
port = 8000
timeout = 1.2
  • 思路

        反序列化:将配置文件读上来,根据根据结构体的tag,赋值到对应结构体属性字段。利用反射(reflect)获取到tag和赋值

        序列化:利用反射(reflect)获取到对应字段tag和值,编写为对应形式,再写道文件中。

main.go:主函数

package main

import (
	"io/ioutil"
	"log"
)

// 反序列化
func parseFile(infilename string, res interface{}) {
	data, err := ioutil.ReadFile(infilename)
	if err != nil {
		log.Println("read file fail ", err)
		return
	}
	//fmt.Println(string(data))

	err = Unmarshal(data, res)
	if err != nil {
		log.Println("Unmarshal fail ", err)
		return
	}
}

// 序列化
func parseDataToFile(filename string, res interface{}) {
	data, err := Marshal(res)
	if err != nil {
		log.Println("marshal fail ", err)
		return
	}

	err = ioutil.WriteFile(filename, data, 0666)
	if err != nil {
		log.Println("write file fail  ", err)
		return
	}
}

func main() {
	var cfg Cofig
	parseFile("./config.ini", &cfg)
	log.Printf("反序列化成功%#v", cfg)

	parseDataToFile("./my.ini", &cfg)
}

model.go:结构体

package main

//定义结构体

type ServerConfig struct {
	IP   string `ini:"ip"`
	Port int    `ini:"port"`
}

type MysqlConfig struct {
	UserName string  `ini:"username"`
	Passwd   string  `ini:"passwd"`
	Database string  `ini:"database"`
	Host     string  `ini:"host"`
	Port     int     `ini:"port"`
	Timeout  float64 `ini:"timeout"`
}

type Cofig struct {
	ServerConf ServerConfig `ini:"server"`
	MysqlConf  MysqlConfig  `ini:"mysql"`
}

init_config.go:序列化和反序列化细节实现。

package main

import (
	"errors"
	"fmt"
	"log"
	"reflect"
	"strconv"
	"strings"
)

func myLable(str string, res_type reflect.Type) (fieldname string, err error) {
	//去掉前后[]
	lable_str := str[1 : len(str)-1]
	//循环查找tag
	for i := 0; i < res_type.NumField(); i++ {
		field := res_type.Field(i)
		lable_name := field.Tag.Get("ini")
		if lable_name == lable_str {
			//属性名,不是tag名
			fieldname = field.Name
			return
		}
	}
	return
}

func myField(fieldName string, line string, res interface{}) (err error) {
	//获取key和value
	key_str := strings.TrimSpace(line[0:strings.Index(line, "=")])
	val_str := strings.TrimSpace(line[strings.Index(line, "=")+1:])

	//找到res中属性名为fieldName的field
	res_val := reflect.ValueOf(res).Elem()
	//fmt.Println(fieldName)
	lable_name := res_val.FieldByName(fieldName)

	//找到tag为key_str的属性
	lable_type := lable_name.Type()
	if lable_type.Kind() != reflect.Struct {
		return errors.New(fieldName + " is not a struct")
	}
	var key_name string
	for i := 0; i < lable_type.NumField(); i++ {
		field := lable_type.Field(i)
		tag_str := field.Tag.Get("ini")
		if tag_str == key_str {
			key_name = field.Name
			break
		}
	}

	//设置值
	val_field := lable_name.FieldByName(key_name)
	switch val_field.Type().Kind() {
	case reflect.String:
		val_field.SetString(val_str)
	case reflect.Int:
		tmp, err := strconv.ParseInt(val_str, 10, 32)
		if err != nil {
			log.Println(err)
			return err
		}
		val_field.SetInt(tmp)
	case reflect.Float64:
		tmp, err := strconv.ParseFloat(val_str, 64)
		if err != nil {
			log.Println(err)
			return err
		}
		val_field.SetFloat(tmp)
	}

	return
}

// 解析配置,序列化和反序列化
func Unmarshal(data []byte, res interface{}) (err error) {
	//得到res类型
	res_type := reflect.TypeOf(res)
	//是输出型参数,需要是指针类型
	if res_type.Kind() != reflect.Ptr {
		return errors.New("args type error not pointer")
	}
	//获得实际类型
	real_res_type := res_type.Elem()
	if real_res_type.Kind() != reflect.Struct {
		return errors.New("args real type error not struct")
	}

	//以'\n'分隔每一行
	var myFieldName string
	datalines := strings.Split(string(data), "\n")
	for _, line := range datalines {
		//去掉空格
		line = strings.TrimSpace(line)

		//处理有注释的情况
		if len(line) == 0 || line[0] == '#' || line[0] == ';' {
			continue
		}

		//按方括号判断大标签的情况
		if line[0] == '[' {
			//赋值大标签
			myFieldName, err = myLable(line, real_res_type)
			if err != nil {
				log.Println("handle lable fail ", err)
				return
			}
			continue
		}

		err = myField(myFieldName, line, res)
		if err != nil {
			continue
		}
	}

	return
}

func Marshal(res interface{}) (data []byte, err error) {
	res_type := reflect.TypeOf(res).Elem()
	res_val := reflect.ValueOf(res).Elem()
	if res_type.Kind() != reflect.Struct {
		return nil, errors.New("type error")
	}

	var lines []string
	for i := 0; i < res_type.NumField(); i++ {
		//取类型字段
		type_field := res_type.Field(i)
		//取值
		val_field := res_val.Field(i)
		//获得标签
		tag_str := type_field.Tag.Get("ini")
		if len(tag_str) == 0 {
			continue
		}
		//标签
		line := fmt.Sprintf("[%s]\n", tag_str)
		fmt.Print(line)
		lines = append(lines, line)

		if type_field.Type.Kind() != reflect.Struct {
			continue
		}
		//遍历成员的属性
		for j := 0; j < type_field.Type.NumField(); j++ {
			sub_type_field := type_field.Type.Field(j)
			//取标签
			sub_tag := sub_type_field.Tag.Get("ini")
			if len(sub_tag) == 0 {
				continue
			}
			//取值
			sub_val_field := val_field.Field(j)
			//Interface为真正的值
			line = fmt.Sprintf("%s = %v\n", sub_tag, sub_val_field.Interface())
			fmt.Print(line)
			lines = append(lines, line)
		}
		lines = append(lines, "\n")
	}

	for _, str := range lines {
		d := []byte(str)
		data = append(data, d...) //...将切片打散
	}
	return
}

 写入到文件演示:

        问题:

        我直接编译main.go出现报错:

        原因:

  • go run和go build命令都会编译文件,go run不仅编译还会直接执行文件。由于上面只输入了main.go文件,在main.go文件中引用其它文件的函数,结构或变量会出现找不到的情况。

        解决:

  • 由于go run必须输入文件,此时需要输入所有引用文件

  • go build可以不输入文件,默认自动查找引用文件并打开包。或者go build加上所有引用文件。

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1684759.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

冯喜运:5.23黄金市场风云变幻,黄金原油美盘趋势分析

【黄金消息面分析】&#xff1a;在经历了一段时期的强劲上涨后&#xff0c;黄金市场似乎迎来了调整期。北京时间周四(5月23日)&#xff0c;国际黄金价格连续第三个交易日下跌&#xff0c;目前交投在2365美元附近&#xff0c;较周一触及的纪录高点2449.89美元已下跌约4%。这一跌…

知识分享|非凸问题求解方法及代码示例【分类迭代】【大M法】

主要内容 之前发布了非线性问题线性化的几种方法&#xff0c;如知识分享|分段函数线性化及matlab测试&#xff0c;学习园地 | yalmip实用操作-线性化&#xff0c;非线性优化 | 非线性问题matlabyalmip求解案例&#xff0c;但是在实际建模及编程过程中&#xff0c;会遇到各种…

vue+canvas实现逐字手写效果

在pc端进行逐字手写的功能。用户可以在一个 inputCanvas 上书写单个字&#xff0c;然后在特定时间后将这个字添加到 outputCanvas 上&#xff0c;形成一个逐字的手写效果。用户还可以保存整幅图像或者撤销上一个添加的字。 <template><div class"container"…

【B站 heima】小兔鲜Vue3 项目学习笔记

系列文章目录 Day 01 目录 系列文章目录前言Day011.项目使用相关技术栈2. 项目规模和亮点3. Vue2和Vue3实现一个小案例4. vue3的优势5. create-vue脚手架工具6. 熟悉我们的项目目录和文件7. 组合式API-setup选项8. 组合式API-reactive和ref函数9. 组合式API-computed计算属性…

动态IP与静态IP有什么区别?如何选择?

动态IP和静态IP都是指网络设备&#xff08;如计算机、服务器、路由器等&#xff09;在互联网上分配的IP地址的类型。 一、什么是动态IP&#xff0c;什么是静态IP&#xff1f; 1、什么是动态IP&#xff1f; 动态IP是指由Internet服务提供商&#xff08;ISP&#xff09;动态分配…

xrdp多用户多控制界面远程控制

1、无桌面安装桌面&#xff08;原本有ubuntu桌面的可以直接跳过这一步&#xff09; Gnome 与 xfce 相比&#xff0c;xfce 由于其轻巧&#xff0c;它可以安装在低端台式机上。Xfce 优雅的外观&#xff0c;增强了用户体验&#xff0c;它对用户非常友好&#xff0c;性能优于其他桌…

docker- 购建服务镜像并启动

文章目录 前言docker- 购建服务镜像并启动1. 前期准备2. 构建镜像3. 运行容器4. 验证 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0c;实…

扩散模型自动管道AutoPipeline

推荐&#xff1a;write_own_pipeline.ipynb - Colab (google.com) 为您的任务选择一个 AutoPipeline 首先选择一个检查点。例如&#xff0c;如果您对使用 runwayml/stable-diffusion-v1-5 检查点的文本到图像感兴趣&#xff0c;请使用 AutoPipelineForText2Image&#xff1a; f…

linux ping https是否连接

在Linux系统中&#xff0c;ping通常用于测试网络上另一台主机的可达性。它使用的是ICMP协议&#xff0c;这是一种设计用来处理网络通信问题的协议。HTTPS则是一种安全的网络传输协议&#xff0c;它使用SSL/TLS加密。 如果你想要测试到某个HTTPS服务器的连接&#xff0c;你可以使…

【MySQL精通之路】InnoDB(6)-磁盘结构(2)-索引

主博客&#xff1a; 【MySQL精通之路】InnoDB(6)-磁盘上的InnoDB结构-CSDN博客 上一篇&#xff1a; 下一篇&#xff1a; 【MySQL精通之路】磁盘上的InnoDB结构-表空间-CSDN博客 目录 1.聚集索引和二级索引 1.1 Innodb 如何建立聚集索引 1.2 聚集索引如何加快查询速度 1…

CustomTkinter:便捷美化Tkinter的UI界面(附模板)

CustomTkinter是一个基于Tkinter的Python用户界面库。 pip3 install customtkinter它提供了各种UI界面常见的小部件。这些小部件可以像正常的Tkinter小部件一样创建和使用&#xff0c;也可以与正常的Tkinter元素一起使用。 它的优势如下&#xff1a; CustomTkinter的小部件和…

四天学会JS高阶(学好vue的关键)——深入面向对象(理论+实战)(第三天)

***本章面试使用居多* 理论篇**一、编程思想 1.1 面向过程 JS 前端居多 按照步骤 性能高 适合跟硬件关系很紧密 没有面向对象易维护易复用易扩展 1.2 面向对象 java典型 按照功能&#xff0c;把事务分别成一个个对象&#xff0c;对象之间分工合作 比较灵活 适合多人合作的…

模拟笔试 - 卡码网周赛第十八期(23年科大讯飞提前批笔试真题)

第一题&#xff1a; 参考思路解析&#xff1a;&#xff08;遍历nums中的每个数字&#xff0c;得到不为0的数位即可。&#xff09; 1.导入Scanner类&#xff1a; import java.util.Scanner;&#xff1a;引入 Scanner 类&#xff0c;用于读取用户输入。 2.主方法&#xff1a; …

力扣1809 没有广告的剧集(postgresql)

需求 Table: Playback ----------------- | Column Name | Type | ----------------- | session_id | int | | customer_id | int | | start_time | int | | end_time | int | ----------------- 该表主键为&#xff1a;session_id &#xff08;剧集id&#xff09; customer_…

【C++算法】BFS解决FloodFill算法相关经典算法题

1.图像渲染 我们这道题可以使用深搜来解决&#xff0c;利用一个队列遍历到与该点相连的所有像素相同的点&#xff0c;然后将其修改成指定的像素即可&#xff0c;直接上思路&#xff1a; 直接上代码&#xff1a; class Solution {int dx[4] {0, 0, 1, -1};int dy[4] {1, -1, …

计组期末必考大题

一.寻址方式详解 1.直接寻址 指令地址码直接给到操作数所在的存储单元地址 2.间接寻址 A为操作数EA的地址 3.寄存寻址 4.寄存器间接寻址 5.变址寻址 6.基地址寻址 7.小结 二、指令周期详解 一、基本概念 指令周期:去除指令并执行指令所需要的时间指令周期:由若干个CPU周…

go语言的一些常见踩坑问题

开始之前&#xff0c;介绍一下​最近很火的开源技术&#xff0c;低代码。 作为一种软件开发技术逐渐进入了人们的视角里&#xff0c;它利用自身独特的优势占领市场一角——让使用者可以通过可视化的方式&#xff0c;以更少的编码&#xff0c;更快速地构建和交付应用软件&#…

docker 安装minio 服务 ssl 证书

minio 安装 ssl 证书 下载apache 证书 &#xff08;可以使用免费的证书&#xff09; 放在/opt/minio/conf/certs 下 (安装minio 时的 挂载目录 参考文章 docker 安装minio,详细图解 ) 拷贝进容器 /root/.minio docker cp /opt/minio/conf/certs/private.key minio:/root/.mi…

蜂窝物联四情监测:助力农业升级,科技赋能打造丰收新篇章!

农业四情指的是田间的虫情、作物的苗情、气候的灾情和土壤墒情。“四情”监测预警系统的组成包括管式土壤墒情监测站、虫情测报灯、气象站、农情监测摄像机&#xff0c;可实时监测基地状况,可以提高监测的效率和准确性&#xff0c;为农业生产提供及时、科学的数据支持&#xff…

知识图谱数据预处理笔记

知识图谱数据预处理笔记 0. 引言1. 笔记1-1. \的转义1-2. 特殊符号的清理1-3. 检查结尾是否正常1-4. 检查<>是否存在1-5. 两端空格的清理1-6. 检查object内容长时是否以<开始 0. 引言 最近学习知识图谱&#xff0c;发现数据有很多问题&#xff0c;这篇笔记记录遇到的…