golang结构体的使用

结构体(struct)是用户自定义的类型,它代表若干字段的集合。有些时候将多个数据看做一个整体要比单独使用这些数据更有意义,这种情况下就适合使用结构体。比如将一个员工的 firstName, lastName 和 age 三个属性打包在一起成为一个 employee 结构就是很有意义的。其使用需要遵循以下要求:

使用type struct{} 定义结构,名称遵循可见性规则
支持指向自身的指针类型成员
支持匿名结构,可用作成员或定义成员变量
匿名结构也可以用于map的值
可以使用字面值对结构进行初始化
允许直接通过指针来读写结构成员
相同类型的成员可进行直接拷贝赋值
支持==与!=比较运算符,但不支持>或<
支持匿名字段,本质上是定义了以某个类型名为名称的字段
嵌入结构作为匿名字段看起来像继承,但不是继承
可以使用匿名字段指针

一、结构体的定义

一般情况下对结构体进行定义时,结构体里的字段要求大写,因为小写为私有定义,这就导致在写模块时,该模块对应的结构体能容不能导出,所以需要大写。其使用方法如下:

package main
import (
    "fmt"
)
type Person struct { //结构也是一中类型
    Name string //定义struct的属性
    Age  int
}
func main() {
    a := Person{}
    a.Name = "joe" //对struct的属性进行操作,类型与class的使用方法
    a.Age = 19
    fmt.Println(a)
}

对结构体赋值时,也可以直接进行赋值,如下:

a := Person{
    Name: "jack",
    Age:  19, //对结构的属性进行字面值的初始化
}

二、直接赋值与指针赋值

结构体在进行赋值时,有直接赋值和指针赋值的区别。指针赋值是针对的结构体的内存地址。修改值内容后,源数据也会变化,而直接赋值是在修改后,原数据只在调用只时会变化,调用完成后还会变成原来的值,因为是值拷贝。

1、直接赋值(值拷贝)

package main
import (
    "fmt"
)
type Person struct {
    Name string
    Age  int
}
func main() {
    a := Person{
        Name: "jack",
        Age:  19, //对结构的属性进行字面值的初始化
    }
    fmt.Println(a)
    A(a)
    fmt.Println(a) //结构也是一种值类型,对它进行传递的时候,也是进行了值得拷贝
}
func A(per Person) {
    per.Age = 13
    fmt.Println("A", per)
}
PS G:\mygo\src\mytest> go run .\temp.go
{jack 19}
A {jack 13}
{jack 19}

2、指针赋值

package main
import (
    "fmt"
)
type Person struct {
    Name string
    Age  int
}
func main() {
    a := Person{
        Name: "jack",
        Age:  19, //对结构的属性进行字面值的初始化
    }
    fmt.Println(a)
    A(&a)
    fmt.Println(a) //结构也是一种值类型,对它进行传递的时候,也是进行了值得拷贝
}
func A(per *Person) { //通过一个指针进行传递,此时就不是值得拷贝了
    per.Age = 13
    fmt.Println("A", per)
}
PS G:\mygo\src\mytest> go run .\temp.go
{jack 19}
A &{jack 13}
{jack 13}

3、初始化指针

在进行结构体初始化赋值时,可以直接就取结构体的指针地址。这样会使读取的速度更快,如下:

package main
import (
    "fmt"
)
type Person struct {
    Name string
    Age  int
}
func main() {
    a := &Person{
        Name: "jack",
        Age:  19, //此时初始化的时候就将这个struct的指针取出来
    }
    //在进行struct的初始化的时候,就加上&取地址符号
    fmt.Println(a)
    A(a)
    B(a)
    fmt.Println(a) //结构也是一种值类型,对它进行传递的时候,也是进行了值得拷贝
}
func A(per *Person) { //通过一个指针进行传递,此时就不是值得拷贝了
    per.Age = 13
    fmt.Println("A", per)
}
func B(per *Person) { //通过一个指针进行传递,此时就不是值得拷贝了
    per.Age = 15
    fmt.Println("B", per)
}
PS G:\mygo\src\mytest> go run .\temp.go
&{jack 19}
A &{jack 13}
B &{jack 15}
&{jack 15}

二、匿名结构

匿名结构分类为:一种是不给结构体进行命名,直接进行赋值并使用;另一种是字段匿名,只指明类型,然后直接使用。

1、匿名结构

package main
import (
    "fmt"
)
func main() {
    a := &struct { //匿名结构,需要先对结构本身进行一个定义
        Name string
        Age  int
    }{
        Name: "jack",
        Age:  20,
    }
    fmt.Println(a)
}

2、匿名结构的嵌套

