Ответ 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)
}