Lặp lại các trường của một cấu trúc trong Go


107

Về cơ bản, cách duy nhất (mà tôi biết) để lặp qua các giá trị của các trường của a structlà như sau:

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
}

Tôi đã tự hỏi, nếu có một cách tốt hơn và linh hoạt hơn để đạt được []interface{}{ r.a_number, r.a_string, }, vì vậy tôi không cần phải liệt kê từng tham số riêng lẻ, hoặc cách khác, có cách nào tốt hơn để lặp qua một cấu trúc không?

Tôi đã cố gắng xem qua reflectgói hàng, nhưng tôi đã va phải một bức tường, vì tôi không biết phải làm gì sau khi lấy ra reflect.ValueOf(*r).Field(0).

Cảm ơn!


5
Đây là một bài viết rất thú vị liên quan đến phản chiếu: blog.golang.org/laws-of-reflection Theo một trong những ví dụ từ bài viết: play.golang.org/p/_bKAQ3dQlu Tuy nhiên, lưu ý rằng bạn không thể tra cứu các trường không được xuất với phản ánh gói (trường tức là bắt đầu với chữ thường)
creack

Câu trả lời:


126

Sau khi bạn đã truy xuất trường reflect.Valuecủa trường bằng cách sử dụng, Field(i)bạn có thể nhận được giá trị giao diện từ nó bằng cách gọi Interface(). Giá trị giao diện cho biết sau đó đại diện cho giá trị của trường.

Không có chức năng nào để chuyển đổi giá trị của trường thành một kiểu cụ thể như bạn có thể biết, không có số liệu chung. Do đó, không có chức năng nào với chữ ký GetValue() T với Tviệc là kiểu của trường đó (tất nhiên sẽ thay đổi, tùy thuộc vào trường).

Gần nhất bạn có thể đạt được GetValue() interface{}và đây chính xác là những gì reflect.Value.Interface() mang lại.

Đoạn mã sau minh họa cách lấy các giá trị của mỗi trường được xuất trong một cấu trúc bằng cách sử dụng phản chiếu ( phát ):

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
Đúng vì đi không cần chung chung. Khụ khụ :-) Có cách nào lấy được loại trường không?
U Avalos

1
thông qua reflect.Value.Type(), có. Nhưng lưu ý rằng các loại không phải là công dân hạng nhất, vì vậy bạn chỉ có thể khởi tạo các giá trị mới của loại đó bằng cách sử dụng reflect.
nemo

6
v.Field(i).Interface()hoảng sợ nếu bạn cố gắng truy cập vào các trường riêng tư không được xuất. Chỉ cần cẩn thận :)
Tarion

10
Sử dụng v.Field(i).CanInterface() một có thể tránh được sự hoảng loạn trong trường hợp các trường không được báo cáo.
Pedram Esmaeeli

1
Làm cách nào để lấy tên trường?
Sathesh

33

Nếu bạn muốn Lặp lại các Trường và Giá trị của một cấu trúc thì bạn có thể sử dụng mã Go bên dưới làm tham chiếu.

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

Chạy trong sân chơi

Lưu ý: Nếu các Trường trong cấu trúc của bạn không được xuất thì v.Field(i).Interface()sẽ gây hoảng sợpanic: reflect.Value.Interface: cannot return value obtained from unexported field or method.


0

Dùng giải pháp Chetan Kumar và trong trường hợp bạn cần áp dụngmap[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"))
}

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.