当前位置:   article > 正文

【go从入门到精通】初识struct结构体

【go从入门到精通】初识struct结构体

作者简介:


        高科,先后在 IBM PlatformComputing从事网格计算,淘米网,网易从事游戏服务器开发,拥有丰富的C++,go等语言开发经验,mysql,mongo,redis等数据库,设计模式和网络库开发经验,对战棋类,回合制,moba类页游,手游有丰富的架构设计和开发经验。 (谢谢你的关注)

-------------------------------------------------------------------------------------------------------------------------------

          在Go语言中,没有类的概念,但可以使用struct来定义自定义类型。struct是一种可以包含多个不同类型字段的复合数据类型。可以将它看作是一个数据结构,其中每个字段都有自己的类型和值。

        本文相当于go的数据类型的一部分,在分享struct之前我有必要解释下类型别名和自定义类型的区别:       

类型别名和自定义类型

自定义类型

在Go语言中有一些基本的数据类型,如string、整型、浮点型、布尔等数据类型,Go语言中可以使用type关键字来定义自定义类型。

自定义类型是定义了一个全新的类型。我们可以基于内置的基本类型定义,也可以通过struct定义。例如:

  1. type RANKTYPE int32
  2. const (
  3. RANKTYPE_GLOBAL_DECORATE RANKTYPE = 0 //全服装扮排名
  4. RANKTYPE_PRIVANCE_DECORATE RANKTYPE = 1 //省级装扮排名
  5. RANKTYPE_FRIEND_DECORATE RANKTYPE = 2 //好友的装扮排名
  6. RANKTYPE_SEASON_SCORE RANKTYPE = 3 //赛季积分排行榜
  7. RANKTYPE_LOCAL_SCORE RANKTYPE = 4 //本地积分排行
  8. RANKTYPE_BIGSEASON_SCORE RANKTYPE = 5 //赛季积分世界排行榜
  9. RANKTYPE_ACTIVITY_SCORE RANKTYPE = 6 //活动积分排行
  10. RANKTYPE_CROSSSEASON_SCORE RANKTYPE = 7 //跨服美食积分排行 暂时不用,
  11. RANKTYPE_SCENCEPROCESS_SCORE RANKTYPE = 8 //场景进度跨服排行
  12. RANKTYPE_DECORATE_SCORE RANKTYPE = 9 //场景进度跨服排行
  13. RANKTYPE_HANDBOOK_SCORE RANKTYPE = 10 //场景图鉴跨服排行
  14. RANKTYPE_COSTUMDESIGNVOTE_SCORE RANKTYPE = 11 //装扮设计投票排行
  15. RANKTYPE_PANDFISH_SCORE RANKTYPE = 12 //钓鱼 池塘鱼的排行记录
  16. RANKTYPE_SCTREAMFISH_SCORE RANKTYPE = 13 //钓鱼 溪流鱼的排行记录
  17. RANKTYPE_MARINEFISH_SCORE RANKTYPE = 14 // 钓鱼 海洋鱼的排行记录
  18. RANKTYPE_SCOREFIISHING_SCORE RANKTYPE = 15 //钓鱼积分的排行记录
  19. )

通过type关键字的定义,RANKTYPE就是一种新的类型,它具有int的特性。

类型别名

类型别名是Go1.9版本添加的新功能。

类型别名规定:TypeAlias只是Type的别名,本质上TypeAlias与Type是同一个类型。就像一个孩子小时候有小名、乳名,上学后用学名,英语老师又会给他起英文名,但这些名字都指的是他本人。

    type TypeAlias = Type 

我们之前见过的rune和byte就是类型别名,他们的定义如下:

  1. type byte = uint8
  2. type rune = int32

类型定义和类型别名的区别

类型别名与类型定义表面上看只有一个等号的差异,我们通过下面的这段代码来理解它们之间的区别。

  1. package main
  2. import "fmt"
  3. //类型定义
  4. type NewInt int
  5. //类型别名
  6. type MyInt = int
  7. func main() {
  8. var a NewInt
  9. var b MyInt
  10. fmt.Printf("type of a:%T\n", a)
  11. fmt.Printf("type of b:%T\n", b)
  12. }

输出:

结果显示a的类型是main.NewInt,表示main包下定义的NewInt类型。b的类型是int。MyInt类型只会在代码中存在,编译完成时并不会有MyInt类型。

struct结构体

结构体的定义

