golang - 对嵌入式结构的反思

时间:2014-06-20 18:26:58

标签: go

给出如此结构:

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)进行成像,通过使用嵌入在多个其他结构中使用。使用反射,尝试将D复制到另一个包中的另一个类似结构中。复制的目标结构将平面布局所有属性(不嵌入)。因此,从源到目标(嵌入与不嵌入)存在不匹配,但所有平面布局的属性都是相同的。我不想为每个结构创建自定义解决方案。

2 个答案:

答案 0 :(得分:21)

透明度'你期望的只是语法糖,与数据表示无关。如果你想要一个能够平衡数据结构的函数,你必须自己编写它。

例如(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
}

答案 1 :(得分:0)

使用以下代码收集所有提升的字段名称作为映射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)
        }
    }
}

像这样使用它:

m := make(map[string]struct{})
collectFieldNames(reflect.TypeOf((*D)(nil)), m)
for name := range m {
    fmt.Println(name)
}

Run it on the playground

此程序将按问题的要求打印X,Y和Z,但也会打印B,因为B也是字段名。

此答案中的此功能可以改进:

  • 不要为递归类型定义而烦恼。
  • 请勿在层次结构中包含在同一级别重复的名称。

encoding/json/encode.go中的typeField函数可以解决这两个问题。