英文:
How to get a pointer to a variable that's masked as an interface?
问题
我不愿意深入探讨下面情况的原理。它涉及到对一个序列化对象进行解组,该对象可以是一组固定类型中的任何一个,但你不知道具体是哪种类型。
我有以下类型:
type I interface {
Do()
}
type someI struct{}
func (i *someI) Do() {}
type otherI struct{}
func (i *otherI) Do() {}
所以,有两个结构体它们的指针都实现了接口I
。
现在我有一个方法,想要返回类型为I
的值:
func GetSomeI(marshalled []byte) (I, error) {
var obj interface{}
// 下面的方法会神奇地将someI或otherI的实例放入obj中。
magicUnmarshall(marshalled, obj)
// 现在的问题是我们不能返回obj,因为原始结构体并没有实现I接口。
// 一种解决方法是使用类型断言:
switch obj.(type) {
case someI:
i := obj.(someI)
return &i, nil
case otherI:
i := obj.(otherI)
return &i, nil
default:
return nil, errors.New("marshalled object was not of type I")
}
// 但是现在考虑一下有很多不同的I接口实现的情况。
// 我们更希望有一种通用的方法来获取obj的引用。
}
英文:
I prefer not to dive into the rationale of the situation below. It has to do with unmarshaling an serialized object that can be any of a fixed set of types, but you don't know which type.
I have the following types:
<!-- language-all: lang-go -->
type I interface {
Do()
}
type someI struct {}
func (i *someI) Do() {}
type otherI struct {}
func (i *otherI) Do() {}
So, two structs of which the pointers implement interface I
.
Now I have this method that wants to return a value of type I
:
func GetSomeI(marshalled []byte) (I, error) {
var obj interface{}
// The following method magically puts an instance
// of either someI or otherI into obj.
magicUnmarshall(marshalled, obj)
// The problem now is that we cannot return obj,
// because the raw structs don't implement I.
// One solution would be to do a type switch like this:
switch obj.(type) {
case someI:
i := obj.(someI)
return &i, nil
case otherI:
i := obj.(otherI)
return &i, nil
default:
return nil, errors.New("marschalled object was not of type I")
}
// But now consider the case that there are quite some
// different implementations of I.
// We would prefer to have a general way of getting
// a reference to obj.
}
答案1
得分: 3
要判断一个包裹在interface{}
中的值是否实现了某个其他接口(I
),你可以简单地使用类型断言。
请注意,你必须传递你想要解包结果的变量的地址。
为了演示,让我们使用以下的magicUnmarshal()
函数:
func magicUnmarshal(what int, obj interface{}) {
v := reflect.ValueOf(obj).Elem()
switch what {
case 0:
v.Set(reflect.ValueOf(&someI{}))
case 1:
v.Set(reflect.ValueOf(&otherI{}))
case 2:
v.Set(reflect.ValueOf("just a string"))
case 3:
v.Set(reflect.ValueOf(someI{}))
case 4:
v.Set(reflect.ValueOf(otherI{}))
}
}
请注意,case 3
和case 4
返回的是非指针类型。
你的GetSomeI()
实现可以是:
func GetSomeI(what int) (I, error) {
var obj interface{}
magicUnmarshal(what, &obj)
// 尝试原值:
if i, ok := obj.(I); ok {
return i, nil
}
// 没有成功。尝试值的指针:
v := reflect.Indirect(reflect.New(reflect.TypeOf(obj)))
v.Set(reflect.ValueOf(obj))
pobj := v.Addr().Interface()
if i, ok := pobj.(I); ok {
return i, nil
}
return nil, fmt.Errorf("%T does not implement I!", obj)
}
首先,GetSomeI()
测试从magicUnmarshal()
得到的值是否实现了I
,如果是,则直接使用。如果不是,我们使用反射构造一个新的值,并获取其地址(一个指向值的指针),然后进行测试。如果该指针实现了I
,我们返回它。
进行测试:
func main() {
for what := 0; what < 5; what++ {
i, err := GetSomeI(what)
fmt.Printf("%T %v\n", i, err)
}
}
输出结果是(在Go Playground上尝试):
*main.someI <nil>
*main.otherI <nil>
<nil> string does not implement I!
*main.someI <nil>
*main.otherI <nil>
英文:
To tell if a value wrapped in an interface{}
implements some other interface (I
), you may simply use a type assertion.
Note that you must pass the address of the variable you want results unmarshaled to.
For demonstration purposes, let's use the following magicUnmarshal()
function:
func magicUnmarshal(what int, obj interface{}) {
v := reflect.ValueOf(obj).Elem()
switch what {
case 0:
v.Set(reflect.ValueOf(&someI{}))
case 1:
v.Set(reflect.ValueOf(&otherI{}))
case 2:
v.Set(reflect.ValueOf("just a string"))
case 3:
v.Set(reflect.ValueOf(someI{}))
case 4:
v.Set(reflect.ValueOf(otherI{}))
}
}
Note that case 3
and case 4
are returning non-pointers.
Your GetSomeI()
implementation can be:
func GetSomeI(what int) (I, error) {
var obj interface{}
magicUnmarshal(what, &obj)
// Try the value as-is:
if i, ok := obj.(I); ok {
return i, nil
}
// No success. Try a pointer to the value:
v := reflect.Indirect(reflect.New(reflect.TypeOf(obj)))
v.Set(reflect.ValueOf(obj))
pobj := v.Addr().Interface()
if i, ok := pobj.(I); ok {
return i, nil
}
return nil, fmt.Errorf("%T does not implement I!", obj)
}
First GeSomeI()
tests if the value got form magicUnmarshal()
implements I
, and if so, it is used as-is. If not, we construct a new using reflection, and get its address (a pointer to a value), and we test that. If that pointer implements I
, we return it.
Testing it:
func main() {
for what := 0; what < 5; what++ {
i, err := GetSomeI(what)
fmt.Printf("%T %v\n", i, err)
}
}
And the output is (try it on the Go Playground):
*main.someI <nil>
*main.otherI <nil>
<nil> string does not implement I!
*main.someI <nil>
*main.otherI <nil>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论