使用type和struct关键字来定义结构体,具体代码格式如下:

  1. type 类型名 struct {
  2. 字段名 字段类型
  3. 字段名 字段类型
  4. }

其中:

  1. 1.类型名:标识自定义结构体的名称,在同一个包内不能重复。
  2. 2.字段名:表示结构体字段名。结构体中的字段名必须唯一。
  3. 3.字段类型:表示结构体字段的具体类型。

举个例子,我们定义一个游戏玩家的Player结构体,代码如下:

  1. type Player struct {
  2. playerid string
  3. name string
  4. level int
  5. exp int
  6. logintime int64
  7. registertime int64
  8. coin int32
  9. gem int32
  10. }

我们可以把具有相同数据类型的(比如name和playerid)的所有键都在一行上分组和定义:

  1. type Player struct {
  2. playerid,name string
  3. level ,exp int
  4. logintime,registertime int64
  5. coin ,gem int32
  6. }

这样我们就拥有了一个Player的自定义类型,它有playerid,name等字段。

结构体的tags

结构体的tags是附加到字段中的元数据的小片段,为struct使用该结构的其他 Go 代码提供指令。

例如:这里是名为的自定义类型,Employee可以注释为

  1. type Employee struct {
  2. FirstName string `json: "first_name"`
  3. LastName string `json: "last_name"`
  4. EmployeeID string `json: "employee_id"`
  5. Salary float64 `json: "salary"`
  6. }

然后,Go 代码能够检查这些结构并提取分配给它请求的特定键的值。如果没有其他代码检查结构标记,则结构标记不会影响代码的操作。

如果我们正在读取YAMLJSON文件,那么我们可以注释struct这样的内容

  1. type Employee struct {
  2. FirstName string `yaml: "first_name"`
  3. LastName string `yaml: "last_name"`
  4. EmployeeID string `yaml: "employee_id"`
  5. Salary float64 `yaml: "salary"`
  6. }
  7. type Manager struct {
  8. ManagerFirstName string `json: "manager_first_name"`
  9. ManagerLastName string `json: "manager_last_name"`
  10. ManagerEmployeeID string `json: "manager_employee_id"`
  11. ManagerSalary float64 `json: "manager_salary"`
  12. }

下面的代码读取文件YAML并将文件中的值分配YAML给变量

  1. var mgr Manager
  2. f, err := os.Open("manager_list.json")
  3. if err != nil {
  4. log.Fatalf("os.Open() failed with '%s'\n", err)
  5. }
  6. defer func(f *os.File) {
  7. err := f.Close()
  8. if err != nil {
  9. }
  10. }(f)
  11. mrgObj := yaml.NewDecoder(f)
  12. err = mrgObj.Decode(&mgr)
  13. if err != nil {
  14. log.Fatalf("dec.Decode() failed with '%s'\n", err)
  15. }
  16. fmt.Println("%s %s employ_id is %s", mgr.FirstName, mgr.LastName, mgr.EmployeeID)

标准库中的JSON 编码器使用结构标记作为注释,向编码器指示您希望如何命名JSON输出中的字段。这些JSON编码解码机制可以在encoding/json 中找到。

现在假设您有一个空的 JSON 字段,您想要消除它,那么您可以使用它omitempty,如果JSON对象没有该键的值,则不会填充和跳过它

  1. type Manager struct {
  2. ManagerFirstName string `json: "manager_first_name"`
  3. ManagerLastName string `json: "manager_last_name"`
  4. ManagerEmployeeID string `json: "manager_employee_id"`
  5. ManagerSalary float64 `json: "manager_salary,omitempty"`
  6. }

如果你想忽略某些字段,那么你可以使用-in tags,它将被忽略

  1. type Manager struct {
  2. ManagerFirstName string `json: "manager_first_name"`
  3. ManagerLastName string `json: "manager_last_name"`
  4. ManagerEmployeeID string `json: "manager_employee_id"`
  5. ManagerSalary float64 `json: "-"`
  6. }

如果您想更深入地访问,tags那么您可以使用允许运行时反射的反射包

使用它tag可以让您更轻松地导航存储数据及其表示形式。您可以使用go-playground/validator,它提供了更多有关tags.有些能力就像

  • 字段之间的比较
  • 领域之间的调节
  • 管理字段之间的依赖关系等等……

