Go反射:获取接口的正确结构类型

3
考虑以下内容:
type myStruct struct {
    Foo string `json:"foo"`
}

func main() {
    somelibrary.DoThing(func(thing myStruct) {
        // myStruct should contain unmarshaled JSON
        // provided by somelibrary

        fmt.Printf("%v\n", thing)
    })
}

我刚接触 Go 语言,所以担心这段代码可能不符合惯用写法。我想实现 somelibrary.DoThing 函数,使其能够通过反射正确地从函数参数中推断出结构体类型(如果可能的话)。以下是我的代码:

const jsonData := []byte{`{"foo": "bar"}`}

func DoThing(fn interface{}) {
    // Get first arg of the function
    firstArg := reflect.TypeOf(fn).In(0)
    structPtr := reflect.New(firstArg)

    // Convert to Interface
    // Note that I can't assert this to .(myStruct) type
    instance := structPtr.Elem().Interface()

    // Unmarshal the JSON
    json.Unmarshal(jsonData, &instance)

    // Call the function
    vfn := reflect.ValueOf(fn)
    vfn.Call([]reflect.Value{reflect.ValueOf(instance)})
}

在不预先知道结构类型的情况下,json.Unmarshal只会假设instancemap[string]interface{},因此在调用vfn.Call(...)时会出现错误:

panic: reflect: Call using map[string]interface {} as type main.myStruct

是否可以将instance接口转换为正确的类型?换句话说,我是否可以通过传递字符串(或使用某些反射方法)来进行类型断言,而不是将类型作为符号提供给程序?

1个回答

3

是的,这是可能的。这是更新后的代码:

func DoThing(fn interface{}) {
    // Get first arg of the function
    firstArg := reflect.TypeOf(fn).In(0)

    // Get the PtrTo to the first function parameter
    structPtr := reflect.New(firstArg)

    // Convert to Interface
    // Note that I can't assert this to .(myStruct) type
    instance := structPtr.Interface()

    // Unmarshal the JSON
    json.Unmarshal(jsonData, instance)

    // Call the function
    vfn := reflect.ValueOf(fn)
    vfn.Call([]reflect.Value{structPtr.Elem()})
}

修改的内容:

  1. structPtr(一个指针)传递给json.Unmarshal;如果传递一个值,您将看不到更改。
  2. 在传递给json.Unmarshal时,删除获取instance地址的操作;通常情况下没有理由对接口使用指针
  3. 调用fn时,使用structPtr而不是instance

https://play.golang.org/p/POmOyQBJYC


1
请注意,尽管这是正确的,但OP的代码甚至意图似乎非常可疑和错误,并且不会通过任何合理的代码审查。 - Yerken
@lxe:如果这个回答解决了你的问题,请将其标记为已接受的答案 - user142162
@Yerken 怎么了? - lxe
因为DoThing仅仅是将数据解组成结构体,所以可以写得非常简单,如下所示: json.Unmarshal(somelibrary.JsonData, myStructPtr) - Yerken
reflect包是一个低级别的包,与其他标准库相比速度相对较慢,因为它进行了相当多的分配。很少有好的理由使用它,如果不小心使用,它容易引起恐慌。例如,Tim的代码在许多不同的情况下都会引发恐慌。 - Yerken

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