结构是具有声明数据类型的数据字段的集合。Golang能够通过组合一个或多个类型(包括内置类型和用户定义类型)来声明和创建自己的数据类型。结构中的每个数据字段都使用已知类型声明,该类型可以是内置类型或其他用户定义类型。

结构是在Golang中创建具体的用户定义类型的唯一方法。结构类型是通过组合一组固定的唯一字段来声明的。结构可以提高模块化,并允许在系统周围创建和传递复杂的数据结构。您还可以将结构视为创建数据记录(如员工记录或电子商务产品)的模板。

声明以关键字type开头,然后是新结构的名称,最后是关键字struct。在大括号内,使用名称和类型指定了一系列数据字段。

type identifier struct{
  field1 data_type
  field2 data_type
  field3 data_type
}

 

结构类型的声明

声明了一个结构类型矩形,该rectangle具有三个不同数据类型的数据字段。在这里,使用的结构不实例化该类型的新实例。

package main
 
import "fmt"
 
type rectangle struct {
	length  float64
	breadth float64
	color   string
}
 
func main() {
	fmt.Println(rectangle{10.5, 25.10, "red"})
}

rectangle结构及其字段不会导出到其他包,因为标识符以小写字母开头。在Golang中,如果名称以大写字母开头,则标识符将导出到其他包中,否则可访问性将仅在包内受到限制。

 

创建结构类型的实例

var关键字初始化变量rect。使用表示法,将值分配给结构字段。

package main
 
import "fmt"
 
type rectangle struct {
	length  int
	breadth int
	color   string
 
	geometry struct {
		area      int
		perimeter int
	}
}
 
func main() {
	var rect rectangle
	rect.length = 10
	rect.breadth = 20
	rect.color = "Green"
 
	rect.geometry.area = rect.length * rect.breadth
	rect.geometry.perimeter = 2 * (rect.length + rect.breadth)
 
	fmt.Println(rect)
	fmt.Println("Area:\t", rect.geometry.area)
	fmt.Println("Perimeter:", rect.geometry.perimeter)
}

结构将打印到终端,显示已分配值。

 

创建结构实例

通过使用结构文本并为结构的字段赋值来创建rectangle结构的实例。

package main

import "fmt"

type rectangle struct {
	length  int
	breadth int
	color   string
}

func main() {
	var rect1 = rectangle{10, 20, "Green"}
	fmt.Println(rect1)

	var rect2 = rectangle{length: 10, color: "Green"}
	fmt.Println(rect2)

	rect3 := rectangle{10, 20, "Green"}
	fmt.Println(rect3)

	rect4 := rectangle{length: 10, breadth: 20, color: "Green"}
	fmt.Println(rect4)

	rect5 := rectangle{breadth: 20, color: "Green"}
	fmt.Println(rect5)
}

 

使用 new 关键字的结构实例化

还可以使用new关键字创建结构的实例。然后可以使用点表示法将数据值分配给数据字段。

package main
 
import "fmt"
 
type rectangle struct {
	length  int
	breadth int
	color   string
}
 
func main() {
	rect1 := new(rectangle) // rect1是指针
	rect1.length = 10
	rect1.breadth = 20
	rect1.color = "Green"
	fmt.Println(rect1)
 
	var rect2 = new(rectangle) // rect2是实例对象
	rect2.length = 10
	rect2.color = "Red"
	fmt.Println(rect2)
}

实例化矩形结构的两个实例,rect1 指向实例化结构的地址,rect2是它所表示的结构的名称。

 

使用指针地址运算符的结构实例化

使用指针地址创建rectangle结构的实例运算符&符号表示。

package main

import "fmt"

type rectangle struct {
	length  int
	breadth int
	color   string
}

func main() {
	var rect1 = &rectangle{10, 20, "Green"} // 不能省略任何属性值
	fmt.Println(rect1)

	var rect2 = &rectangle{}
	rect2.length = 10
	rect2.color = "Red"
	fmt.Println(rect2)// breadth没有赋值

	var rect3 = &rectangle{}
	(*rect3).breadth = 10
	(*rect3).color = "Blue"
	fmt.Println(rect3)// length没有赋值
}

 

嵌套结构类型

可以通过使用其他结构类型作为结构字段的类型来创建结构类型来嵌套结构。将一个结构嵌套在另一个结构中可能是对更复杂的结构进行建模的有用方法。

package main

import "fmt"

type Salary struct {
	Basic, HRA, TA float64
}

type Employee struct {
	FirstName, LastName, Email string
	Age                        int
	MonthlySalary              []Salary
}

func main() {
	e := Employee{
		FirstName: "Mark",
		LastName:  "Jones",
		Email:     "mark@gmail.com",
		Age:       25,
		MonthlySalary: []Salary{
			Salary{
				Basic: 15000.00,
				HRA:   5000.00,
				TA:    2000.00,
			},
			Salary{
				Basic: 16000.00,
				HRA:   5000.00,
				TA:    2100.00,
			},
			Salary{
				Basic: 17000.00,
				HRA:   5000.00,
				TA:    2200.00,
			},
		},
	}
	fmt.Println(e.FirstName, e.LastName)
	fmt.Println(e.Age)
	fmt.Println(e.Email)
	fmt.Println(e.MonthlySalary[0])
	fmt.Println(e.MonthlySalary[1])
	fmt.Println(e.MonthlySalary[2])
}

 

在结构类型定义中使用字段标记

