eli*_*rel 3 struct initialization go composite-literals
考虑这个例子。假设我有一个在我的代码库中无处不在的对象:
type Person struct {
Name string
Age int
[some other fields]
}
Run Code Online (Sandbox Code Playgroud)
在代码库的某个深处,我也有一些代码可以创建新的Person结构。也许类似于以下实用程序功能:
func copyPerson(origPerson Person) *Person {
copy := Person{
Name: origPerson.Name,
Age: origPerson.Age,
[some other fields]
}
return ©
}
Run Code Online (Sandbox Code Playgroud)
随之而来的是另一个开发人员,Gender并向该Person结构添加了新字段。但是,由于该copyPerson函数位于一段遥远的代码中,因此它们忘记更新copyPerson。如果在创建结构时省略参数,golang不会引发任何警告或错误,因此代码将编译并可以正常工作;唯一的区别是该copyPerson方法现在将无法在Gender结构上进行复制,并且的结果copyPerson将Gender被替换为nil值(例如,空字符串)。
防止这种情况发生的最佳方法是什么?有没有一种方法可以让golang在特定的结构初始化中不强制执行任何遗失的参数?是否有短毛绒可以检测到这种潜在的错误?
我要解决的方法是只使用NewPerson(params)而不导出该人。这样一来,获取person实例的唯一方法就是遍历您的New方法。
package person
// Struct is not exported
type person struct {
Name string
Age int
Gender bool
}
// We are forced to call the constructor to get an instance of person
func New(name string, age int, gender bool) person {
return person{name, age, gender}
}
Run Code Online (Sandbox Code Playgroud)
这迫使每个人都从同一位置获取实例。添加字段时,可以将其添加到函数定义中,然后在它们构造新实例的任何地方都出现编译时错误,因此您可以轻松地找到它们并进行修复。
首先,你的copyPerson()函数名不副实。它复制a 的一些字段Person,但不是(不一定)全部。它应该被命名为copySomeFieldsOfPerson()。
要复制完整的结构值,只需分配该结构值即可。如果您有一个接收非指针的函数Person,那么它已经是一个副本,因此只需返回其地址:
func copyPerson(p Person) *Person {
return &p
}
Run Code Online (Sandbox Code Playgroud)
就这样,这将复制Person.
现在,在某些情况下,字段可能是指针或类似标头的值(如切片),它们应该与原始字段(更准确地说是与指向的对象)“分离”,在这种情况下,您确实需要进行手动调整,例如
type Person struct {
Name string
Age int
Data []byte
}
func copyPerson(p Person) *Person {
p2 := p
p2.Data = append(p2.Data, p.Data...)
return &p2
}
Run Code Online (Sandbox Code Playgroud)
或者另一种解决方案,它不会制作另一个副本p但仍然分离Person.Data:
func copyPerson(p Person) *Person {
var data []byte
p.Data = append(data, p.Data...)
return &p
}
Run Code Online (Sandbox Code Playgroud)
当然,如果有人添加了一个也需要手动处理的字段,这对你没有帮助。
您还可以使用未键入的文字,如下所示:
func copyPerson(p Person) *Person {
return &Person{
p.Name,
p.Age,
}
}
Run Code Online (Sandbox Code Playgroud)
如果有人向 中添加新字段,这将导致编译时错误Person,因为无键复合结构文字必须列出所有字段。同样,如果有人更改了可将新字段分配给旧字段的字段(例如,有人交换了彼此相邻的两个具有相同类型的字段),那么这不会对您有帮助,也不鼓励使用未键入的文字。
最好的办法是包所有者在类型定义旁边提供一个复制构造函数Person。因此,如果有人发生变化Person,他/她应该负责保持CopyPerson()正常运行。正如其他人提到的,您应该已经有了单元测试,如果CopyPerson()不符合其名称,单元测试应该会失败。
如果您无法将CopyPerson()next 放置在类型旁边Person并让其作者维护它,请继续进行结构值复制以及手动处理指针和类似标头的字段。
您可以创建一个person2类型,该类型是该类型的“快照” Person。如果原始类型发生更改,请使用空白全局变量来接收编译时警报Person,在这种情况下,copyPerson()包含的源文件将拒绝编译,因此您会知道它需要调整。
可以这样做:
type person2 struct {
Name string
Age int
}
var _ = Person(person2{})
Run Code Online (Sandbox Code Playgroud)
Person如果和的字段person2不匹配,空白变量声明将不会编译。
上述编译时检查的一种变体可以是使用类型化nil指针:
var _ = (*Person)((*person2)(nil))
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
508 次 |
| 最近记录: |