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]
  }
}
我已排除的选项:

  • 跟踪映射中每个结构中的字段,然后在中使用FieldByName和Set*函数集合的组合

  • 手动在Person字段中创建循环(如下所示)。因为我想对许多其他结构(学校、动物等)进行这种类型的“更新”

  • 下面的问题让我半途而废,但仍然不能扩展到我想要使用这个“更新”函数的所有结构

    **其他有用的链接**
    反射应该就是你所需要的一切。这似乎与“深度复制”语义相似(尽管不完全相同),后者已在


    您应该能够根据自己的需要调整它,或者至少从中吸取一些想法。

    您应该使用
    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)
    
    }