Récupération de l'adresse de valeur à l'aide de la réflexion
Réfléchir sur une interface permet de parcourir ses champs. Cependant, obtenir l’adresse d’un champ non pointeur présente un défi. Cet article aborde ce problème en examinant les limites du déréférencement de pointeur et présente une solution.
Aperçu du problème
Considérez le code suivant :
<code class="go">type Z struct { Id int } type V struct { Id int F Z } type T struct { Id int F V }</code>
La fonction InspectStruct parcourt récursivement une structure et répertorie les détails de ses champs, y compris l'adresse. Voici une implémentation simplifiée :
<code class="go">func InspectStruct(o interface{}) { val := reflect.ValueOf(o) if val.Kind() == reflect.Interface && !val.IsNil() { val = val.Elem() } if val.Kind() == reflect.Ptr { val = val.Elem() } for i := 0; i < val.NumField(); i++ { valueField := val.Field(i) if valueField.Kind() == reflect.Ptr { valueField = valueField.Elem() } address := "not-addressable" if valueField.CanAddr() { address = fmt.Sprint(valueField.Addr().Pointer()) } fmt.Printf("Field Name: %s,\t Field Value: %v,\t Address: %v\n", val.Type().Field(i).Name, valueField.Interface(), address) if valueField.Kind() == reflect.Struct { InspectStruct(valueField.Interface()) } } }</code>
L'exécution de cette fonction sur une instance de type T donne :
Field Name: Id, Field Value: 1, Address: 408125440 Field Name: F, Field Value: {2 {3}}, Address: 408125444 Field Name: Id, Field Value: 2, Address: not-addressable Field Name: F, Field Value: {3}, Address: not-addressable Field Name: Id, Field Value: 3, Address: not-addressable
Comme vous pouvez le constater, les adresses des champs non pointeurs ("Id" et "F" de type "Z" et "V") ne sont pas accessibles.
Solution
Le problème vient du déréférencement de la valeur obtenue à partir de valueField.Interface( ). Cela renvoie une interface{} qui, lorsqu'elle est utilisée dans une valeur de champ, peut entraîner la perte des informations d'adresse.
Voici une solution modifiée qui résout ce problème :
<code class="go">func InspectStructV(val reflect.Value) { if val.Kind() == reflect.Interface && !val.IsNil() { val = val.Elem() } if val.Kind() == reflect.Ptr { val = val.Elem() } for i := 0; i < val.NumField(); i++ { valueField := val.Field(i) if valueField.Kind() == reflect.Ptr { valueField = valueField.Elem() } address := "not-addressable" if valueField.CanAddr() { address = fmt.Sprintf("0x%X", valueField.Addr().Pointer()) } fmt.Printf("Field Name: %s,\t Field Value: %v,\t Address: %v\n", val.Type().Field(i).Name, valueField.Interface(), address) if valueField.Kind() == reflect.Struct { InspectStructV(valueField) } } } func InspectStruct(v interface{}) { InspectStructV(reflect.ValueOf(v)) }</code>
En passant un reflet .Value au lieu d'une interface{} vers InspectStructV, l'adresse du champ peut être obtenue correctement.
Conclusion
Lors du parcours de champs non pointeurs dans une structure en utilisant réflexion, il est crucial de préserver la réflexion.Value au lieu de s'appuyer sur valueField.Interface(). En passant Reflect.Value à la fonction récursive InspectStructV, vous pouvez récupérer avec succès les adresses des champs à n'importe quelle profondeur de la structure.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!