วนซ้ำในฟิลด์ของโครงสร้างใน 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อย่างไรก็ตามคุณไม่สามารถค้นหาฟิลด์ที่ไม่ได้ส่งออก ด้วยแพ็กเกจสะท้อนแสง (เช่นฟิลด์ที่ขึ้นต้นด้วยตัวพิมพ์เล็ก)
creack

คำตอบ:


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
ใช่เพราะไปไม่จำเป็นต้องมียาสามัญ ไอ :-) มีวิธีรับประเภทของสนามไหม?
U Avalos

1
ผ่านreflect.Value.Type()ใช่ แต่ทราบว่าประเภทประชาชนไม่ได้ชั้นแรกในการเดินทางเพื่อให้คุณสามารถค่าใหม่เท่านั้น instantiate reflectประเภทที่ใช้
nemo

7
v.Field(i).Interface()ตื่นตระหนกหากคุณพยายามเข้าถึงช่องส่วนตัวที่ไม่ได้ส่งออก ระวังตัวด้วย :)
Tarion

10
การใช้v.Field(i).CanInterface() อย่างใดอย่างหนึ่งสามารถหลีกเลี่ยงความตื่นตระหนกในกรณีของช่องที่ไม่ได้ส่งออก
Pedram Esmaeeli

1
ฉันจะได้รับชื่อฟิลด์ได้อย่างไร?
Sathesh

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

ใช้วิธีแก้ปัญหาของChetan Kumarและในกรณีที่คุณต้องการใช้กับ 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"))
}

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.