Iterasi melalui bidang struct di Go

107

Pada dasarnya, satu-satunya cara (yang saya ketahui) untuk melakukan iterasi melalui nilai bidang a structadalah seperti ini:

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
}

Saya bertanya-tanya, jika ada cara yang lebih baik dan lebih fleksibel untuk mencapai []interface{}{ r.a_number, r.a_string, }, jadi saya tidak perlu membuat daftar setiap parameter satu per satu, atau sebagai alternatif, apakah ada cara yang lebih baik untuk mengulang melalui struct?

Saya mencoba melihat-lihat reflectpaketnya, tetapi saya menemui hambatan, karena saya tidak yakin apa yang harus dilakukan setelah saya mengambilnya reflect.ValueOf(*r).Field(0).

Terima kasih!

omninonsense
sumber
5
Berikut adalah artikel yang sangat menarik tentang refleksi: blog.golang.org/laws-of-reflection Mengikuti salah satu contoh dari artikel: play.golang.org/p/_bKAQ3dQlu Namun perlu diperhatikan bahwa Anda tidak dapat mencari bidang yang tidak diekspor dengan paket refleksi (yaitu bidang yang dimulai dengan huruf kecil)
kreack

Jawaban:

126

Setelah Anda mengambil reflect.Valuebidang dengan menggunakan Field(i)Anda bisa mendapatkan nilai antarmuka dari itu dengan memanggil Interface(). Nilai antarmuka tersebut kemudian mewakili nilai bidang.

Tidak ada fungsi untuk mengonversi nilai bidang menjadi tipe beton karena, seperti yang Anda ketahui, tidak ada obat generik yang digunakan. Dengan demikian, tidak ada fungsi dengan tanda tangan GetValue() T dengan Tmenjadi jenis lapangan yang (yang mengubah tentu saja, tergantung di lapangan).

Hal terdekat yang dapat Anda capai dalam perjalanan adalah GetValue() interface{}dan inilah yang sebenarnya reflect.Value.Interface() ditawarkan.

Kode berikut mengilustrasikan cara mendapatkan nilai dari setiap bidang yang diekspor dalam struct menggunakan refleksi ( play ):

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)
}
nemo
sumber
24
Ya karena go tidak membutuhkan obat generik. Batuk, Batuk :-) Adakah cara untuk mendapatkan jenis medan tersebut?
U Avalos
1
melalui reflect.Value.Type(), ya. Tetapi perhatikan bahwa tipe bukan warga negara kelas satu, jadi Anda hanya dapat membuat contoh nilai baru dari tipe itu menggunakan reflect.
nemo
6
v.Field(i).Interface()panik jika Anda mencoba mengakses bidang pribadi yang tidak diekspor. Hati-hati :)
Tarion
10
Menggunakan v.Field(i).CanInterface() salah satunya dapat menghindari kepanikan dalam kasus bidang yang tidak diekspor.
Pedram Esmaeeli
1
Bagaimana saya bisa mendapatkan nama field?
Sathesh
33

Jika Anda ingin Iterasi melalui Fields and Values ​​dari sebuah struct maka Anda dapat menggunakan kode Go di bawah ini sebagai referensi.

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())
    }
}

Jalankan di taman bermain

Catatan: Jika Fields di struct Anda tidak diekspor maka v.Field(i).Interface()akan menimbulkan kepanikanpanic: reflect.Value.Interface: cannot return value obtained from unexported field or method.

Chetan Kumar
sumber
0

Mengambil solusi Chetan Kumar dan jika Anda perlu mendaftar ke amap[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"))
}

BAJA
sumber