go语言学习之反射

go语言学习之反射

1.先看一个问题,

package main

import (
	"encoding/json"
	"fmt"
)

type Monster struct {
	Name string `json:"monsterName"`
	Age string `json:"monsterAge"`
	Sal float64 `json:"monsterSal"`
	Sex string `json:"monsterSex"`
}

func main() {
	m:=Monster{
		Name :"白骨精",
		Age :"28",
		Sal:888.88,
		Sex:"female",
	}
	data,_:=json.Marshal(m)
	fmt.Println("json result:",string(data))
}

输出结果:

json result: {"monsterName":"玉兔精","monsterAge":"28","monsterSal":888.88,"monsterSex":"female"}

思考问题:

为什么序列化后,key-value的值是结构体Tag的值。而不是字段的名称,比如:不是 name 而是 “monsterName":"白骨精”

引出反射:

2.反射的基本介绍

2.1基本介绍

1》翻身可以在运行时动态获取变量的各种信息,比如变量的类型 (type),类别(kind)

2》如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段,方法)

3》通过反射,可以修改变量的值,可以调用关联的方法

4》使用反射,需要 import(“reflect”)

2.2反射的应用场景

1》不知道接口调用哪个函数,根据传入参数在运行时确定调用的具体接口,这种需要对函数或方法反射。根据以下这种桥接模式,比如我前面提出问题


第一个参数funcPtr以接口的形式传入函数指针,函数参数 args 以可变参数的形式传入,bridge函数中可以用反射来动态执行 funcPtr函数

2》对结构体序列化时,如果 结构体有指定的tag ,也会用到反射生成对应的字符串

type Monster struct {
	Name string `json:"monsterName"`
	Age string `json:"monsterAge"`
	Sal float64 `json:"monsterSal"`
	Sex string `json:"monsterSex"`
}

func main() {
	m:=Monster{
		Name :"白骨精",
		Age :"28",
		Sal:888.88,
		Sex:"female",
	}
	data,_:=json.Marshal(m)
	fmt.Println("json result:",string(data))
}
2.3.反射重要的函数和概念

1》reflect.TypeOf(变量名),获取变量的类型,返回 reflect.Type类型

2》reflect.ValueOf(变量名),获取变量的值,返回 reflect.Value类型reflect.value 是一个结构体类型。

看文档,通过 reflect.Value,可以获取到关于该变量的很多信息

https://golang.google.cn/pkg/reflect/#Value


type Value

func Append(s Value, x …Value) Value

func AppendSlice(s, t Value) Value

func Indirect(v Value) Value

func MakeChan(typ Type, buffer int) Value

[func MakeFunc(typ Type, fn func(args ]Value) (results []Value)) Value

func MakeMap(typ Type) Value

func MakeMapWithSize(typ Type, n int) Value

func MakeSlice(typ Type, len, cap int) Value

func New(typ Type) Value

func NewAt(typ Type, p unsafe.Pointer) Value

[func Select(cases ]SelectCase) (chosen int, recv Value, recvOK bool)

func ValueOf(i interface{}) Value

func Zero(typ Type) Value

func (v Value) Addr() Value

func (v Value) Bool() bool

[func (v Value) Bytes() ]byte

[func (v Value) Call(in ]Value) []Value

[func (v Value) CallSlice(in ]Value) []Value

func (v Value) CanAddr() bool

func (v Value) CanInterface() bool

func (v Value) CanSet() bool

func (v Value) Cap() int

func (v Value) Close()

3》变量,interface{}和reflect.Value是可以相互转换的,这点在实际开发中,会经常使用到。

var student Stu
func test(b interface{}){
	//1.如何将 interface{}转成reflect.Value
	rVal:=reflect.ValueOf(b)
	//2.如何·将 reflect.Value-->interface{}
	iVal:=rVal.Interface{}
	//3.如何将 interface{}转成原来的变量类型,使用类型断言
	v:=iVal.(Stu)
}

3.反射的快速入门

3.1快速入门

》编写一个 案例,演示对(基本数据类型,interface{},reflect.Value)

进行反射的基本操作

代码演示,见下面表格:

