如何获得指向被掩盖为接口的变量的指针? [英] How to get a pointer to a variable that's masked as an interface?

查看:91
本文介绍了如何获得指向被掩盖为接口的变量的指针?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我宁愿不深入探讨以下情况的理由.它与拆封序列化对象有关,该序列化对象可以是一组固定的类型,但是您不知道哪种类型.

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.

我有以下几种类型:

type I interface {
    Do()
}

type someI struct {}
func (i *someI) Do() {}

type otherI struct {}
func (i *otherI) Do() {}

因此,两个结构的指针实现了接口I .

So, two structs of which the pointers implement interface I.

现在,我有此方法想要返回类型为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.
}

推荐答案

要判断包装在interface{}中的值是否实现了其他接口(I),您可以简单地使用

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.

出于演示目的,让我们使用以下magicUnmarshal()函数:

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{}))
    }
}

请注意,case 3case 4返回非指针.

Note that case 3 and case 4 are returning non-pointers.

您的GetSomeI()实现可以是:

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)
}

第一个GeSomeI()测试值是否由magicUnmarshal()实现了I,如果是,则按原样使用它.如果不是,我们使用反射构造一个新的,并获取其地址(指向值的指针),然后进行测试.如果该指针实现了I,我们将其返回.

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.

测试:

func main() {
    for what := 0; what < 5; what++ {
        i, err := GetSomeI(what)
        fmt.Printf("%T %v\n", i, err)
    }
}

输出是(在进入游乐场上尝试):

*main.someI <nil>
*main.otherI <nil>
<nil> string does not implement I!
*main.someI <nil>
*main.otherI <nil>

这篇关于如何获得指向被掩盖为接口的变量的指针?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