Go语言的反射

本文详细介绍了Go语言中的反射库`reflect`,包括如何获取和设置变量的类型与值,如何通过反射修改结构体字段,以及如何调用函数。通过实例展示了反射在结构体标签、变量修改、类型检查等方面的应用,还演示了如何利用反射将数据序列化为JSON格式。

 反射

方法和类型的反射

变量的最基本信息就是类型和值:反射包的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
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值