Reflection 如何循环Golang结构中的字段,以可扩展的方式获取和设置值?
我有一个结构人Reflection 如何循环Golang结构中的字段,以可扩展的方式获取和设置值?,reflection,go,Reflection,Go,我有一个结构人 type Person struct { Firstname string Lastname string Years uint8 } 然后我有两个这个结构的实例,PersonA和PersonB PersonA := {"", "Obama", 6} PersonB := {"President", "Carter", 8} 我想编写一个函数,在给定每个字段的某些条件(即非空)的情况下,将值从Pers
type Person struct {
Firstname string
Lastname string
Years uint8
}
然后我有两个这个结构的实例,PersonA和PersonB
PersonA := {"", "Obama", 6}
PersonB := {"President", "Carter", 8}
我想编写一个函数,在给定每个字段的某些条件(即非空)的情况下,将值从PersonA复制到PersonB。我知道如何通过硬编码字段名来实现这一点,但我想要一个即使我更改Person结构也能工作的函数
我知道Go reflections很有帮助,但问题是获取和设置值需要知道类型,如果您想使用SetInt之类的东西。但是有没有一种“简单”的方法可以做到这一点
**Javascript类比**
在Javascript中,您只需执行(someObject中的for属性)循环即可
(for propt in personA) {
if personA[propt] != "" {
// do something
personB[propt] = personA[propt]
}
}
我已排除的选项:
反射应该就是你所需要的一切。这似乎与“深度复制”语义相似(尽管不完全相同),后者已在
您应该能够根据自己的需要调整它,或者至少从中吸取一些想法。您应该使用
map[string]接口{}
,速度会快得多(尽管仍然不如您在实际结构中使用正确的逻辑那么快)
这里是解决方案
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}
主程序包
进口(
“fmt”
“反映”
)
func main(){
T型结构{
整数
B弦
}
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)
对于i:=0;i
使用reflect.ValueOf()
转换为具体类型。之后,您可以使用设置所需的值
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")
}
}
structValue:=FooBar{Foo:“Foo”,Bar:10}
字段:=reflect.TypeOf(structValue)
值:=reflect.ValueOf(structValue)
num:=字段。NumField()
对于i:=0;i
使用反射会带来巨大的性能和代码复杂性成本@platwp,可能值得发布一个更高层次的问题,当这个问题出现时,关于您试图构建什么。如果JavaScript的灵活性如此重要,您甚至可能希望使用动态集合(
map[string]interface{}
或其他什么)而不是对象类型。@twoo我希望避免“复杂性”部分。如果没有“简单”的方法,那么动态集合可能是有意义的。谢谢
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)
}
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}
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")
}
}
package main
import (
"fmt"
"encoding/json"
)
type Serializable interface {
fromMap(map[string]interface{}) error
toMap() (map[string]interface{}, error)
}
type Person struct {
Firstname string
Lastname string
Years uint8
}
func (p *Person) fromMap(m map[string]interface{}) error {
b, err := json.Marshal(m)
if err != nil {
return err
}
if err := json.Unmarshal(b, p); err != nil {
return err
}
return nil
}
func (p Person) toMap() (map[string]interface{}, error) {
b, err := json.Marshal(p)
if err != nil {
return nil, err
}
m := map[string]interface{}{}
if err := json.Unmarshal(b, &m); err != nil {
return nil, err
}
return m, nil
}
func copy(p1 Serializable, p2 Serializable) error {
m1, err := p1.toMap()
if err != nil {
return err
}
m2, err := p2.toMap()
if err != nil {
return err
}
for k := range m1 {
m2[k] = m1[k]
}
if err := p2.fromMap(m2); err != nil {
return err
}
return nil
}
func main() {
p1 := Person{
"Mary",
"Jane",
26,
}
p2 := Person {
"Random",
"Lady",
26,
}
if err := copy(&p1, &p2); err != nil {
fmt.Printf("ERR: %s\n", err.Error())
return
}
fmt.Printf("%v\n", p2)
}