package main
import (
    "fmt"
)
type Person struct {
    Name    string
    Age     int
    Contact struct {
        Phone, City string //匿名结构嵌套在Person中
    }
}
func main() {
    a := Person{Name: "Jack", Age: 20}
    a.Contact.Phone = "123321" //通过这种方法对嵌套在Person中的匿名结构进行字面值的初始化
    a.Contact.City = "BeiJing"
    fmt.Println(a)
}
PS G:\mygo\src\mytest> go run .\temp2.go
{Jack 20 {123321 BeiJing}}

3、匿名字段

package main
import (
    "fmt"
)
type Person struct {
    string //匿名字段 在进行字面值初始化的时候 必须严格按照字段声明的顺序
    int
}
func main() {
    a := Person{"Jack", 20} //此时将string 和 int类型对调的时候就会报错
    fmt.Println(a)
}

三、结构体的嵌套与比较

1、结构体的嵌套

package main
import (
    "fmt"
)
type human struct {
    Sex int
}
type teacher struct {
    human
    Name string
    Age  int
}
type student struct {
    human //这里的human也是一种类型,此时它相当于一种匿名字段,嵌入结构作为匿名字段的话
    //它本质上是将结构名称作为我们的字段名称
    Name string
    Age  int
}
func main() {
    a := teacher{Name: "Jack", Age: 20, human: human{Sex: 0}} //因此我们需要在这里进行这种初始化
    b := student{Name: "Tom", Age: 19, human: human{Sex: 1}}
    a.Name = "Fack"
    a.Age = 13
    a.human.Sex = 100 //保留这种调用的方法,是因为会涉及到名称的冲突
    //a.Sex = 101 这种写法也是可以的
    fmt.Println(a, b)
}
PS G:\mygo\src\mytest> go run .\temp3.go
{{100} Fack 13} {{1} Tom 19}

2、结构体的比较

package main
import (
    "fmt"
)
type Person struct {
    Name string
    Age  int
}
func main() {
    a := Person{Name: "Jack", Age: 20}
    b := Person{Name: "Jack", Age: 20}
    fmt.Println(a == b)
}
PS G:\mygo\src\mytest> go run .\temp3.go
true

四、tag标签的使用

这个比较多的使用场景是json和结构体的转换中,因为结构体中字段的首字母要大写,而json中的数据不见得都是首字母都是大写的,这时候就可以通过tag字段实现该功能,如下:

package main
import (
	"encoding/json"
	"fmt"
)
type peerInfo struct {
	HTTPPort int `json:"http_port"`
	TCPPort  int `json:"tcp_port"`
	versiong string
}
func main() {
	var v peerInfo
	data := []byte(`{"http_port":80,"tcp_port":3306}`)
	err := json.Unmarshal(data, &v)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Printf("%+v\n", v)
}

后面小写的tag标签就与json中的数据保持了一致。

json转为struct结构体可以在如下两个网站上进行转换,https://mholt.github.io/json-to-go/https://oktools.net/json2go

通过反射,获取tag,进行打印的代码如下:

package main
import (
	"fmt"
	"reflect"
	"strings"
)
type Person struct {
	Name        string `label:"Person Name: " uppercase:"true"`
	Age         int    `label:"Age is: "`
	Sex         string `label:"Sex is: "`
	Description string
}
// 按照tag打印结构体
func PrintUseTag(ptr interface{}) error {
	// 获取入参的类型
	t := reflect.TypeOf(ptr)
	// 入参类型校验
	if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct {
		return fmt.Errorf("参数应该为结构体指针")
	}
	// 取指针指向的结构体变量
	v := reflect.ValueOf(ptr).Elem()
	// 解析字段
	for i := 0; i < v.NumField(); i++ {
		// 取tag
		fieldInfo := v.Type().Field(i)
		tag := fieldInfo.Tag
		// 解析label tag
		label := tag.Get("label")
		if label == "" {
			label = fieldInfo.Name + ": "
		}
		// 解析uppercase tag
		value := fmt.Sprintf("%v", v.Field(i))
		if fieldInfo.Type.Kind() == reflect.String {
			uppercase := tag.Get("uppercase")
			if uppercase == "true" {
				value = strings.ToUpper(value)
			} else {
				value = strings.ToLower(value)
			}
		}
		fmt.Println(label + value)
	}
	return nil
}
func main() {
	person := Person{
		Name:        "Tom",
		Age:         29,
		Sex:         "Male",
		Description: "Cool",
	}
	PrintUseTag(&person)
}




本站的发展离不开您的资助,金额随意,欢迎来赏!

You can donate through PayPal.
My paypal id: itybku@139.com
Paypal page: https://www.paypal.me/361way

  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.