donglin5770 2018-03-13 01:02
浏览 107
已采纳

反映传递给interface {}函数参数的结构

I pass struct into a function as interface{}. Then inside I work with it using reflect to get the struct attributes. Here's the code:

func (db *DB) Migrate(domain ...interface{}) {
    // statement := "CREATE TABLE IF NOT EXISTS %s (%s, %s, %s, %s, %s)"
    for _,i := range domain {
        params := BindStruct(&i)
        statement := CreateStatement("create", len(params))
        _,err := db.Exec(fmt.Sprintf(statement, params...))
        if err != nil {
            log.Fatal("Error migrating database schema - ", err)
            break
        }
    }
} 

func BindStruct(domain interface{}) (params []interface{}) {
    tableName := reflect.TypeOf(domain).Elem().Name()
    params = append(params, tableName)

    val := reflect.ValueOf(domain).Elem()
    for i:=0; i < val.NumField(); i++ {
        field := val.Type().Field(i)
        tag := field.Tag

        fieldName := field.Name
        fieldType := tag.Get("sql_type")
        fieldTags := tag.Get("sql_tag")

        paramstring := fieldName + " " + fieldType + " " + fieldTags
        params = append(params, paramstring)
    }
    return params
}

I got an error on the line for i:=0; i < val.NumField(); i++ { in BindStruct function. The error message is:

panic: reflect: call of reflect.Value.NumField on interface Value

If I remove & from params := BindStruct(&i) becoming params := BindStruct(i) in Migrate function, I get this error:

panic: runtime error: invalid memory address or nil pointer

dereference[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4ff457]

What gives?

  • 写回答

2条回答 默认 最新

  • doufud21086 2018-03-13 02:43
    关注

    When you call BindStruct(&i) you are passing a pointer to an interface. so this line:

    val := reflect.ValueOf(domain).Elem()
    

    will set val to a reflect.Value representing your interface because reflect.ValueOf(domain) gets a pointer then .Elem() resolves the interface - which results in your first error as it is indeed an interface value (and they don't have fields):

    panic: reflect: call of reflect.Value.NumField on interface Value

    So, calling params := BindStruct(i) would always be correct as you need to pass the actual interface in not a pointer to it.

    You don't make clear what the underlying data types being passed into Migrate() are - are they values or pointers? It's important to know, e.g to inspect struct tags using reflection we need to get back to the struct values type, so the chain goes:

    interface -> (pointer ?) -> value -> type

    I suspect you are using values as if interface was a value then the line:

    val := reflect.ValueOf(domain).Elem()
    

    would be expected to panic since reflect.ValueOf(domain) would resolve the value and then .Elem() would try to de-refrence a value.

    To be on the safe side we will check the Kind() of the incoming value to make sure we have a struct:

    https://play.golang.org/p/6lPOwTd1Q0O

    func BindStruct(domain interface{}) (params []interface{}) {
    
        val := reflect.ValueOf(domain) // could be any underlying type
    
        // if its a pointer, resolve its value
        if val.Kind() == reflect.Ptr {
            val = reflect.Indirect(val)
        }
    
        // should double check we now have a struct (could still be anything)
        if val.Kind() != reflect.Struct {
             log.Fatal("unexpected type")
        }
    
        // now we grab our values as before (note: I assume table name should come from the struct type)
        structType := val.Type()  
        tableName := structType.Name()
        params = append(params, tableName)
    
        for i:=0; i < structType.NumField(); i++ {
            field := structType.Field(i)
            tag := field.Tag
    
            fieldName := field.Name
            fieldType := tag.Get("sql_type")
            fieldTags := tag.Get("sql_tag")
    
            paramstring := fieldName + " " + fieldType + " " + fieldTags
            params = append(params, paramstring)
        }
        return params
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 救!ENVI5.6深度学习初始化模型报错怎么办?
  • ¥30 eclipse开启服务后,网页无法打开
  • ¥30 雷达辐射源信号参考模型
  • ¥15 html+css+js如何实现这样子的效果?
  • ¥15 STM32单片机自主设计
  • ¥15 如何在node.js中或者java中给wav格式的音频编码成sil格式呢
  • ¥15 不小心不正规的开发公司导致不给我们y码,
  • ¥15 我的代码无法在vc++中运行呀,错误很多
  • ¥50 求一个win系统下运行的可自动抓取arm64架构deb安装包和其依赖包的软件。
  • ¥60 fail to initialize keyboard hotkeys through kernel.0000000000