【问题标题】:golang - reflection on embedded structsgolang - 对嵌入式结构的反思
【发布时间】:2014-08-11 13:53:47
【问题描述】:

给定这样的结构:

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

有没有办法让 B 在反射到 D 时完全透明?

我为什么要这个?

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

【问题讨论】:

    标签: go


    【解决方案1】:

    使用以下代码收集所有提升的字段名称作为映射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 函数可以处理这两个问题。

    【讨论】:

      【解决方案2】:

      您期望的“透明度”只是语法糖,与数据表示无关。如果你想拥有一个扁平化数据结构的函数,你必须自己编写。

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

      【讨论】:

      • 如果要模拟 OP 所期望的“透明度”,也许您应该先检查该字段是否已嵌入以将其展平?即if v.Kind() == reflect.Struct &amp;&amp; v.Anonymous{
      猜你喜欢
      • 2017-12-24
      • 2022-11-22
      • 1970-01-01
      • 2015-05-16
      • 2021-06-20
      • 2019-09-15
      • 1970-01-01
      • 2013-10-17
      相关资源
      最近更新 更多