【问题标题】:How to construct slice of structs dynamically using reflection如何使用反射动态构造结构切片
【发布时间】:2020-07-21 04:14:30
【问题描述】:

我试图用指针构造 Book 结构的切片,但我无法通过 Go 中的反射使其工作。

[]*Book 结构指针的Book 切片,请注意scanResults 方法可能接收任何类型的切片,而不仅仅是Book struct。所以我希望在运行时动态构建一个切片

你能告诉我我在下面的代码 sn-p 中哪里出错了吗?

package main

import (
  "reflect"
"errors"
"fmt"
)

type Book struct {
    Id    int
    Title string
    Price float32
}

func main() {
    var dest []*Book
    scanResults(&dest)
}

func scanResults(dest interface{}) error{
   resultsFromExternalSource := []interface{}{10 , "user-name" , float32(22)}

    value := reflect.ValueOf(dest)
    if value.Kind() != reflect.Ptr {
        return errors.New("must pass a pointer, not a value, to scan results into struct destination")
    }

    sliceElement := reflect.TypeOf(dest).Elem()
    if sliceElement.Kind() != reflect.Slice {
        return fmt.Errorf("expected %s but got %s", reflect.Slice, sliceElement.Kind())
    }

    structPtr := sliceElement.Elem()
    if structPtr.Kind() != reflect.Ptr {
        return fmt.Errorf("expected %s but got %s", reflect.Ptr, structPtr.Kind())
    }

    structElemType := reflect.TypeOf(structPtr).Elem()
    if structElemType.Kind() != reflect.Struct {
        return fmt.Errorf("expected %s but got %s", reflect.Struct, structElemType.Kind())
    }

 structRecordInterface := reflect.New(structElemType).Elem().Interface() // create a new struct
            structRecordType := reflect.TypeOf(structRecordInterface)
            structRecordValue := reflect.ValueOf(structRecordType)

    for i, result := range resultsFromExternalSource {



                if structRecordValue.Field(i).CanSet() {
                    structRecordValue.Field(i).Set(reflect.ValueOf(result))
                } else {
                    varName := structRecordType.Field(i).Name
                    varType := structRecordType.Field(i).Type
                    return fmt.Errorf("cannot scan results into passed struct destination as the struct field %v with %v type is not settable", varName, varType)
                }
       }
     return nil

}

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

【问题讨论】:

  • 你能包含更多的代码吗? config 在哪里定义?最后似乎有一些额外的}

标签: go reflection go-reflect


【解决方案1】:

你快到了。这是一些带有注释的工作代码:

var errBadArg = errors.New("must pass pointer to slice of pointer to struct")

func scanResults(dest interface{}) error {
    resultsFromExternalSource := [][]interface{}{
        {10, "user-name", float32(22)},
        {20, "i-love-reflect", float32(100)},
    }

    // Get reflect.Value for the destination confirm that
    // the destination is a pointer to a slice of pointers
    // to a struct. The tests can be omitted if it's acceptable
    // to panic on bad input argument.

    destv := reflect.ValueOf(dest)

    if destv.Kind() != reflect.Ptr {
        return errBadArg
    }

    // Deference the pointer to get the slice.
    destv = destv.Elem()
    if destv.Kind() != reflect.Slice {
        return errBadArg
    }

    elemt := destv.Type().Elem()
    if elemt.Kind() != reflect.Ptr {
        return errBadArg
    }

    // "deference" the element type to get the struct type.
    elemt = elemt.Elem() 
    if elemt.Kind() != reflect.Struct {
        return errBadArg
    }


    // For each row in the result set...
    for j, row := range resultsFromExternalSource {

        // Return error if more columns than fields in struct.
        if len(row) > elemt.NumField() {
            return errors.New("result larger than struct")
        }

        // Allocate a new slice element.
        elemp := reflect.New(elemt)

        // Dereference the pointer for field access.
        elemv := elemp.Elem()

        for i, col := range row {
            fieldv := elemv.Field(i)
            colv := reflect.ValueOf(col)

            // Check to see if assignment to field will work
            if !colv.Type().AssignableTo(fieldv.Type()) {
                return fmt.Errorf("cannot assign %s to %s in row %d column %d", colv.Type(), fieldv.Type(), j, i)
            }

            // Set the field.
            fieldv.Set(colv)
        }

        // Append element to the slice.
        destv.Set(reflect.Append(destv, elemp))
    }
    return nil
}

Run it on the playground.

【讨论】:

  • @iLoveReflection 非常喜欢反思问题。请添加一些解释和注释,以便对其他人有所帮助
  • @Eklavya 正如答案中所述,解释在评论中。
猜你喜欢
  • 1970-01-01
  • 2021-05-26
  • 1970-01-01
  • 2018-04-21
  • 2021-02-19
  • 2018-12-27
  • 1970-01-01
  • 2017-07-06
  • 2021-01-18
相关资源
最近更新 更多