Golang——reflect(反射)

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

        一. 变量的内在机制

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

        二. 反射的使用

  • 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加上所有引用文件。

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unity数据持久化2——XML

简介&#xff1a; 基础知识 XML文件格式 XML基本语法 XML属性 练习&#xff1a; C#读取存储XML XML文件存放位置 读取XML文件 练习&#xff1a; 存储修改XML文件 练习&#xff1a; 总结 实践小项目 必备知识点 必备知识点——C#中XML序列化 必备知识点——C#中XML反序列化 必备…

奇偶数递增递减-第13届蓝桥杯选拔赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第70讲。 奇偶数递增递减&a…

SSE 与 SASE哪个云原生安全框架更加适合

近年来&#xff0c;随着云计算和网络技术的不断发展&#xff0c;出现了一种新的网络安全解决方案——SASE&#xff08;安全访问服务边缘&#xff09;。SASE是一种将网络和安全功能融合到单个基于云的服务中的框架&#xff0c;旨在提供更加安全、高效和便捷的网络访问体验。SASE…

MySQL之Schema与数据类型优化(三)

Schema与数据类型优化 BLOB和TEXT类型 BLOB和TEXT都是为存储很大的数据而设计的字符串数据类型&#xff0c;分别采用二进制和字符方式存储。 实际上它们分别属于两组不同的数据类型家族:字符类型是TINYTEXT&#xff0c;SMALLTEXT,TEXT&#xff0c;MEDIUMTEXT&#xff0c;LONG…

实现本地访问云主机,以及在云主机搭建FTP站点

前言 云计算是一种基于互联网的计算模式&#xff0c;通过网络提供按需访问的计算资源和服务。核心概念是把计算能力视作一种公共资源&#xff0c;用户可以根据自身需求动态分配和管理这些资源。 云主机 ECS (Elastic Compute Server)是一种按需获取的云端服务器&#xff0c;提…

【MySQL】库的操作和表的操作

库的操作和表的操作 一、库的操作1、创建数据库(create)2、字符集和校验规则&#xff08;1&#xff09;查看系统默认字符集以及校验规则&#xff08;2&#xff09;查看数据库支持的字符集&#xff08;3&#xff09;查看数据库支持的字符集校验规则&#xff08;4&#xff09;校验…

【stm32/CubeMX、HAL库】嵌入式实验五:定时器(2)|PWM输出

参考&#xff1a; 【【正点原子】手把手教你学STM32CubeIDE开发】 https://www.bilibili.com/video/BV1Wp42127Cx/?p13&share_sourcecopy_web&vd_source9332b8fc5ea8d349a54c3989f6189fd3 《嵌入式系统基础与实践》刘黎明等编著&#xff0c;第九章定时器&#xff0c…

【Linux】-Zookeeper安装部署[17]

简介 apache ZooKeeper是一个分布式的&#xff0c;开放源码的分布式应用程序协调服务&#xff0c;是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件&#xff0c;提供的功能包括&#xff1a;配置维护、域名服务、分布式同步、组服务等。 除了为Hadoop和H…

五步定位性能瓶颈

一、着手测试前的准备&#xff1a;优化数据流向与系统架构分析 在进行性能测试或系统优化之前&#xff0c;明确数据流向和系统架构的细节是至关重要的步骤。这不仅能够帮助识别潜在的瓶颈&#xff0c;还能确保测试用例设计的全面性与针对性。以下是关键步骤和方法&#xff1a;…

17.3zabbix主动模式和被动模式

主动与被动区别 默认是被动: 被动模式,100个监控,需要100个回合&#xff1b;&#xff08;zabbix-server依次去找每一个agent节点取值&#xff0c;效率低&#xff09; 主动模式,100个监控,需要1个回合&#xff1b;&#xff08;agent节点将自己的信息整理好主动去找zabbix-serve…

QT调用Tinyxml2库解析XML结构文件

在学习SVG结构的时候&#xff0c;发现SVG结构可以通过以XML文件直接解析&#xff0c;所以就去了解了Tinyxml2库的使用&#xff0c;相关教程也比较多。 个人感觉Tinyxml2库比官方的XML解析库更好用&#xff0c;这里做个技术总结&#xff0c;记录Tinyxml2库解析XML文件结构的简单…

计组期末必考大题

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

分割训练日志的可视化

这一部分主要是将mmdetection训练得到的json文件可视化&#xff0c;代码主要源于github&#xff0c;具体哪一个忘记了&#xff08;readme里面没有原址…&#xff09;是专门做的mmdetection 结果可视化的&#xff0c;非常强&#xff01;&#xff01;。使用时如果出现keyerror的话…

Servlet的request对象

request对象的继承关系 1.HttpServletRequest接口继承了ServletRequest接口&#xff0c;对其父接口进行了扩展&#xff0c;可以处理满足所有http协议的请求 2.HttpServletRequest和ServletRequest都是接口&#xff0c;不能创建对象&#xff0c;因此在tomcat底层定义实现类并创…

文本三剑客之 sed 编辑器

一.sed 概述 1.sed 介绍 sed是一种流编辑器&#xff0c;流编辑器会在编辑器处理数据之前基于预先提供的一组规则来编辑数据流。 sed编辑器可以根据命令来处理数据流中的数据&#xff0c;这些命令要么从命令行中输入&#xff0c;要么存储在一个 命令文本文件中。 2.sed 的工…

基于python实现搜索的目标站点内容监测系统

基于python实现搜索的目标站点内容监测系统 开发语言:Python 数据库&#xff1a;MySQL所用到的知识&#xff1a;Django框架工具&#xff1a;pycharm、Navicat、Maven 系统功能实现 登录页面 后台的登录一般是为了管理员的管理方便进行一个用户权限的验证。也是为管理员提供的唯…

【贪心算法题目】

1. 柠檬水找零 这一个题目是一个比较简单的模拟算法&#xff0c;只需要根据手里的钱进行找零即可&#xff0c;对于贪心的这一点&#xff0c;主要是在20元钱找零的情况下&#xff0c;此时会出现两种情况&#xff1a;10 5 的组合 和 5 5 5 的组合&#xff0c;根据找零的特点&a…

DVWA代码审计--SQL注入

NO.1 Low 首先来看下代码 <?php if( isset( $_REQUEST[ Submit ] ) ) { // Get input $id $_REQUEST[ id ]; // Check database $query "SELECT first_name, last_name FROM users WHERE user_id $id;"; $result mysql_query( $query ) or die( <pre>…

vue中数据已经改变了,但是table里面内容没更新渲染!

解决方案&#xff1a; 给table或者el-table标签上添加一个动态key值&#xff0c;只要数据发生改变&#xff0c;key值变动一下即可 标签上&#xff1a; :key“timeStamp” 初始data&#xff1a;timeStamp:0, 更新数据&#xff1a;this.timeStamp 这样每次更新数据&#xff…

微信小程序---小程序文档配置(2)

一、小程序文档配置 1、小程序的目录结构 1.1、目录结构 小程序包含一个描述整体程序的 app 和多个描述各自页面的 page 一个小程序主体部分由三个文件组成&#xff0c;必须放在项目的根目录 比如当前我们的《第一个小程序》项目根目录下就存在这三个文件&#xff1a; 1…