Как вы устанавливаете значение поля структуры с помощью отражения?

трудно работать с полями структуры с использованием пакета reflect. в частности, не придумали, как задать значение поля.

type t struct { fi int; fs string }
var r t = t{ 123, "jblow" }
var i64 int64 = 456
  1. получение имени поля i - похоже, это работает

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

  2. получение значения поля i как a) interface {}, 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 )

    panic: reflection.Value · SetInt с использованием значения, полученного с использованием неэкспортированного поля

    предполагая, что ему не нравятся имена полей «id» и «name», поэтому он переименован в «Id» и «Name»

    а) верно ли это предположение?

    б) если верно, то считал ненужным, так как в том же файле / пакете

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

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

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

    panic: reflection.Value · SetInt с использованием неадресуемого значения


Инструкции, приведенные ниже от @peterSO, являются подробными и качественными.

Четыре. это работает:

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

он также подтверждает, что имена полей должны быть экспортируемыми (начинаются с заглавной буквы)


person cc young    schedule 18.06.2011    source источник
comment
Ближайшим примером, который я мог найти для кого-то, использующего reflect для установки данных, были комментарии . gmane.org/gmane.comp.lang.go.general/35045, но даже там он использовал json.Unmarshal для выполнения самой грязной работы   -  person cc young    schedule 19.06.2011
comment
(вышеприведенный комментарий устарел)   -  person cc young    schedule 20.06.2011


Ответы (2)


Пакет Go json маршалирует и демаршалирует JSON из и в структуры Go.

Вот пошаговый пример, который устанавливает значение поля struct, тщательно избегая ошибок.

Пакет Go reflect содержит _ 3_.

func (v Value) CanAddr() bool

CanAddr возвращает истину, если адрес значения можно получить с помощью Addr. Такие значения называются адресуемыми. Значение является адресуемым, если оно является элементом среза, элементом адресуемого массива, полем адресуемой структуры или результатом разыменования указателя. Если CanAddr возвращает false, вызов Addr вызовет панику.

Пакет Go reflect имеет _ 6_, которая, если true, означает, что CanAddr также true.

func (v Value) CanSet() bool

CanSet возвращает истину, если значение v можно изменить. Значение можно изменить, только если оно адресуемое и не было получено с помощью неэкспортированных полей структуры. Если CanSet возвращает false, вызов Set или любого другого сеттера, зависящего от типа (например, SetBool, SetInt64), вызовет панику.

Нам нужно убедиться, что мы можем Set поле struct. Например,

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

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

person peterSO    schedule 18.06.2011
comment
Сдаться! где-то там есть ответ, но четыре часа работы над json pkg мне его не дали. В случае с отражением pkg, получение информации довольно просто, но для настройки данных требуется некоторая доля черной магии, для которой я хотел бы где-нибудь увидеть простой пример! - person cc young; 19.06.2011
comment
Выдающийся! если вы когда-нибудь будете в Таиланде, позвольте мне угостить вас пивом, двумя или тремя! большое спасибо - person cc young; 20.06.2011
comment
Отличный практический пример, эта статья полностью демистифицировала его для меня golang.org/doc/articles/laws_of_reflection.html - person danmux; 15.05.2013
comment
Замечательный пример. Вот пример того же кода play.golang.org/p/RK8jR_9rPh. - person Sarath Sadasivan Pillai; 21.07.2015
comment
Это не устанавливает поле структуры. Это устанавливает поле в указателе на структуру. Очевидно, это не ответ на вопрос, заданный OP. - person wvxvw; 06.03.2017
comment
боже, почему go так больно использовать. Ворчать, ворчать, ворчать. Повторите описанное выше для всех типов данных, и вы поймете, что я имею в виду. - person U Avalos; 03.05.2018
comment
Отражать - ужасная вещь. Программист может посетить неэкспортное поле так же, как fmt.Printf может показать нам неэкспортное поле. Спасибо команде golang, они не позволят программистам менять неэкспортное поле. А то придет что-то нелепое. - person g10guang; 25.10.2018
comment
Дополнение. Для возврата CanSet необходимо, чтобы атрибут структуры должен иметь префикс в верхнем регистре. - person Carson; 04.03.2021

Кажется, это работает:

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
person Asgeir    schedule 19.06.2011
comment
Благодарность! Теперь, когда я прочитал заметки peterSO, это имеет смысл. Я использовал foo, а не & foo, поэтому его нельзя было изменить, и я не был уверен, что такое Elem (). - person cc young; 20.06.2011