使用反射向Go语言切片追加元素。

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

Appending to go lang slice using reflection

问题

由于某种原因,使用反射将新元素添加到切片中似乎不会更新切片本身。以下是演示的代码:

package main

import (
	"fmt"
	"reflect"
)

func appendToSlice(arrPtr interface{}) {
	valuePtr := reflect.ValueOf(arrPtr)
	value := valuePtr.Elem()
	value = reflect.Append(value, reflect.ValueOf(55))
	
	fmt.Println(value.Len()) // 输出 1
}

func main() {
	arr := []int{}
	appendToSlice(&arr)
	fmt.Println(len(arr)) // 输出 0
}

Playground链接:https://play.golang.org/p/j3532H_mUL

这里有什么我漏掉的东西吗?

英文:

For some reason, it appears that adding new element to slice using reflection doesn't update slice itself. This is the code to demonstrate:

package main

import (
	"fmt"
	"reflect"
)

func appendToSlice(arrPtr interface{}) {
	valuePtr := reflect.ValueOf(arrPtr)
	value := valuePtr.Elem()
	value = reflect.Append(value, reflect.ValueOf(55))
	
	fmt.Println(value.Len()) // prints 1
}

func main() {
	arr := []int{}
	appendToSlice(&arr)
	fmt.Println(len(arr)) // prints 0
}

Playground link : https://play.golang.org/p/j3532H_mUL

Is there something I'm missing here?

答案1

得分: 16

reflect.Append的工作方式类似于append,它返回一个新的切片值。

你将这个值赋给appendToSlice函数中的value变量,它替换了先前的reflect.Value,但不会更新原始参数。

为了更清楚地说明正在发生的情况,考虑一下没有反射的等效函数:

func appendToSlice(arrPtr *[]int) {
    value := *arrPtr
    value = append(value, 55)
    fmt.Println(len(value))
}

你需要使用Value.Set方法来更新原始值:

func appendToSlice(arrPtr interface{}) {
    valuePtr := reflect.ValueOf(arrPtr)
    value := valuePtr.Elem()

    value.Set(reflect.Append(value, reflect.ValueOf(55)))

    fmt.Println(value.Len())
}

链接:https://play.golang.org/p/Nhabg31Sju

英文:

reflect.Append works like append in that it returns a new slice value.

You are assigning this value to the value variable in the appendToSlice function, which replaces the previous reflect.Value, but does not update the original argument.

To make it more clear what's happening, take the equivalent function to your example without reflection:

func appendToSlice(arrPtr *[]int) {
	value := *arrPtr
	value = append(value, 55)
	fmt.Println(len(value))
}

What you need to use is the Value.Set method to update the original value:

func appendToSlice(arrPtr interface{}) {
	valuePtr := reflect.ValueOf(arrPtr)
	value := valuePtr.Elem()

	value.Set(reflect.Append(value, reflect.ValueOf(55)))

	fmt.Println(value.Len())
}

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

答案2

得分: 3

package main

import "fmt"
import "reflect"

type Foo struct {
    Name string
}

func main() {
    _type := []Foo{}

    fmt.Printf("_type: v(%v) T(%T)\n", _type, _type)

    reflection := reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(_type).Elem()), 0, 0)

    reflectionValue := reflect.New(reflection.Type())
    reflectionValue.Elem().Set(reflection)

    slicePtr := reflect.ValueOf(reflectionValue.Interface())

    sliceValuePtr := slicePtr.Elem()

    sliceValuePtr.Set(reflect.Append(sliceValuePtr, reflect.ValueOf(Foo{"a"})))
    sliceValuePtr.Set(reflect.Append(sliceValuePtr, reflect.ValueOf(Foo{"b"})))
    sliceValuePtr.Set(reflect.Append(sliceValuePtr, reflect.ValueOf(Foo{"c"})))

    values := []Foo{Foo{"d"}, Foo{"e"}}

    for _, val := range values {
        sliceValuePtr.Set(reflect.Append(sliceValuePtr, reflect.ValueOf(val)))
    }

    result := sliceValuePtr.Interface()

    fmt.Printf("result: %T = (%v)\n", result, result)
}

请注意,这是一个使用反射的Go语言示例代码。它创建了一个名为Foo的结构体类型,并在main函数中使用反射来动态创建一个切片,并向其中添加元素。最后,它打印出结果。

你可以在提供的链接中查看代码的运行示例。

英文:
    package main
    
    import "fmt"
    import "reflect"
    
    type Foo struct {
        Name string
    }
    
    func main() {
        _type := []Foo{}
        
        fmt.Printf("_type: v(%v) T(%T)\n", _type, _type)
    
        reflection := reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(_type).Elem()), 0, 0)
        
        reflectionValue := reflect.New(reflection.Type())
        reflectionValue.Elem().Set(reflection)
    
        slicePtr := reflect.ValueOf(reflectionValue.Interface())
        
        sliceValuePtr := slicePtr.Elem()
        
        sliceValuePtr.Set(reflect.Append(sliceValuePtr, reflect.ValueOf(Foo{"a"})))
        sliceValuePtr.Set(reflect.Append(sliceValuePtr, reflect.ValueOf(Foo{"b"})))
        sliceValuePtr.Set(reflect.Append(sliceValuePtr, reflect.ValueOf(Foo{"c"})))
    
        values := []Foo{Foo{"d"}, Foo{"e"}}
    
        for _, val := range values {
            sliceValuePtr.Set(reflect.Append(sliceValuePtr, reflect.ValueOf(val)))
        }
        
        result := sliceValuePtr.Interface()
        
        fmt.Printf("result: %T = (%v)\n", result, result)
    }

take a look at: https://play.golang.org/p/vXOqTVSEleO

huangapple
  • 本文由 发表于 2017年2月28日 03:39:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/42494333.html
匿名

发表评论

匿名网友

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

确定