课程地址 go-class-slides/xmas-2020 at trunk · matt4biz/go-class-slides (github.com)
主讲老师 Matt Holiday
12-Structs, Struct tags & JSON
Struct
结构通常是不同类型的聚合,所以有不同类型的字段,通过字段查找值。
type Employee struct {
Name string
Number int
Boss *Employee
Hired time.Time
}
func main() {
var e Employee
fmt.Printf(\"%T %+[1]v\", e)
}
main.Employee {Name: Number:0 Boss:<nil> Hired:0001-01-01 00:00:00 +0000 UTC}
通过 \\(\\%+v\\) 显示结构体的字段。通过点表示法插入值。另外的声明方法
var e2 = Employee{
\"Matt\",
1,
nil,
time.Now(),
}
这种需要按顺序填写所有字段。我们可以指定字段名就可以只写部分
var e2 = Employee{
Name: \"Matt\",
Number: 1,
Hired: time.Now(),
}
boss := Employee{\"Lamine\", 2, nil, time.Now()}
e2.Boss = &boss
fmt.Printf(\"%T %+[1]v\\n\", e2)
main.Employee {Name:Matt Number:1 Boss:0xc00005e100 Hired:2022-04-08 07:40:49.042803 +0800 CST m=+0.006431301}
由于 \\(Boss\\) 是指针,在 \\(e2\\) 的输出中显示的是指针。上方代码也可以写成
boss := &Employee{\"Lamine\", 2, nil, time.Now()}
e2.Boss = boss
使 \\(boss\\) 指向结构体指针,在某种意义上创建结构体,匿名获取指针。
使用 \\(map\\) 管理所有 \\(Employee\\) 对象
c := map[string]*Employee{}
// c := make(map[string]*Employee)
c[\"Lamine\"] = &Employee{\"Lamine\", 2, nil, time.Now()}
c[\"Matt\"] = &Employee{
Name: \"Matt\",
Number: 1,
Boss: c[\"Lamine\"],
Hired: time.Now(),
}
fmt.Printf(\"%T %+[1]v\\n\", c[\"Lamine\"])
fmt.Printf(\"%T %+[1]v\\n\", c[\"Matt\"])
*main.Employee &{Name:Lamine Number:2 Boss:<nil> Hired:2022-04-08 07:51:11.8676147 +0800 CST m=+0.004987001}
*main.Employee &{Name:Matt Number:1 Boss:0xc00005e040 Hired:2022-04-08 07:51:11.8676147 +0800 CST m=+0.004987001}
Struct Gotcha
c := map[string]Employee{}
c[\"Lamine\"] = Employee{\"Lamine\", 2, nil, time.Now()}
c[\"Matt\"] = Employee{
Name: \"Matt\",
Number: 1,
Boss: &c[\"Lamine\"],
Hired: time.Now(),
}
fmt.Printf(\"%T %+[1]v\\n\", c[\"Lamine\"])
fmt.Printf(\"%T %+[1]v\\n\", c[\"Matt\"])
修改 \\(map\\) 存储对象,从结构体指针变为结构体,而 \\(Employee\\) 内的 \\(Boss\\) 字段需要一个指针,在这种情况下,假设我们从映射中获取对象,并得到其指针,那么 \\(IDE\\) 会报错。
invalid operation: cannot take address of c[\"Lamine\"]
映射有限制,你不能获取映射内实体的地址。原因在于每当操作地图的时候,如果我将某些内容插入地图或从地图中删除某些内容,地图可以在内部重新排列,因为哈希表数据结构是动态的,那样获得的地址是非常不安全的,可能会变成过时的指针。
c[\"Lamine\"] = Employee{\"Lamine\", 2, nil, time.Now()}
c[\"Lamine\"].Number++
cannot assign to struct field c[\"Lamine\"].Number in map
如果有一张结构体的映射,对映射中一个该结构体中的值进行修改是不可能的。必须要将结构体的映射修改为结构体指针的映射。
Anonymous Struct Type
func main() {
var album = struct {
title string
artist string
year int
copies int
}{
\"The White Album\",
\"The Beatles\",
1968,
1000000000,
}
var pAlbum *struct {
title string
artist string
year int
copies int
}
fmt.Println(album, pAlbum)
}
基于匿名结构类型,并用结构文字初始化,但并不是特别方便。比如创建一个空的匿名结构体指针的时候。
var album1 = struct {
title string
}{
\"The White Album\",
}
var album2 = struct {
title string
}{
\"The Black Album\",
}
album1 = album2
fmt.Println(album1, album2)
可以执行这种赋值操作,将拷贝 \\(album2\\) 的副本复制给 \\(album1\\) ,两个匿名结构体具有相同的结构和行为(有相同的字段和字段类型)
type album1 struct {
title string
}
type album2 struct {
title string
}
func main() {
var a1 = album1{
\"The White Album\",
}
var a2 = album2{
\"The Black Album\",
}
a1 = a2
// a1 = album1(a2)
fmt.Println(a1, a2)
}
而在这种情况下会报错,因为他们不是同一个类型名,但是他们是可以互相转换的。
cannot use a2 (variable of type album2) as album1 value in assignment
判断结构体一致的条件
- 字段一样,字段类型也一样
- 字段按顺序排列
- 相同的字段标签
红圈用于包含一些如何以各种方式进行编码的信息协议。比如为 \\(json\\) 创建 \\(key\\),当我们查看 \\(json\\) 的工作原理时它将使用反射。
但如果它们是一致的,可以进行强制转换。
需要注意的是,从 \\(go\\ 1.8\\) 起,不同字段标签不阻碍类型转换。
Make the zero value useful
\\(nil\\ [\\ ]byte\\) 可以使用 \\(append\\),当 \\(buffer\\) 被创建时就可以直接被使用,不需要做什么前置工作。
Empty structs
\\(struct\\{\\}\\) 在内存中作为单例对象存在,构建空结构体集合比布尔值集合更省空间。
JSON
type Response struct {
Page int `json:\"page\"`
Words []string `json:\"words,omitempty\"`
}
func main() {
r := Response{
Page: 1,
Words: []string{\"up\", \"in\", \"out\"},
}
j, _ := json.Marshal(r)
fmt.Println(string(j))
fmt.Printf(\"%#v\\n\", r)
var r2 Response
_ = json.Unmarshal(j, &r2)
fmt.Printf(\"%#v\\n\", r2)
r3 := Response{
Page: 100,
}
j3, _ := json.Marshal(r3)
fmt.Println(string(j3))
fmt.Printf(\"%#v\\n\", r3)
}
\\(json.Marshal()\\) 返回字节切片,输出到控制台需要转换成 \\(string\\)。\\(json.Unmarshal\\) 需要提供一个结构体指针用于存放解析的数据。\\(omitempty\\) 关键词用于判空,如果为空就省去。否则转换为 \\(json\\) 的时候会给该字段默认加 \\(null\\) 值。
字段都以大写开头,这样它们可以被导出。如果字段名以小写开头,\\(json\\) 不会对它进行编码。
struct field words has json tag but is not exported
从编译器来看程序是正确的,而从 \\(linting\\ tool\\) 静态分析工具来看会给出一个警告。
来源:https://www.cnblogs.com/linxiaoxu/p/16115243.html
本站部分图文来源于网络,如有侵权请联系删除。