golang-反射(reflect)

mac2023-06-10  17

TypeOf()和ValueOf()函数

TypeOf() 这个函数可以接收任何类型的参数,会返回一个该参数的相关参数(接口类型)

ValueOf 这个函数可以传入任意类型的参数,能够返回一个结构体类型

这里绑定ValueOf函数传入参数返回的value结构体

可以使用该结构体绑定的相应方法如下所示:

反射入门:

下边两图为反射类型转换的重要图示

// 演示reflect的快速入门 package main import ( "fmt" "reflect" ) // 演示反射 函数参数为空接口则可传入任意类型 func reflectlist(a interface{}){ // 通过反射获取传入参数的 type/kind/值 // 1.先获取到reflect.Type // func TypeOf(i interface{}) rTyp := reflect.TypeOf(a) fmt.Println(rTyp) // 2.获取到reflect.Value [func ValueOf(i interface{}) Value] rValue := reflect.ValueOf(a) // rValue真实type并不是int类型而是reflect.Value fmt.Printf("rValue type = %T value = %v\n",rValue,rValue) // rValue.Int()将其类型转为int64,就可以实现int类型操作 rInt := rValue.Int() fmt.Printf("rInt type = %T value = %v\n",rInt,rInt+10) // 将rValue转为interface{} iv := rValue.Interface() num2 := iv.(int) fmt.Printf("num2 type = %T value = %v\n",num2,num2) } func main(){ a := 10 reflectlist(a) } int rValue type = reflect.Value value = 10 rInt type = int64 value = 20 num2 type = int value = 10

上边这个不太好解释,反射根本上就是运行时反射,通过reflect.ValueOf()可以返回该变量由接口保管的值,然后可以将该值转为interface{}空接口类型,但是运行时仍然是传入的变量类型,所以如果要对该变量值进行操作就需要通过类型断言就行转化才能进行操作

演示对struct类型的反射操作:

