Ayu*_*lik 12 struct naming-conventions go
我正在使用go,特别是QT绑定.但是我不明白在下面的结构中使用前导下划线.我知道一般使用下划线但不是这个具体的例子.
type CustomLabel struct {
core.QObject
_ func() `constructor:"init"`
_ string `property:"text"`
}
Run Code Online (Sandbox Code Playgroud)
它与struct标签有关吗?
mko*_*iva 10
您可以将其视为类型的元信息,它不能通过该类型的实例访问,但可以使用reflect
或访问 go/ast
.这为感兴趣的软件包/程序提供了一些关于如何处理该类型的指令.例如,基于这些标签,它可以使用go:generate生成代码.
考虑到其中一个标签constructor:"init"
和字段的类型func()
很可能与go:generate一起使用以生成init
为该类型命名的构造函数或初始化方法CustomLabel
.
这是一个reflect
用于获取"元"信息的示例(尽管我已经提到过,特定的qt示例可能意味着由go:generate处理).
type CustomLabel struct {
_ func() `constructor:"init"`
_ string `property:"text"`
}
fmt.Println(reflect.ValueOf(CustomLabel{}).Type().Field(0).Tag)
// constructor:"init"
fmt.Println(reflect.ValueOf(CustomLabel{}).Type().Field(0).Type)
// func()
Run Code Online (Sandbox Code Playgroud)
https://play.golang.org/p/47yWG4U0uit
这些称为空白字段,因为空白标识符用作字段名称.
它们不能被引用(就像任何具有空白标识符作为其名称的变量),但它们参与结构的内存布局.通常并且实际上它们用作填充,以将后续字段与字节位置(或存储器位置)对齐,所述字节位置(或存储器位置)匹配来自(或去往)另一系统的数据的布局.增益是因为这些结构值(或者更确切地说是它们的存储空间)可以在一个步骤中简单有效地转储或读取.
@ mkopriva的回答详细说明了问题的具体用例.
警告:这些空白字段作为"类型注释"应该谨慎使用,因为它们会给这种结构的所有(!)值增加不必要的开销.这些字段不能被引用,但它们仍然需要内存.如果添加一个大小为8字节的空白字段(例如int64
),如果创建一百万个元素,那么这8个字节将计数一百万次.因此,这是空白字段的"有缺陷"使用:目的是将元信息添加到类型本身(而不是其实例),但成本是所有元素都需要增加内存.
您可能会说使用大小为0的类型,例如struct{}
.它更好,就像在正确的位置使用一样(例如,作为第一个字段,为了推理,如果字段顺序不同,请看Struct具有不同的大小 ;以及为什么结构中"[0] byte"的位置很重要?),不会改变结构的大小.尽管如此,使用反射迭代结构字段的代码仍然必须循环遍历这些代码,因此它会使这些代码效率降低(通常是所有编组/解组过程).此外,由于现在我们不能使用任意类型,我们失去了携带类型信息的优势.
最后的陈述(关于何时使用struct{}
我们丢失携带类型信息)可以被规避.struct{}
不是唯一具有0大小的类型,所有具有0长度的数组也具有零大小(无论实际的元素类型如何).因此,我们可以使用我们想要合并的0大小的数组来保留类型信息,例如:
type CustomLabel struct {
_ [0]func() `constructor:"init"`
_ [0]string `property:"text"`
}
Run Code Online (Sandbox Code Playgroud)
现在这种CustomLabel
类型在性能方面看起来要好得多:它的大小仍为0.并且仍然可以使用Type.Elem()
类似的示例来访问数组的元素类型:
type CustomLabel struct {
_ [0]func() `constructor:"init"`
_ [0]string `property:"text"`
}
func main() {
f := reflect.ValueOf(CustomLabel{}).Type().Field(0)
fmt.Println(f.Tag)
fmt.Println(f.Type)
fmt.Println(f.Type.Elem())
}
Run Code Online (Sandbox Code Playgroud)
输出(在Go Playground上试试):
constructor:"init"
[0]func()
func()
Run Code Online (Sandbox Code Playgroud)