通过反射在Go语言中给结构体成员赋值。

huangapple go评论85阅读模式
英文:

Assigning a value to struct member through reflection in Go

问题

我有一个结构体v,其中包含成员A、B、C字符串。使用反射,我可以获取字段的名称和它们的值:

typ := v.Type()
for i := 0; i < v.NumField(); i++ {
    // 获取StructField
    fi := typ.Field(i)
    fieldname := fi.Name
    fmt.Println(fieldname)
    val := fmt.Sprintf("%v", v.Field(i).Interface())
}

由于我有字段的名称,并且可以获取值,那么我能否给字段赋新值呢?我想要做的是:

v.Field(fieldname).Interface() = "new value"

但显然这是行不通的。如果只知道字段的名称,是否可以将值赋给结构体的字段呢?

实际上,我试图将map[string]string中的值分配给结构体中对应的字段,其中结构体和映射的定义可能随时间扩展或更改,并且映射中的值可能比结构体多或少。我考虑过使用JSON来实现,但这种方法让我有些不满意,因为使用反射几乎可以实现这一目标!

谢谢,
Ken

英文:

I have a struct v with members A, B, C string. Using reflection, I can get the names of the fields and their values:

typ := v.Type()
for i := 0; i &lt; v.NumField(); i++ {
	// gets us a StructField
	fi := typ.Field(i)
    fieldname := fi.Name
	fmt.Println(fieldname)
	val := fmt.Sprintf(&quot;%v&quot;, v.Field(i).Interface())
 }

since I have the name, and can get the value OUT, can I assign new values to the fields? I would like to do essentially:

v.Field(fieldname).Interface() = &quot;new value&quot;

but that obviously doesn't work. Is it possible to assign a value into a struct if you only know the name of the field?

In practice, I'm trying to assign values from a map[string]string to corresponding fields in a struct, where the struct and map definitions may expand of change over time, and the map may contain more, or less, values than the struct. I've considered doing it w/JSON, but that approach leaves me a little cold, seeing as how easy it was to use reflection to get "almost" there!

Thanks,
Ken

答案1

得分: 1

是的,这是可能的。

介绍

由于您想要访问和修改变量(或字段)的值,您需要使用reflect.Value类型,而不是reflect.Type。您可以使用reflect.ValueOf()获取它。此外,为了使用反射修改它,您需要传递要修改的struct或值的地址(指针)(否则您只能读取它而不能修改它)。

但是您不想修改地址/指针,而是修改指向的值,因此您需要从指针的Value导航到指向的变量(struct)的Value,这就是Value.Elem()的作用。它的用法如下:reflect.ValueOf(&s).Elem()

您可以使用Value.FieldByName()方法获取结构字段的Value,由于我们将指针的地址传递给了ValueOf()函数,因此它将是可设置的。

代码

一旦理解了介绍,代码就比较简单。您还可以在Go Playground上尝试它:

var s struct {
    A, B, C string
}
s.A, s.B, s.C = "a1", "b2", "c3"

fmt.Println("Before:  ", s)

v := reflect.ValueOf(&s).Elem()

v.FieldByName("A").SetString("2a")
v.FieldByName("B").SetString("2b")
v.FieldByName("C").SetString("2c")

fmt.Println("After:   ", s)

// Using a map:
m := map[string]string{"A": "ma", "B": "mb", "C": "mc"}
for mk, mv := range m {
    v.FieldByName(mk).SetString(mv)
}

fmt.Println("From Map:", s)

输出:

Before:   {a1 b2 c3}
After:    {2a 2b 2c}
From Map: {ma mb mc}

我建议阅读这篇博文,以了解Go中反射的基础知识:

The Laws of Reflection

英文:

Yes, it is possible.

Introduction

Since you want to access and modify the value of a variable (or field), you need to use the reflect.Value type instead of reflect.Type. You can acquire it with reflect.ValueOf(). Also in order to modify it with reflection, you need to pass the address (a pointer) of the struct or value you want to modify (else you could only read it but not modify it).

But you don't want to modify the address/pointer but the pointed value, so you have to "navigate" from the Value of the pointer to the Value of the pointed variable (struct), this is what Value.Elem() is for. It looks like this: reflect.ValueOf(&amp;s).Elem()

You can get the Value of a struct field with the Value.FieldByName() method, which since we passed the address of the pointer to the ValueOf() function will be settable.

The Code

The code is much simpler than the introduction once you understand it. You can also try it on the Go Playground:

var s struct {
	A, B, C string
}
s.A, s.B, s.C = &quot;a1&quot;, &quot;b2&quot;, &quot;c3&quot;

fmt.Println(&quot;Before:  &quot;, s)

v := reflect.ValueOf(&amp;s).Elem()

v.FieldByName(&quot;A&quot;).SetString(&quot;2a&quot;)
v.FieldByName(&quot;B&quot;).SetString(&quot;2b&quot;)
v.FieldByName(&quot;C&quot;).SetString(&quot;2c&quot;)

fmt.Println(&quot;After:   &quot;, s)

// Using a map:
m := map[string]string{&quot;A&quot;: &quot;ma&quot;, &quot;B&quot;: &quot;mb&quot;, &quot;C&quot;: &quot;mc&quot;}
for mk, mv := range m {
	v.FieldByName(mk).SetString(mv)
}

fmt.Println(&quot;From Map:&quot;, s)

Output:

Before:   {a1 b2 c3}
After:    {2a 2b 2c}
From Map: {ma mb mc}

I recommend to read this blog post to learn the basics of the reflection in Go:

The Laws of Reflection

huangapple
  • 本文由 发表于 2015年1月24日 01:53:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/28115815.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定