如何将断言 reflect.Value 结构返回到我知道它实现的接口?
How can I type assert a reflect.Value struct back to an interface that I know it implements?
最近在用go写一些审计流程。其中大部分只是将任意构造分开的反射。存在一个可用于对象相等的接口。并非所有被拆分的东西都实现了这个接口,并且可以在任意类型之间进行比较(对象相等并不意味着类型相等)。
由于被比较的东西不需要是同一类型,因此在某些情况下,某物在一侧是指针而在另一侧是值(*string 与 string 或 *ChickenBox 与 BoxOfChicken)。当我遇到 pointers/interface 值时,我只是取消引用它们,所以完成后的比较很简单。
我的问题是,一旦我取消引用 pointer/interface,我似乎无法将断言键入父 pointer/interface 实现的接口。
这里有一个例子(playground:https://play.golang.org/p/O_dvyW07fu)演示了这一点:
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\n", 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
}
我对此的理解是界面分为两部分(https://blog.golang.org/laws-of-reflection)。在反映中,这由 reflect.Type(广义方法)和 reflect.Value(广义数据)表示。在演示中,我们成功评估了传递的 reflect.Value 的 reflect.Type 实现了第 67 行的接口:
if x.IsValid() && ImplementsID(x) {
这是因为在第 53 行我们可以使用 reflect.PtrTo:
重建我们与界面的关系
return reflect.PtrTo(x.Type()).Implements(idType)
我无法在 reflect.Value 端复制这个过程(在 reflect.Type 端)。所以在第 70 行,我们只有底层结构(没有方法):
val := x.Interface().(IHasID)
这失败了,我一直找不到任何语法可以让我做在 reflect.Type 方面似乎可行的事情;回到针对满足断言接口的结构定义的方法(我想 reflect.Type 只是针对缓存的接口存储工作是合理的,此时结构方法对我来说已经丢失了)。
我有一个可行的解决方案,可以在执行任何工作之前强制执行 pointer/interface 的存在,但这感觉就像我遗漏了什么。鉴于 reflect.Value 的 reflect.Type 可以成功找到接口,我肯定可以在 reflect.Value 端做类似的事情?
GetID
方法需要一个指针接收器,但您正在对结构值进行操作。由于该值不可寻址,您不能调用 GetID
方法,也不能调用 Addr()
来获取指向该值的指针。
这与无法编译的原因相同:https://play.golang.org/p/ZPQnVXtx01
hid := HasID{ID: "testID"}
_ = IHasID(hid)
调用时需要使用指向原始结构值的指针 GetIDFrom
id, _ := GetIDFrom(reflect.ValueOf(&testy))
然后您应该首先检查指针是否实现了接口,然后再使用 Elem()
取消引用它以检查结构类型。
最近在用go写一些审计流程。其中大部分只是将任意构造分开的反射。存在一个可用于对象相等的接口。并非所有被拆分的东西都实现了这个接口,并且可以在任意类型之间进行比较(对象相等并不意味着类型相等)。
由于被比较的东西不需要是同一类型,因此在某些情况下,某物在一侧是指针而在另一侧是值(*string 与 string 或 *ChickenBox 与 BoxOfChicken)。当我遇到 pointers/interface 值时,我只是取消引用它们,所以完成后的比较很简单。
我的问题是,一旦我取消引用 pointer/interface,我似乎无法将断言键入父 pointer/interface 实现的接口。
这里有一个例子(playground:https://play.golang.org/p/O_dvyW07fu)演示了这一点:
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\n", 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
}
我对此的理解是界面分为两部分(https://blog.golang.org/laws-of-reflection)。在反映中,这由 reflect.Type(广义方法)和 reflect.Value(广义数据)表示。在演示中,我们成功评估了传递的 reflect.Value 的 reflect.Type 实现了第 67 行的接口:
if x.IsValid() && ImplementsID(x) {
这是因为在第 53 行我们可以使用 reflect.PtrTo:
重建我们与界面的关系return reflect.PtrTo(x.Type()).Implements(idType)
我无法在 reflect.Value 端复制这个过程(在 reflect.Type 端)。所以在第 70 行,我们只有底层结构(没有方法):
val := x.Interface().(IHasID)
这失败了,我一直找不到任何语法可以让我做在 reflect.Type 方面似乎可行的事情;回到针对满足断言接口的结构定义的方法(我想 reflect.Type 只是针对缓存的接口存储工作是合理的,此时结构方法对我来说已经丢失了)。
我有一个可行的解决方案,可以在执行任何工作之前强制执行 pointer/interface 的存在,但这感觉就像我遗漏了什么。鉴于 reflect.Value 的 reflect.Type 可以成功找到接口,我肯定可以在 reflect.Value 端做类似的事情?
GetID
方法需要一个指针接收器,但您正在对结构值进行操作。由于该值不可寻址,您不能调用 GetID
方法,也不能调用 Addr()
来获取指向该值的指针。
这与无法编译的原因相同:https://play.golang.org/p/ZPQnVXtx01
hid := HasID{ID: "testID"}
_ = IHasID(hid)
调用时需要使用指向原始结构值的指针 GetIDFrom
id, _ := GetIDFrom(reflect.ValueOf(&testy))
然后您应该首先检查指针是否实现了接口,然后再使用 Elem()
取消引用它以检查结构类型。