go语言学习之reflect
2023-02-19 12:21:08 时间
本学习笔记全部以代码块的形式展示,具体的内容都包含在代码里:
package types
import (
"fmt"
"reflect"
)
// 1. reflect 是用程序检查其所拥有的结构,尤其是类型的一种能力;这是元编程的一种形式。
// 反射可以在运行时检查类型和变量,例如它的大小、方法,并且能动态的调用其中的方法。
// 2. reflect.TypeOf 和 reflect.ValueOf,返回被检查对象的类型和值。
// 3. 原理上,反射是通过检查一个接口的值,变量首先被转换成空接口。
// 4. 反射可以从接口值反射到对象,也可以从对象反射回接口值。
// 5. reflect.Type 和 reflect.Value 都有许多方法用于检查和操作自身
// 6. 利用reflect 可以更改对象值,反射中有些内容是需要用地址去改变它的状态的。
// 7. reflect 可以反射结构struct。
// 定义 NotKnownType
type NotKnownType struct {
a, b, c string
}
// 定义方法
func (n NotKnownType) GetStr() string {
return n.a + n.b + n.c
}
func (n NotKnownType) GetStr1(str string) string {
return n.a + n.b + n.c + str
}
// 定义 NotKnownType
type NotKnownType1 struct {
A, B, C string
}
func ReflectExample() {
var x int8 = 10
// t 是 reflect.Type 类型
t := reflect.TypeOf(x)
// v 是 reflect.Value 类型
v := reflect.ValueOf(x)
fmt.Println("t: ", t)
fmt.Println("v: ", v)
// 5
fmt.Println("reflect.Value.Type: ", v.Type())
// Kind 方法总是返回底层类型
fmt.Println("reflect.Type.Kind, reflect.Value.Kind: ", t.Kind(), v.Kind(), t.Kind() == reflect.Int8)
fmt.Println("reflect.Value.Int: ", v.Int())
// 6
// 使用 CanSet 判断是否可以更改值
fmt.Println("reflect.Value.CanSet: ", v.CanSet())
// 上述得到的是 false,因为 v 是通过 x 的拷贝创建的,改变 v 并不能改变 x,所以要使用 x的地址
v1 := reflect.ValueOf(&x)
fmt.Println("v1: ", v1, v1.Type())
// 这时还是不能更改值
fmt.Println("reflect.Value.CanSet: ", v1.CanSet())
// 还需要使用 Elem()
v1 = v1.Elem()
fmt.Println("reflect.Value.CanSet: ", v1.CanSet())
// 更改值
v1.SetInt(20)
fmt.Println("reflect.Value.SetInt: ", v1, x)
// 7
// 假设不知道 n 具体的struct类型
var n interface{} = NotKnownType{"a", "b", "c"}
t2 := reflect.TypeOf(n)
v2 := reflect.ValueOf(n)
fmt.Println("t2: ", t2)
fmt.Println("v3: ", v2)
// 可使用 NumField 返回结构体的字段数量,使用 Field 得到该字段的值
for i := 0; i < v2.NumField(); i++ {
fmt.Printf("reflect.Value.Field %d: %v\n", i, v2.Field(i))
}
// 使用 Method(n).Call(nil) 调用结构体中的方法
m1 := v2.Method(0).Call(nil)
param := []reflect.Value{reflect.ValueOf("d")}
m2 := v2.Method(1).Call(param)
fmt.Printf("m1: %v, m2: %v\n", m1, m2)
// 当更改struct字段时,字段必须可导出(即首字母要大写),然后结合上述更改的规则
n1 := NotKnownType1{"A", "B", "C"}
v3 := reflect.ValueOf(&n1)
v3 = v3.Elem()
fmt.Println("reflect.Value.CanSet: ", v3.CanSet())
for i := 0; i < v3.NumField(); i++ {
origin := v3.Field(i)
v3.Field(i).SetString(origin.String() + origin.String())
}
fmt.Println("n1: ", n1)
}
相关文章
- ES6躬行记(8)——数字
- ES6躬行记(7)——代码模块化
- ES6躬行记(6)——Symbol
- ES6躬行记(5)——对象字面量的扩展
- ES6躬行记(4)——模板字面量
- ES6躬行记(3)——解构
- ES6躬行记(2)——扩展运算符和剩余参数
- ES6躬行记(1)——let和const
- 【翻译】停止学习框架(Stop Learning Frameworks)
- FC游戏 《三国志2-霸王的大陆》攻略
- 我喜欢这样阅读一本书
- 防御式编程和开发者测试
- 120分钟React快速扫盲教程
- 忆2011年的秋天:一个人的项目
- 横看成岭侧成峰,远近高低各不同——从面试官的角度谈面试
- 使用Scratch进行少儿编程
- 面试题参考
- 初识少儿编程
- 一份来自于全球的前端面试题清单,看看老外喜欢考哪些题(部分有答案)
- 程序员也需要的软技能,既为了生存,也为了早日财务自由