深度复制具有未导出字段的结构

问题描述 投票:0回答:1

我正在尝试在 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 读取未导出的字段,但我不知道如何在结构副本上设置其值。

go struct reflection deep-copy
1个回答
0
投票

您无法编写深度复制具有未导出字段的结构的代码。就是这么简单。你必须重新设计。

© www.soinside.com 2019 - 2024. All rights reserved.