反射
方法和类型的反射
变量的最基本信息就是类型和值:反射包的Type用来表示一个Go语言类型,反射包的Value为Go值提供了反射接口。
代码如下:
package main
import (
"fmt"
"reflect"
)
func main() {
var x float64 = 3.4
fmt.Println("type:", reflect.TypeOf(x))
v := reflect.ValueOf(x)
fmt.Println("value:", v)
fmt.Println("type:", v.Type())
fmt.Println("kind:", v.Kind())
fmt.Println("value:", v.Float())
fmt.Println(v.Interface())
fmt.Printf("value is %5.2e\n", v.Interface())
y := v.Interface().(float64)
fmt.Println(y)
}
运行结果如下:
type: float64
value: 3.4 //书上说的是value: <float64 Value>
type: float64
kind: float64
value: 3.4
3.4
value is 3.40e+00
3.4
通过反射修改设置值
代码如下:
package main
import (
"fmt"
"reflect"
)
func main() {
var x float64 = 3.1415
v := reflect.ValueOf(x)
fmt.Println("v: ", v.CanSet())
v = reflect.ValueOf(&x)
fmt.Println("v的类型: ", v.Type())
fmt.Println("v: ", v.CanSet())
v = v.Elem()
fmt.Println("v的Elem是:", v)
fmt.Println("v: ", v.CanSet())
v.SetFloat(3.1415)
fmt.Println(v.Interface())
fmt.Println(v)
}
运行结果如下:
v: false
v的类型: *float64
v: false
v的Elem是: 3.1415 //书上说:v的Elem是:<float64 value>
v: true
3.1415
3.1415 //书上说:<float64 Value>
反射结构
代码如下:
package main
import (
"fmt"
"reflect"
)
type NotknownType struct {
s1, s2, s3 string
}
func (n NotknownType) String() string {
return n.s1 + " - " + n.s2 + " - " + n.s3
}
var secret interface{} = NotknownType{"Google", "Go", "Code"}
func main() {
value := reflect.ValueOf(secret)
typ := reflect.TypeOf(secret) //typ:=value.Type()
fmt.Println(typ)
knd := value.Kind()
fmt.Println(knd)
for i := 0; i < value.NumField(); i++ {
fmt.Printf("Field %d: %v\n", i, value.Field(i))
}
results := value.Method(0).Call(nil)
fmt.Println(results)
}
运行结果如下:
main.NotknownType
struct
Field 0: Google
Field 1: Go
Field 2: Code
[Google - Go - Code]
代码如下:
package main
import (
"fmt"
"reflect"
)
type T struct {
A int
B string
}
func main() {
t := T{23, "Hello"}
s := reflect.ValueOf(&t).Elem()
typeOfT := s.Type()
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
fmt.Printf("%d: %s %s = %v\n", i, typeOfT.Field(i).Name, f.Type(), f.Interface())
}
s.Field(0).SetInt(77)
s.Field(1).SetString("The World")
fmt.Println("t现在的值是:", t)
}
运行结果如下:
0: A int = 23
1: B string = Hello
t现在的值是: {77 The World}
Printf和反射
代码如下:
package main
import (
"os"
"strconv"
)
type Stringer interface {
String() string
}
type Celsius float64
func (c Celsius) String() string {
return strconv.FormatFloat(float64(c), 'f', 1, 64) + "°C"
}
type Day int
var dayName = []string{"星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期天"}
func (day Day) string() string {
return dayName[day]
}
func print(args ...interface{}) {
for i, arg := range args {
if i > 0 {
os.Stdout.WriteString(" ")
}
switch a := arg.(type) {
case Stringer:
os.Stdout.WriteString(a.String())
case int:
os.Stdout.WriteString(strconv.Itoa(a))
case string:
os.Stdout.WriteString(a)
default:
os.Stdout.WriteString("???")
}
}
}
func main() {
print(Day(1), "温度是", Celsius(18.36))
}
运行结果如下:
??? 温度是 18.4°C
反射的类型对象(reflect.Type)
代码如下:
package main
import (
"fmt"
"reflect"
)
func main() {
var a int
typeOfA := reflect.TypeOf(a)
fmt.Println(typeOfA.Name(), typeOfA.Kind())
}
运行结果如下:
int int
理解反射的类型(Type)与种类(Kind)
代码如下:
package main
import (
"fmt"
"reflect"
)
//定义一个Enum类型
type Enum int
const (
Zero Enum = 0
)
func main() {
//声明一个空结构体
type cat struct {
}
//获取结构体实例的反射类型对象
typeOfCat := reflect.TypeOf(cat{})
//显示反射类型对象的名称和种类
fmt.Println(typeOfCat.Name(), typeOfCat.Kind())
//获取Zero常量的反射类型对象
typeOfA := reflect.TypeOf(Zero)
//显示反射类型对象的名称和种类
fmt.Println(typeOfA.Name(), typeOfA.Kind())
}
运行结果如下:
cat struct
Enum int
指针与指针指向的元素
代码如下:
package main
import (
"fmt"
"reflect"
)
func main() {
type cat struct {
}
ins := &cat{}
typeOfCat := reflect.TypeOf(ins)
fmt.Printf("name:'%v' kind:'%v'\n", typeOfCat.Name(), typeOfCat.Kind())
typeOfCat = typeOfCat.Elem()
fmt.Printf("element name: '%v', element kind: '%v'\n", typeOfCat.Name(), typeOfCat.Kind())
}
运行结果如下:
name:'' kind:'ptr'
element name: 'cat', element kind: 'struct'
使用反射获取结构体的成员类型
| 方法 | 说明 |
| Field(i int) StructField | 根据索引,返回索引对应的结构体字段的信息。当值不是结构体或索引超界时发生宕机。 |
| NumField() int | 返回结构体成员字段数量。当类型不是结构体或索引超界时发生宕机。 |
| FieldByName(name string) (StructField, bool) | 根据给定字符串返回字符串对应的结构体字段的信息。没有找到时bool返回false,当类型不是结构体或索引超界时发生宕机。 |
| FieldByIndex(index []int) StructField | 多层成员访问时,根据[]int提供的每个结构体的字段索引,返回字段的信息。没有找到时返回零值。当类型不是结构体或索引超界时发生宕机。 |
| FieldByNameFunc(match func(string) bool) (StructField,bool) | 根据匹配函数匹配需要的字段。当值不是结构体或索引超界时发生宕机。 |
代码如下:
package main
import (
"fmt"
"reflect"
)
func main() {
//声明一个空结构体
type cat struct {
Name string
//带有结构体tag的字段
Type int `json:"type" id:"100"`
}
//创建cat的实例
ins := cat{Name: "mini", Type: 1}
//获取结构体实例的反射类型对象
typeOfCat := reflect.TypeOf(ins)
//遍历结构体所有成员
for i := 0; i < typeOfCat.NumField(); i++ {
//获取每个成员的结构体字段类型
fieldType := typeOfCat.Field(i)
//输出成员名和tag
fmt.Printf("name: %v tag: '%v'\n", fieldType.Name, fieldType.Tag)
}
//通过字段名,找到字段类型信息
if catType, ok := typeOfCat.FieldByName("Type"); ok {
//从tag中取出需要的tag
fmt.Println(catType.Tag.Get("json"), catType.Tag.Get("id"))
}
}
运行结果如下:
name: Name tag: ''
name: Type tag: 'json:"type" id:"100"'
type 100
结构体标签(struct Tag)——对结构体字段的额外信息标签
Tag在结构体字段后方书写的格式如下:
`key1:"value1" key2:"value2"`
代码如下:
package main
import (
"fmt"
"reflect"
)
func main() {
type cat struct {
Name string
Type int `json:"type" id:"100"`
}
typeOfCat := reflect.TypeOf(cat{})
if catType, ok := typeOfCat.FieldByName("Type"); ok {
fmt.Println(catType.Tag.Get("json"))
}
}
运行结果如下:
type
代码如下:
package main
import (
"fmt"
"reflect"
)
func main() {
var x float64 = 3.14
fmt.Println("type:", reflect.TypeOf(x))
fmt.Println("value:", reflect.ValueOf(x))
}
运行结果如下:
type: float64
value: 3.14
代码如下:
package main
import (
"fmt"
"reflect"
)
type X struct {
y byte
z complex128
}
func (x *X) String() string {
return fmt.Sprintf("%v", x)
}
func main() {
var x X
fmt.Println(x)
v := reflect.TypeOf(x)
fmt.Println("type:", v)
fmt.Println("Align:", v.Align())
fmt.Println("FieldAlign:", v.FieldAlign())
for i := 0; i < v.NumMethod(); i++ {
fmt.Println("Method", i, v.Method(i).Name)
}
fmt.Println("Name:", v.Name())
fmt.Println("PkgPath:", v.PkgPath())
fmt.Println("Kind:", v.Kind())
for i := 0; i < v.NumField(); i++ {
fmt.Println("Field", i, v.Field(i).Name)
}
fmt.Println("Size:", v.Size())
}
运行结果如下:
{0 (0+0i)}
type: main.X
Align: 8
FieldAlign: 8
Name: X
PkgPath: main
Kind: struct
Field 0 y
Field 1 z
Size: 24
代码如下:
package main
import (
"fmt"
"reflect"
)
func main() {
var x float64 = 3.14
v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())
fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
fmt.Println("value:", v.Float())
}
运行结果如下:
type: float64
kind is float64: true
value: 3.14
代码如下:
package main
import (
"fmt"
"reflect"
)
func main() {
var x float64 = 3.14
fmt.Println("x:", x)
v := reflect.ValueOf(&x)
fmt.Println("v.CanSet:", v.CanSet())
i := v.Elem()
fmt.Println("i.Canset:", i.CanSet())
i.SetFloat(3.1415)
fmt.Println("i:", i.Float())
fmt.Println("x:", x)
}
运行结果如下:
x: 3.14
v.CanSet: false
i.Canset: true
i: 3.1415
x: 3.1415
代码如下:
package main
import (
"fmt"
"math"
"reflect"
)
type X struct {
Y byte
Z complex128
}
func main() {
var x X
fmt.Println(x)
v := reflect.ValueOf(&x)
e := v.Elem()
e.Field(0).SetUint('e')
𝛑 := math.Pi
c := complex(math.Cos(𝛑), math.Sin(𝛑))
e.Field(1).SetComplex(c)
fmt.Println("最美的公式:")
fmt.Printf("%c^i𝛑 + 1 = %g\n", x.Y, 1+real(x.Z))
fmt.Println("可惜虚部有点精度误差:", imag(x.Z))
}
运行结果如下:
{0 (0+0i)}
最美的公式:
e^i𝛑 + 1 = 0
可惜虚部有点精度误差: 1.2246467991473515e-16
代码如下:
package main
import (
"fmt"
"reflect"
)
type Bird struct {
Name string
LifeExpectance int
}
func (b *Bird) Fly() {
fmt.Println("I am flying...")
}
func main() {
sparrow := &Bird{"Sparrow", 3}
s := reflect.ValueOf(sparrow).Elem()
typeOfT := s.Type()
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
fmt.Printf("%d: %s %s = %v\n", i, typeOfT.Field(i).Name, f.Type(), f.Interface())
}
}
运行结果如下:
0: Name string = Sparrow
1: LifeExpectance int = 3
反射的值对象(reflect.Value)
一、使用反射值对象包装任意值
Go语言中,使用reflect.ValueOf()函数获得值的反射值对象(reflect.Value)。书写格式如下:
value:=reflect.ValueOf(rawValue)
二、从反射值对象获取被包装的值
1、从反射值对象(reflect.Value)中获取值的方法
| 方法名 | 说明 |
| Interface() interface{} | 将值以interface{}类型返回,可以通过类型断言转换为指定类型 |
| Int() int64 | 将值以int类型返回,所有有符号整型均可以此方式返回 |
| Uint() uint64 | 将值以uint类型返回,所有无符号整形均可以此方式返回 |
| Float() float64 | 将值以双精度(float64)类型返回,所有浮点数(float32、float64)均可以此方式返回 |
| Bool() bool | 将值以bool类型返回 |
| Bytes() []bytes | 将值以字节数组[]bytes类型返回 |
| String() string | 将值以字符串类型返回 |
2、从反射值对象(reflect.Value)中获取值的例子
代码如下:
package main
import (
"fmt"
"reflect"
)
func main() {
//声明整形变量a并赋初值
var a int = 1024
//获取变量a的反射值对象
valueOfA := reflect.ValueOf(a)
//获取interface{}类型的值,通过类型断言转换
var getA int = valueOfA.Interface().(int)
//获取64位的值,强制类型转换为int类型
var getA2 int = int(valueOfA.Int())
fmt.Println(getA, getA2)
}
运行结果 如下:
1024 1024
三、使用反射访问结构体的成员字段的值
四、反射对象的空和有效性判断
五、使用反射值对象修改变量的值
1、判定及获取元素的相关方法
| 方法名 | 备注 |
| Elem() Value | 取值指向的元素值,类似于语言层“*”操作。当值类型不是指针或接口时发生宕机,空指针时返回nil的Value。 |
| Addr() Value | 对可寻址的值返回其地址,类似于语言层“&”操作。当值不可寻址时发生宕机。 |
| CanAddr() bool | 表示值是否可寻址 |
| CanSet() bool | 返回值能否被修改。要求值可寻址且是导出的字段。 |
2、值修改相关方法
| Set(x Value) | 将值设置为传入的反射值对象的值 |
| SetInt(x int64) |
使用int64设置值 。当值的类型不是int、int8、int16、int32、int64时会发生宕机。 |
| SetUint(x uint64) | 使用uint64设置值 。当值的类型不是uint、uint8、uint16、uint32、uint64时会发生宕机。 |
| SetFloat(x float64) | 使用float64设置值。当值的类型不是float32、float64时会发生宕机。 |
| SetBool(x bool) | 使用bool设置值。当值的类型不是bool时会发生宕机。 |
| SetBytes(x []byte) | 设置字节数组[]bytes值。当值的类型不是[]byte时会发生宕机。 |
| SetString(x string) | 设置字符串值。当值的类型不是string时会发生宕机。 |
3、值可修改条件之一:可被寻址
代码如下:
package main
import (
"fmt"
"reflect"
)
func main() {
//声明整形变量a并赋初值
var a int = 1024
//获取变量a的反射值对象(a的地址)
valueOfA := reflect.ValueOf(&a)
//取出a地址的元素(a的值)
valueOfA = valueOfA.Elem()
//修改a的值为1
valueOfA.SetInt(1)
//打印a的值
fmt.Println(valueOfA.Int())
}
运行结果如下:
1
4、值可修改条件之一:被导出
六、通过类型创建类型的实例
代码如下:
package main
import (
"fmt"
"reflect"
)
func main() {
var a int
//取变量a的反射类型对象
typeOfA := reflect.TypeOf(a)
//根据反射类型对象创建类型实例
aIns := reflect.New(typeOfA)
//输出Value的类型和种类
fmt.Println(aIns.Type(), aIns.Kind())
}
运行结果如下:
*int ptr
七、使用反射调用函数
代码如下:
package main
import (
"fmt"
"reflect"
)
//普通函数
func add(a, b int) int {
return a + b
}
func main() {
//将函数包装为反射值对象
funcValue := reflect.ValueOf(add)
//构造函数参数,传入两个整型值
paramList := []reflect.Value{reflect.ValueOf(10), reflect.ValueOf(20)}
//反射调用函数
retList := funcValue.Call(paramList)
//获取第一个返回值,取整数值
fmt.Println(retList[0].Int())
}
运行结果如下:
30
示例:将结构体的数据保存为JSON格式的文本数据
创建一个文件夹名marshaljson。
1、数据结构及入口函数
main.go的代码如下:
package main
import (
"fmt"
)
func main() {
//声明技能结构
type Skill struct {
Name string
Level int
}
//声明角色结构
type Acotr struct {
Name string
Age int
Skills []Skill
}
//填充基本角色数据
a := Acotr{
Name: "cow boy",
Age: 37,
Skills: []Skill{
{Name: "Roll and roll", Level: 1},
{Name: "Flash your dog eye", Level: 2},
{Name: "Time to have Lunch", Level: 3},
},
}
if result, err := MarshalJson(a); err == nil {
fmt.Println(result)
} else {
fmt.Println(err)
}
}
2、序列化主函数
marshal.go的代码如下:
package marshaljson
import (
"bytes"
"reflect"
)
//给外部使用序列化值为JSON的接口
func MarshalJson(v interface{}) (string, error) {
//准备一个缓冲
var b bytes.Buffer
//将任意值转换为JSON并输出到缓冲中
if err := writeAny(&b, reflect.ValueOf(v)); err == nil {
return b.String(), nil
} else {
return "", err
}
}
3、任意值序列化
any.go的代码如下:
package marshaljson
import (
"bytes"
"errors"
"reflect"
"strconv"
)
//将任意值转换为JSON格式并输出到缓冲中
func writeAny(buff *bytes.Buffer, value reflect.value) error {
switch value.Kind() {
case reflect.String:
//写入带有双引号括起来的字符串
buff.WriteString(strconv.Quote(value.String()))
case reflect.Int:
//将整形转换为字符串并写入缓冲中
buff.WriteString(strconv.FormatInt(value.Int(), 10))
case reflect.Slice:
return writeSlice(buff, value)
case reflect.Struct:
return writeStruct(buff, value)
default:
//遇到不认识的种类,返回错误
return errors.New("unsupport kind:" + value.Kind().String())
}
return nil
}
4、切片序珍化
slice.go的代码如下:
package marshaljson
import "bytes"
//将切片转换为JSON格式并输出到缓冲中
func writeSlice(buff *bytes.Buffer, value reflect.value) error {
//写入切片开始标记
buff.WriteString("[")
//遍历每个切片元素
for s := 0; s < value.Len(); s++ {
sliceValue := value.Index(s)
//写入每个切片元素
writeAny(buff, sliceValue)
//每个元素尾部写入逗号,最后一个字段不添加
if s < value.Len()-1 {
buff.WriteString(",")
}
}
//写入切片结束标记
buff.WriteString("]")
return nil
}
5、结构体序列化
struct.go的代码如下:
package marshaljson
import "bytes"
//将结构体序列化为JSON格式并输出到缓冲中
func writeStruct(buff *bytes.Buffer, value reflect.value) error {
//取值的类型对象
valueType := value.Type()
//写入结构体左大括号
buff.WriteString("{")
//遍历结构体的所有值
for i := 0; i < value.NumField(); i++ {
//获取每一个字段的字段值(reflect.Value)
fieldValue := value.Field(i)
//获取每个字段的类型(reflect.StructField)
fieldType := valueType.Field(i)
//写入字段名左双引号
buff.WriteString("\"")
//写入字段名
buff.WriteString(fieldType.Name)
//写入字段名的右双引号和冒号
buff.WriteString("\":")
//写入每个字段值
writeAny(buff, fieldValue)
//每个字段尾部写入逗号,最后一个字段不添加
if i < value.NumField()-1 {
buff.WriteString(",")
}
}
//写入结构体右大括号
buff.WriteString("}")
return nil
}
本文详细介绍了Go语言中的反射库`reflect`,包括如何获取和设置变量的类型与值,如何通过反射修改结构体字段,以及如何调用函数。通过实例展示了反射在结构体标签、变量修改、类型检查等方面的应用,还演示了如何利用反射将数据序列化为JSON格式。
2546

被折叠的 条评论
为什么被折叠?



