golang 反射解惑
Type和Kind的区别直接看例子: type Myint int type Person struct { } func main() { var myint Myint= 1 var person Person= Person{} s := 1 var intPtr =&s mySlice := []string{} myMap := map[string]int{} myintType := reflect.TypeOf(myint) personType := reflect.TypeOf(person) intPtrType := reflect.TypeOf(intPtr) mySliceType := reflect.TypeOf(mySlice) myMapType := reflect.TypeOf(myMap) myintKind := myintType.Kind() personKind := personType.Kind() intPtrKind := intPtrType.Kind() mySliceKind := mySliceType.Kind() myMapKind := myMapType.Kind() fmt.Printf("myint Type():%s,Kind():%sn",myintType,myintKind) fmt.Printf("person Type():%s,personType,personKind) fmt.Printf("intPtr Type():%s,intPtrType,intPtrKind) fmt.Printf("mySlice Type():%s,mySliceType,mySliceKind) fmt.Printf("myMap Type():%s,myMapType,myMapKind) } 运行结果如下: myint Type():main.Myint,Kind():int person Type():main.Person,Kind():struct intPtr Type():*int,Kind():ptr mySlice Type():[]string,Kind():slice myMap Type():map[string]int,Kind():map 这里看出来Type是实际类型,Kind是底层类型。实际类型和底层类型是我给起的名字。比如 官方文档对Kind的解释是:
翻译过来就是Kind是Type的类型。总之记住Kind比Type更加原始就好。 下面是go语言支持的所有Kind: 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 ) Elm方法Type接口和Value接口都有Elem() 方法。 Type接口的Elem() 方法原型是
Elem返回的是调用者的元素的类型,如果调用者的Kind不是Array,Slice.那么会抛出异常。 func main() {} s := 1 var intPtr =&s mySlice := []string{} myMap := map[string]int{} myArray:= [...]string{} var myChan chan int= make(chan int) intPtrKind := reflect.TypeOf(intPtr).Elem() mySliceKind := reflect.TypeOf(mySlice).Elem() myMapKind := reflect.TypeOf(myMap).Elem() myArrayKind := reflect.TypeOf(myArray).Elem() myChanKind := reflect.TypeOf(myChan).Elem() fmt.Printf("Elem():%sn",intPtrKind) fmt.Printf("Elem():%sn",mySliceKind) fmt.Printf("Elem():%sn",myMapKind) fmt.Printf("Elem():%sn",myArrayKind) fmt.Printf("Elem():%sn",myChanElem) } /*打印结果 intPtr Type():*int,Elem():int mySlice Type():[]string,Elem():string myMap Type():map[string]int,Elem():int myArray Type():[0]string,Elem():string myChan Type():chan int,Elem():int */ 从上面可以看到: Value接口的Elem() 方法原型是
调用方的的Kind必须是Interface或者指针,否则发生panic,Elem函数返回接口包含的值或者指针指向的值。 func main() { s := 1 var intPtr = &s intPtrValueElem := reflect.ValueOf(intPtr) fmt.Println("pointer Kind:",intPtrValueElem.Kind()," Value.Elem():",intPtrValueElem.Elem()) } /*打印结果 pointer Kind: ptr Value.Elem(): 1 */ 从结果可以看出Value.Elem方法取出来的是指针指向的值。(这里只举了一个Kind是Ptr的例子,关于Kind是Interface的具体可以看看这里In Go,which value’s kind is reflect.Interface? ) 如何设置值这里直接给几个例子 1.设置切片: func main() { names := make([]string,3) val := reflect.ValueOf(names) for i:=0;i<val.Len();i++{ name := fmt.Sprintf("names%d",i) val.Index(i).SetString(name) } fmt.Println(val) } //运行结果[names0 names1 names2] 2.设置Map func main() { names := make(map[string]int) val := reflect.ValueOf(names) val.SetMapIndex(reflect.ValueOf("name1"),reflect.ValueOf(1)) //key=name1,value=name2 val.SetMapIndex(reflect.ValueOf("name2"),reflect.ValueOf(2)) fmt.Println(val) //打印map[name1:1 name2:2] value1 :=val.MapIndex(reflect.ValueOf("name1")) value2 :=val.MapIndex(reflect.ValueOf("name2")) fmt.Println("value1:",value1," value2:",value2) //打印value1: 1 value2: 2 } 3.修改通道类型的值 func main() { var myChan chan int= make(chan int,1) myChan <-1 val := reflect.ValueOf(myChan) fmt.Println(val.Recv()) //接收数据 val.Send(reflect.ValueOf(7)) fmt.Println(<-myChan) //发送数据 } /*结果: 1 true 7 */ 4.修改基本类型和struct类型 func main() { //int type a := 5 val := reflect.ValueOf(&a) if val.Elem().CanSet() { val.Elem().SetInt(10) } fmt.Println(a) //string s := "yuanjize" str := reflect.ValueOf(&s) if str.Elem().CanSet() { str.Elem().SetString("Tom") } fmt.Println(s) // struct user := &User{UserName: "yuanjize",Passwd: "123",Age: 3} typ := reflect.TypeOf(user).Elem() value := reflect.ValueOf(user).Elem() //reflect.ValueOf(user)得到的Value打印出来是指针指向的地址,比如0xff998877。reflect.ValueOf(user).Elem() 得到的Value打印出来的是user的值,所我们后面要修改User的字段,所以这里要调用Elem方法。 for i := 0; i < typ.NumField(); i++ { fieldType := typ.Field(i) fieldValue := value.Field(i) fmt.Printf("[BEFORE CHANGE] FieldName:%s,FieldValue:%v canSet:%t n",fieldType.Name,fieldValue.Interface(),fieldValue.CanSet()) if fieldValue.CanSet() { //这个字段是否可以修改 switch fieldValue.Kind() { case reflect.Int: { fieldValue.SetInt(10) } case reflect.String: { fieldValue.SetString("dean") } default: fmt.Println("no such type") } } fmt.Printf("[AFTER CHANGE] FieldName:%s,fieldValue.CanSet()) } } 上面提供了几种反射golang数据类型的例子,可以发现对于map,slice和chan这几种引用类型来说我们在调用 在修改值之前可以先调用 如果我们传入到 总结一下:要想通过反射改变类型的值,如果想要改变的是引用类型那么就直接传递引用类型,如果要改变的是非引用类型那么就需要传递该类型的指针,这个规则和通过函数调用修改函数变量的方法是一样的。 仿照Gin框架的bind部分写的一个练习Odm函数会把form参数中存放的字段一一映射到structPtr指向的结构体中。 /* 使用例子: type Person struct{ Name string `form:"name"` Age int `form:"age"` } person := &Person{} mMap :=map[string]string}{} mMap["name"]="yuanjize" mMap["age"] = "22" Odm(person,mMap) */ func Odm(structPtr interface{},form map[string][]string) { typ := reflect.TypeOf(structPtr) fmt.Println("------------------->:",typ) if typ.Kind() != reflect.Ptr || typ.Elem().Kind() != reflect.Struct{ panic("first Param must be a pointer of struct") } val := reflect.ValueOf(structPtr).Elem() typ = typ.Elem() for i:=0;i< val.NumField();i++{ fieldValue := val.Field(i) fieldType := typ.Field(i) if !fieldValue.CanSet(){ fmt.Printf("field:%s can‘t be set",fieldType.Name) continue } fieldKind := fieldValue.Kind() if fieldKind == reflect.Struct{ Odm(fieldValue.Addr().Interface(),form) }else{ tag := fieldType.Tag.Get("form") if tag == ""{ fmt.Printf("no such Named:%s Field in Form n",tag) continue } if fieldKind == reflect.Slice{ //1.get type of ele 2.make s slice 3.fill slice length := len(form[tag]) if length <=0{ continue } itemType := fieldType.Type.Elem().Kind() slice := reflect.MakeSlice(fieldType.Type,length,length) for i,v:=range form[tag]{ setValue(itemType,slice.Index(i),v) } fieldValue.Set(slice) }else{ //no slice or struct,find the field and write if formVal,ok := form[tag];ok{ setValue(fieldKind,fieldValue,formVal[0]) }else{ fmt.Printf("no such Named:%s Field in Form n",tag) } } } } } func setValue(kind reflect.Kind,field reflect.Value,value string){ switch kind { case reflect.Int:{ setInts(field,value,0) } case reflect.Int8:{ setInts(field,8) } case reflect.Int16:{ setInts(field,16) } case reflect.Int32:{ setInts(field,32) } case reflect.Int64:{ setInts(field,64) } case reflect.Uint:{ setUints(field,0) } case reflect.Uint8:{ setUints(field,8) } case reflect.Uint16:{ setUints(field,16) } case reflect.Uint32:{ setUints(field,32) } case reflect.Uint64:{ setUints(field,64) } case reflect.Bool:{ setBool(field,value) } case reflect.String:{ setString(field,value) } case reflect.Float32:{ setFloat(field,32) } case reflect.Float64:{ setFloat(field,64) } default: fmt.Println("undefine type :",kind) } } func setInts(field reflect.Value,value string,bitSize int) { val,_ := strconv.ParseInt(value,10,bitSize) field.SetInt(val) } func setUints(field reflect.Value,_ := strconv.ParseUint(value,bitSize) field.SetUint(val) } func setBool(field reflect.Value,value string) { val,_ := strconv.ParseBool(value) field.SetBool(val) } func setString(field reflect.Value,value string) { field.SetString(value) } func setFloat(field reflect.Value,_ := strconv.ParseFloat(value,bitSize) field.SetFloat(val) } 参考: (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |