这篇文章给大家分享的是有关golang怎么用反射reflect操作结构体的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。
golang是一种编译语言,可以将代码编译为机器代码,编译后的二进制文件可以直接部署到目标机器而无需额外的依赖,所以golang的性能优于其他的解释性语言,且可以在golang中使用goroutine来实现并发性,它提供了一个非常优雅的goroutine调度程序系统,可以很容易地生成数百万个goroutine。
需要遍历结构体的所有field
对于exported的field, 动态set这个field的value
对于unexported的field, 通过强行取址的方法来获取该值(tricky?)
下面的代码实现了从一个strct ptr对一个包外结构体进行取值的操作,这种场合在笔者需要用到反射的场合中出现比较多
simpleStrtuctField 函数接受一个结构体指针,因为最后希望改变其值,所以传参必须是指针。然后解引用。
接下来遍历结构体的每个field, exported字段是CanInterface的,对于unexported字段,需要强行取址来获取其值
model.go
package model type Person struct { Name string age int } func NewPerson(name string, age int) *Person { return &Person{ Name: name, age: age, } }
main.go
package main import ( "github.com/miaomiao3/log" "../model" "reflect" "unsafe" ) func main() { person := model.NewPerson("haha", 12) log.Debug("before:%+v", person) simpleStrtuctField(person) simpleStrtuctField(person) log.Debug("after:%+v", person) } // get unexported field func simpleStrtuctField(v interface{}) { dataType := reflect.TypeOf(v) dataValue := reflect.ValueOf(v) if dataType.Kind() == reflect.Ptr { if dataValue.IsNil() { panic("nil ptr") } // 如果是指针,则要判断一下是否为struct originType := reflect.ValueOf(v).Elem().Type() if originType.Kind() != reflect.Struct { return } // 解引用 dataValue = dataValue.Elem() dataType = dataType.Elem() } else { panic("non ptr") } num := dataType.NumField() for i := 0; i < num; i++ { field := dataType.Field(i) fieldName := field.Name fieldValue := dataValue.FieldByName(fieldName) if !fieldValue.IsValid() { continue } if fieldValue.CanInterface() { log.Debug("exported fieldName:%v value:%v", fieldName, fieldValue.Interface()) if fieldValue.CanSet() && fieldValue.Kind() == reflect.String { oldValue := fieldValue.Interface().(string) fieldValue.SetString(oldValue + " auto append") } } else { // 强行取址 forceValue := reflect.NewAt(fieldValue.Type(), unsafe.Pointer(fieldValue.UnsafeAddr())).Elem() log.Debug("unexported fieldName:%v value:%v", fieldName, forceValue.Interface()) } } }
output:
2019/06/02 17:15:31.64 [D] before:&{Name:haha age:12}
2019/06/02 17:15:31.64 [D] exported fieldName:Name value:haha
2019/06/02 17:15:31.64 [D] unexported fieldName:age value:12
2019/06/02 17:15:31.64 [D] after:&{Name:haha auto append age:12}
可以看到,Name字段被反射改变了,age的值也已经获取到
补充:go语言通过反射创建结构体、赋值、并调用对应方法
package main import ( "fmt" "reflect" "testing" ) type Call struct { Num1 int Num2 int } func (call Call) GetSub(name string){ fmt.Printf("%v 完成了减法运算,%v - %v = %v \n", name, call.Num1, call.Num2, call.Num1 - call.Num2) } func (call *Call) GetSum(name string){ fmt.Printf("%v 完成了加法运算,%v + %v = %v \n", name, call.Num1, call.Num2, call.Num1 + call.Num2) } func TestReflect(t *testing.T) { var ( call *Call rValues []reflect.Value rValues2 []reflect.Value ) ptrType := reflect.TypeOf(call) //获取call的指针的reflect.Type trueType := ptrType.Elem() //获取type的真实类型 ptrValue := reflect.New(trueType) //返回对象的指针对应的reflect.Value call = ptrValue.Interface().(*Call) trueValue := ptrValue.Elem() //获取真实的结构体类型 trueValue.FieldByName("Num1").SetInt(123)//设置对象属性,注意这个一定要是真实的结构类型的reflect.Value才能调用,指针类型reflect.Value的会报错 //ptrValue.FieldByName("Num2").SetInt(23) trueValue.FieldByName("Num2").SetInt(23) //rValues = make([]reflect.Value, 0) rValues = append(rValues, reflect.ValueOf("xiaopeng"))//调用对应的方法 fmt.Println(rValues) trueValue.MethodByName("GetSub").Call(rValues) /* fixme 在反射中,指针的方法不可以给实际类型调用,实际类型的方法可以给指针类型调用,因为go语言对这种操作做了封装 所以下面一句是没问题的 下下一句会运行时报错 */ //ptrValue.MethodByName("GetSub").Call(rValues) //trueValue.MethodByName("GetSum").Call(append(rValues2, reflect.ValueOf("hiram"))) ptrValue.MethodByName("GetSum").Call(append(rValues2, reflect.ValueOf("hiram"))) fmt.Println(call) /* fixme 在实际使用中 指针和实体都能相互转换,不会影响调用 但是指针的方法在方法体内的操作会影响到结构体本身属性 而实体的方法不会,因为go对于结构体、数组、基本类型都是值传递 */ call.GetSub("aaa") (*call).GetSub("bbb") call.GetSum("ccc") (*call).GetSum("ddd") }
感谢各位的阅读!关于“golang怎么用反射reflect操作结构体”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。