您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關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進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。