dougou8458
dougou8458
2017-06-09 23:45

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

已采纳

Consider this:

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
", thing)
    })
}

I'm new to Go, so I fear this might not be idiomatic code. I'd like to implement somelibrary.DoThing so it correctly infers the struct type from the function argument via reflection, if it's possible. Here's what I have:

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

Without knowing the struct type beforehand, json.Unmarshal just assumes that instance is map[string]interface{}, so I get a panic when calling vfn.Call(...):

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

Is it possible to convert the instance interface into the correct type? In other words, can I do type type assertion by passing a string (or using some reflection method) instead of having the type available to the program as a symbol?

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

1条回答

  • dpr77335 dpr77335 4年前

    Yes, this is possible. Here is your code updated:

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

    Changes made:

    1. Pass structPtr (a pointer) to json.Unmarshal; pass a value and you will not see the changes
    2. Remove taking the address of instance when passing to json.Unmarshal; there is usually never a good reason to have a pointer to an interface
    3. Use structPtr instead of instance when calling fn

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

    点赞 评论 复制链接分享

相关推荐