Як відобразити, як ви встановлюєте значення структури структури?


106

маючи орієнтовний час роботи з полями структури за допомогою reflectпакету. зокрема, не з'ясували, як встановити значення поля.

наберіть t структура {fi int; fs string}
var rt = t {123, "jblow"}
var i64 int64 = 456
  1. отримання назви поля i - це, здається, працює

    var field = reflect.TypeOf(r).Field(i).Name

  2. отримання значення поля i як а) інтерфейсу {}, b) int - це, здається, працює

    var iface interface{} = reflect.ValueOf(r).Field(i).Interface()

    var i int = int(reflect.ValueOf(r).Field(i).Int())

  3. встановлення значення поля i - спробуйте одне - паніка

    reflect.ValueOf(r).Field(i).SetInt( i64 )

    паніка : Reflection.Value · SetInt, використовуючи значення, отримані за допомогою експортованого поля

    припускаючи, що це не любить назви полів "id" та "name", тому перейменовано на "Id" та "Name"

    а) чи правильне це припущення?

    б) якщо правильно, то думка не потрібна, оскільки в одному файлі / пакеті

  4. значення параметра поля i - спробуйте два (з великими іменами полів) - паніка

    reflect.ValueOf(r).Field(i).SetInt( 465 )

    reflect.ValueOf(r).Field(i).SetInt( i64 )

    паніка : Reflection.Value · SetInt, використовуючи неадресное значення


Нижче наведені інструкції від @peterSO є ретельними та якісними

Четверо. це працює:

reflect.ValueOf(&r).Elem().Field(i).SetInt( i64 )

він також документи, що назви полів повинні бути експортованими (починати з великої літери)


Найближчим прикладом, який я міг би знайти для когось, хто використовує reflectдля встановлення даних, був comments.gmane.org/gmane.comp.lang.go.general/35045 , але навіть там він використовував json.Unmarshalсправжню брудну роботу
куб юного

(вищевказаний коментар застарілий)
куб. молодих

Відповіді:


156

Go доступний як відкритий код . Хороший спосіб дізнатися про роздуми - це побачити, як основні розробники Go використовують це. Наприклад, пакети Go fmt та json . Документація на пакунок містить посилання на файли вихідного коду під заголовком Файли пакунків.

Пакет Go json маршали та відмежує JSON від та до Go структур.


Ось покроковий приклад, який встановлює значення structполя, ретельно уникаючи помилок.

Пакет Go reflectмає CanAddrфункцію.

func (v Value) CanAddr() bool

CanAddr повертає значення true, якщо адресу значення можна отримати за допомогою Addr. Такі значення називаються адресними. Значення адресоване, якщо це елемент фрагмента, елемент адресованого масиву, поле адреси адреси структури або результат перенаправлення покажчика. Якщо CanAddr поверне помилкове значення, виклик Addr панікує.

У reflectпакеті Go є CanSetфункція, яка, якщо true, означає, CanAddrце теж є true.

func (v Value) CanSet() bool

CanSet повертає true, якщо значення v можна змінити. Значення може бути змінено лише в тому випадку, якщо воно адресоване і не було отримане за допомогою неекспортованих полів структури. Якщо CanSet поверне помилкове значення, виклик Set або будь-який специфічний сетер (наприклад, SetBool, SetInt64) панікує.

Ми повинні переконатися , що ми можемо поле. Наприклад,Setstruct

package main

import (
    "fmt"
    "reflect"
)

func main() {
    type t struct {
        N int
    }
    var n = t{42}
    // N at start
    fmt.Println(n.N)
    // pointer to struct - addressable
    ps := reflect.ValueOf(&n)
    // struct
    s := ps.Elem()
    if s.Kind() == reflect.Struct {
        // exported field
        f := s.FieldByName("N")
        if f.IsValid() {
            // A Value can be changed only if it is 
            // addressable and was not obtained by 
            // the use of unexported struct fields.
            if f.CanSet() {
                // change value of N
                if f.Kind() == reflect.Int {
                    x := int64(7)
                    if !f.OverflowInt(x) {
                        f.SetInt(x)
                    }
                }
            }
        }
    }
    // N at end
    fmt.Println(n.N)
}

Output:
42
7

Якщо ми можемо бути впевнені, що всі перевірки помилок непотрібні, приклад спрощується,

package main

import (
    "fmt"
    "reflect"
)

func main() {
    type t struct {
        N int
    }
    var n = t{42}
    fmt.Println(n.N)
    reflect.ValueOf(&n).Elem().FieldByName("N").SetInt(7)
    fmt.Println(n.N)
}

5
Здаватися! десь там є відповідь, але чотири години роботи на json pkg мені не дали цього. Повторно відбиваючи кг, витягати інформацію досить просто, але для встановлення даних потрібна якась чорна магія, для якої я б хотів десь побачити простий приклад!
cc молодий

2
Видатний! якщо ви коли-небудь перебували в Таїланді, будь ласка, дозвольте мені пригостити вас пивом-двома-трьома! велике дякую
куб. молодих

5
Чудовий практичний приклад, ця стаття повністю демістифікувала її для мене golang.org/doc/articles/laws_of_reflection.html
danmux

Дивовижний приклад. Ось
Сарат Садасіван Піллай

Тут не встановлено структуру поля. Це встановлює поле в покажчику на структура. Очевидно, що це не відповідає на запитання ОП.
wvxvw

13

Це, здається, працює:

package main

import (
    "fmt"
    "reflect"
)

type Foo struct {
    Number int
    Text string
}

func main() {
    foo := Foo{123, "Hello"}

    fmt.Println(int(reflect.ValueOf(foo).Field(0).Int()))

    reflect.ValueOf(&foo).Elem().Field(0).SetInt(321)

    fmt.Println(int(reflect.ValueOf(foo).Field(0).Int()))
}

Друкує:

123
321

Дякую! тепер, коли я читав нотатки peterSO, це має ідеальний сенс. Я використовував foo, не & foo, тому змінити його не вдалося, і не знав, про що йдеться в Elem ().
cc молодий
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.