golang — отражение встроенных структур

#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

Есть ли способ сделать B полностью прозрачным при отражении D?

Зачем мне это нужно?

Отображение общей структуры (B в приведенном здесь примере), которая используется во многих других структурах с помощью встраивания. Используя отражение, попытка состоит в том, чтобы скопировать D в другую аналогичную структуру в другом пакете. Целевая структура для копирования будет иметь все атрибуты, четко изложенные (без встраивания туда). Таким образом, существует несоответствие между источником и местом назначения (встраивание или отсутствие встраивания), но все атрибуты, четко изложенные, одинаковы. Я не хочу создавать собственные решения для каждой структуры.

Ответ №1:

Ожидаемая вами «прозрачность» — это просто синтаксический сахар и не имеет ничего общего с представлением данных. Если вы хотите иметь функцию, которая выравнивает вашу структуру данных, вам придется написать ее самостоятельно.

Например (при воспроизведении):

 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. возможно, вам следует проверить, встроено ли поле, прежде чем сглаживать его, если оно должно имитировать «прозрачность», ожидаемую операцией? т. е. if v.Kind() == reflect.Struct amp;amp; v.Anonymous{

Ответ №2:

Используйте следующий код, чтобы собрать все продвигаемые имена полей в качестве ключей в 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)
        }
    }
}
 

Используйте это так:

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

Запустите его на игровой площадке.

Эта программа печатает X, Y и Z, как указано в вопросе, но также и B, потому что B также является именем поля.

Эта функция в этом ответе может быть улучшена:

  • Не раздувайте определения рекурсивных типов.
  • Не включайте имена, повторяющиеся на одном уровне иерархии.

typeField Функция в encoding / json / encode.go решает обе эти проблемы.