简体   繁体   English

golang - 对嵌入式结构的反思

[英]golang - reflection on embedded structs

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.我想反思 D 并进入 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.直观地说,在尝试解决方案之前,我假设我能够遍历结构 D 并使用反射(X、Y、Z)获取所有字段,而不必处理 B。

But as you can see, I only see the embedded struct B using reflection and not its fields.但是正如您所看到的,我只看到使用反射的嵌入式结构 B 而不是它的字段。

http://play.golang.org/p/qZQD5GdTA8 http://play.golang.org/p/qZQD5GdTA8

Is there a way I can make B fully transparent when reflecting on D?有没有办法让 B 在反射到 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.对一个公共结构(此处示例中的 B)进行成像,该结构通过使用嵌入在多个其他结构中使用。 Using reflection, the attempt is to copy D into another similar struct in a different package.使用反射,尝试将 D 复制到不同包中的另一个类似结构中。 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.我不想为每个结构创建自定义解决方案。

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
}

Use the following code to collect all promoted field names as keys in map m :使用以下代码收集所有提升的字段名称作为映射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)
}

Run it on the playground .在操场上运行它

This program prints X, Y an Z as requested in the question, but also B because B is also a field name.该程序按照问题的要求打印 X、Y 和 Z,但也打印 B,因为 B 也是字段名称。

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. encoding/json/encode.go 中typeField函数处理这两个问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM