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



我将结构作为interface{}传递到函数中。然后在里面,我使用reflect来获取结构属性。代码如下:

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
}

我在函数中的行for i:=0; i < val.NumField(); i++ {上遇到错误BindStruct。错误消息是:

恐慌:反映:反映的呼唤。接口值上的值.数字字段

如果我从params := BindStruct(&i)迁移功能中变得params := BindStruct(i)中删除&,则会收到此错误:

panic:运行时错误:无效的内存地址或 nil 指针

取消引用[信号 SIGSEGV:分段违规代码=0x1 addr=0x0 pc=0x4ff457]

什么给?

当您调用BindStruct(&i)时,您正在传递指向接口的指针。 所以这一行:

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

将 val 设置为反射。表示接口的值,因为reflect.ValueOf(domain)获取指针然后.Elem()解析接口 - 这会导致您的第一个错误,因为它确实是一个接口值(并且它们没有字段):

恐慌:反映:反映的呼唤。接口值上的值.数字字段

因此,调用params := BindStruct(i)始终是正确的,因为您需要传递实际接口而不是指向它的指针。

您不清楚传递给Migrate()的基础数据类型是什么 - 它们是值还是指针?重要的是要知道,例如,使用反射检查结构标签,我们需要回到结构值类型,所以链是:

接口 ->(指针?) ->值 ->类型

我怀疑您正在使用值,就好像接口是一个值,然后是行:

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

预计会恐慌,因为reflect.ValueOf(domain)会解析该值,然后.Elem()会尝试取消引用一个值。

为了安全起见,我们将检查传入值的 Kind() 以确保我们有一个结构:

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
}

Remove & and Elem(),如下所示:

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).Name()
params = append(params, tableName)
val := reflect.ValueOf(domain)
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
}

最新更新