如何在golang中通过interface{}传递结构体并获取指针?

6

不,我不认为这是如何确定interface{}值的“真实”类型?的重复。我知道如何获取接口变量的Type,但我找不到一种方法来获取interface{}的实际类型的指针。

最近,我遇到了interface{}的问题。我有一个类型为A的变量通过interface{}传递,一个以*A为接收器的方法Tt被定义。

我想调用方法Tt,但失败了,因为变量在interface{}中,我无法获取指向该变量的指针。

正如您所看到的,reflect.TypeOf(v)给出了正确的类型A,但reflect.TypeOf(&v)却给出了*interface {}而不是*A。

是否有任何方法可以获得*A?

package main

import (
    "fmt"
    "reflect"
)

type SomeStruct1 struct{
}

type SomeStruct2 struct{
}

type SomeStruct3 struct{
}
etc...

func (*SomeStruct1) SomeMethod(){
    fmt.Println("hello")
}
func (*SomeStruct2) SomeMethod(){
    fmt.Println("hello")
}
func (*SomeStruct3) SomeMethod(){
    fmt.Println("hello")
}
etc...

func DoSomething(arg interface{}){
    switch v:=b.(type){
        []byte:{
            dosomething for []byte
        }
        default:{
            var m reflect.Value
            if value.Kind() != reflect.Ptr {
                m = reflect.ValueOf(&v).MethodByName("SomeMethod")
            } else {
                m = reflect.ValueOf(v).MethodByName("SomeMethod")
            }
            m.Call(nil)
        }
}

func main() {
    DoSomething([]byte{...})
    DoSomething(SomeStruct1{})
    DoSomething(&SomeStruct1{})
    etc..
}

使用反射包 - Filip Haglund
可能是如何确定interface{}值的“真实”类型?的重复。 - Adrian Forsius
1
为什么不直接将方法Tt放在接口中并完成呢?而不使用任何反射? - gonutz
请提供一个合理的例子来证明这一点。即使我在这里看起来不对。 - Yerken
@Yerken,我有修改,以提供更详细的示例。我希望函数DoSomething可以接受SomeStruct*SomeStruct - user1895011
3个回答

8

使用反射:

//
// Return a pointer to the supplied struct via interface{}
//
func to_struct_ptr(obj interface{}) interface{} {
    vp := reflect.New(reflect.TypeOf(obj))
    vp.Elem().Set(reflect.ValueOf(obj))
    return vp.Interface()
}

传入带有 T 的接口,然后您可以获得带有 *T 的接口。


3
要调用指针方法 Tt(),您必须拥有一个 *A(或者取一个可寻址的 可寻址A 值的地址)。变量 b 中的 A 值不是可寻址,因此通过 b 是无法访问 A 指针方法的。
解决方法是从 a 的地址开始:
var a A
var b interface{}
b = &a // Note change on this line
switch v := b.(type) {
default:
    reflect.ValueOf(v).MethodByName("Tt").Call(nil)
}

在调用reflect.ValueOf(v)时,将v中的值作为参数传递。函数ValueOf解包空接口以恢复类型为A的值。
在调用reflect.ValueOf(&v)时,将*interface{}存储在空接口中,然后将其作为参数传递。函数ValueOf解包空接口以恢复类型为*interface{}的值。这是变量v的地址,而不是变量a的地址。
此特定示例中不需要反射。
var a A
var b interface{}
b = &a
switch v := b.(type) {
case interface {
    Tt()
}:
    v.Tt()
default:
    fmt.Println("not handled")
}

是的,那个可以正常运行。但我认为很奇怪的是 reflect.ValueOf(v) 返回 A,而 reflect.ValueOf(&v) 返回 * interface{} - user1895011
我在答案中添加了更多的解释。 - Charlie Tumahai

0
你只需要进行类型转换。 例如:
type SomeStruct1 struct{
    someList []string
}

func (s *SomeStruct1) print() {
    fmt.Printf("%v\n", s.someList)
...

func Call(T interface{}){
    switch T.(type){
    case *SomeStruct1:
        t := T.(*SomeStruct1) // cast as pointer to struct
        t.print()
    ...
    }
}

func main() {
    a := SomeStruct{
        someList: []string{"a", "b"}
    }
    Call(&a) // pass a as ptr
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接