声明结构时,可以用``字段标记定义对应的字段字符串。

package main
 
import (
    "fmt"
    "encoding/json"
)
 
type Employee struct {
    FirstName  string `json:"firstname"`
    LastName   string `json:"lastname"`
    City string `json:"city"`
}
 
func main() {
    json_string := `
    {
        "firstname": "Rocky",
        "lastname": "Sting",
        "city": "London"
    }`
 
    emp1 := new(Employee)
    json.Unmarshal([]byte(json_string), emp1)
    fmt.Println(emp1)
 
    emp2 := new(Employee)
    emp2.FirstName = "Ramesh"
    emp2.LastName = "Soni"
    emp2.City = "Mumbai"
    jsonStr, _ := json.Marshal(emp2)
    fmt.Printf("%s\n", jsonStr)
}

标记表示为原始字符串值(包装在一对``中),并由正常代码执行忽略。

 

将方法添加到结构类型

还可以使用接收器将方法添加到结构类型。将方法EmpInfo添加到Employee结构中。

package main

import "fmt"

type Salary struct {
	Basic, HRA, TA float64
}

type Employee struct {
	FirstName, LastName, Email string
	Age                        int
	MonthlySalary              []Salary
}

func (e Employee) EmpInfo() string {
	fmt.Println(e.FirstName, e.LastName)
	fmt.Println(e.Age)
	fmt.Println(e.Email)
	for _, info := range e.MonthlySalary {
		fmt.Println("===================")
		fmt.Println(info.Basic)
		fmt.Println(info.HRA)
		fmt.Println(info.TA)
	}
	return "----------------------"
}

func main() {

	e := Employee{
		FirstName: "Mark",
		LastName:  "Jones",
		Email:     "mark@gmail.com",
		Age:       25,
		MonthlySalary: []Salary{
			Salary{
				Basic: 15000.00,
				HRA:   5000.00,
				TA:    2000.00,
			},
			Salary{
				Basic: 16000.00,
				HRA:   5000.00,
				TA:    2100.00,
			},
			Salary{
				Basic: 17000.00,
				HRA:   5000.00,
				TA:    2200.00,
			},
		},
	}

	fmt.Println(e.EmpInfo())
}

 

为结构字段分配默认值

分配自定义默认值的方法可以通过使用构造函数来实现。Info函数可用于创建具有name和age字段的自定义默认值的结构,而不是直接创建结构。

package main

import "fmt"

type Employee struct {
	Name string
	Age  int
}

func (obj *Employee) Info() {
	if obj.Name == "" {
		obj.Name = "John Doe"
	}
	if obj.Age == 0 {
		obj.Age = 25
	}
}

func main() {
	emp1 := Employee{Name: "Mr. Fred"}
	emp1.Info()
	fmt.Println(emp1)

	emp2 := Employee{Age: 26}
	emp2.Info()
	fmt.Println(emp2)
}

这是一种技术,而不是 Golang 规范的一部分。

 

查找结构类型

reflect包支持检查结构的基础类型。

package main

import (
	"fmt"
	"reflect"
)

type rectangle struct {
	length  float64
	breadth float64
	color   string
}

func main() {
	var rect1 = rectangle{10, 20, "Green"}
	fmt.Println(reflect.TypeOf(rect1))         // main.rectangle
	fmt.Println(reflect.ValueOf(rect1).Kind()) // struct

	rect2 := rectangle{length: 10, breadth: 20, color: "Green"}
	fmt.Println(reflect.TypeOf(rect2))         // main.rectangle
	fmt.Println(reflect.ValueOf(rect2).Kind()) // struct

	rect3 := new(rectangle)
	fmt.Println(reflect.TypeOf(rect3))         // *main.rectangle
	fmt.Println(reflect.ValueOf(rect3).Kind()) // ptr

	var rect4 = &rectangle{}
	fmt.Println(reflect.TypeOf(rect4))         // *main.rectangle
	fmt.Println(reflect.ValueOf(rect4).Kind()) // ptr
}

 

将结构与分配给数据字段的不同值进行比较

可以使用比较运算符比较相同类型的结构。

package main

import "fmt"

type rectangle struct {
	length  float64
	breadth float64
	color   string
}

func main() {
	var rect1 = rectangle{10, 20, "Green"}
	rect2 := rectangle{length: 20, breadth: 10, color: "Red"}

	if rect1 == rect2 {
		fmt.Println("True")
	} else {
		fmt.Println("False")
	}

	rect3 := new(rectangle)
	var rect4 = &rectangle{}

	if rect3 == rect4 {
		fmt.Println("True")
	} else {
		fmt.Println("False")
	}
}

 

使用值和指针引用复制结构类型

r2将与r1相同,它是R1的副本而不是对它的引用。 对r2所做的任何更改都不会应用于r1,反之亦然。更新r3时,分配给r1的基础内存也会更新。

package main

import "fmt"

type rectangle struct {
	length  float64
	breadth float64
	color   string
}

func main() {
	r1 := rectangle{10, 20, "Green"}
	fmt.Println(r1)

	r2 := r1
	r2.color = "Pink"
	fmt.Println(r2)

	r3 := &r1
	r3.color = "Red"
	fmt.Println(r3)

	fmt.Println(r1)
}

输出

{10 20 Green}
{10 20 Pink}
&{10 20 Red}
{10 20 Red}

由于r1和r3都引用相同的底层内存,因此它们的值是相同的。打印r3和r1的值显示值相同。