练习 - 使用结构

已完成

有时,你需要在一个结构中表示字段的集合。 例如,要编写工资核算程序时,需要使用员工数据结构。 在 Go 中,可使用结构将可能构成记录的不同字段组合在一起。

Go 中的结构也是一种数据结构,它可包含零个或多个任意类型的字段,并将它们表示为单个实体。

在本部分,我们将探索结构为何很重要以及如何使用它们。

声明和初始化结构

要声明结构,需要使用 struct 关键字,还要使用希望新的数据类型具有的字段及其类型的列表。 例如,若要定义员工结构,可使用以下代码:

type Employee struct {
    ID        int
    FirstName string
    LastName  string
    Address   string
}

然后,可像操作其他类型一样使用新类型声明一个变量,如下所示:

var john Employee

如果要在声明变量的同时对其进行初始化,可按以下方式操作:

employee := Employee{1001, "John", "Doe", "Doe's Street"}

请注意,必须为结构中的每个字段指定一个值。 但这有时也可能会导致出现问题。 或者,可更具体地了解要在结构中初始化的字段:

employee := Employee{LastName: "Doe", FirstName: "John"}

请注意从上述声明中看,为每个字段分配值的顺序不重要。 此外,如果未指定任何其他字段的值,也并不重要。 Go 将根据字段数据类型分配默认值。

若要访问结构的各个字段,可使用点表示法 (.) 做到这一点,如下例所示:

employee.ID = 1001
fmt.Println(employee.FirstName)

最后,可使用 & 运算符生成指向结构的指针,如以下代码所示:

package main

import "fmt"

type Employee struct {
    ID        int
    FirstName string
    LastName  string
    Address   string
}

func main() {
    employee := Employee{LastName: "Doe", FirstName: "John"}
    fmt.Println(employee)
    employeeCopy := &employee
    employeeCopy.FirstName = "David"
    fmt.Println(employee)
}

运行上述代码时,你会看到以下输出:

{0 John Doe }
{0 David Doe }

请注意在使用指针时结构是如何变为可变结构的。

结构嵌入

通过 Go 中的结构,可将某结构嵌入到另一结构中。 有时,你需要减少重复并重用一种常见的结构。 例如,假设你想要重构之前的代码,使其具有两种数据类型,一种针对员工,一种针对合同工。 你可具有一个包含公共字段的 Person 结构,如下例所示:

type Person struct {
    ID        int
    FirstName string
    LastName  string
    Address   string
}

然后,你可声明嵌入 Person 类型的其他类型,例如 EmployeeContractor。 若要嵌入另一个结构,请创建一个新字段,如下例所示:

type Employee struct {
    Information Person
    ManagerID   int
}

但是,若要引用 Person 结构中的字段,你需要包含员工变量中的 Information 字段,如下例所示:

var employee Employee
employee.Information.FirstName = "John"

如果你要像我们这样重构代码,则会破坏我们的代码。 或者,你可包含一个与你要嵌入的结构同名的新字段,如下例所示:

type Employee struct {
    Person
    ManagerID int
}

可使用以下代码进行演示:

package main

import "fmt"

type Person struct {
    ID        int
    FirstName string
    LastName  string
    Address   string
}

type Employee struct {
    Person
    ManagerID int
}

type Contractor struct {
    Person
    CompanyID int
}

func main() {
    employee := Employee{
        Person: Person{
            FirstName: "John",
        },
    }
    employee.LastName = "Doe"
    fmt.Println(employee.FirstName)
}

请注意如何在无需指定 Person 字段的情况下访问 Employee 结构中的 FirstName 字段,因为它会自动嵌入其所有字段。 但在你初始化结构时,必须明确要给哪个字段分配值。

用 JSON 编码和解码结构

最后,可使用结构来对 JSON 中的数据进行编码和解码。 Go 对 JSON 格式提供很好的支持,该格式已包含在标准库包中。

你还可执行一些操作,例如重命名结构中字段的名称。 例如,假设你不希望 JSON 输出显示 FirstName 而只显示 name,或者忽略空字段, 可使用如下例所示的字段标记:

type Person struct {
    ID        int    
    FirstName string `json:"name"`
    LastName  string
    Address   string `json:"address,omitempty"`
}

然后,若要将结构编码为 JSON,请使用 json.Marshal 函数。 若要将 JSON 字符串解码为数据结构,请使用 json.Unmarshal 函数。 下例将所有内容组合在一起,将员工数组编码为 JSON,并将输出解码为新的变量:

package main

import (
    "encoding/json"
    "fmt"
)

type Person struct {
    ID        int
    FirstName string `json:"name"`
    LastName  string
    Address   string `json:"address,omitempty"`
}

type Employee struct {
    Person
    ManagerID int
}

type Contractor struct {
    Person
    CompanyID int
}

func main() {
    employees := []Employee{
        Employee{
            Person: Person{
                LastName: "Doe", FirstName: "John",
            },
        },
        Employee{
            Person: Person{
                LastName: "Campbell", FirstName: "David",
            },
        },
    }

    data, _ := json.Marshal(employees)
    fmt.Printf("%s\n", data)

    var decoded []Employee
    json.Unmarshal(data, &decoded)
    fmt.Printf("%v", decoded)
}

运行上述代码时,你会看到以下输出:

[{"ID":0,"name":"John","LastName":"Doe","ManagerID":0},{"ID":0,"name":"David","LastName":"Campbell","ManagerID":0}]
[{{0 John Doe } 0} {{0 David Campbell } 0}]