通过反射获取接口的正确结构类型

huangapple go评论99阅读模式
英文:

go reflection: get correct struct type of interface

问题

考虑以下代码:

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{}) {
    // 获取函数的第一个参数
    firstArg := reflect.TypeOf(fn).In(0)
    structPtr := reflect.New(firstArg)

    // 转换为接口类型
    // 注意,我不能将其断言为.(myStruct)类型
    instance := structPtr.Elem().Interface()

    // 反序列化JSON
    json.Unmarshal(jsonData, &instance)

    // 调用函数
    vfn := reflect.ValueOf(fn)
    vfn.Call([]reflect.Value{reflect.ValueOf(instance)})
}

在不事先知道结构体类型的情况下,json.Unmarshal默认将instance解析为map[string]interface{},因此在调用vfn.Call(...)时会出现恐慌:

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

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

英文:

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\n", 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

得分: 3

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

func DoThing(fn interface{}) {
    // 获取函数的第一个参数
    firstArg := reflect.TypeOf(fn).In(0)

    // 获取第一个函数参数的指针
    structPtr := reflect.New(firstArg)

    // 转换为接口类型
    // 注意,我不能断言为 .(myStruct) 类型
    instance := structPtr.Interface()

    // 反序列化 JSON
    json.Unmarshal(jsonData, instance)

    // 调用函数
    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

英文:

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

huangapple
  • 本文由 发表于 2017年6月10日 07:45:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/44468048.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定