zoukankan      html  css  js  c++  java
  • 反射

    一、反射的基本介绍

    反射可以在运行时动态获取变量的各种信息, 比如变量的类型(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
    ", 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
    ", iV, iV)
    
    	stu, ok := iV.(Student)
    	if ok {
    		fmt.Printf("stu.Name=%v
    ", 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
    ", val)
    	val.Elem().SetInt(110)
    	fmt.Printf("val=%v
    ", 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
    ", val, val)
    
    	tVal := reflect.TypeOf(b)
    	fmt.Printf("tVal =%v
    ", tVal)
    
    	iT := val.Interface()
    	fNum := iT.(float64)
    	fmt.Printf("fNum=%v
    ", 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
    ", 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
    ", num)
    
    	//结构体的所有字段
    	for i := 0; i < num; i++ {
    		fmt.Printf("Field %d:值为%v
    ", i, val.Field(i))
    		//获取struct标签,需要通过reflect.Type来获取tag标签的值
    		tagVal := typ.Field(i).Tag.Get("json")
    		//如果该字段有tag标签及显示,否则就不显示
    		if tagVal != "" {
    			fmt.Printf("Field %d:tag为%v
    ", i, tagVal)
    		}
    	}
    
    	//获取结构体有多少个方法
    	numOfMethod := val.NumMethod()
    	fmt.Printf("struct has %d methods
    ", 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)
    }
    
  • 相关阅读:
    英语范文——人的名字的重要性
    英语写作常用句型
    英语范文——构建绿色校园
    OpenGL实例:三角形
    Python+Selenium笔记(二):配置谷歌+IE环境
    Python+Selenium笔记(一):环境配置+简单的例子
    Python笔记(八):web开发
    Python笔记(七):字典、类、属性、对象实例、继承
    Python笔记(六):推导数据
    Python笔记(五):异常处理和数据存储
  • 原文地址:https://www.cnblogs.com/xidian2014/p/10681626.html
Copyright © 2011-2022 走看看