【问题标题】:How to create a copy of a struct only having interface it implements and it's value如何创建仅具有它实现的接口及其值的结构的副本
【发布时间】:2021-05-27 12:24:46
【问题描述】:

假设我有这个实现Setter 接口的A 结构:

type Setter interface {
    Set(key string, value string)
}

type A struct {
    m map[string]string
}

func (a *A) Set(key string, value string) {
    a.m[key] = value
}

我有一个不同的结构,其中包含Setter 接口的一些实现

type Holder struct {
    val Setter
}

h := Holder{
    val: &A{ map[string]string{} },
}

我需要得到一份 h.val 结构的副本,其中保存了所有值。

我已经尝试了以下解决方案,但它不起作用,导致panic: assignment to entry in nil map

(我们在初始化h时已经定义了映射,所以运行h.val.Set(k, v)不会出错)

l := reflect.New(
    reflect.ValueOf(h.val).Elem().Type(),
).Interface().(Setter)

l.Set("A", "B")

如何创建一个结构的副本而不知道它包含哪些字段,只知道它实现的接口并将其值保存在变量中?

附言将Clone 方法添加到Setter 接口不是首选解决方案

【问题讨论】:

  • reflect.New,和内置的new一样只初始化根类型。也就是说,如果你给它A,你会得到初始化*A,但m字段将被初始化,它会被初始化为它的“零”值,在某些情况下是可以的,但在这种情况下地图是nil,因此出现了恐慌。做深拷贝是一回事,做“深”初始化虽然相似,但又是另一回事。在这两种情况下,您都必须使用reflect 来遍历从根到叶的结构,并分别复制/初始化每个结构。祝你好运。

标签: go struct interface


【解决方案1】:

向 Setter 接口添加 Clone 方法不是首选解决方案

总的来说,由于只有 Setter 实现知道应该如何“克隆”它,所以没有太多选择。

例如:是否应该“克隆”A 结构创建一个指向同一映射的新结构?还是应该复制映射?

如果您真的打算克隆,您可能想要第二个,但您会发现它很快就会超出简单的reflect 操作的范围。


如果你的代码实际上只处理A 结构,你可以传递明确的A 值。您可能仍需要.Clone() 方法或Clone(a *A) 函数。


如果您唯一的问题是 A 的零值无效,您可以修复 Set()

func (a *A) Set(key string, value string) {
    if a.m == nil {
        a.m = make(map[string]string)
    }
    a.m[key] = value
}

【讨论】:

  • 1.不仅是A 结构,而且任何实现Setter 接口的结构。 2. 如果没有特定的Clone 方法就无法进行深度复制,有没有办法通过为所有文件提供空值来使用反射初始化它,例如自动将Holder.val 设置为空映射而不是nil 值?
  • 有,但它会是你需要的吗?你需要Clone() 还是New() 函数?
  • Clone() 函数可能不会创建空对象
  • Clone() 功能更可取,但如果不可能,New() 就足够了
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-11
  • 2010-12-13
  • 1970-01-01
相关资源
最近更新 更多