練習 - 使用結構

已完成

有時您需要以單一結構表示一組欄位。 例如,當您需要撰寫薪資程式時,便需要使用員工資料結構。 在 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)
}

請注意,您可以從 Employee 結構存取 FirstName 欄位,而不需指定 Person 欄位,因為其會自動內嵌其所有欄位。 但是,當您將結構初始化時,便必須指定要指派值的欄位。

使用 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}]