Как вы прокручиваете поля в структуре Golang, чтобы получать и устанавливать значения расширяемым способом?
У меня есть структура Person.
type Person struct {
Firstname string
Lastname string
Years uint8
}
Затем у меня есть два экземпляра этой структуры, PersonA и PersonB.
PersonA := {"", "Obama", 6}
PersonB := {"President", "Carter", 8}
Я хочу написать функцию, которая копирует значения из PersonA в PersonB, учитывая некоторые условия для каждого поля (т.е. не пустые). Я знаю, как сделать это путем жесткого кодирования имен полей, но мне нужна функция, которая работает, даже если я изменяю структуру Person.
Я знаю, что отражения Go полезны, но проблема в получении и установке значений требует знания типов, если вы хотите использовать что-то вроде SetInt. Но есть ли "простой" способ сделать это?
** Аналогия с Javascript ** В Javascript вы можете просто выполнить (для свойства в someObject) цикл.
(for propt in personA) {
if personA[propt] != "" {
// do something
personB[propt] = personA[propt]
}
}
Опции, которые я исключил:
-
Отслеживание полей в каждой структуре на карте с последующим использованием комбинации FieldByName и коллекции функций Set * в отражающем пакете.
-
Создание цикла по полям Person вручную (ниже). Потому что я хочу сделать этот тип "обновления" для многих других структур (школа, животные и т.д.)
if PersonA.Firstname != "" {
PersonB.Firstname = PersonA.Firstname
}
...
if PersonA.Years != "" {
PersonB.Years = PersonA.Years
}
Приведенный ниже вопрос ставит меня на полпути, но все еще не распространяется на все структуры, для которых я хочу использовать эту функцию "обновления".
в Голанге, используя отражение, как вы устанавливаете значение поля структуры?
** Другие полезные ссылки ** GoLang: Доступ к свойству struct по имени
Ответы
Ответ 1
Используйте reflect.ValueOf()
для преобразования в конкретный тип. После этого вы можете использовать refle.Value.SetString, чтобы установить желаемое значение.
structValue := FooBar{Foo: "foo", Bar: 10}
fields := reflect.TypeOf(structValue)
values := reflect.ValueOf(structValue)
num := fields.NumField()
for i := 0; i < num; i++ {
field := fields.Field(i)
value := values.Field(i)
fmt.Print("Type:", field.Type, ",", field.Name, "=", value, "\n")
switch value.Kind() {
case reflect.String:
v := value.String())
fmt.Print(v, "\n")
case reflect.Int:
v := strconv.FormatInt(value.Int(), 10)
fmt.Print(v, "\n")
case reflect.Int32:
v := strconv.FormatInt(value.Int(), 10)
fmt.Print(v, "\n")
case reflect.Int64:
v := strconv.FormatInt(value.Int(), 10)
fmt.Print(v, "\n")
default:
assert.Fail(t, "Not support type of struct")
}
}
Ответ 2
Вот решение f2.Set(reflect.Value(f))
- это ключ здесь
package main
import (
"fmt"
"reflect"
)
func main() {
type T struct {
A int
B string
}
t := T{23, "skidoo"}
t2:= T{}
s := reflect.ValueOf(&t).Elem()
s2 := reflect.ValueOf(&t2).Elem()
typeOfT := s.Type()
fmt.Println("t=",t)
fmt.Println("t2=",t2)
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
f2:= s2.Field(i)
fmt.Printf("%d: %s %s = %v\n", i,
typeOfT.Field(i).Name, f.Type(), f.Interface())
fmt.Printf("%d: %s %s = %v\n", i,
typeOfT.Field(i).Name, f2.Type(), f2.Interface())
f2.Set(reflect.Value(f))
fmt.Printf("%d: %s %s = %v\n", i,
typeOfT.Field(i).Name, f2.Type(), f2.Interface())
}
fmt.Println("t=",t)
fmt.Println("t2=",t2)
}
Output:
t= {23 skidoo}
t2= {0 }
0: A int = 23
0: A int = 0
0: A int = 23
1: B string = skidoo
1: B string =
1: B string = skidoo
t= {23 skidoo}
t2= {23 skidoo}
http://play.golang.org/p/UKFMBxfbZD
Ответ 3
Отражение должно быть всем, что вам нужно. Это похоже (хотя и не идентично) на семантику "глубокого копирования", которая была реализована на https://godoc.org/github.com/getlantern/deepcopy
Вы должны быть в состоянии приспособить это к вашим потребностям или, по крайней мере, взять некоторые идеи из этого.
Ответ 4
Вместо этого вы должны использовать map[string]interface{}
, гораздо быстрее (хотя и не так быстро, как вы использовали правильную логику с фактическими структурами).
package main
import "fmt"
type Object map[string]interface{}
var m = Object{
"Firstname": "name",
"Lastname": "",
"years": uint8(10),
}
func main() {
var cp = Object{}
for k, v := range m {
if s, ok := v.(string); ok && s != "" {
cp[k] = s
} else if ui, ok := v.(uint8); ok {
cp[k] = ui
}
}
fmt.Printf("%#v\n", cp)
}