Используя отражение, как вы устанавливаете значение поля struct?

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

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

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

  • получение значения поля я как a) интерфейса {}, b) int - это, похоже, работает

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

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

  • значение настройки поля я - попробуйте один - панику

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

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

    при условии, что ему не нравятся имена полей "id" и "name", поэтому они переименованы в "Id" и "Name"

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

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

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

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

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

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


Инструкции, приведенные @peterSO, являются тщательным и качественным

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

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

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

Ответ 1

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

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


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

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

func (v Value) CanAddr() bool

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

В пакете Go reflect функция CanSet, которая, если имеет значение true, подразумевает, что CanAddr также имеет значение true.

func (v Value) CanSet() bool

CanSet возвращает true, если значение 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)
}

Ответ 2

Это работает:

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