使用反射设置字符串

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

Using reflection SetString

问题

我有一个类似这样的结构体:

type ProductionInfo struct {
    StructA []Entry
}

type Entry struct {
    Field1 string
    Field2 int
}

我想使用反射来更改Field1的值,但是反射对象始终返回CanSet() = false。我该怎么办?请参考playground示例。

以下是代码:

func SetField(source interface{}, fieldName string, fieldValue string) {
    v := reflect.ValueOf(source)
    tt := reflect.TypeOf(source)

    for k := 0; k < tt.NumField(); k++ {
        fieldValue := reflect.ValueOf(v.Field(k))

        fmt.Println(fieldValue.CanSet())
        if fieldValue.CanSet() {
            fieldValue.SetString(fieldValue.String())
        }
    }
}

func main() {
    source := ProductionInfo{}
    source.StructA = append(source.StructA, Entry{Field1: "A", Field2: 2})

    SetField(source, "Field1", "NEW_VALUE")
}

请注意,由于你要求只返回翻译好的部分,因此我只提供了代码的翻译,不会回答你关于代码的问题。

英文:

I have a struct like this one:

type ProductionInfo struct {
	StructA []Entry
}

type Entry struct {
	Field1 string
	Field2 int
}

I would like to change the value of Field1 using reflection but the reflect object returned always CanSet() = false. What can I do? See playground example.

https://play.golang.org/p/eM_KHC3kQ5

Here is the code:

func SetField(source interface{}, fieldName string, fieldValue string) {
	v := reflect.ValueOf(source)
	tt := reflect.TypeOf(source)

	for k := 0; k &lt; tt.NumField(); k++ {
		fieldValue := reflect.ValueOf(v.Field(k))

		fmt.Println(fieldValue.CanSet())
		if fieldValue.CanSet() {
			fieldValue.SetString(fieldValue.String())
		}
	}
}

func main() {
	source := ProductionInfo{}
	source.StructA = append(source.StructA, Entry{Field1: &quot;A&quot;, Field2: 2})

	SetField(source, &quot;Field1&quot;, &quot;NEW_VALUE&quot;)
}

答案1

得分: 5

多个错误。让我们逐个解决。

首先,你传递的是 ProductionInfo 的值,而不是你想要修改的 Entry 的值,所以首先将其更改为:

SetField(&source.StructA[0], "Field1", "NEW_VALUE")

接下来,你传递的是一个非指针的值。你不能使用反射修改非指针结构体的字段,因为那只会修改一个将被丢弃的副本。为了避免这种情况(和进一步的混淆),这是不允许的(CanSet() 返回 false)。所以你必须传递一个指向结构体的指针:

SetField(&source.StructA[0], "Field1", "NEW_VALUE")

现在在 SetField() 内部,reflect.ValueOf(source) 将描述传递的指针。你可以使用 Value.Elem() 导航到指向对象(结构体值)的 reflect.Value

v := reflect.ValueOf(source).Elem()

现在它可以工作了。修改后的代码如下:

func SetField(source interface{}, fieldName string, fieldValue string) {
    v := reflect.ValueOf(source).Elem()

    fmt.Println(v.FieldByName(fieldName).CanSet())

    if v.FieldByName(fieldName).CanSet() {
        v.FieldByName(fieldName).SetString(fieldValue)
    }
}

func main() {
    source := ProductionInfo{}
    source.StructA = append(source.StructA, Entry{Field1: "A", Field2: 2})

    fmt.Println("Before: ", source.StructA[0])
    SetField(&source.StructA[0], "Field1", "NEW_VALUE")
    fmt.Println("After: ", source.StructA[0])
}

输出结果(在 Go Playground 上尝试):

Before:  {A 2}
true
After:  {NEW_VALUE 2}
英文:

Multiple errors. Let's iterate over them.

First, you pass a value of ProductionInfo and not a value of Entry whose field you want to modify, so first change it to:

SetField(source.StructA[0], &quot;Field1&quot;, &quot;NEW_VALUE&quot;)

Next, you are passing a (non-pointer) value. You can't modify the fields of a non-pointer struct with reflection, because that would only modify a copy which would be discarded. In order to avoid this (and further confusion), this is not allowed (CanSet() returns false). So you have to pass a pointer to the struct:

SetField(&amp;source.StructA[0], &quot;Field1&quot;, &quot;NEW_VALUE&quot;)

Now inside SetField() the reflect.ValueOf(source) will describe the passed pointer. You may use Value.Elem() to navigate to the reflect.Value of the pointed object (the struct value):

v := reflect.ValueOf(source).Elem()

And now it works. Modified code:

func SetField(source interface{}, fieldName string, fieldValue string) {
	v := reflect.ValueOf(source).Elem()

	fmt.Println(v.FieldByName(fieldName).CanSet())

	if v.FieldByName(fieldName).CanSet() {
		v.FieldByName(fieldName).SetString(fieldValue)
	}
}

func main() {
	source := ProductionInfo{}
	source.StructA = append(source.StructA, Entry{Field1: &quot;A&quot;, Field2: 2})

	fmt.Println(&quot;Before: &quot;, source.StructA[0])
	SetField(&amp;source.StructA[0], &quot;Field1&quot;, &quot;NEW_VALUE&quot;)
	fmt.Println(&quot;After: &quot;, source.StructA[0])
}

Output (try it on the Go Playground):

Before:  {A 2}
true
After:  {NEW_VALUE 2}

huangapple
  • 本文由 发表于 2017年5月30日 14:48:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/44255344.html
匿名

发表评论

匿名网友

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

确定