Итерации по полям структуры в Go

108

По сути, единственный способ (который я знаю) перебирать значения полей a structвыглядит следующим образом:

type Example struct {
    a_number uint32
    a_string string
}

//...

r := &Example{(2 << 31) - 1, "...."}:
for _, d:= range []interface{}{ r.a_number, r.a_string, } {
  //do something with the d
}

Мне было интересно, есть ли лучший и более универсальный способ достижения []interface{}{ r.a_number, r.a_string, }, поэтому мне не нужно перечислять каждый параметр по отдельности или, альтернативно, есть лучший способ перебрать структуру?

Я попытался просмотреть reflectпакет, но наткнулся на стену, потому что не знаю, что делать после того, как заберу его reflect.ValueOf(*r).Field(0).

Спасибо!

всезнайка
источник
5
Вот очень интересная статья об отражении: blog.golang.org/laws-of-reflection Следуя одному из примеров из статьи: play.golang.org/p/_bKAQ3dQlu Обратите внимание, однако, что вы не можете искать неэкспортированные поля с пакетом отражения (т.е. поля, которые начинаются со строчных
букв

Ответы:

126

После того, как вы получили reflect.Valueполе с помощью, Field(i)вы можете получить из него значение интерфейса, вызвав Interface(). Упомянутое значение интерфейса затем представляет значение поля.

Нет функции для преобразования значения поля в конкретный тип, поскольку, как вы знаете, нет универсальных шаблонов. Таким образом, нет никакой функции с подписью GetValue() T с Tявляется типом этого поля (который изменяет конечно, в зависимости от области).

Самое близкое, что вы можете достичь на ходу, - GetValue() interface{}это именно то, что reflect.Value.Interface() предлагает.

В следующем коде показано, как получить значения каждого экспортированного поля в структуре с помощью отражения ( воспроизведения ):

import (
    "fmt"
    "reflect"
)

func main() {
    x := struct{Foo string; Bar int }{"foo", 2}

    v := reflect.ValueOf(x)

    values := make([]interface{}, v.NumField())

    for i := 0; i < v.NumField(); i++ {
        values[i] = v.Field(i).Interface()
    }

    fmt.Println(values)
}
Немо
источник
24
Да, потому что для go не нужны дженерики. Кашель, кашель :-) Есть ли способ узнать тип поля?
U Avalos
1
через reflect.Value.Type(), да. Но обратите внимание, что типы не являются первоклассными гражданами, поэтому вы можете создавать только новые значения этого типа, используя reflect.
nemo
7
v.Field(i).Interface()паникует, если вы пытаетесь получить доступ к неэкспортированным частным полям. Только будьте осторожны :)
Тарион
11
С его помощью v.Field(i).CanInterface() можно избежать паники в случае неэкспортируемых полей.
Педрам Эсмаэли
1
Как я могу получить название поля?
Сатеш
33

Если вы хотите перебирать поля и значения структуры, вы можете использовать приведенный ниже код Go в качестве справочника.

package main

import (
    "fmt"
    "reflect"
)

type Student struct {
    Fname  string
    Lname  string
    City   string
    Mobile int64
}

func main() {
    s := Student{"Chetan", "Kumar", "Bangalore", 7777777777}
    v := reflect.ValueOf(s)
    typeOfS := v.Type()

    for i := 0; i< v.NumField(); i++ {
        fmt.Printf("Field: %s\tValue: %v\n", typeOfS.Field(i).Name, v.Field(i).Interface())
    }
}

Беги на детской площадке

Примечание: если поля в вашей структуре не экспортируются, то v.Field(i).Interface()возникнет паника.panic: reflect.Value.Interface: cannot return value obtained from unexported field or method.

Четан Кумар
источник
0

Принимая раствор Четана Кумара и в случае необходимости обратиться кmap[string]int

package main

import (
    "fmt"
    "reflect"
)

type BaseStats struct {
    Hp           int
    HpMax        int
    Mp           int
    MpMax        int
    Strength     int
    Speed        int
    Intelligence int
}

type Stats struct {
    Base map[string]int
    Modifiers []string
}

func StatsCreate(stats BaseStats) Stats {
    s := Stats{
        Base: make(map[string]int),
    }

    //Iterate through the fields of a struct
    v := reflect.ValueOf(stats)
    typeOfS := v.Type()

    for i := 0; i< v.NumField(); i++ {
        val := v.Field(i).Interface().(int)
        s.Base[typeOfS.Field(i).Name] = val
    }
    return s
}

func (s Stats) GetBaseStat(id string) int {
    return s.Base[id]
}


func main() {
    m := StatsCreate(BaseStats{300, 300, 300, 300, 10, 10, 10})

    fmt.Println(m.GetBaseStat("Hp"))
}

СТАЛИ
источник