英文:
golang - reflection on embedded structs
问题
给定一个如下的结构体:
type B struct {
X string
Y string
}
type D struct {
B
Z string
}
我想要通过反射来获取结构体D中的字段X、Y和Z。
直观地说,在尝试解决这个问题之前,我假设我可以遍历结构体D并使用反射获取所有字段(X、Y、Z),而不需要处理B。
但是正如你所看到的,我只能通过反射看到嵌入的结构体B,而无法看到它的字段。
http://play.golang.org/p/qZQD5GdTA8
有没有办法在反射结构体D时使B完全透明?
为什么我想要这样做?
想象一个常见的结构体(在这个例子中是B),它通过嵌入在多个其他结构体中被使用。使用反射,尝试将D复制到另一个不同包中的类似结构体中。用于复制的目标结构体将会将所有属性平铺布置(没有嵌入)。因此,源结构体与目标结构体之间存在不匹配(嵌入与非嵌入),但所有平铺布置的属性都是相同的。我不想为每个结构体创建自定义解决方案。
英文:
Given a struct like so:
type B struct {
X string
Y string
}
type D struct {
B
Z string
}
I want to reflect on D and get to the fields X, Y, Z.
Intuitively, before attempting the solution, I was assuming I would be able to traverse the struct D and get all fields using reflection (X, Y, Z) and won't have to deal with B.
But as you can see, I only see the embedded struct B using reflection and not its fields.
http://play.golang.org/p/qZQD5GdTA8
Is there a way I can make B fully transparent when reflecting on D?
Why do I want this?
Imaging a common struct (B in the example here), that is used in multiple other structs by using embedding. Using reflection, the attempt is to copy D into another similar struct in a different package. The destination struct for copying will have all attributes flatly laid out (no embedding there). So there is a mismatch from the source to the destination (embedding vs no embedding) but all the attributes flatly laid out are the same. I don't want to create custom solutions for each struct.
答案1
得分: 40
你期望的“透明度”只是语法糖,与数据表示无关。如果你想要一个能够展开数据结构的函数,你需要自己编写它。
例如(在play链接中):
func DeepFields(iface interface{}) []reflect.Value {
fields := make([]reflect.Value, 0)
ifv := reflect.ValueOf(iface)
ift := reflect.TypeOf(iface)
for i := 0; i < ift.NumField(); i++ {
v := ifv.Field(i)
switch v.Kind() {
case reflect.Struct:
fields = append(fields, DeepFields(v.Interface())...)
default:
fields = append(fields, v)
}
}
return fields
}
英文:
The 'transparency' you expected is just syntactic sugar and has nothing to do with the data representation. If you want to have a function that flattens your data structure, you would have to write it by yourself.
For example (On play):
func DeepFields(iface interface{}) []reflect.Value {
fields := make([]reflect.Value, 0)
ifv := reflect.ValueOf(iface)
ift := reflect.TypeOf(iface)
for i := 0; i < ift.NumField(); i++ {
v := ifv.Field(i)
switch v.Kind() {
case reflect.Struct:
fields = append(fields, DeepFields(v.Interface())...)
default:
fields = append(fields, v)
}
}
return fields
}
答案2
得分: 3
使用以下代码将所有提升的字段名作为键收集到映射m
中:
func collectFieldNames(t reflect.Type, m map[string]struct{}) {
// 如果不是结构体或指向结构体的指针,则返回。
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
if t.Kind() != reflect.Struct {
return
}
// 遍历字段,在映射中收集字段名。
for i := 0; i < t.NumField(); i++ {
sf := t.Field(i)
m[sf.Name] = struct{}{}
// 递归进入匿名字段。
if sf.Anonymous {
collectFieldNames(sf.Type, m)
}
}
}
使用方法如下:
m := make(map[string]struct{})
collectFieldNames(reflect.TypeOf((*D)(nil)), m)
for name := range m {
fmt.Println(name)
}
这个程序按照问题中的要求打印了 X、Y 和 Z,但也打印了 B,因为 B 也是一个字段名。
这个答案中的函数可以改进:
- 不会在递归类型定义时出错。
- 不会包含在层级结构中重复的名称。
encoding/json/encode.go 中的 typeField
函数处理了这两个问题。
英文:
Use the following code to collect all promoted field names as keys in map m
:
func collectFieldNames(t reflect.Type, m map[string]struct{}) {
// Return if not struct or pointer to struct.
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
if t.Kind() != reflect.Struct {
return
}
// Iterate through fields collecting names in map.
for i := 0; i < t.NumField(); i++ {
sf := t.Field(i)
m[sf.Name] = struct{}{}
// Recurse into anonymous fields.
if sf.Anonymous {
collectFieldNames(sf.Type, m)
}
}
}
Use it like this:
m := make(map[string]struct{})
collectFieldNames(reflect.TypeOf((*D)(nil)), m)
for name := range m {
fmt.Println(name)
}
This program prints X, Y an Z as requested in the question, but also B because B is also a field name.
This function in this answer can be improved:
- Don't blow up on recursive type definitions.
- Do not include names repeated at the same level in the hierarchy.
The typeField
function in encoding/json/encode.go handles both of these issues.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论