登录
首页 >  Golang >  Go问答

使用反射动态创建切片结构

来源:stackoverflow

时间:2024-03-23 17:21:32 140浏览 收藏

本文旨在解决在 Go 语言中使用反射动态创建切片结构时遇到的问题。文章首先展示了一个无法工作的代码片段,其中包含一系列错误。随后提供了经过注释和修正的代码,详细说明如何正确地使用反射来创建指向结构体的指针切片。修正后的代码还包含了错误处理,以确保在遇到不匹配或不可分配的类型时返回错误。

问题内容

我试图用指针构造 book 结构的切片,但无法在 go 中使用反射来使其工作。

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

您能否告诉我下面的代码片段中出了什么问题?

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


解决方案


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

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

理论要掌握,实操不能落!以上关于《使用反射动态创建切片结构》的详细介绍,大家都掌握了吧!如果想要继续提升自己的能力,那么就来关注golang学习网公众号吧!

声明:本文转载于:stackoverflow 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>