赞
踩
高科,先后在 IBM PlatformComputing从事网格计算,淘米网,网易从事游戏服务器开发,拥有丰富的C++,go等语言开发经验,mysql,mongo,redis等数据库,设计模式和网络库开发经验,对战棋类,回合制,moba类页游,手游有丰富的架构设计和开发经验。 (谢谢你的关注)
-------------------------------------------------------------------------------------------------------------------------------
在Go语言中,没有类的概念,但可以使用struct来定义自定义类型。struct是一种可以包含多个不同类型字段的复合数据类型。可以将它看作是一个数据结构,其中每个字段都有自己的类型和值。
本文相当于go的数据类型的一部分,在分享struct之前我有必要解释下类型别名和自定义类型的区别:
在Go语言中有一些基本的数据类型,如string、整型、浮点型、布尔等数据类型,Go语言中可以使用type关键字来定义自定义类型。
自定义类型是定义了一个全新的类型。我们可以基于内置的基本类型定义,也可以通过struct定义。例如:
- type RANKTYPE int32
-
- const (
- RANKTYPE_GLOBAL_DECORATE RANKTYPE = 0 //全服装扮排名
- RANKTYPE_PRIVANCE_DECORATE RANKTYPE = 1 //省级装扮排名
- RANKTYPE_FRIEND_DECORATE RANKTYPE = 2 //好友的装扮排名
- RANKTYPE_SEASON_SCORE RANKTYPE = 3 //赛季积分排行榜
- RANKTYPE_LOCAL_SCORE RANKTYPE = 4 //本地积分排行
- RANKTYPE_BIGSEASON_SCORE RANKTYPE = 5 //赛季积分世界排行榜
- RANKTYPE_ACTIVITY_SCORE RANKTYPE = 6 //活动积分排行
- RANKTYPE_CROSSSEASON_SCORE RANKTYPE = 7 //跨服美食积分排行 暂时不用,
- RANKTYPE_SCENCEPROCESS_SCORE RANKTYPE = 8 //场景进度跨服排行
- RANKTYPE_DECORATE_SCORE RANKTYPE = 9 //场景进度跨服排行
- RANKTYPE_HANDBOOK_SCORE RANKTYPE = 10 //场景图鉴跨服排行
- RANKTYPE_COSTUMDESIGNVOTE_SCORE RANKTYPE = 11 //装扮设计投票排行
- RANKTYPE_PANDFISH_SCORE RANKTYPE = 12 //钓鱼 池塘鱼的排行记录
- RANKTYPE_SCTREAMFISH_SCORE RANKTYPE = 13 //钓鱼 溪流鱼的排行记录
- RANKTYPE_MARINEFISH_SCORE RANKTYPE = 14 // 钓鱼 海洋鱼的排行记录
- RANKTYPE_SCOREFIISHING_SCORE RANKTYPE = 15 //钓鱼积分的排行记录
- )
通过type关键字的定义,RANKTYPE就是一种新的类型,它具有int的特性。
类型别名是Go1.9版本添加的新功能。
类型别名规定:TypeAlias只是Type的别名,本质上TypeAlias与Type是同一个类型。就像一个孩子小时候有小名、乳名,上学后用学名,英语老师又会给他起英文名,但这些名字都指的是他本人。
type TypeAlias = Type
我们之前见过的rune和byte就是类型别名,他们的定义如下:
- type byte = uint8
- type rune = int32
类型别名与类型定义表面上看只有一个等号的差异,我们通过下面的这段代码来理解它们之间的区别。
- package main
-
- import "fmt"
- //类型定义
- type NewInt int
-
- //类型别名
- type MyInt = int
-
- func main() {
- var a NewInt
- var b MyInt
-
- fmt.Printf("type of a:%T\n", a)
- fmt.Printf("type of b:%T\n", b)
- }
输出:
结果显示a的类型是main.NewInt,表示main包下定义的NewInt类型。b的类型是int。MyInt类型只会在代码中存在,编译完成时并不会有MyInt类型。
结构体的定义
使用type和struct关键字来定义结构体,具体代码格式如下:
- type 类型名 struct {
- 字段名 字段类型
- 字段名 字段类型
- …
- }
其中:
- 1.类型名:标识自定义结构体的名称,在同一个包内不能重复。
- 2.字段名:表示结构体字段名。结构体中的字段名必须唯一。
- 3.字段类型:表示结构体字段的具体类型。
举个例子,我们定义一个游戏玩家的Player结构体,代码如下:
- type Player struct {
- playerid string
- name string
- level int
- exp int
- logintime int64
- registertime int64
- coin int32
- gem int32
- }
我们可以把具有相同数据类型的(比如name和playerid)的所有键都在一行上分组和定义:
- type Player struct {
- playerid,name string
- level ,exp int
- logintime,registertime int64
- coin ,gem int32
- }
这样我们就拥有了一个Player的自定义类型,它有playerid,name等字段。
结构体的tags
是附加到字段中的元数据的小片段,为struct
使用该结构的其他 Go 代码提供指令。
例如:这里是名为的自定义类型,Employee
可以注释为
- type Employee struct {
- FirstName string `json: "first_name"`
- LastName string `json: "last_name"`
- EmployeeID string `json: "employee_id"`
- Salary float64 `json: "salary"`
- }
然后,Go 代码能够检查这些结构并提取分配给它请求的特定键的值。如果没有其他代码检查结构标记,则结构标记不会影响代码的操作。
如果我们正在读取YAML
或JSON
文件,那么我们可以注释struct
这样的内容
- type Employee struct {
- FirstName string `yaml: "first_name"`
- LastName string `yaml: "last_name"`
- EmployeeID string `yaml: "employee_id"`
- Salary float64 `yaml: "salary"`
- }
- type Manager struct {
- ManagerFirstName string `json: "manager_first_name"`
- ManagerLastName string `json: "manager_last_name"`
- ManagerEmployeeID string `json: "manager_employee_id"`
- ManagerSalary float64 `json: "manager_salary"`
- }
下面的代码读取文件YAML
并将文件中的值分配YAML
给变量
- var mgr Manager
- f, err := os.Open("manager_list.json")
- if err != nil {
- log.Fatalf("os.Open() failed with '%s'\n", err)
- }
- defer func(f *os.File) {
- err := f.Close()
- if err != nil {
- }
- }(f)
- mrgObj := yaml.NewDecoder(f)
- err = mrgObj.Decode(&mgr)
- if err != nil {
- log.Fatalf("dec.Decode() failed with '%s'\n", err)
- }
- fmt.Println("%s %s employ_id is %s", mgr.FirstName, mgr.LastName, mgr.EmployeeID)
标准库中的JSON 编码器使用结构标记作为注释,向编码器指示您希望如何命名JSON输出中的字段。这些JSON编码和解码机制可以在encoding/json
包中找到。
现在假设您有一个空的 JSON 字段,您想要消除它,那么您可以使用它omitempty
,如果JSON对象没有该键的值,则不会填充和跳过它
- type Manager struct {
- ManagerFirstName string `json: "manager_first_name"`
- ManagerLastName string `json: "manager_last_name"`
- ManagerEmployeeID string `json: "manager_employee_id"`
- ManagerSalary float64 `json: "manager_salary,omitempty"`
- }
如果你想忽略某些字段,那么你可以使用-
in tags
,它将被忽略
- type Manager struct {
- ManagerFirstName string `json: "manager_first_name"`
- ManagerLastName string `json: "manager_last_name"`
- ManagerEmployeeID string `json: "manager_employee_id"`
- ManagerSalary float64 `json: "-"`
- }
如果您想更深入地访问,tags
那么您可以使用允许运行时反射的反射包
使用它tag
可以让您更轻松地导航存储数据及其表示形式。您可以使用go-playground/validator,它提供了更多有关tags
.有些能力就像
例如:下面的示例展示了如何使用go-playground/validator来验证字段,而无需编写任何额外的代码。
- type Manager struct {
- ManagerFirstName string `json:"manager_first_name" validate:"required"`
- ManagerLastName string `json:"manager_last_name" validate:"required_if=ManagerFirstName"`
- ManagerEmployeeID string `json:"manager_employee_id" validate:"required, gte=1000,lt=10000"`
- ManagerSalary float64 `json: "manager_salary,omitempty"`
- }
在上面的例子中我能够validate
遵循
ManagerFirstName
是required
字段ManagerLastName
isrequired_if
字段ManagerFirstName
已提供ManagerEmployeeID
是required
字段并且不能小于1000
因此,我们可以使用go-playground/validator,而不是为某些基本和条件验证编写数据验证代码,因为它们具有相同的内置逻辑,完全基于tags
只有当结构体实例化时,才会真正地分配
只有当结构体实例化时,才会真正地分配内存。也就是必须实例化后才能使用结构体的字段。
结构体本身也是一种类型,我们可以像声明内置类型一样使用var关键字声明结构体类型。
var 结构体实例 结构体类型
我们通过var p1 Player的方式来实例化一个Player结构体,并通过.操作来访问或者修改其成员变量
- package main
-
- import (
- "fmt"
- )
- type Player struct {
- Playerid string `json: "playerid"`
- Name string `json: "name"`
- Level int `json: "level"`
- Exp int `json: "exp"`
- Logintime int64 `json: "logintime"`
- Registertime int64 `json: "registertime"`
- Coin int32 `json: "coin"`
- Gem int32 `json: "gem"`
- }
-
- func main() {
- var p1 Player
- p1.Playerid = "12222222"
- p1.Name = "高科"
- p1.Level = 100
- fmt.Printf("p1=%v\n", p1) //p1={12222222 高科 100 0 0 0 0 0}
- fmt.Printf("p1=%#v\n", p1) //p1=main.Player{Playerid:"12222222", Name:"高科", Level:100, Exp:0, Logintime:0, Registertime:0, Coin:0, Gem:0}
- }
或者你可以直接在实例化的同时进行初始化操作,所以下面的初始化方式都可以:
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}
在定义一些临时数据结构等场景下还可以使用匿名结构体。
- package main
-
- import (
- "fmt"
- )
- func main() {
- var p1 struct{
- playerid string `json: "playerid"`
- Name string `json: "name"`
- Level int `json: "level"`
- Exp int `json: "exp"`
- Logintime int64 `json: "logintime"`
- Registertime int64 `json: "registertime"`
- Coin int32 `json: "coin"`
- Gem int32 `json: "gem"`
- }
- p1.playerid = "12222222"
- p1.Name = "高科"
- p1.Level = 100
- fmt.Printf("p1=%v\n", p1) //p1={12222222 高科 100 0 0 0 0 0}
- fmt.Printf("p1=%#v\n", p1) //p1=main.Player{Playerid:"12222222", Name:"高科", Level:100, Exp:0, Logintime:0, Registertime:0, Coin:0, Gem:0}
- }
我们还可以通过使用new关键字或者&对结构体进行实例化,得到的是结构体的地址。 格式如下:
var 变量名 new(struct类型)
- package main
-
- import (
- "fmt"
- )
- type Player struct {
- playerid string `json: "playerid"`
- Name string `json: "name"`
- Level int `json: "level"`
- Exp int `json: "exp"`
- Logintime int64 `json: "logintime"`
- Registertime int64 `json: "registertime"`
- Coin int32 `json: "coin"`
- Gem int32 `json: "gem"`
- }
-
- func main() {
- var p1 = new(Player)
- var p2 = &Player{}
- fmt.Printf("p1=%v ,p2=%v \n", p1,p2) //p1=&{ 0 0 0 0 0 0}
- 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}
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。