》请编写一个案例,演示对(结构体类型,interface{},reflect.Value)进行反射的基本操作

package main

import (
	"fmt"
	"reflect"
)

//专门演示反射
func reflectTest01(b interface{}) {
	//通过反射获取得传入的变量的 type,kind,值
	//1.先获取到 reflect.Type
	rTyp := reflect.TypeOf(b)
	fmt.Println("rType=", rTyp)
	//2.获取到 reflect.Value
	rVal := reflect.ValueOf(b)

	n2 := 2 + rVal.Int()
	fmt.Println("n2=", n2)

	fmt.Printf("rVal=%v rVal type=%T\n", rVal, rVal)

	//下面我们将 rVal转成 interface{}
	iV:=rVal.Interface()
	//将 interface{}通过断言转成需要的类型
	num2:=iV.(int)
	fmt.Println("num2=",num2)
}
//专门演示反射【对结构体的反射】
func reflectTest02(b interface{}){
	//通过 反射获取的传入的变量的 type,kind,值
	//1.先获取到 reflect.Type
	rTyp:=reflect.TypeOf(b)
	fmt.Println("rTyp=",rTyp)

	//2.获取到 reflect.Value
	rVal:=reflect.ValueOf(b)
	//下面我们将 rVal转成 interface{}
	iV:=rVal.Interface()
	fmt.Printf("iv=%v iv type=%T \n",iV,iV)
	//将 interface{}通过 断言转成需要的类型·
	//这里,我们就简单使用了一带检测的类型断言
	//ke以使用 switch 的断言形式来做的更加的灵活
	stu,ok:=iV.(Student)
	if ok{
		fmt.Printf("stu.Name=%v\n",stu.Name)
	}


}
func main() {
	//1.先定义一个 Int
	var  num int  =10
	reflectTest01(num)

	//2.定义一个 Student的实例
	stu:=Student{
		Name: "王阿猫",
		Age:  25,
	}
	reflectTest02(stu)
}

type Student struct {
	Name string
	Age int
}
type Monsterx struct {
	Name string
	Age int
}

4.反射的注意事项和细节

1》reflect.Value,kind 获取变量的类别,返回的是一个常量

type Kind

Kind代表Type类型值展示的具体分类,零值表示非法分类

type Kind uint
const (
    Invalid Kind = iota
    Bool
    Int
    Int8
    Int16
    Int32
    Int64
    Uint
    Uint8
    Uint16
    Uint32
    Uint64
    Uintptr
    Float32
    Float64
    Complex64
    Complex128
    Array
    Chan
    Func
    Interface
    Map
    Ptr
    Slice
    String
    Struct
    UnsafePointer
)

2》Type 和 Kind 的区别

Type是类型,kind 是类别,Type和 Kind可能是相同的,也可能是不同的

比如:var num int=10 num 的Type 是 int,Kind 也是 int

比如: var stu Student stu 的 Type 是 pkg.Student,Kind 是 struct

3》通过 反射 可以让变量在 Interface{}和 reflect.Value 之间相互转换,这点在前面画过示意图并在快速入门案例中讲解过,这里我们看下是如何在代码中体现的

变量《----------------》interface{}《--------》reflect.Value

4》使用反射的方式 来获取变量的值(并返回对应的类型),

要求数据类型匹配,比如x 是 Int,那么 就应该使用 reflect.Value(x).int()

而不能使用其他的,否则报 panic

func (Value)int

func (v Value)Int() int64

返回v 特有的有符号整数(表示为 int64).如果v 的Kind 不是 int,int8,int16,int32,int64会 panic)

5》通过 反射的来修改变量,注意当使用 SetXXX方法来设置需要通过 对应的

指针类型来完成,这样才能改变传入的变量的值,同时需要使用到

reflect.Value.Elem()方法

package main

import (
	"fmt"
	"reflect"
)

func testInt(b interface{})  {
	val:=reflect.ValueOf(b)
	fmt.Printf("val type=%T\n",val)
	val.Elem().SetInt(110)
	fmt.Printf("val=%v\n",val)
}
func main() {

	var num  int=20
	testInt(&num)
	fmt.Println("num=",num)
}

