Я хочу извлечь тег элементов карты структуры с помощью отражения, в то время как я обнаружил, что если получить значение члена из MapIndex, его тип будет распознан как «* интерфейс {}», и, следовательно, вся информация о типе будет потеряна, никакое упоминание об отражении не может извлекать детали Информация.
package main
import (
"fmt"
"reflect"
)
type Student struct {
Sname string `MyTag:"student-name"`
}
type Teacher struct {
Name string `MyTag:"teacher-name"`
Students map[string]Student `MyTag:"teacher-students"`
}
var sam = Teacher{
Name: "Sam",
Students: map[string]Student{
"Sen": {
Sname: "Sen",
},
},
}
func traversalTag(obj interface{}) {
theType := reflect.TypeOf(obj)
fmt.Printf("Traversal tag with obj: type %v, value %v\n", theType.String(), obj)
elem := reflect.TypeOf(obj).Elem()
for i := 0; i < elem.NumField(); i++ {
fmt.Printf("Tag name %s, value %s\n", elem.Field(i).Name, elem.Field(i).Tag)
}
}
func tryMapWithType(students map[string]Student) {
for key, theValue := range students {
fmt.Printf("Key: %v, Value: %v, value pointer %p\n", key, theValue, &theValue)
traversalTag(&theValue)
}
}
func tryMapWithReflect(obj interface{}) {
reflectMap := reflect.ValueOf(obj)
for _, key := range reflectMap.MapKeys() {
theValue := reflectMap.MapIndex(key).Interface()
fmt.Printf("Key: %v, Value: %v, value pointer %p\n", key, theValue, &theValue)
traversalTag(&theValue) // Will have error
}
}
func main() {
tryMapWithType(sam.Students)
tryMapWithReflect(sam.Students)
}
После запуска я получил следующую ошибку:
Starting: C:\Users\Mento\go\bin\dlv.exe dap --check-go-version=false --listen=127.0.0.1:50308 from d:\Coding\Golang\demo
DAP server listening at: 127.0.0.1:50308
Key: Sen, Value: {Sen}, value pointer 0xc000044230
Traversal tag with obj: type *main.Student, value &{Sen}
Tag name Sname, value MyTag:"student-name"
Key: Sen, Value: {Sen}, value pointer 0xc0000442c0
Traversal tag with obj: type *interface {}, value 0xc0000442c0
panic: reflect: NumField of non-struct type interface {}
goroutine 1 [running]:
reflect.(*rtype).NumField(0xec2d20)
C:/Program Files/Go/src/reflect/type.go:1015 +0xc8
main.traversalTag({0xebd9e0, 0xc0000442c0})
d:/Coding/Golang/demo/demo.go:31 +0x1cb
main.tryMapWithReflect({0xec3d40, 0xc00007a480})
d:/Coding/Golang/demo/demo.go:48 +0x2c9
main.main()
d:/Coding/Golang/demo/demo.go:54 +0x38
Process 8716 has exited with status 2
dlv dap (11448) exited with code: 0
Может ли кто-нибудь подсказать, как получить указатель членов карты с исходной информацией о типе?
Спасибо, Менто
Как вы знаете, использование &theValue
разрешается в тип *interface{}
. Тип *interface{}
отличается от типа *Student
, который вы передаете в traversalTag
из tryMapWithType
.
Если вы хотите передать *Student
в traversalTag
из tryMapWithReflect
, вам нужно создать это значение указателя с помощью отражения. Простого нативного оператора адреса Go &
недостаточно.
Когда у вас есть адресуемый reflect.Value
, все, что вам нужно сделать, это вызвать метод .Addr()
, чтобы получить указатель на адресуемое значение, однако элементы карты не адресуются и, следовательно, reflectMap.MapIndex(key)
не адресуются. Так что, к сожалению для вас, невозможно сделать reflectMap.MapIndex(key).Addr().Interface()
, чтобы получить *Student
.
Таким образом, ваш единственный вариант — использовать отражение для создания нового значения типа *Student
, установить указываемое значение на значение на карте, а затем вернуть .Interface()
этого.
func tryMapWithReflect(obj interface{}) {
reflectMap := reflect.ValueOf(obj)
for _, key := range reflectMap.MapKeys() {
theValue := reflectMap.MapIndex(key).Interface()
// allocate a new value of type *Student
newValue := reflect.New(reflectMap.MapIndex(key).Type())
// use Elem do dereference *Stunded
// and then use Set to set the Student to the content of theValue
newValue.Elem().Set(reflect.ValueOf(theValue))
fmt.Printf("Key: %v, Value: %v, value pointer %p\n", key, newValue.Elem().Interface(), newValue.Interface())
// return the newValue *Student
traversalTag(newValue.Interface())
}
}
https://go.dev/play/p/pNL2wjsOW5y
В качестве альтернативы просто удалите .Elem()
из traversalTag
, и тогда вам не нужно будет передавать на него указатели.
func traversalTag(obj interface{}) {
theType := reflect.TypeOf(obj)
fmt.Printf("Traversal tag with obj: type %v, value %v\n", theType.String(), obj)
elem := reflect.TypeOf(obj)
for i := 0; i < elem.NumField(); i++ {
fmt.Printf("Tag name %s, value %s\n", elem.Field(i).Name, elem.Field(i).Tag)
}
}
https://go.dev/play/p/EwJ5e0uc2pd
это или изменение типа поля на
map[string]*Student
также помогает