【问题标题】:How to get a pointer to a variable that's masked as an interface?如何获取指向被屏蔽为接口的变量的指针?
【发布时间】:2017-05-02 04:49:29
【问题描述】:

我不想深入探讨以下情况的基本原理。它与解组可以是任何一组固定类型的序列化对象有关,但您不知道是哪种类型。

我有以下类型:

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{}
    // 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.
}

【问题讨论】:

    标签: pointers go types interface


    【解决方案1】:

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

    请注意,您必须传递要将结果解组到的变量的地址。

    出于演示目的,让我们使用以下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 3case 4 正在返回非指针。

    您的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,我们就返回它。

    测试它:

    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>
    

    【讨论】:

    • 就像我说的,unmarshall 函数确实返回原始结构,而不是指针:play.golang.org/p/RC-Jqs6a8W
    • @StevenRoose 查看已编辑的答案。添加了示例和代码以获取指向动态值的指针。
    • 啊哈,间接什么我不知道的方法。谢谢!
    猜你喜欢
    • 2019-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-11
    • 2011-08-04
    • 2014-02-16
    • 2023-03-23
    • 1970-01-01
    相关资源
    最近更新 更多