使用反射

时间:2018-04-11 00:14:25

标签: go reflection

我正在尝试通过反思来设置nil *int的值。

在下面的示例中,replaceNilWithNegativeOne应使用指向-1的指针替换任何nil *int32字段(标有grib:"foo")。

然而,当代码运行时,反映出现了panic: reflect: reflect.Value.Set using unaddressable value恐慌。

我在其他几个地方看到了几乎所有我要问的确切问题,例如:

我知道答案必须在某些地方的答案中,但我仍然无法连接点。除了你在下面的例子中看到的那个之外,我已经尝试了几个实现,但它们似乎都导致了段错误,或者更常见的是,导致无法抑制的值恐慌。

我很清楚fieldPtr.CanSet() == false,但是,话虽如此,我将如何完成我想要完成的任务?

示例

// https://play.golang.org/p/yZOtYxwTzUs
package main

import (
    "log"
    "reflect"
    "strings"
)

func main() {
    type testStruct struct {
        SomeField *int32 `grib:"foo"`
    }
    testStructInstance := testStruct{
        SomeField: nil,
    }
    replaceNilWithNegativeOne(testStructInstance)

    if *testStructInstance.SomeField != int32(-1) {
        // We never get here
        log.Println("Didn't get set")
    }
}

func replaceNilWithNegativeOne(obj interface{}) (err error) {
    objType := reflect.TypeOf(obj)
    for i := 0; i < objType.NumField(); i++ {
        if t, ok := objType.Field(i).Tag.Lookup("grib"); ok {
            if strings.Contains(t, "foo") {
                fieldPtr := reflect.ValueOf(obj).Field(i)
                if fieldPtr.Kind() != reflect.Ptr {
                    // do some stuff
                    break
                }
                if fieldPtr.IsNil() {
                    v := -1
                    // I know this isn't working because the CanSet() == false
                    // But I want to set the value to -1.
                    fieldPtr.Set(reflect.ValueOf(&v))
                    continue
                }
            }
        }
    }
    return
}

1 个答案:

答案 0 :(得分:1)

这里有两个问题。首先是需要addressable值。将指向结构的指针传递给函数而不是结构值:

replaceNilWithNegativeOne(&testStructInstance)

在函数中,调用Value.Elem()以获取结构的反射值。

另一个问题是代码将int分配给int32。使用int32(-1)来解决问题。

以下是更新后的功能:

replaceNilWithNegativeOne(obj interface{}) (err error) {
    v := reflect.ValueOf(obj).Elem()
    t := v.Type()
    for i := 0; i < t.NumField(); i++ {
        if grib, ok := t.Field(i).Tag.Lookup("grib"); ok {
            if strings.Contains(grib, "foo") {
                fv := v.Field(i)
                if fv.Kind() != reflect.Ptr {
                    // do some stuff
                    break
                }
                if fv.IsNil() {
                    iv := int32(-1)
                    fv.Set(reflect.ValueOf(&iv))
                    continue
                }
            }
        }
    }
    return nil
}

Playground Example