dongye1143 2016-08-04 15:07
浏览 73
已采纳

如何在将其实现的接口上断言一个reflect.refreak.Value结构?

I have recently been writing some auditing processes in go. Most of this is just reflection ripping arbitrary constructs apart. There exists an interface that can be used for object equality. Not all of the things being ripped apart implement this interface and the comparisons can be between arbitrary types (object equality does not imply equality of type).

Since the things being compared do not need to be the same type there are cases where something is a pointer on one side and a value on the other (*string vs string or *ChickenBox vs BoxOfChicken). I have been simply dereferencing pointers/interface values when I encounter them so the comparison when done is simple.

My problem is that once I have dereferenced the pointer/interface I don't seem to be able to type assert to interfaces that the parent pointer/interface implemented.

Here is an example (playground: https://play.golang.org/p/O_dvyW07fu) that demonstrates this:

package main

import (
    "fmt"
    "reflect"
)

// ID interface
type IHasID interface {
    GetID() string
    SetID(id string)
}

// implementing type
type HasID struct {
    ID string `bson:"_id,omitempty"`
}

func (be *HasID) GetID() string {
    return be.ID
}
func (be *HasID) SetID(id string) {
    be.ID = id
}

// static comparison reflect.Type of IHasID
var idType = reflect.TypeOf((*IHasID)(nil)).Elem()

func main() {
    testy := struct {
        HasID
        Somefield int
    }{
        HasID{
            ID: "testymctest",
        },
        4,
    }

    id, _ := GetIDFrom(reflect.ValueOf(testy))
    fmt.Printf("ID: %v
", id)
}

// check if the reflect.Value implements IHasID
func ImplementsID(x reflect.Value) bool {
    switch x.Kind() {
    case reflect.Interface, reflect.Ptr:
        if x.IsValid() && !x.IsNil() {
            return ImplementsID(x.Elem())
        }
    case reflect.Struct:
        if x.IsValid() {
            return reflect.PtrTo(x.Type()).Implements(idType)
        }
    }
    return false
}

// check and get the reflect.Value's ID
func GetIDFrom(x reflect.Value) (string, bool) {
    switch x.Kind() {
    case reflect.Ptr, reflect.Interface:
        if x.IsValid() && !x.IsNil() {
            return GetIDFrom(x.Elem())
        }
    case reflect.Struct:
        if x.IsValid() && ImplementsID(x) {
            // not using val,ok syntax to demo issue, if it gets here it should
            // implement ID since we did the check in the if statement above
            val := x.Interface().(IHasID)
            return val.GetID(), true
        }
    }
    return "", false
}

My understanding of this is that the interface is two parts (https://blog.golang.org/laws-of-reflection). Within reflect this is represented by reflect.Type (broadly methods) and reflect.Value (broadly data). Within the demonstration we evaluate successfully that the passed reflect.Value's reflect.Type implements the interface on line 67:

if x.IsValid() && ImplementsID(x) {

This is because on line 53 we can reconstruct our relationship to the interface using reflect.PtrTo:

return reflect.PtrTo(x.Type()).Implements(idType)

It is this process (on the reflect.Type side) that I am unable to replicate on the reflect.Value side. So on line 70 we simply have the underlying struct (without methods):

val := x.Interface().(IHasID)

This fails and I have been unable to find any syntax that will allow me to do what seems to have been possible on the reflect.Type side; get back to the methods defined against that struct that meet the assert's interface (I suppose it is plausible that the reflect.Type is simply working against a cached interface store and the structs methods have been lost to me by this point).

I have a working solution that simply enforces the pointer/interface's presence before doing any work but this feels like I am missing something. Given that the reflect.Value's reflect.Type can successfully find the interface surely there is something similar I can do on the reflect.Value side?

  • 写回答

1条回答 默认 最新

  • dongying6179 2016-08-04 15:41
    关注

    The GetID method requires a pointer receiver, but you are operating on a struct value. Since that value isn't addressable, you can't call the GetID method, nor can you call Addr() to get a pointer to the value.

    This is the same reason that this won't compile: https://play.golang.org/p/ZPQnVXtx01

    hid := HasID{ID: "testID"}
    _ = IHasID(hid)
    

    You need to use a pointer to your original struct value when calling GetIDFrom

    id, _ := GetIDFrom(reflect.ValueOf(&testy))
    

    You should then be first checking if the pointer implements the interface, before dereferencing it with Elem() to check the struct type.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 python的qt5界面
  • ¥15 无线电能传输系统MATLAB仿真问题
  • ¥50 如何用脚本实现输入法的热键设置
  • ¥20 我想使用一些网络协议或者部分协议也行,主要想实现类似于traceroute的一定步长内的路由拓扑功能
  • ¥30 深度学习,前后端连接
  • ¥15 孟德尔随机化结果不一致
  • ¥15 apm2.8飞控罗盘bad health,加速度计校准失败
  • ¥15 求解O-S方程的特征值问题给出边界层布拉休斯平行流的中性曲线
  • ¥15 谁有desed数据集呀
  • ¥20 手写数字识别运行c仿真时,程序报错错误代码sim211-100