5 回答
TA贡献1875条经验 获得超5个赞
我解决这个问题的方法是只使用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}
}
这迫使每个人都从同一个地方获得一个实例。当您添加一个字段时,您可以将它添加到函数定义中,然后在它们构造新实例的任何地方都会出现编译时错误,因此您可以轻松找到它们并修复它们。
TA贡献1796条经验 获得超4个赞
首先,您的copyPerson()函数名不副实。它复制a 的一些字段Person,但不是(必须)全部。它应该被命名为copySomeFieldsOfPerson().
要复制完整的结构值,只需分配结构值。如果你有一个接收非指针的函数Person,那已经是一个副本,所以只需返回它的地址:
func copyPerson(p Person) *Person {
return &p
}
就是这样,这将复制所有现在和未来的领域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
}
或者不制作另一个副本p但仍然分离的替代解决方案Person.Data:
func copyPerson(p Person) *Person {
var data []byte
p.Data = append(data, p.Data...)
return &p
}
当然,如果有人添加了一个也需要手动处理的字段,这将无济于事。
您还可以使用未加密的文字,如下所示:
func copyPerson(p Person) *Person {
return &Person{
p.Name,
p.Age,
}
}
如果有人向 中添加新字段,这将导致编译时错误Person,因为未键控的复合结构文字必须列出所有字段。同样,如果有人更改了可将新字段分配给旧字段的字段(例如,有人交换了具有相同类型的彼此相邻的 2 个字段),这将无济于事,而且不鼓励使用未加密的文字。
包所有者最好在Person类型定义旁边提供一个复制构造函数。因此,如果有人发生变化Person,他/她应该负责保持CopyPerson()运营。正如其他人提到的,你应该已经有了单元测试,如果CopyPerson()不符合它的名字,它应该会失败。
最佳可行方案?
如果您不能将CopyPerson()next 放在类型旁边Person并让其作者维护它,请继续进行结构值复制和手动处理指针和类似标头的字段。
您可以创建一个person2类型,它是该Person类型的“快照”。如果原始类型发生变化,使用空白全局变量接收编译时警报Person,在这种情况下,copyPerson()包含 的源文件将拒绝编译,因此您将知道它需要调整。
这是可以做到的:
type person2 struct {
Name string
Age int
}
var _ = Person(person2{})
Person如果和的字段person2不匹配,则空白变量声明将不会编译。
上述编译时检查的一种变体可能是使用类型化nil指针:
var _ = (*Person)((*person2)(nil))
TA贡献1824条经验 获得超6个赞
惯用的方法是根本不这样做,而是使零值有用。复制函数的例子并没有真正意义,因为它完全没有必要——你可以说:
copy := new(Person) *copy = *origPerson
不需要专门的功能,也不必保持字段列表是最新的。如果你想要一个新实例的构造函数NewPerson
,就像理所当然的那样,只需编写一个并使用它。Linters 在某些方面非常有用,但没有什么能比得上广为人知的最佳实践和同行代码审查。
TA贡献1827条经验 获得超9个赞
我不知道强制执行该规则的语言规则。
也就是说,我会重新考虑这里的设计。如果Person
struct 在您的代码库中如此重要,请集中创建和复制它,以便“遥远的地方”不只是创建和移动Person
s。重构您的代码,以便仅使用一个构造函数来构建Person
s(可能类似于person.New
返回 a person.Person
),然后您将能够集中控制其字段的初始化方式。
TA贡献1784条经验 获得超2个赞
我能想出的最好的解决方案(而且不是很好)是定义一个与tempPerson该结构相同的新结构Person,并将其放在任何初始化新 Person 结构的代码附近,并更改初始化一个Person以便它改为将其初始化为 atempPerson但随后将其转换为 a Person。像这样:
type tempPerson struct {
Name string
Age int
[some other fields]
}
func copyPerson(origPerson Person) *Person {
tempCopy := tempPerson{
Name: orig.Name,
Age: orig.Age,
[some other fields]
}
copy := (Person)(tempCopy)
return ©
}
这样,如果将另一个字段Gender添加到Person但不添加到tempPerson代码中,将在编译时失败。据推测,开发人员然后会看到错误,编辑tempPerson以匹配他们对 的更改Person,并在这样做时注意到附近的代码使用tempPerson并认识到他们应该编辑该代码以也处理该Gender字段。
我不喜欢这个解决方案,因为它涉及在我们初始化结构Person并希望获得这种安全性的任何地方复制和粘贴结构定义。有没有更好的办法?
- 5 回答
- 0 关注
- 129 浏览
添加回答
举报