在函数名称之前使用下划线结构标记 [英] Go struct tags with underscore before function names

查看:30
本文介绍了在函数名称之前使用下划线结构标记的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am working with go, specifically QT bindings. However, I do not understand the use of leading underscores in the struct below. I am aware of the use of underscores in general but not this specific example.

type CustomLabel struct {
    core.QObject

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

它与结构标签有关吗?

推荐答案

那些被称为空白字段是因为 blank 标识符用作字段名称.

Those are called blank-fields because the blank identifier is used as the field name.

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

They cannot be referred to (just like any variable that has the blank identifier as its name) but they take part in the struct's memory layout. Usually and practically they are used as padding, to align subsequent fields to byte-positions (or memory-positions) that match layout of the data coming from (or going to) another system. The gain is that so these struct values (or rather their memory space) can be dumped or read simply and efficiently in one step.

@mkopriva 的回答详细说明了问题中的具体用例是什么.

@mkopriva's answer details what the specific use case from the question is for.

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

A word of warning: these blank fields as "type-annotations" should be used sparingly, as they add unnecessary overhead to all (!) values of such struct. These fields cannot be referred to, but they still require memory. If you add a blank field whose size is 8 bytes (e.g. int64), if you create a million elements, those 8 bytes will count a million times. As such, this is a "flawed" use of blank fields: the intention is to add meta info to the type itself (not to its instances), yet the cost is that all elements will require increased memory.

然后您可能会说使用大小为 0 的类型,例如 struct{}.更好,就像在正确的位置使用一样(例如作为第一个字段,推理参见 如果字段顺序不同,则结构具有不同的大小以及为什么结构中`[0]byte`的位置很重要?),它们不会改变结构的大小.尽管如此,使用反射来迭代结构体字段的代码仍然必须循环遍历这些字段,因此它会降低此类代码的效率(通常是所有编组/解组过程).另外,由于现在我们不能使用任意类型,我们失去了携带类型信息的优势.

You might say then to use a type whose size is 0, such as struct{}. It's better, as if used in the right position (e.g. being the first field, for reasoning see Struct has different size if the field order is different and also Why position of `[0]byte` in the struct matters?), they won't change the struct's size. Still, code that use reflection to iterate over the struct's fields will still have to loop over these too, so it makes such code less efficient (typically all marshaling / unmarshaling process). Also, since now we can't use an arbitrary type, we lose the advantage of carrying a type information.

最后一条语句(关于当使用 struct{} 时我们会丢失携带的类型信息)可以绕过.struct{} 不是唯一一个长度为 0 的类型,所有长度为 0 的数组的长度也为零(不管实际的元素类型如何).所以我们可以通过使用我们想要合并的类型的 0 大小的数组来保留类型信息,例如:

This last statement (about when using struct{} we lose the carried type information) can be circumvented. struct{} is not the only type with 0 size, all arrays with 0 length also have zero size (regardless of the actual element type). So we can retain the type information by using a 0-sized array of the type we'd like to incorporate, such as:

type CustomLabel struct {
    _ [0]func() `constructor:"init"`
    _ [0]string `property:"text"`
}

现在这个 CustomLabel 类型在性能方面看起来要好得多:它的大小仍然是 0.并且仍然可以使用 Type.Elem 访问数组的元素类型() 就像这个例子:

Now this CustomLabel type looks much better performance-wise as the type in question: its size is still 0. And it is still possible to access the array's element type using Type.Elem() like in this example:

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 上试试):

Output (try it on the Go Playground):

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

有关结构标记的概述,请阅读相关问题:Go 中标签的用途是什么?

For an overview of struct tags, read related question: What are the use(s) for tags in Go?

这篇关于在函数名称之前使用下划线结构标记的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