函数名前带下划线的Go结构标记
我正在使用go,特别是QT绑定。但是,我不理解在下面的结构中使用前导下划线。我知道一般使用下划线,但不知道这个具体的例子函数名前带下划线的Go结构标记,go,struct,naming-conventions,Go,Struct,Naming Conventions,我正在使用go,特别是QT绑定。但是,我不理解在下面的结构中使用前导下划线。我知道一般使用下划线,但不知道这个具体的例子 type CustomLabel struct { core.QObject _ func() `constructor:"init"` _ string `property:"text"` } 它是否与结构标记相关?您可以将其视为该类型的元信息,它不能通过该类型的实例访问,但可以使用reflect或go/ast访问。这为感兴趣的包/程序提供了一些
type CustomLabel struct {
core.QObject
_ func() `constructor:"init"`
_ string `property:"text"`
}
它是否与结构标记相关?您可以将其视为该类型的元信息,它不能通过该类型的实例访问,但可以使用
reflect
或go/ast
访问。这为感兴趣的包/程序提供了一些关于如何处理该类型的指令。例如,基于这些标记,它可以使用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()
考虑到其中一个标记表示constructor:“init”
,并且字段的类型是func()
,很可能这与go:generate一起使用,为类型CustomLabel
生成名为init
的构造函数或初始值设定项方法
下面是一个使用
reflect
获取“meta”信息的示例(尽管我已经提到过,具体的qt示例可能是由go:generate处理的)
这些被称为空白字段,因为标识符用作字段名
它们不能被引用(就像任何具有空白标识符的变量一样),但是它们参与了结构的内存布局。通常和实际情况下,它们被用作填充,以将后续字段和字节位置(或内存位置)对齐,这些字节位置和来自(或前往)另一个系统的数据布局相匹配。这样做的好处是,这些结构值(或者更确切地说,它们的内存空间)可以在一个步骤中简单高效地转储或读取
@mkopriva的回答详细说明了问题中的特定用例的用途 警告一句:这些空白字段作为“类型注释”应该谨慎使用,因为它们会给此类结构的所有(!)值增加不必要的开销。无法引用这些字段,但它们仍然需要内存。如果添加一个大小为8字节的空白字段(例如,int64
),如果创建一百万个元素,那么这8个字节将计数一百万次。因此,使用空白字段是“有缺陷的”:目的是向类型本身(而不是其实例)添加元信息,但代价是所有元素都需要增加内存
然后,您可能会说使用大小为0的类型,例如struct{}
。更好的是,如果在正确的位置使用(例如,作为第一个字段,用于推理,请参见,还有),它们不会更改结构的大小。尽管如此,使用反射来迭代结构字段的代码仍然必须在这些字段上循环,因此这会降低此类代码的效率(通常是所有封送/解封过程)。而且,由于现在我们不能使用任意类型,我们失去了携带类型信息的优势
最后一条语句(关于当使用struct{}
时,我们会丢失所携带的类型信息)可以绕过struct{}
不是唯一一个大小为0的类型,所有长度为0的数组的大小也都为零(不管实际的元素类型如何)。因此,我们可以使用要合并的类型的0大小数组来保留类型信息,例如:
type CustomLabel struct {
_ [0]func() `constructor:"init"`
_ [0]string `property:"text"`
}
现在,这个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())
}
输出(在上尝试):
有关struct标记的概述,请阅读相关问题:为什么要使用不同的类型?它们可能都不是空白字符串,或者您只能有一个空白标识符?@AyubMalik这两个空白字段有两个不同的用途。第一个包含应该生成
init()
构造函数(不带参数)的信息。第二个字段包含应生成类型为string
的text
属性的信息。@icza零大小空白字段的另一个选项是定义描述意图的空结构类型。例如,type Func struct{}
,然后字段看起来像\ufunc`constructor:“init”`
@mkopriva不幸的是,该解决方案不会为类型提供反射,因此基本上该解决方案比在标记本身中提供类型弱,如`property:“text”type:“string”`
@mkopriva因为您有名称“Func”
,但这本身并不能识别类型Func()
,这需要在生成器中内置一个额外的类型注册表,或者在源代码中创建一个单独的表。字符串“Func”
无法神奇地转换为标识Func()
类型的reflect.Type
。
constructor:"init"
[0]func()
func()