6》reflect.Value.Elem()应该如何理解

package main

import (
	"fmt"
	"reflect"
)

func testInt(b interface{})  {
	val:=reflect.ValueOf(b)
	fmt.Printf("val type=%T\n",val)
	val.Elem().SetInt(110)
	fmt.Printf("val=%v\n",val)
}
func main() {

	var num  int=20

	fn:=reflect.ValueOf(&num)
	fn.Elem().SetInt(200)
	fmt.Printf("%v \n",num)

}

5.反射课堂练习

1》给你一个变量, var v float64=1.2,请使用反射来得到它的reflect.Value,然后获取对应的 Type和Kind和值,并将 reflect.Value转换成 interface{},再将interface{}转换成 float64

2》,看段代码,判断是否正确,为什么

package main

import (
	"fmt"
	"reflect"
)

func main() {
	var str string="tom"
	fs:=reflect.ValueOf(str)
	fs.SetString("TOM")
	fmt.Printf("%v \n",str)
}

修改后:

package main

import (
	"fmt"
	"reflect"
)

func main() {
	var str string="tom"
	fs:=reflect.ValueOf(&str)//用指针
	fs.Elem().SetString("TOM")//加上 Elem()
	fmt.Printf("%v \n",str)
}

6.反射最佳实践

1》使用反射来遍历结构体的字段,调用结构体的方法,并获取结构体标签值

package main

import (
	"fmt"
	"reflect"
)

//定义一个 Dog 结构体
type Dog struct {
	Name string `json:"name"`
	Age int `json:"Dog_age"`
	Score float32 `json:"成绩"`
	Sex string
}
//方法 返回两个数的和
func (d Dog)GetSum(n1,n2 int)int{
	return n1+n2
}
//方法,接收四个值,给 s赋值
func (d Dog)Set(name string,age int,score float32,sex string){
	d.Name=name
	d.Age=age
	d.Score=score
	d.Sex=sex
}
//方法 ,显示 s的值
func (d  Dog)Show(){
	fmt.Println("---start---")
	fmt.Println(d)
	fmt.Println("----end----")
}

func TestStruct(a interface{})  {
	//获取reflect.Type类型
	typ:=reflect.TypeOf(a)
	//获取 reflect.Value类型
	val:=reflect.ValueOf(a)
	//获取到 a 对应类别
	kd:=val.Kind()
	//如果传入的不是 struct 就退出
	if kd!=reflect.Struct{
		fmt.Println("expect Struct")
		return
	}
	//获取到该结构体有几个字段
	num:=val.NumField()
	//
	fmt.Printf("struct has %d fileds \n",num)

	//变量结构体的所有字段
	for i:=0;i<num;i++{
		fmt.Printf("Field %d:值为 =%v\n",i,val.Field(i))
		//获取到 struct 标签,注意需要通过 reflect.Type来获取
		//tag标签的值
		tagVal:=typ.Field(i).Tag.Get("json")
		//如果该字段等于tag 标签 就显示,否则就不显示
		if tagVal!=""{
			fmt.Printf("Field %d :tag 为 =%v\n",i,tagVal)
		}
	}
	//获取到 该结构体有多少个方法
	numOfMethod:=val.NumMethod()
	fmt.Printf("struct has %d method \n",numOfMethod)
	//方法的排序默认是按照 函数名 的排序(ASCII码)
	val.Method(1).Call(nil)//获取到 第二个方法,调用它

	//调用结构体的第 1个方法 Method(0)
	var params[]reflect.Value//声明了·[]reflect.Value
	params=append(params,reflect.ValueOf(10))
	params=append(params,reflect.ValueOf(50))

	res:=val.Method(0).Call(params)//传入的参数是 []reflect.Value,返回 []reflect.Value
	fmt.Println("res=",res[0].Int())//返回结果,返回的结果是[]reflect.Value

}
func main(){
	//创建了一个 dog 实例
	var d Dog=Dog{
		Name:  "大黄",
		Age:   20,
		Score:80,
		Sex:   "female",
	}
	//将
	TestStruct(d)
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值