英文:
How to use reflect.NewAt on interface{}?
问题
package main
import (
"encoding/json"
"fmt"
"reflect"
"unsafe"
)
type Stu struct {
Name string `json:"name"`
}
func MakeStu() interface{} {
return Stu{
Name: "Test",
}
}
func main() {
jsonData := []byte(`{"name":"New"}`)
t1 := MakeStu()
t2 := MakeStu()
t3 := MakeStu()
json.Unmarshal(jsonData, &t1)
fmt.Println(t1) // t1的类型变为map[string]interface{},而不是struct Stu
newPointer := reflect.New(reflect.ValueOf(t2).Type()).Interface()
json.Unmarshal(jsonData, newPointer)
fmt.Println(newPointer) // 这样可以工作,但需要分配内存来保存临时的新变量
brokenPointer := reflect.NewAt(reflect.ValueOf(t3).Type(), unsafe.Pointer(&t3)).Interface()
json.Unmarshal(jsonData, brokenPointer)
fmt.Println(brokenPointer) // 我想根据已存在的变量t3获取原始类型的指针,但会导致崩溃。
}
如果在编码时不知道interface{}的具体类型,就无法使用interface.(Type)进行类型转换。
那么如何在interface{}上使用reflect.NewAt呢?
如果有一个包含返回interface{}类型的方法的接口,其具体类型为struct,但在编码时无法确定struct的类型。我需要使用json.Unmarshal解码由json编码的数据。我不想得到map[string]interface{},所以我需要将接收者的类型设置为interface{}的具体类型的指针。使用reflect.New很容易,但会消耗额外的内存,所以我想知道如何在现有的interface{}上使用reflect.NewAt。
英文:
package main
import (
"encoding/json"
"fmt"
"reflect"
"unsafe"
)
type Stu struct {
Name string `json:"name"`
}
func MakeStu() interface{} {
return Stu{
Name: "Test",
}
}
func main() {
jsonData := []byte(`{"name":"New"}`)
t1 := MakeStu()
t2 := MakeStu()
t3 := MakeStu()
json.Unmarshal(jsonData, &t1)
fmt.Println(t1) //Type of t1 becomes map[string]interface{} instead of struct Stu
newPointer := reflect.New(reflect.ValueOf(t2).Type()).Interface()
json.Unmarshal(jsonData, newPointer)
fmt.Println(newPointer) //It works,but it need allocate memory to hold temp new variable
brokenPointer := reflect.NewAt(reflect.ValueOf(t3).Type(), unsafe.Pointer(&t3)).Interface()
json.Unmarshal(jsonData, brokenPointer)
fmt.Println(brokenPointer) // I want to get the pointer of original type based on the existed variable t3,but it crashes.
}
If I don't know the concrete type of the interface{} when coding,I can't use interface.(Type) to cast.
so how to use reflect.NewAt on interface{}?
If there is an interface that contains a method who returns interface{} whose concrete type is struct, but I can't determine the type of struct when coding. I need use json.Unmarshal to decode data that encoded by json. I don't want to get map[string]interface{} , so I need make the receiver's type that is the pointer of concrete type of the interface{} . Using reflect.New is easy but costing extra memory, so I am curious about how to use reflect.NewAt with existing interface{}.
答案1
得分: 1
我想解释一下问题是什么。
函数MakeStu()
返回的是空接口(empty interface),我们无法控制它的具体类型。这会使具体类型对json.Unmarshal()
来说是“不可见的”,json.Unmarshal()
将其视为空接口,而不是具体类型Stu
。我们需要以某种方式将具体类型传递给解码器。
我会使用类型断言来解决这个问题:
func main() {
jsonData := []byte(`{"name":"New"}`)
t1 := MakeStu()
switch c := t1.(type) {
default:
_ = json.Unmarshal(jsonData, &c)
t1 = c
}
fmt.Println(t1)
}
类型断言将空接口转换为具体类型,json.Unmarshal()
将按照具体类型进行处理。可能仍然会有额外的分配,但代码更易读,并且不依赖反射。
如果我感到非常冒险,我会添加一个接受空接口指针的辅助函数,像这样:
func unmarshal(data []byte, i *interface{}) (err error) {
switch c := (*i).(type) {
default:
err = json.Unmarshal(data, &c)
*i = c
}
return err
}
这样就可以像这样使用:
jsonData := []byte(`{"name":"New"}`)
t1 := MakeStu()
unmarshal(jsonData, &t1)
我觉得这看起来很简洁,不涉及反射,但它没有使用你自己建议的reflect.NewAt()
。希望你仍然觉得我的建议有用。
英文:
I would like to spend a few sentences explaining what the problem is.
The function MakeStu()
, which we have no control over, returns the empty interface - not a concrete type. This will make the concrete type "invisible" to json.Unmarshal()
, and json.Unmarshal()
treats it as the empty interface, not as the concrete type Stu
. Somehow we must convey the concrete type to the unmarshaller.
I would solve this problem with a type switch:
func main() {
jsonData := []byte(`{"name":"New"}`)
t1 := MakeStu()
switch c := t1.(type) {
default:
_ = json.Unmarshal(jsonData, &c)
t1 = c
}
fmt.Println(t1)
}
The type switch turns the empty interface into a concrete type, and json.Unmarshal()
will treat it as such. You will probably still have the extra allocation, but the code is more readable, and you're not depending on reflection.
If I was feeling really adventurous, I would add a helper function accepting a pointer to an empty interface like this:
func unmarshal(data []byte, i *interface{}) (err error) {
switch c := (*i).(type) {
default:
err = json.Unmarshal(data, &c)
*i = c
}
return err
}
That would enable usage like this:
jsonData := []byte(`{"name":"New"}`)
t1 := MakeStu()
unmarshal(jsonData, &t1)
This looks clean to me and doesn't involve reflection, but it doesn't use reflect.NewAt()
as you suggested yourself. I hope you find my suggestions useful none the less.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论