在函数名

时间:2018-05-17 08:47:26

标签: go struct naming-conventions

我正在使用go,特别是QT绑定。但是我不明白在下面的结构中使用前导下划线。我知道一般使用下划线但不是这个具体的例子。

type CustomLabel struct {
    core.QObject

    _ func() `constructor:"init"`
    _ string `property:"text"`
}

它与struct标签有关吗?

2 个答案:

答案 0 :(得分:8)

您可以将其视为该类型的元信息,但无法通过该类型的实例访问,但可以使用reflectgo/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()

https://play.golang.org/p/47yWG4U0uit

答案 1 :(得分:6)

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

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

@ mkopriva的回答详细说明了问题的具体用例。

警告:这些空白字段作为“type-annotations”应该谨慎使用,因为它们会增加此类结构的所有(!)值的不必要开销。这些字段不能被引用,但它们仍然需要内存。如果添加一个大小为8字节的空白字段(例如int64),如果创建一百万个元素,那么这8个字节将计数一百万次。因此,这是空白字段的“有缺陷”使用:目的是将元信息添加到类型本身(而不是其实例),但成本是所有元素都需要增加内存

您可能会说使用大小为0的类型,例如struct{}。它更好,就像在正确的位置使用一样(例如,作为第一个字段,推理见Struct has different size if the field order is different;还有Why position of `[0]byte` in the struct matters?),它们不会改变结构的大小。尽管如此,使用反射迭代结构字段的代码仍然必须循环遍历这些代码,因此它会使这些代码效率降低(通常是所有编组/解组过程)。此外,由于现在我们不能使用任意类型,我们失去了携带类型信息的优势。

最后一句话(关于何时使用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())
}

输出(在Go Playground上尝试):

constructor:"init"
[0]func()
func()