例如:下面的示例展示了如何使用go-playground/validator来验证字段,而无需编写任何额外的代码。

  1. type Manager struct {
  2. ManagerFirstName string `json:"manager_first_name" validate:"required"`
  3. ManagerLastName string `json:"manager_last_name" validate:"required_if=ManagerFirstName"`
  4. ManagerEmployeeID string `json:"manager_employee_id" validate:"required, gte=1000,lt=10000"`
  5. ManagerSalary float64 `json: "manager_salary,omitempty"`
  6. }

在上面的例子中我能够validate遵循

  • ManagerFirstNamerequired字段
  • ManagerLastNameisrequired_if字段ManagerFirstName已提供
  • ManagerEmployeeIDrequired字段并且不能小于1000

因此,我们可以使用go-playground/validator,而不是为某些基本和条件验证编写数据验证代码,因为它们具有相同的内置逻辑,完全基于tags

只有当结构体实例化时,才会真正地分配
 

结构体实例化

只有当结构体实例化时,才会真正地分配内存。也就是必须实例化后才能使用结构体的字段。

结构体本身也是一种类型,我们可以像声明内置类型一样使用var关键字声明结构体类型。

    var 结构体实例 结构体类型 

基本实例化

我们通过var p1 Player的方式来实例化一个Player结构体,并通过.操作来访问或者修改其成员变量

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. type Player struct {
  6. Playerid string `json: "playerid"`
  7. Name string `json: "name"`
  8. Level int `json: "level"`
  9. Exp int `json: "exp"`
  10. Logintime int64 `json: "logintime"`
  11. Registertime int64 `json: "registertime"`
  12. Coin int32 `json: "coin"`
  13. Gem int32 `json: "gem"`
  14. }
  15. func main() {
  16. var p1 Player
  17. p1.Playerid = "12222222"
  18. p1.Name = "高科"
  19. p1.Level = 100
  20. fmt.Printf("p1=%v\n", p1) //p1={12222222 高科 100 0 0 0 0 0}
  21. fmt.Printf("p1=%#v\n", p1) //p1=main.Player{Playerid:"12222222", Name:"高科", Level:100, Exp:0, Logintime:0, Registertime:0, Coin:0, Gem:0}
  22. }

或者你可以直接在实例化的同时进行初始化操作,所以下面的初始化方式都可以:

var p1 = Player{playerid:"12222222",Name:"高科",Level: 100, Exp:20, Logintime: 1711296000, Registertime:1701296000, Coin:100000, Gem:999999}

    p2 := Player{playerid:"12222223",Name:"高科2",Level: 100, Exp:20, Logintime: 1711296000, Registertime:1701296000, Coin:100000, Gem:999999}

匿名结构体

在定义一些临时数据结构等场景下还可以使用匿名结构体。

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func main() {
  6. var p1 struct{
  7. playerid string `json: "playerid"`
  8. Name string `json: "name"`
  9. Level int `json: "level"`
  10. Exp int `json: "exp"`
  11. Logintime int64 `json: "logintime"`
  12. Registertime int64 `json: "registertime"`
  13. Coin int32 `json: "coin"`
  14. Gem int32 `json: "gem"`
  15. }
  16. p1.playerid = "12222222"
  17. p1.Name = "高科"
  18. p1.Level = 100
  19. fmt.Printf("p1=%v\n", p1) //p1={12222222 高科 100 0 0 0 0 0}
  20. fmt.Printf("p1=%#v\n", p1) //p1=main.Player{Playerid:"12222222", Name:"高科", Level:100, Exp:0, Logintime:0, Registertime:0, Coin:0, Gem:0}
  21. }

创建指针类型结构体

我们还可以通过使用new关键字或者&对结构体进行实例化,得到的是结构体的地址。 格式如下:

var 变量名 new(struct类型)

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. type Player struct {
  6. playerid string `json: "playerid"`
  7. Name string `json: "name"`
  8. Level int `json: "level"`
  9. Exp int `json: "exp"`
  10. Logintime int64 `json: "logintime"`
  11. Registertime int64 `json: "registertime"`
  12. Coin int32 `json: "coin"`
  13. Gem int32 `json: "gem"`
  14. }
  15. func main() {
  16. var p1 = new(Player)
  17. var p2 = &Player{}
  18. fmt.Printf("p1=%v ,p2=%v \n", p1,p2) //p1=&{ 0 0 0 0 0 0}
  19. fmt.Printf("p1=%#v ,p2=%#v \n", p1,p2) //p1=&main.Player{playerid:"", Name:"", Level:0, Exp:0, Logintime:0, Registertime:0, Coin:0, Gem:0}
  20. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/404523
推荐阅读
相关标签
  

闽ICP备14008679号