在函数名称之前使用下划线来转换struct标记

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


icz*_*cza 8

这些称为空白字段,因为空白标识符用作字段名称.

它们不能被引用(就像任何具有空白标识符作为其名称的变量),但它们参与结构的内存布局.通常并且实际上它们用作填充,以将后续字段与字节位置(或存储器位置)对齐,所述字节位置(或存储器位置)匹配来自(或去往)另一系统的数据的布局.增益是因为这些结构值(或者更确切地说是它们的存储空间)可以在一个步骤中简单有效地转储或读取.

@ 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)