Obtenir pointeur vers la valeur à l'aide de la réflexion
J'ai une fonction qui parcourt tous les champs d'une interface passé comme paramètre. Afin d'atteindre cet objectif est, je suis l'aide de la réflexion. Le problème est que je ne sais pas comment faire pour obtenir l'adresse d'un non-pointeur de champ. Voici un exemple:
type Z struct {
Id int
}
type V struct {
Id int
F Z
}
type T struct {
Id int
F V
}
Le code ci-dessus représente mon test de structures. Maintenant, voici la fonction qui traverse une structure et des listes de détails à ce sujet:
func InspectStruct(o interface{}) {
val := reflect.ValueOf(o)
if val.Kind() == reflect.Interface && !val.IsNil() {
elm := val.Elem()
if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr {
val = elm
}
}
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
for i := 0; i < val.NumField(); i++ {
valueField := val.Field(i)
typeField := val.Type().Field(i)
address := "not-addressable"
if valueField.Kind() == reflect.Interface && !valueField.IsNil() {
elm := valueField.Elem()
if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr {
valueField = elm
}
}
if valueField.Kind() == reflect.Ptr {
valueField = valueField.Elem()
}
if valueField.CanAddr() {
address = fmt.Sprint(valueField.Addr().Pointer())
}
fmt.Printf("Field Name: %s,\t Field Value: %v,\t Address: %v\t, Field type: %v\t, Field kind: %v\n", typeField.Name,
valueField.Interface(), address, typeField.Type, valueField.Kind())
if valueField.Kind() == reflect.Struct {
InspectStruct(valueField.Interface())
}
}
}
Et voici le test après l'instanciation de la structure/initialisation:
t := new(T)
t.Id = 1
t.F = *new(V)
t.F.Id = 2
t.F.F = *new(Z)
t.F.F.Id = 3
InspectStruct(t)
Et enfin la sortie de InspectStruct appel:
Field Name: Id, Field Value: 1, Address: 408125440 , Field type: int , Field kind: int
Field Name: F, Field Value: {2 {3}}, Address: 408125444 , Field type: main.V , Field kind: struct
Field Name: Id, Field Value: 2, Address: not-addressable , Field type: int , Field kind: int
Field Name: F, Field Value: {3}, Address: not-addressable , Field type: main.Z , Field kind: struct
Field Name: Id, Field Value: 3, Address: not-addressable , Field type: int , Field kind: int
Comme vous pouvez le voir, je suis en utilisant la récursivité, de sorte que si l'un des champs est un struct type puis-je appeler InspectStruct.
Mon problème est que si tous les champs ont été initialisé pour l'ensemble de la structure en "t" de la hiérarchie, je ne suis pas en mesure d'obtenir l'adresse de tout le terrain situé à plus de profondeur que de "t". Je serais vraiment reconnaissant de toute aide.
OriginalL'auteur Mihai H | 2014-06-22
Vous devez vous connecter pour publier un commentaire.
Passant
reflect.Value
au lieu deinterface{}
semble résoudre le problème, mais je ne sais pas pourquoivalueField.Interface()
ne fonctionne pas.De travail exemple : http://play.golang.org/p/nleA2YWMj8
OriginalL'auteur OneOfOne
La raison
Interface()
ne fonctionne pas est à cause de l'interface wrapper il retourne. Pour donner une idée de ce qui se passe, nous allons regarder ce que nous faisons sans réflexion:(Aire de jeux)
Cependant, penser à la
interface{}
cela renvoie. Qu'est-ce que c'emballage? Le valeur deptr.F
- copie. C'est ce quevalue.Interface
n', il vous renvoie lainterface{}
enveloppant le terrain. Il n'y a plus aucun pointeur de métadonnées, il est complètement détaché de l'original struct.Comme vous l'aurez remarque, le passage d'une valeur directement à
reflect.ValueOf
retournera toujoursfalse
pour CanAddr pour le "top niveau" -- parce que l'adresse est vide de sens, car il vous donnera l'adresse de la copie de la valeur, l'évolution n'était pas vraiment dire quoi que ce soit. (Gardez à l'esprit que les pointeurs sont des valeurs trop -- si vous voulez l'adresse d'un pointeur de champ à valeurs comme*Foo
, vous êtes vraiment à la recherche pour**Foo
).Ainsi, dans notre exemple ci-dessus, si nous étions à la naïveté de passer dans
reflect.ValueOf(ExtractField(ms))
nous aimerions tirer leValueOf
f
, qui non seulement n'ont pas l'adresse que vous souhaitez, il n'est même pas adressable selon réfléchir, car il serait jamais donner une adresse valide autant que réfléchir, c'est la seule adresse qu'il pourrait vous donner l'adresse de la valeur interne de la copie dans leValue
struct).Alors pourquoi passer le
Value
vers le bas le trou de lapin de travail? Eh bien, la seule vraie manière de le dire est quereflect.Value
conserve les métadonnées nécessaires lorsque vous utilisezElem
etField
, tandis que leinterface{}
ne peut pas. Ainsi, alors que lareflect.Value
peut ressembler à:Tout ce qu'il peut vous donner est ce
Merci pour compléter @OneOfOne réponse. Beaucoup apprécié!
OriginalL'auteur LinearZoetrope
Je suis descendu un reflètent trou de lapin aujourd'hui, beaucoup appris de l'étude de ce code et Jsor réponse, je vous remercie. Je suis venu à une conclusion différente si sur votre problème, qui conduit à peut-être plus simple solution:
1) Vous êtes en passant un pointeur vers une struct lorsque vous avez initialement appel de la fonction, mais...
2) Lorsque vous recurse en appelant 'InspectStruct(valueField.Interface())', plutôt que de passer à la structure intégré par pointeur, vous êtes le passage par valeur.
Puisque vous êtes de passage par valeur, allez va créer un temporaire et ne vous laisse pas prendre l'adresse. Au lieu de cela, lorsque vous de manière récursive, appel valueField.Addr().Interface(), qui va passer un pointeur vers les systèmes embarqués struct.
Avec ce changement, j'obtiens le résultat que vous attendez:
OriginalL'auteur Bill Soudan
Réponse de @OneofOne est parfait, mais il est préférable d'ajouter un contrôle supplémentaire
il est nécessaire car parfois, vous pouvez demander de l'interface à partir d'une valeur nulle struct. Celles qu'il arrive, il va paniquer.
OriginalL'auteur Ihor