使用反射获取指向值的指针

Mih*_*i H 11 reflection go

我有一个函数遍历作为参数传递的接口的所有字段.为了实现这一点,我正在使用反射.问题是我不知道如何获取非指针字段的地址.这是一个例子:

type Z struct {
    Id int
}

type V struct {
    Id int
    F Z
}

type T struct {
    Id int
    F V
}
Run Code Online (Sandbox Code Playgroud)

上面的代码代表我的测试结构.现在这里是遍历指定结构的实际函数,并列出了有关它的详细信息:

func InspectStruct(o interface{}) {
     val := reflect.ValueOf(o)
     if val.Kind() == reflect.Interface && !val.IsNil() {
        elm := val.Elem()
        if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr {
            val = elm
        }
     }
     if val.Kind() == reflect.Ptr {
        val = val.Elem()
     }

    for i := 0; i < val.NumField(); i++ {
        valueField := val.Field(i)
        typeField := val.Type().Field(i)
        address := "not-addressable"

        if valueField.Kind() == reflect.Interface && !valueField.IsNil() {
            elm := valueField.Elem()
            if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr {
                valueField = elm
            }
        }
        if valueField.Kind() == reflect.Ptr {
            valueField = valueField.Elem()
        }
        if valueField.CanAddr() {
            address = fmt.Sprint(valueField.Addr().Pointer())
        }

        fmt.Printf("Field Name: %s,\t Field Value: %v,\t Address: %v\t, Field type: %v\t, Field kind: %v\n", typeField.Name, 
            valueField.Interface(), address, typeField.Type, valueField.Kind())

        if valueField.Kind() == reflect.Struct {
            InspectStruct(valueField.Interface())
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是结构实例化/初始化后的实际测试:

t := new(T)
t.Id = 1
t.F = *new(V)
t.F.Id = 2
t.F.F = *new(Z)
t.F.F.Id = 3

InspectStruct(t)
Run Code Online (Sandbox Code Playgroud)

最后InspectStruct的输出调用:

Field Name: Id,  Field Value: 1,     Address: 408125440 , Field type: int   , Field kind: int
Field Name: F,   Field Value: {2 {3}},   Address: 408125444 , Field type: main.V    , Field kind: struct
Field Name: Id,  Field Value: 2,     Address: not-addressable   , Field type: int   , Field kind: int
Field Name: F,   Field Value: {3},   Address: not-addressable   , Field type: main.Z    , Field kind: struct
Field Name: Id,  Field Value: 3,     Address: not-addressable   , Field type: int   , Field kind: int
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我正在使用递归,因此如果其中一个字段是结构类型,那么我为它调用InspectStruct. 我的问题是,尽管所有字段都已针对整个结构"t"层次结构进行了初始化,但我无法获得位于比"t"更高深度的任何字段的地址.我真的很感激任何帮助.

One*_*One 16

通过reflect.Value而不是interface{}似乎解决问题,但我不知道为什么valueField.Interface()不起作用.

工作示例:http://play.golang.org/p/nleA2YWMj8

func InspectStructV(val reflect.Value) {
    if val.Kind() == reflect.Interface && !val.IsNil() {
        elm := val.Elem()
        if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr {
            val = elm
        }
    }
    if val.Kind() == reflect.Ptr {
        val = val.Elem()
    }

    for i := 0; i < val.NumField(); i++ {
        valueField := val.Field(i)
        typeField := val.Type().Field(i)
        address := "not-addressable"

        if valueField.Kind() == reflect.Interface && !valueField.IsNil() {
            elm := valueField.Elem()
            if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr {
                valueField = elm
            }
        }

        if valueField.Kind() == reflect.Ptr {
            valueField = valueField.Elem()

        }
        if valueField.CanAddr() {
            address = fmt.Sprintf("0x%X", valueField.Addr().Pointer())
        }

        fmt.Printf("Field Name: %s,\t Field Value: %v,\t Address: %v\t, Field type: %v\t, Field kind: %v\n", typeField.Name,
            valueField.Interface(), address, typeField.Type, valueField.Kind())

        if valueField.Kind() == reflect.Struct {
            InspectStructV(valueField)
        }
    }
}

func InspectStruct(v interface{}) {
    InspectStructV(reflect.ValueOf(v))
}
Run Code Online (Sandbox Code Playgroud)


Lin*_*ope 11

原因Interface()不起作用是因为它返回的接口包装器.为了了解正在发生的事情,让我们看看我们在没有反思的情况下做了些什么:

type MyStruct struct {
    F Foo
}

type Foo struct {
    i int
}

func ExtractField(ptr *MyStruct) interface{} {
    return ptr.F
}

func main() {
    ms := &MyStruct{Foo{5}}
    f := ExtractField(ms).(Foo) // extract value
    f.i = 19
    fmt.Println(f, ms.F)            // ???
    fmt.Println(&f == &ms.F)        // Not the same!
}
Run Code Online (Sandbox Code Playgroud)

(游乐场)

但是,想想interface{}这个回报.什么是包装?该ptr.F-即复制它.这就是value.Interface它,它返回你interface{}包装字段.不再有任何指针元数据,它与原始结构完全分离.

正如你会注意到,直接传递一个值,以reflect.ValueOf将始终返回false为CanAddr为"顶级" -因为该地址是没有意义的,因为它会给你的价值的副本的地址,改变它真的不会什么意思.(请记住,指针也是值 - 如果你想要指针值字段的地址*Foo,你真的在​​寻找**Foo).

所以,在我们上面的例子中,如果我们天真地传入,reflect.ValueOf(ExtractField(ms))我们就会得到它ValueOf f,它不仅没有你想要的地址 - 它甚至不能根据反映进行寻址,因为它永远不会给出有效的地址就反射而言(它可以给你的唯一地址是Value结构中内部值副本的地址).

那么为什么传递Value下来的兔子洞呢?那么,唯一真正的办法可以说的是,reflect.Value当您使用保持必要的元数据ElemField,而interface{}不能.所以虽然reflect.Value可能看起来像:

// Disclaimer: not the real structure of a reflect.Value
type Value struct {
    fieldAddress uintptr
    value        Foo
}
Run Code Online (Sandbox Code Playgroud)

它能给你的一切就是这个

// Again, an abstraction of the real interface wrapper 
// just for illustration purposes
type interface{} struct {
    value Foo
}
Run Code Online (Sandbox Code Playgroud)