如何遍历Golang结构中的字段以可扩展的方式获取和设置值?

pla*_*twp 14 reflection go

我有一个结构人员.

type Person struct {
    Firstname string       
    Lastname  string       
    Years     uint8       
}
Run Code Online (Sandbox Code Playgroud)

然后我有这个结构的两个实例,PersonA和PersonB.

PersonA := {"", "Obama", 6}
PersonB := {"President", "Carter", 8}
Run Code Online (Sandbox Code Playgroud)

我想编写一个函数,在给定每个字段的某些条件(即非空)的情况下,将值从PersonA复制到PersonB.我知道如何通过对字段名称进行硬编码来实现这一点,但是我想要一个即使我更改Person结构也能工作的函数.

我知道Go反射是有帮助的,但问题是获取并设置值需要知道类型,如果你想使用像SetInt这样的东西.但有一种"简单"的方法吗?

**Javascript类比**在Javascript中,你可以做一个(在someObject中的属性)循环.

(for propt in personA) {
  if personA[propt] != "" {
    // do something
    personB[propt] = personA[propt]
  }
}
Run Code Online (Sandbox Code Playgroud)

我已经排除的选项:

  1. 跟踪地图中每个结构中的字段,然后使用反射pkg中的FieldByName和Set*函数集合的组合.

  2. 手动创建一个循环遍历Person(下面).因为我想为许多其他结构(学校,动物等)做这种类型的"更新"

    if PersonA.Firstname != "" {
      PersonB.Firstname = PersonA.Firstname 
    }
    
    Run Code Online (Sandbox Code Playgroud)

    ...

    if PersonA.Years != "" {
      PersonB.Years = PersonA.Years 
    }
    
    Run Code Online (Sandbox Code Playgroud)

下面的问题让我在那里中途,但仍然不能扩展到我想要利用这个"更新"功能的所有结构.

在Golang中,使用reflect,如何设置struct字段的值?

**其他有用的链接** GoLang:按名称访问struct属性

jti*_*ing 7

使用reflect.ValueOf()转换为具体类型.之后,您可以使用reflect.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")
    }
}
Run Code Online (Sandbox Code Playgroud)


arc*_*hie 5

这是解决方案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}
Run Code Online (Sandbox Code Playgroud)

http://play.golang.org/p/UKFMBxfbZD


Eva*_*van 2

反思应该是你所需要的。这看起来与“深度复制”语义相似(尽管不完全相同),该语义已在https://godoc.org/github.com/getlantern/deepcopy上实现

您应该能够根据您的需求进行调整,或者至少从中汲取一些想法。