英文:
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()})
}
所做的更改:
- 将
structPtr
(一个指针)传递给json.Unmarshal
;如果传递一个值,你将看不到更改。 - 在传递给
json.Unmarshal
时,删除对instance
取地址的操作;通常没有理由将接口的指针 - 在调用
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:
- Pass
structPtr
(a pointer) tojson.Unmarshal
; pass a value and you will not see the changes - Remove taking the address of
instance
when passing tojson.Unmarshal
; there is usually never a good reason to have a pointer to an interface - Use
structPtr
instead ofinstance
when callingfn
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论