package main import ( "fmt" "reflect" ) type Person struct{ name string age int } type Json struct{ name string age int } func shwomaker(show interface{}){ rTyp := reflect.TypeOf(show) // rValue为反射出来的 rValue := reflect.ValueOf(show) fmt.Printf("TypeOf=%v ValueOf=%v\n",rTyp,rValue) // 此时rvia虽然被转为interface{}类型但是打印出type仍是Person类型 // 虽然是Person类型但编译时候并不能识别出来无法取出结构体里的值 // 需要通过类型断言转为Person类型才能正常取出 rvia := rValue.Interface() fmt.Printf("rviatype=%T value=%v\n",rvia,rvia) // 这里使用一个带判断的类型断言,如果rvia断言成功则ok为true否则为false // 这里使用类型断言转化成功则可以取出stu里的name字段(Person类型) stu,ok := rvia.(Person) if ok{ fmt.Printf("stu.type=%T stu.value=%v stu.name=%v",stu,stu,stu.name) } } func main(){ var a = Person{"tom",18} shwomaker(a) } TypeOf=main.Person ValueOf={tom 18} rviatype=main.Person value={tom 18} stu.type=main.Person stu.value={tom 18} stu.name=tom

反射的注意事项和细节说明:

类别栗子来说:类型=变量/类别=常量就像是我们在淘宝买衣服,男装里类型有大衣,卫衣,毛衣,类别为男装 package main import ( "fmt" "reflect" ) type Person struct{ name string age int } func main(){ // a的类型为Person类别为struct var a Person fmt.Printf("a.type=%T\n",a) // Kind()返回该对象的类别 rTyp := reflect.ValueOf(a) rkind := rTyp.Kind() fmt.Printf("a.kind=%v",rkind) }

shell输出可知a的type为main.Person,类别为struct

a.type=main.Person a.kind=struct

细节五: 通过反射来修改指针类型的变量需要 Elem() 来返回指针指向的值,然后使用 SetInt() 来进行修改 int类型就使用SetInt,string就是用SetString其他类型也是一样

package main import ( "fmt" "reflect" ) func reflectdemo(a interface{}){ rValue := reflect.ValueOf(a) fmt.Printf("rValue.value=%v rValue.type=%T\n",rValue,rValue) // Elem()返回rValue持有的指针指向的值,然后再使用SetInt()进行更改 rValue.Elem().SetInt(20) } func main(){ var a int a = 10 reflectdemo(&a) fmt.Println(a) } rValue.value=0xc042046058 rValue.type=reflect.Value 20

细节六:

反射课堂练习:

题一:

package main import ( "fmt" "reflect" ) func reflectdemo(re interface{}){ rValue := reflect.ValueOf(re) rTyp := reflect.TypeOf(re) fmt.Printf("rValue=%v rTyp=%v \n",rValue,rTyp) rValueKind := rValue.Kind() fmt.Println(rValueKind) rInterface := rValue.Interface() fmt.Printf("rInterface.Value=%v rInterface.type=%T\n",rInterface,rInterface) rFloat64 := rInterface.(float64) rFloat64 = 7.64 fmt.Println(rFloat64) } func main(){ var re float64 re = 0.618 reflectdemo(re) fmt.Println("re=",re) }

题二: 描述错误,ValueOf()下的SetInt()/SetString()方法是用来修改pointer类型的变量语法为:

变量.Elem().SetInt(新值) package main import ( "fmt" "reflect" ) func main(){ a := "tom" rValue := reflect.ValueOf(&a) rValue.Elem().SetString("jack") fmt.Println(a) }

struct字段添加tag反射原理解析:

struct实例传入函数后初始化为ValueOf()和TypeOf()通过Kind() 方法获取到变量初始为ValueOf的类型加一个判断如果不是struct类型就退出再通过V下的方法NumField()获取到变量字段数rTyp.Field(i).Tag.Get("json") T下的这一坨方法最后的Get()方法传入一个struct tag的key值,如果匹配到就返回这个tag的value没有匹配到就返回为空 package main import ( "fmt" "reflect" ) type Cat struct{ Name string `json:"name"` Age int `json:"age"` } func reflectjson(p1 interface{}){ rTyp := reflect.TypeOf(p1) rVal := reflect.ValueOf(p1) // 获取rVal的类型 rValKind := rVal.Kind() fmt.Println(rValKind) // rValKind输出为struct但是要写为reflect.Struct if rValKind != reflect.Struct{ return } // 返回rVal的字段数 num := rVal.NumField() fmt.Println(num) for i:=0;i<num;i++{ //Field 返回结构体的第i个字段(的Value封装) // 如果v的Kind不是Struct或i出界会panic // fmt.Printf("Field %d 值为%v\n",i,rVal.Field(i)) // Field(i int) StructField // 返回索引序列指定的嵌套字段的类型, // 等价于用索引中每个值链式调用本方法,如非结构体将会panic // Get方法返回标签字符串中键key对应的值。如果标签中没有该键 // 会返回""。如果标签不符合标准格式,Get的返回值是不确定的。 tagval := rTyp.Field(i).Tag.Get("json") if tagval != ""{ fmt.Printf("字段:%v tag为:%v\n",rVal.Field(i),tagval) } } } func main(){ c1 := Cat{"tom",20} reflectjson(c1) } struct 2 字段:tom tag为:name 字段:20 tag为:age

反射终极案例:

使用反射获取传入的struct类型字段tag,传值调用绑定此struct的方法

注意事项:

struct绑定的方法名首字母需大写否则反射跨包检测传入变量绑定方法检测不到反射调用方法主要使用value下的方法方法排序是按照方法名的ASCII码排序,并不是按顺序排序,调用需注意rValue.Method(1).Call(nil) | func (v Value) Call(in []Value) []Value Call方法传入参数需要是一个[]slice类型,同样返回一个[]slice,方法如果有一个返回值则此返回的[]slice有一个元素,同原理 package main import ( "fmt" "reflect" ) type Person struct{ Name string `json:"name"` Age int `json:"age"` } // 方法名首字母需大写否则反射跨包检测方法检测不到 func (p Person)Cat(n1 int,n2 int)int{ return n1 + n2 } func (p Person)Date(){ fmt.Println(p.Name,p.Age) } func Tian(showmaker interface{}){ rTyp := reflect.TypeOf(showmaker) rValue := reflect.ValueOf(showmaker) // 获取rvalue.kind rValueKind := rValue.Kind() // if rvalue!=reflect.value return if rValueKind != reflect.Struct{ return } // 获取rValue所持有的字段个数 num := rValue.NumField() for i:=0;i<num;i++{ // tag标签匹配到json则把字段tag返回给tag tag := rTyp.Field(i).Tag.Get("json") fmt.Printf("value=%v tag=%v",rValue.Field(i),tag) } // 获取rvalue所持有的方法个数,方法首字母大写方能检测到 nummuthod := rValue.NumMethod() fmt.Println(nummuthod) // Method参数为第几个方法,方法排序默认按照函数名排序(ASCII码) rValue.Method(1).Call(nil) //call为访问方法的参数,nil为不添加参数 // 声明一个reflect.Value类型的slice var reslice []reflect.Value // 访问方法参数需要为value类型切片 // 给reslice追加两个元素 reslice = append(reslice,reflect.ValueOf(20)) reslice = append(reslice,reflect.ValueOf(30)) // rValue.Method访问第一个方法,并传入参数reslice res := rValue.Method(0).Call(reslice) //传入value类型切片返回value类型切片 // 由于返回值为value类型切片,方法返回一个值,则取值为res[0]取第一个 fmt.Println("res=",res[0].Int()) } func main(){ showmaker := Person{"tom",20} Tian(showmaker) }


常量介绍

常量由const修饰常量在定义必须初始化并赋值常量不能修改常量只能修饰bool,数值类型(int,float系列),string类型常量和普通变量一样有访问范围限制,写在main函数外为全局变量,main函数中为局部变量,首字母大写则可以被别的包引用,小写则只能本包使用 演示: 表示给a赋值为0,b在a基础上+1,c在b基础+1 package main import ( "fmt" ) func main(){ const( a = iota b c ) fmt.Println(a,b,c) } 0 1 2

常规用法:

package main import ( "fmt" ) func main(){ const( a = 10 b = 0.16 c = "string" ) fmt.Println(a,b,c) } 10 0.16 string
最新回复(0)