#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 решает обе эти проблемы.