(四十五)golang--反射
时间:2022-07-23
本文章向大家介绍(四十五)golang--反射,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
反射基本介绍:
(1)反射可以在运行时动态获取变量的各种信息,比如变量的类型、类别;
(2)如果是结构体变量,还可以获取结构体本身的信息(包括结构体字段、方法);
(3)通过反射,可以修改变量的值,可以调用关联的方法;
(4)使用反射,需要Import "reflect:"
变量--空接口--relect.Value之间的转换关系:
int变量与反射变量之间的转换:
package main
import (
"fmt"
"reflect"
)
func reflectTest(b interface{}) {
rTyp := reflect.TypeOf(b)
fmt.Printf("rTyp=%v,type=%Tn", rTyp, rTyp)
rVal := reflect.ValueOf(b)
fmt.Printf("rVal=%v,type=%Tn", rVal, rVal)
iVal := rVal.Interface()
num2 := iVal.(int)
fmt.Printf("num2=%v,type=%Tn", num2, num2)
}
func main() {
var num int = 100
reflectTest(num)
}
结构体变量与反射变量之间的转换:
type student struct {
name string
age int
}
func reflectTest02(b interface{}) {
rTyp := reflect.TypeOf(b)
fmt.Printf("rTyp=%v,type=%Tn", rTyp, rTyp)
rVal := reflect.ValueOf(b)
fmt.Printf("rVal=%v,type=%Tn", rVal, rVal)
iVal := rVal.Interface()
stu2 := iVal.(student)
fmt.Printf("stu2=%v,type=%Tn", stu2, stu2)
fmt.Printf("name:%v,age:%v", stu2.name, stu2.age)
}
func main() {
stu := student{
name: "tom",
age: 20,
}
reflectTest02(stu)
}
反射注意事项和使用细节: (1)reflect.Vale.Kind,获取变量的类别,返回的是一个常量;
(2)Type是类型,Kind是类别,Type和Kind可能是相同的,也可能是不同的;
比如:var num int=10的type是Int,Kind也是int
var stu Student, stu的rype是pkg1.Student,Kind是struct
(3)通过反射介意让变量在interface{}和reflect.Value之间相互转换;
(4)使用反射的方式来获取变量的值,要求数据类型匹配;
(5)通过反射来修改变量,注意当使用SetXxx方法来设置需要通过对指针类型来完成,这样才能改变传入变量的值,同时需要使用到reflect.Value.Elem()方法;即rVal.Elem().SetInt(20)
具体实践;
使用反射来遍历结构体的字段,并调用结构体的方法,并获取结构体标签的值。
package main
import (
"fmt"
"reflect"
)
type monster struct {
Name string `json:"name"`
Age int `json:"monster_age"`
Score float32
Sex string
}
func (m monster) Print() {
fmt.Println("----strat----")
fmt.Println()
fmt.Println("----end----")
}
func (m monster) GetSum(n1, n2 int) int {
return n1 + n2
}
func (m monster) Set(name string, age int, score float32, sex string) {
m.Name = name
m.Age = age
m.Score = score
m.Sex = sex
}
func testStruct(b interface{}) {
//获得类型
rtyp := reflect.TypeOf(b)
//获得值
rval := reflect.ValueOf(b)
//获得类别
kd := rval.Kind()
//如果kd不是指针类型且rval.Elem()不是结构体类别
if kd != reflect.Ptr && rval.Elem().Kind() == reflect.Struct {
fmt.Println("expect struct")
return
}
//获取结构体有几个字段
num := rval.Elem().NumField()
fmt.Printf("struct has %d fieldsn", num)
//遍历每个字段
for i := 0; i < num; i++ {
//输出该字段的值
fmt.Printf("field %d:值为=%vn", i, rval.Elem().Field(i))
//获取该字段的json标签
tagval := rtyp.Elem().Field(i).Tag.Get("json")
//如果标签不为空,则打印出标签
if tagval != "" {
fmt.Printf("field %d:tag为=%vn", i, tagval)
}
}
rval.Elem().Field(0).SetString("孙悟空")
//获取结构体有几个方法
numOfMethod := rval.Elem().NumMethod()
fmt.Printf("struct has %d methodsn", numOfMethod)
//获取第二个方法,函数的顺序是按首字母的ACSII进行排序的,所以这里第二个方法是Print
rval.Elem().Method(1).Call(nil)
//定义一个reflect.Value切片
var params []reflect.Value
//添加两个整型
params = append(params, reflect.ValueOf(10))
params = append(params, reflect.ValueOf(40))
//调用第一个方法,并传入参数(注意参数类型是reflect.Value切片)
res := rval.Elem().Method(0).Call(params)
//注意res是一个切片,里面只有一个值
fmt.Println("res=", res[0].Int())
}
func main() {
var a monster = monster{
Name: "tom",
Age: 400,
Score: 20.9,
}
testStruct(&a)
fmt.Println(a)
}
要在反射中修改结构体的字段的值,必须传入的是地址,并且在反射中使用.Elem().SetXxx()
具体实践2:通过反射完成适配器
func main() {
test1 := func(v1 int, v2 int) {
fmt.Printf("v1=%d,v2=%dn", v1, v2)
}
test2 := func(v1 int, v2 int, s string) {
fmt.Printf("v1=%d,v2=%d,s=%s", v1, v2, s)
}
bridge := func(call interface{}, args ...interface{}) {
//获取参数长度
n := len(args)
//定义参数切片
params := make([]reflect.Value, n)
//将参数存入到切片中
for i := 0; i < n; i++ {
params[i] = reflect.ValueOf(args[i])
}
// for i := 0; i < n; i++ {
// fmt.Println(args[i])
// }
//获取函数名
function := reflect.ValueOf(call)
//将参数传给函数,并调用函数
function.Call(params)
}
bridge(test1, 1, 2)
bridge(test2, 1, 2, "test2")
}
运行结果:
具体实践3:通过反射操作任意结构体类型
type user struct {
UserID string
Name string
}
func testReflectStruct() {
var (
model *user
sv reflect.Value
)
model = &user{}
sv = reflect.ValueOf(model)
//这里是指针类别
fmt.Println(sv.Kind().String())
sv = sv.Elem()
//这里是结构体类别
fmt.Println(sv.Kind().String())
sv.FieldByName("UserID").SetString("1")
sv.FieldByName("Name").SetString("tom")
fmt.Println(model)
}
- FreeMarker模板开发指南知识点梳理
- WCF技术剖析之二十: 服务在WCF体系中是如何被描述的?
- WCF如何克服HTTP传输协议的局限提供对不同消息传输模式的实现
- H5手游大事件:腾讯上线“微信小游戏”!支持群分享与内购
- 我所理解的Remoting(2):远程对象生命周期的管理[上篇]
- 谈谈分布式事务(Distributed Transaction)[共5篇]
- SQLXML初体验:用XML代替T-SQL来操作数据库
- 自己动手写可视化软件(代码已开源)
- 探秘Tomcat——连接篇
- 微信小游戏正式上线,H5游戏迎新机遇
- WCF技术剖析之三十:一个很有用的WCF调用编程技巧[上篇]
- WCF技术剖析之十八:消息契约(Message Contract)和基于消息契约的序列化
- WCF技术剖析之三十:一个很有用的WCF调用编程技巧[上篇]
- 成为更好程序员的8种途径
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Python全栈(二)数据结构和算法之1.算法和数据结构引入
- Android开发(第一行代码 第二版) 常见异常和解决办法(基于Android Studio)(一)
- Python SQLite 基本操作和经验技巧(一)
- Python字典及基本操作(超级详细)
- Python matplotlab库使用方法及注意事项
- 超性感的React Hooks(三):useState
- 数据库编程 MySQL 技巧与经验
- Python 编程开发 实用经验和技巧
- 超性感的React Hooks(四):useEffect
- Python 库的安装及使用 常见错误异常及解决办法
- Kafka三种可视化监控管理工具Monitor/Manager/Eagle
- 超性感的React Hooks(五):自定义hooks
- 三、变量对象
- Java编程 经验技巧汇总
- 关于IP地址的一些相关知识点