我正在尝试在 Go 中深度复制一个结构。我已成功地深度复制仅包含导出字段的结构。但是,当遇到未导出的字段时,我无法复制它。这是代码。
package main
import (
"fmt"
"reflect"
"unsafe"
)
type Example struct {
unexported int
Exported int
}
func read(a any) {
value := reflect.ValueOf(a)
if value.Kind() != reflect.Pointer {
// Create addressable space
tmp := reflect.New(value.Type())
tmp.Elem().Set(value)
value = tmp.Elem()
}
for i := 0; i < value.NumField(); i++ {
// Print each field
field := value.Field(i)
var data int64
if field.CanInterface() {
// Exported fields
data = field.Int()
} else {
// Unexported fields
addr := unsafe.Pointer(field.UnsafeAddr())
fieldValue := reflect.NewAt(field.Type(), addr)
data = fieldValue.Elem().Int()
}
fmt.Printf("Field %d: %d\n", i, data)
}
fmt.Println()
}
func write[T any](a T) T {
oldValue := reflect.ValueOf(a)
if oldValue.Kind() != reflect.Pointer {
// Create addressable space
tmp := reflect.New(oldValue.Type())
tmp.Elem().Set(oldValue)
oldValue = tmp.Elem()
}
newValue := reflect.New(oldValue.Type())
newValue = newValue.Elem()
for i := 0; i < oldValue.NumField(); i++ {
// Set each oldField
newField := newValue.Field(i)
oldField := oldValue.Field(i)
if oldField.CanInterface() {
// Exported fields
data := oldField.Int()
newField.SetInt(data)
} else {
// Unexported fields
oldAddr := unsafe.Pointer(oldField.UnsafeAddr())
oldPtr := reflect.NewAt(oldField.Type(), oldAddr)
data := oldPtr.Elem().Int()
newAddr := unsafe.Pointer(newField.UnsafeAddr())
newPtr := reflect.NewAt(newField.Type(), newAddr)
// panic: reflect: reflect.Value.SetInt using unaddressable value
newPtr.SetInt(data)
}
}
return newValue.Interface().(T)
}
func main() {
example := Example{1, 2}
read(example)
exampleCopy := write(example)
read(exampleCopy)
}
输出。
Field 0: 1
Field 1: 2
panic: reflect: reflect.Value.SetInt using unaddressable value
这个想法是将一个结构体作为any,然后从中创建一个reflect.Value,以允许程序读取所有字段。类似地,reflect.New 用于创建结构体的空副本,然后程序尝试设置所有字段。
如您所见,我已设法使用 unsafe.Pointer 读取未导出的字段,但我不知道如何在结构副本上设置其值。
您无法编写深度复制具有未导出字段的结构的代码。就是这么简单。你必须重新设计。