Используя отражение, как вы устанавливаете значение поля структуры? - PullRequest
84 голосов
/ 18 июня 2011

плохо работает с полями структуры, используя пакет 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) интерфейса {}, 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 )

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

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

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

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

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

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

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

    паника : отражение. Значение · SetInt с использованием неадресуемого значения

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

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

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

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

Ответы [ 2 ]

126 голосов
/ 18 июня 2011

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

Маршалы и демаршалы пакета JSON Go из структур 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)
}
11 голосов
/ 19 июня 2011

Это похоже на работу:

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
...