doulianxing4015 2016-12-16 11:38
浏览 37
已采纳

如何获得指向被掩盖为接口的变量的指针?

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:

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条回答 默认 最新

  • douyu0792 2016-12-16 11:57
    关注

    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
    ", 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>
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥20 求各位懂行的人,注册表能不能看到usb使用得具体信息,干了什么,传输了什么数据
  • ¥15 个人网站被恶意大量访问,怎么办
  • ¥15 Vue3 大型图片数据拖动排序
  • ¥15 Centos / PETGEM
  • ¥15 划分vlan后不通了
  • ¥20 用雷电模拟器安装百达屋apk一直闪退
  • ¥15 算能科技20240506咨询(拒绝大模型回答)
  • ¥15 自适应 AR 模型 参数估计Matlab程序
  • ¥100 角动量包络面如何用MATLAB绘制
  • ¥15 merge函数占用内存过大