一尘不染

如果将数组作为&val传递,然后转换为接口{},则更新数组元素

go

我正在尝试编写一些通用方法(CRUD方法)以在我的服务之间共享它。以下示例是一种GetAll()方法,该方法返回集合中存在的所有文档:

func GetAll(out interface{}) error {
    // mongodb operations

    // iterate through all documents
    for cursor.Next(ctx) {
        var item interface{}
        // decode the document
        if err := cursor.Decode(&item); err != nil {
            return err
        }
        (*out) = append((*out), item)
        // arrays.AppendToArray(out, item) // Read below :)
    }

    return nil // if no error

}

我也做了一些反思,但后来:

package arrays

import "reflect"

func AppendToArray(slicePtrInterface interface{}, item interface{}) {
    // enter `reflect`-land
    slicePtrValue := reflect.ValueOf(slicePtrInterface)
    // get the type
    slicePtrType := slicePtrValue.Type()
    // navigate from `*[]T` to `T`
    _ = slicePtrType.Elem().Elem() // crashes if input type not `*[]T`
    // we'll need this to Append() to
    sliceValue := reflect.Indirect(slicePtrValue)
    // append requested number of zeroes
    sliceValue.Set(reflect.Append(sliceValue, reflect.ValueOf(item)))
}

恐慌:reflect.Set:原始类型D的值不能分配给 mongodb.Test类型[已恢复]恐慌:reflect.Set:原始类型D的值不能分配给
mongodb.Test类型

我想要的是与cursor.Decode(&item)(您可以在上面看到的)相同的方法


阅读 176

收藏
2020-07-02

共1个答案

一尘不染

方法如下:

// GetAll decodes the cursor c to slicep where slicep is a 
// pointer to a slice of pointers to values.
func GetAll(ctx context.Context, c *Cursor, slicep interface{}) error {
    // Get the slice. Call Elem() because arg is pointer to the slice.
    slicev := reflect.ValueOf(slicep).Elem()

    // Get value type. First call to Elem() gets slice 
    // element type. Second call to Elem() dereferences 
    // the pointer type.
    valuet := slicev.Type().Elem().Elem()

    // Iterate through the cursor...
    for c.Next(ctx) {
        // Create new value.
        valuep := reflect.New(valuet)

        // Decode to that value.
        if err := c.Decode(valuep.Interface()); err != nil {
            return err
        }

        // Append value pointer to slice.
        slicev.Set(reflect.Append(slicev, valuep))
    }
    return c.Err()
}

这样称呼它:

var data []*T
err := GetAll(ctx, c, &data)
if err != nil {
   // handle error
}

在Go Playground上运行它

这是与非指针切片元素一起使用的代码的概括:

func GetAll(ctx context.Context, c *Cursor, slicep interface{}) error {
    slicev := reflect.ValueOf(slicep).Elem()
    valuet := slicev.Type().Elem()
    isPtr := valuet.Kind() == reflect.Ptr
    if isPtr {
        valuet = valuet.Elem()
    }
    for c.Next(ctx) {
        valuep := reflect.New(valuet)
        if err := c.Decode(valuep.Interface()); err != nil {
            return err
        }
        if !isPtr {
            valuep = valuep.Elem()
        }
        slicev.Set(reflect.Append(slicev, valuep))
    }
    return c.Err()
}
2020-07-02