Доступ до властивості struct за іменем


76

Ось проста програма go, яка не працює:

package main
import "fmt"

type Vertex struct {
    X int
    Y int
}

func main() {
    v := Vertex{1, 2}
    fmt.Println(getProperty(&v, "X"))
}

func getProperty(v *Vertex, property string) (string) {
    return v[property]
}

Помилка:

prog.go: 18: недійсна операція: v [властивість] (індекс типу * Vertex)

Я хочу отримати доступ до властивості Vertex X, використовуючи його назву. Якщо я v.Xце роблю, це працює, але v["X"]ні.

Хтось може сказати мені, як зробити так, щоб це працювало?

Відповіді:


118

Більшість кодів не потребують такого роду динамічного пошуку. Це неефективно в порівнянні з прямим доступом (компілятор знає зміщення поля X у структурі Vertex, він може скомпілювати vX до однієї машинної інструкції, тоді як для динамічного пошуку потрібна якась реалізація хеш-таблиці або подібне). Він також забороняє статичне введення: компілятор не має можливості перевірити, що ви не намагаєтесь отримати доступ до невідомих полів динамічно, і він не може знати, яким має бути отриманий тип.

Але ... мова забезпечує модуль відображення для тих рідкісних випадків, коли вам це потрібно.

package main

import "fmt"
import "reflect"

type Vertex struct {
    X int
    Y int
}

func main() {
    v := Vertex{1, 2}
    fmt.Println(getField(&v, "X"))
}

func getField(v *Vertex, field string) int {
    r := reflect.ValueOf(v)
    f := reflect.Indirect(r).FieldByName(field)
    return int(f.Int())
}

Тут немає помилок при перевірці, тому ви запанікуєте, якщо попросите поле, яке не існує, або поле не має типу int. Щоб дізнатись більше, перегляньте документацію для відображення .


2
+1, а також дивіться також “Закони роздумів”, що вводять ідею.
Fred Foo

1
Цей модуль відображення виглядає хитро. Я намагався використовувати його без успіху. Здається, я забув зателефонувати Ìndirect. Дякую за робочий приклад і всі пояснення. Дійсно вдячний :-)
Ніколас БАДІЯ

2
Дякуємо за пояснення над кодом. Для мене це навіть корисніше, ніж сам код!
Небулосар

15

Тепер у вас є проект oleiade / reflections, який дозволяє отримати / встановити поля щодо значення структури або покажчиків.
Це робить використання reflectпакету менш складним.

s := MyStruct {
    FirstField: "first value",
    SecondField: 2,
    ThirdField: "third value",
}

fieldsToExtract := []string{"FirstField", "ThirdField"}

for _, fieldName := range fieldsToExtract {
    value, err := reflections.GetField(s, fieldName)
    DoWhatEverWithThatValue(value)
}


// In order to be able to set the structure's values,
// a pointer to it has to be passed to it.
_ := reflections.SetField(&s, "FirstField", "new value")

// If you try to set a field's value using the wrong type,
// an error will be returned
err := reflection.SetField(&s, "FirstField", 123)  // err != nil

0

Ви можете маршалити структуру та демаршалювати її назад map[string]interface{}. Але він перетворить усі числові значення на, float64тому вам доведеться перетворити його на intвручну.

type Vertex struct {
    X int
    Y int
}

func main() {
    v := Vertex{1, 2}
    fmt.Println(getProperty(&v, "X"))
}

func getProperty(v *Vertex, property string) float64 {
    m, _ := json.Marshal(v)
    var x map[string]interface{}
    _ = json.Unmarshal(m, &x)
    return x[property].(float64)
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.