反射

一、反射的基本介绍

反射可以在运行时动态获取变量的各种信息, 比如变量的类型(type),类别(kind)
如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)
通过反射,可以修改变量的值,可以调用关联的方法。
使用反射,需要import (“reflect”)

二、反射的重要函数和概念

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

2、reflect.ValueOf(变量名) 获取变量的值,返回reflect.Value类型。reflect.Value是一个结构体类型。通过reflect.Value可以获取到关于该变量的很多信息。

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

type Student struct {
	Name string
	Age  int
}

func test(b interface{}) {
	//interface{}转成reflect.Value
	rVal := reflect.ValueOf(b)

	//reflect.Value转换成interface{}
	iVal := rVal.Interface()

	//interface{}转换成原来的变量类型,使用类型断言
	v := iVal.(Student)
	fmt.Println(v)
}

对基本数据类型、interface{}、reflect.Value、结构体类型进行反射的基本操作

package main

import (
	"fmt"
	"reflect"
)

func reflectTest01(b interface{}) {
	//通过反射获取传入变量的type、kind、值
	//获取到reflect.Type
	rType := reflect.TypeOf(b)
	fmt.Println("rType=", rType)

	//获取到reflect.Value
	rVal := reflect.ValueOf(b)
	n := 2 + rVal.Int()
	fmt.Println("n=", n)

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

	//将rVal转成interface{}
	iV := rVal.Interface()
	num := iV.(int)
	fmt.Println("num=", num)
}

type Student struct {
	Name string
	Age  int
}

func reflectTest02(b interface{}) {
	rType := reflect.TypeOf(b)
	fmt.Println("rType=", rType)

	rVal := reflect.ValueOf(b)
	iV := rVal.Interface()
	fmt.Printf("iV=%v iV type=%T\n", iV, iV)

	stu, ok := iV.(Student)
	if ok {
		fmt.Printf("stu.Name=%v\n", stu.Name)
	}
}

func main() {
	var num int = 100
	reflectTest01(num)

	stu := Student{
		Name: "tom",
		Age:  20,
	}
	reflectTest02(stu)
}

三、反射的注意细节

1、reflect.Value.Kind获取变量的类别,返回的是一个常量。

2、Type和Kind的区别:

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

var num int=100 num的Type是int,Kind也是int

var stu Student stu的Type是pkg1.Student,Kind是struct

3、通过反射可以让变量在interface{}金额reflect.Value之间相互转换。

4、使用反射的方式来获取变量的值并返回对应的类型,要求数据类型匹配,比如x是int,那么就应该使用reflect.Value(x).Int(),而不能使用其他的,否则报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 = 10
	testInt(&num)
	fmt.Println("num=", num)
}

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

package main

import (
	"fmt"
	"reflect"
)

func testFloat(b interface{}) {
	val := reflect.ValueOf(b)
	fmt.Printf("val =%v val type=%T\n", val, val)

	tVal := reflect.TypeOf(b)
	fmt.Printf("tVal =%v\n", tVal)

	iT := val.Interface()
	fNum := iT.(float64)
	fmt.Printf("fNum=%v\n", fNum)
}

func main() {
	var num float64 = 1.2
	testFloat(num)
}

修改字符串类型的值

package main

import (
	"fmt"
	"reflect"
)

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

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

package main

import (
	"fmt"
	"reflect"
)

type Monster struct {
	Name  string  `json:"name"`
	Age   int     `json:"monster_age"`
	Score float32 `json:"成绩"`
	Sex   string
}

func (s Monster) GetSum(n1, n2 int) int {
	return n1 + n2
}

func (s Monster) Set(name string, age int, score float32, sex string) {
	s.Name = name
	s.Age = age
	s.Score = score
	s.Sex = sex
}

func (s Monster) Print() {
	fmt.Println("---start---")
	fmt.Println(s)
	fmt.Println("---end---")
}

func TestStruct(a interface{}) {
	//获取reflect.Type类型
	typ := reflect.TypeOf(a)

	//获取reflect.Value类型
	val := reflect.ValueOf(a)

	//获取a对应的类别
	kd := val.Kind()

	if kd != reflect.Struct {
		fmt.Println("expect struct")
		return
	}

	//获取结构体有结果字段
	num := val.NumField()
	fmt.Printf("struct has %d fields\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 methods\n", numOfMethod)

	//方法的排序默认是按照函数名排序(ASCII码)
	//调用结构体的第2个方法
	val.Method(1).Call(nil)

	//调用结构体的第1个方法
	var params []reflect.Value
	params = append(params, reflect.ValueOf(10))
	params = append(params, reflect.ValueOf(40))
	res := val.Method(0).Call(params)
	fmt.Println("res=", res[0].Int())
}

func main() {
	var a Monster = Monster{
		Name:  "黄鼠狼",
		Age:   400,
		Score: 30.8,
	}
	TestStruct(a)
}

 

posted on 2019-04-10 10:13  lina2014  阅读(349)  评论(0编辑  收藏  举报

导航