golang结构体的使用
结构体(struct)是用户自定义的类型,它代表若干字段的集合。有些时候将多个数据看做一个整体要比单独使用这些数据更有意义,这种情况下就适合使用结构体。比如将一个员工的 firstName, lastName 和 age 三个属性打包在一起成为一个 employee 结构就是很有意义的。其使用需要遵循以下要求:
- 使用type struct{} 定义结构,名称遵循可见性规则
- 支持指向自身的指针类型成员
- 支持匿名结构,可用作成员或定义成员变量
- 匿名结构也可以用于map的值
- 可以使用字面值对结构进行初始化
- 允许直接通过指针来读写结构成员
- 相同类型的成员可进行直接拷贝赋值
- 支持与!=比较运算符,但不支持>或
一、结构体的定义
一般情况下对结构体进行定义时,结构体里的字段要求大写,因为小写为私有定义,这就导致在写模块时,该模块对应的结构体能容不能导出,所以需要大写。其使用方法如下:
1package main
2import (
3 "fmt"
4)
5type Person struct { //结构也是一中类型
6 Name string //定义struct的属性
7 Age int
8}
9func main() {
10 a := Person{}
11 a.Name = "joe" //对struct的属性进行操作,类型与class的使用方法
12 a.Age = 19
13 fmt.Println(a)
14}
对结构体赋值时,也可以直接进行赋值,如下:
1a := Person{
2 Name: "jack",
3 Age: 19, //对结构的属性进行字面值的初始化
4}
二、直接赋值与指针赋值
结构体在进行赋值时,有直接赋值和指针赋值的区别。指针赋值是针对的结构体的内存地址。修改值内容后,源数据也会变化,而直接赋值是在修改后,原数据只在调用只时会变化,调用完成后还会变成原来的值,因为是值拷贝。
1、直接赋值(值拷贝)
1package main
2import (
3 "fmt"
4)
5type Person struct {
6 Name string
7 Age int
8}
9func main() {
10 a := Person{
11 Name: "jack",
12 Age: 19, //对结构的属性进行字面值的初始化
13 }
14 fmt.Println(a)
15 A(a)
16 fmt.Println(a) //结构也是一种值类型,对它进行传递的时候,也是进行了值得拷贝
17}
18func A(per Person) {
19 per.Age = 13
20 fmt.Println("A", per)
21}
22PS G:\mygo\src\mytest> go run .\temp.go
23{jack 19}
24A {jack 13}
25{jack 19}
2、指针赋值
1package main
2import (
3 "fmt"
4)
5type Person struct {
6 Name string
7 Age int
8}
9func main() {
10 a := Person{
11 Name: "jack",
12 Age: 19, //对结构的属性进行字面值的初始化
13 }
14 fmt.Println(a)
15 A(&a)
16 fmt.Println(a) //结构也是一种值类型,对它进行传递的时候,也是进行了值得拷贝
17}
18func A(per *Person) { //通过一个指针进行传递,此时就不是值得拷贝了
19 per.Age = 13
20 fmt.Println("A", per)
21}
22PS G:\mygo\src\mytest> go run .\temp.go
23{jack 19}
24A &{jack 13}
25{jack 13}
3、初始化指针
在进行结构体初始化赋值时,可以直接就取结构体的指针地址。这样会使读取的速度更快,如下:
1package main
2import (
3 "fmt"
4)
5type Person struct {
6 Name string
7 Age int
8}
9func main() {
10 a := &Person{
11 Name: "jack",
12 Age: 19, //此时初始化的时候就将这个struct的指针取出来
13 }
14 //在进行struct的初始化的时候,就加上&取地址符号
15 fmt.Println(a)
16 A(a)
17 B(a)
18 fmt.Println(a) //结构也是一种值类型,对它进行传递的时候,也是进行了值得拷贝
19}
20func A(per *Person) { //通过一个指针进行传递,此时就不是值得拷贝了
21 per.Age = 13
22 fmt.Println("A", per)
23}
24func B(per *Person) { //通过一个指针进行传递,此时就不是值得拷贝了
25 per.Age = 15
26 fmt.Println("B", per)
27}
28PS G:\mygo\src\mytest> go run .\temp.go
29&{jack 19}
30A &{jack 13}
31B &{jack 15}
32&{jack 15}
二、匿名结构
匿名结构分类为:一种是不给结构体进行命名,直接进行赋值并使用;另一种是字段匿名,只指明类型,然后直接使用。
1、匿名结构
1package main
2import (
3 "fmt"
4)
5func main() {
6 a := &struct { //匿名结构,需要先对结构本身进行一个定义
7 Name string
8 Age int
9 }{
10 Name: "jack",
11 Age: 20,
12 }
13 fmt.Println(a)
14}
2、匿名结构的嵌套
1package main
2import (
3 "fmt"
4)
5type Person struct {
6 Name string
7 Age int
8 Contact struct {
9 Phone, City string //匿名结构嵌套在Person中
10 }
11}
12func main() {
13 a := Person{Name: "Jack", Age: 20}
14 a.Contact.Phone = "123321" //通过这种方法对嵌套在Person中的匿名结构进行字面值的初始化
15 a.Contact.City = "BeiJing"
16 fmt.Println(a)
17}
18PS G:\mygo\src\mytest> go run .\temp2.go
19{Jack 20 {123321 BeiJing}}
3、匿名字段
1package main
2import (
3 "fmt"
4)
5type Person struct {
6 string //匿名字段 在进行字面值初始化的时候 必须严格按照字段声明的顺序
7 int
8}
9func main() {
10 a := Person{"Jack", 20} //此时将string 和 int类型对调的时候就会报错
11 fmt.Println(a)
12}
三、结构体的嵌套与比较
1、结构体的嵌套
1package main
2import (
3 "fmt"
4)
5type human struct {
6 Sex int
7}
8type teacher struct {
9 human
10 Name string
11 Age int
12}
13type student struct {
14 human //这里的human也是一种类型,此时它相当于一种匿名字段,嵌入结构作为匿名字段的话
15 //它本质上是将结构名称作为我们的字段名称
16 Name string
17 Age int
18}
19func main() {
20 a := teacher{Name: "Jack", Age: 20, human: human{Sex: 0}} //因此我们需要在这里进行这种初始化
21 b := student{Name: "Tom", Age: 19, human: human{Sex: 1}}
22 a.Name = "Fack"
23 a.Age = 13
24 a.human.Sex = 100 //保留这种调用的方法,是因为会涉及到名称的冲突
25 //a.Sex = 101 这种写法也是可以的
26 fmt.Println(a, b)
27}
28PS G:\mygo\src\mytest> go run .\temp3.go
29{{100} Fack 13} {{1} Tom 19}
2、结构体的比较
1package main
2import (
3 "fmt"
4)
5type Person struct {
6 Name string
7 Age int
8}
9func main() {
10 a := Person{Name: "Jack", Age: 20}
11 b := Person{Name: "Jack", Age: 20}
12 fmt.Println(a == b)
13}
14PS G:\mygo\src\mytest> go run .\temp3.go
15true
四、tag标签的使用
这个比较多的使用场景是json和结构体的转换中,因为结构体中字段的首字母要大写,而json中的数据不见得都是首字母都是大写的,这时候就可以通过tag字段实现该功能,如下:
1package main
2import (
3 "encoding/json"
4 "fmt"
5)
6type peerInfo struct {
7 HTTPPort int `json:"http_port"`
8 TCPPort int `json:"tcp_port"`
9 versiong string
10}
11func main() {
12 var v peerInfo
13 data := []byte(`{"http_port":80,"tcp_port":3306}`)
14 err := json.Unmarshal(data, &v)
15 if err != nil {
16 fmt.Println(err)
17 }
18 fmt.Printf("%+v\n", v)
19}
后面小写的tag标签就与json中的数据保持了一致。
json转为struct结构体可以在如下两个网站上进行转换,https://mholt.github.io/json-to-go/ ,https://oktools.net/json2go 。
通过反射,获取tag,进行打印的代码如下:
1package main
2import (
3 "fmt"
4 "reflect"
5 "strings"
6)
7type Person struct {
8 Name string `label:"Person Name: " uppercase:"true"`
9 Age int `label:"Age is: "`
10 Sex string `label:"Sex is: "`
11 Description string
12}
13// 按照tag打印结构体
14func PrintUseTag(ptr interface{}) error {
15 // 获取入参的类型
16 t := reflect.TypeOf(ptr)
17 // 入参类型校验
18 if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct {
19 return fmt.Errorf("参数应该为结构体指针")
20 }
21 // 取指针指向的结构体变量
22 v := reflect.ValueOf(ptr).Elem()
23 // 解析字段
24 for i := 0; i < v.NumField(); i++ {
25 // 取tag
26 fieldInfo := v.Type().Field(i)
27 tag := fieldInfo.Tag
28 // 解析label tag
29 label := tag.Get("label")
30 if label == "" {
31 label = fieldInfo.Name + ": "
32 }
33 // 解析uppercase tag
34 value := fmt.Sprintf("%v", v.Field(i))
35 if fieldInfo.Type.Kind() == reflect.String {
36 uppercase := tag.Get("uppercase")
37 if uppercase == "true" {
38 value = strings.ToUpper(value)
39 } else {
40 value = strings.ToLower(value)
41 }
42 }
43 fmt.Println(label + value)
44 }
45 return nil
46}
47func main() {
48 person := Person{
49 Name: "Tom",
50 Age: 29,
51 Sex: "Male",
52 Description: "Cool",
53 }
54 PrintUseTag(&person)
55}
捐赠本站(Donate)
如您感觉文章有用,可扫码捐赠本站!(If the article useful, you can scan the QR code to donate))
- Author: shisekong
- Link: https://blog.361way.com/golang-struct/6330.html
- License: This work is under a 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议. Kindly fulfill the requirements of the aforementioned License when adapting or creating a derivative of this work.