最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go語言學(xué)習(xí)教程之結(jié)構(gòu)體的示例詳解

 更新時間:2022年09月28日 10:01:56   作者:任沫  
結(jié)構(gòu)體是一個序列,包含一些被命名的元素,這些被命名的元素稱為字段(field),每個字段有一個名字和一個類型。本文通過一些示例帶大家深入了解Go語言中結(jié)構(gòu)體的使用,需要的可以參考一下

前言

結(jié)構(gòu)體是一個序列,包含一些被命名的元素,這些被命名的元素稱為字段(field),每個字段有一個名字和一個類型。

結(jié)構(gòu)體用得比較多的地方是聲明與數(shù)據(jù)庫交互時需要用到的Model類型,以及與JSON數(shù)據(jù)進(jìn)行相互轉(zhuǎn)換。(當(dāng)然,項目中任何需要多種數(shù)據(jù)結(jié)構(gòu)組合在一起使用的地方,都可以選擇用結(jié)構(gòu)體)

代碼段1:聲明一個待辦事項的Model類型:

type Todo struct {
    ID        uint `gorm:"primarykey"`
    CreatedAt time.Time
    UpdatedAt time.Time
    DeletedAt gorm.DeletedAt `gorm:"index"`
    Title     string
    Detail    string `gorm:"column:todo_detail;comment:待辦詳情"`
    Done      bool   `gorm:"default:false"`
}

結(jié)構(gòu)體類型Todo包含unit類型的字段ID,time.Time類型的字段CreatedAt,string類型的字段Title...,當(dāng)執(zhí)行db.AutoMigrate(&Todo{})自動遷移model到數(shù)據(jù)庫中時,對應(yīng)的創(chuàng)建表的SQL為:

CREATE TABLE `todos` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `created_at` datetime(3) DEFAULT NULL,
  `updated_at` datetime(3) DEFAULT NULL,
  `deleted_at` datetime(3) DEFAULT NULL,
  `title` longtext,
  `todo_detail` longtext COMMENT '待辦詳情',
  `done` tinyint(1) DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `idx_todos_deleted_at` (`deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 

(關(guān)于gorm的約定,本文不會描述,重點是放在結(jié)構(gòu)體本身)

代碼段2:整理所需的數(shù)據(jù)并轉(zhuǎn)換為JSON格式:

    type Novel struct {
        ID       uint
        Title    string
        Chapters []string
    }

    novel := Novel{
        ID:    1,
        Title: "我與掘金的二三事",
        Chapters: []string{
            "注冊了賬號",
            "寫了一篇文",
            "又寫了一篇文",
            "升級了,開森",
        },
    }

    a, err := json.Marshal(novel)
    if err != nil {
        fmt.Println(err)
    }

    os.Stdout.Write(a)

執(zhí)行以上代碼打印的內(nèi)容如下:

{"ID":1,"Title":"我與掘金的二三事","Chapters":["注冊了賬號","寫了一篇文","又寫了一篇文","升級了,開森"]}%  

可導(dǎo)出的標(biāo)識符

允許從另一個包中訪問的標(biāo)識符是可導(dǎo)出的。只有同時滿足以下兩個條件的標(biāo)識符為可導(dǎo)出的:

  • 標(biāo)識符的名稱的第一個字符是一個Unicode大寫字母。
  • 標(biāo)識符在包塊中聲明或者它是一個字段名或方法名。

所有其他的標(biāo)識符都是不可導(dǎo)出的。

以上是可導(dǎo)出標(biāo)識符的定義,所以對于結(jié)構(gòu)體來說,結(jié)構(gòu)體中的一個字段可導(dǎo)出,字段的名稱需要首字母大寫。

只在當(dāng)前項目中使用的字段可以使用小寫字母開頭,但是對于需要導(dǎo)出到別的包中進(jìn)行使用的結(jié)構(gòu)體字段,必須首字母大寫,否則其他包無法訪問該字段。

代碼段3:

type Novel struct {
        ID       uint
        Title    string
        Chapters []string
        inited   bool
    }

    novel := Novel{
        inited: true,
        ID:     2,
        Title:  "小步慢跑",
        Chapters: []string{
            "?( ? )?",
        },
    }

    a, err := json.Marshal(novel)
    if err != nil {
        fmt.Println(err)
    }

    os.Stdout.Write(a)

打印的內(nèi)容如下:

{"ID":2,"Title":"小步慢跑","Chapters":["?( ? )?"]}%   

可以看到字段inited只是在代碼中賦值為true了,打印出的內(nèi)容中并沒有inited字段。在json.Marshal轉(zhuǎn)換時,不會對字段inited進(jìn)行處理,因為外部的包不能訪問不可導(dǎo)出字段。

嵌入字段

一個聲明了類型但是沒有顯式字段名稱的字段稱為嵌入字段。一個嵌入字段必須聲明一個類型名稱T,或者一個指向非接口類型的指針*T,并且T本身不是一個指針類型。

代碼段4:只有標(biāo)識符列表:

    type Novel struct {
        ID          uint
        Deleted     bool
        CreatedTime time.Time

        Title    string
        Chapters []string
    }

    novel := Novel{
        ID:          2,
        Deleted:     false,
        CreatedTime: time.Now(),
        Title:       "小步慢跑",
        Chapters: []string{
            "?( ? )?",
        },
    }

    a, err := json.Marshal(novel)
    if err != nil {
        fmt.Println(err)
    }

    os.Stdout.Write(a)

代碼段5: 使用了嵌入字段:

    type Common struct {
        ID          uint
        Deleted     bool
        CreatedTime time.Time
    }

    type Novel struct {
        Common
        Title    string
        Chapters []string
    }

    common := Common{
        ID:          2,
        Deleted:     false,
        CreatedTime: time.Now(),
    }
    novel := Novel{
        Common: common,
        Title:  "小步慢跑",
        Chapters: []string{
            "?( ? )?",
        },
    }

    a, err := json.Marshal(novel)
    if err != nil {
        fmt.Println(err)
    }

    os.Stdout.Write(a)

以上兩段代碼打印出的內(nèi)容是一樣的。包含嵌入字段的類型聲明中,Common就是嵌入字段,類型的名稱就是嵌入字段的名稱。

    type Novel struct {
        Common
        Title    string
        Chapters []string
    }

提升

結(jié)構(gòu)體x中的嵌入字段的一個字段或者方法被稱為提升(promoted)。(首先x.f需要為一個表示字段或者方法f的合法的選擇器)

1.提升字段和結(jié)構(gòu)體的原始字段行為很像,除了它們不能在結(jié)構(gòu)體的復(fù)合字面量中作為字段名使用。

如代碼段4中所示,原始字段直接使用結(jié)構(gòu)體字面量進(jìn)行賦值,但是在使用了嵌入字段的代碼段5中,不能直接使用Novel{ID: 2, ...},因為ID是嵌入字段的字段,不能在結(jié)構(gòu)體字面量中使用,否則會報編譯錯誤:

unknown field ID in struct literal

但是代碼段4和代碼段5都可以使用以下方式對字段賦值:

novel.ID = 2

2.給到一個結(jié)構(gòu)體類型S以及一個定義類型T,提升方法被包括在結(jié)構(gòu)體的方法集合中:

  • 如果S包含一個嵌入字段TS*S的方法集都包含接收器T的提升方法。*S的方法集還包括接收器*T的提升方法。
  • 如果S包含一個嵌入字段*T,S*S的方法集都包含接收器T或者*T的提升方法。

代碼段6:

func promotedA() {
    type Todo struct {
        T1
        *T2
        Title string
    }
    todo := Todo{
        Title: "寫一篇小文文",
    }
    todo1 := &Todo{
        Title: "跑步10分鐘",
    }
    PrintMethodSet(todo)
    PrintMethodSet(todo1)
}

type T1 struct{}

func (T1) M1() {
    fmt.Println("m1")
}
func (*T1) M2() {
    fmt.Println("m2")
}

type T2 struct{}

func (T2) M3() {
    fmt.Println("m3")
}
func (*T2) M4() {
    fmt.Println("m4")
}

// 打印類型的方法集
func PrintMethodSet(x interface{}) {
    v := reflect.ValueOf(x)
    t := v.Type()
    fmt.Printf("類型%s的方法集:\n", t)

    for i := 0; i < v.NumMethod(); i++ { // NumMethod()返回值的方法集中,可導(dǎo)出方法的數(shù)量
        fmt.Println(
            t.Method(i).Name, // 獲取第i個方法的名稱
        )
    }
}

打印的內(nèi)容為:

類型main.Todo的方法集:
M1
M3
M4
類型*main.Todo的方法集:
M1
M2
M3
M4

通過反射可以拿到類型的方法集。關(guān)于反射的說明,可以查看我之前寫的Go語言中的反射。

標(biāo)簽

一個字段聲明可能會跟著一個可選的字符串字面量標(biāo)簽(tag),會成為對應(yīng)的字段聲明中的所有字段的一個屬性。一個空的標(biāo)簽被認(rèn)為就是沒有標(biāo)簽。標(biāo)簽通過反射接口可見,并參與結(jié)構(gòu)類型標(biāo)識,但在其他方面被忽略。

按照慣例,標(biāo)簽字符串是可選的空格分隔符分隔的并列的key:"value"對。每個鍵都是一個非空的字符串,由除了空格(U+0020 ' '),引號(U+0022 '"')和冒號(U+003A ':')外的非控制字符組成。每個值使用引號引起來并使用字符串字面量語法。

代碼段7:

    type Todo struct {
        Title  string `gorm:""`
        Detail string `gorm:"column:todo_detail;comment:待辦詳情" json:"detail"`
        Done   bool   `gorm:"default:false" json:"done"`
    }
    todo := Todo{}
    t := reflect.TypeOf(todo)
    for i := 0; i < t.NumField(); i++ { // NumField返回結(jié)構(gòu)體的字段的數(shù)量
        field := t.Field(i)
        fmt.Println(
            fmt.Sprintf("字段 %s 對應(yīng)的標(biāo)簽值:  ", field.Name),
            field.Tag.Get("gorm"),
            field.Tag.Get("json")) // 獲取標(biāo)簽中的鍵"gorm"和"json"對應(yīng)的值
    }

打印的內(nèi)容如下:

字段 Title 對應(yīng)的標(biāo)簽值:    
字段 Detail 對應(yīng)的標(biāo)簽值:   column:todo_detail;comment:待辦詳情 detail
字段 Done 對應(yīng)的標(biāo)簽值:   default:false done

結(jié)構(gòu)體與JSON相互轉(zhuǎn)換

結(jié)構(gòu)體轉(zhuǎn)JSON

在代碼段2中就將結(jié)構(gòu)體轉(zhuǎn)換為了JSON,使用了encoding/json包的json.Marshal方法。

    type Novel struct {
        ID       uint
        Title    string
        Chapters []string
    }

    novel := Novel{
        ID:    1,
        Title: "我與掘金的二三事",
        Chapters: []string{
            "注冊了賬號",
            "寫了一篇文",
            "又寫了一篇文",
            "升級了,開森",
        },
    }

    a, err := json.Marshal(novel)
    if err != nil {
        fmt.Println(err)
    }

    os.Stdout.Write(a)

json.Marshal可以用于將Go對象轉(zhuǎn)換為JSON編碼的數(shù)據(jù),不只是結(jié)構(gòu)體,還包括布爾值、數(shù)組等,這里只說結(jié)構(gòu)體相關(guān)的部分。

每一個結(jié)構(gòu)體字段的編碼方式可以由存儲在結(jié)構(gòu)體標(biāo)簽中的"json"鍵下的格式化字符串決定。格式化字符串給出字段的名字,可能還會跟著一個由逗號分隔的選項。名稱可以為空,以方便在不覆蓋默認(rèn)字段名稱的情況下指定選項。

"omitempty"選項指明字段是一個空值的時候,需要在編碼時被省略??罩禐閒alse、0、空指針、以及任何空的數(shù)組、切片、映射或字符串。

如果字段的標(biāo)簽是"-",字段永遠(yuǎn)會被省略。注意一個名字為"-"的字段可以使用標(biāo)簽"-,"進(jìn)行生成。

在代碼段2的基礎(chǔ)上修改結(jié)構(gòu)體的類型聲明,觀察得到的結(jié)果:

代碼段8:

    type Novel struct{
        ID       uint `json:"novel_id"`
        Title    string
        Chapters []string
    }

打印的內(nèi)容:

{"novel_id":1,"Title":"我與掘金的二三事","Chapters":["注冊了賬號","寫了一篇文","又寫了一篇文","升級了,開森"]}% 

使用"json"的值novel_id作為ID字段轉(zhuǎn)換為JSON后的字段名。

代碼段9:

    type Novel struct {
        ID       uint `json:"novel_id"`
        Title    string
        Chapters []string `json:"novel_chapters,omitempty"`
    }

    novel := Novel{
        ID:       1,
        Title:    "我與掘金的二三事",
        Chapters: []string{},
    }

    a, err := json.Marshal(novel)
    if err != nil {
        fmt.Println(err)
    }

    os.Stdout.Write(a)

打印的內(nèi)容:

{"novel_id":1,"Title":"我與掘金的二三事"}%  

因為[]string{} 是一個空數(shù)組,是一個空值,因為使用了omitempty,所以該字段在轉(zhuǎn)換時被忽略了。

代碼段10:

    type Novel struct {
        ID       uint     `json:",omitempty"`
        Title    string   `json:"-"`
        Chapters []string `json:"-,"`
    }

    novel := Novel{
        ID:       1,
        Title:    "我與掘金的二三事",
        Chapters: []string{},
    }

    a, err := json.Marshal(novel)
    if err != nil {
        fmt.Println(err)
    }

    os.Stdout.Write(a)

打印的內(nèi)容:

{"ID":1,"-":[]}%  

字段ID省略了指定名字的部分,所以轉(zhuǎn)化的JSON字段與結(jié)構(gòu)體的字段名一樣;字段Title由于使用了-標(biāo)簽值,會在轉(zhuǎn)換的時候永遠(yuǎn)被忽略;字段Chapters轉(zhuǎn)換的JSON數(shù)據(jù)使用的字段名為-

JSON轉(zhuǎn)結(jié)構(gòu)體

func Unmarshal(data []byte, v any) error

Unmarshal解析JSON編碼的數(shù)據(jù)并將其存儲到v指向的值中。

還是在代碼段2的基礎(chǔ)上進(jìn)行修改,把轉(zhuǎn)換為JSON的數(shù)據(jù)又轉(zhuǎn)換回來:

代碼段11:

    type Novel struct {
        ID       uint
        Title    string
        Chapters []string
    }

    novel := Novel{
        ID:    1,
        Title: "我與掘金的二三事",
        Chapters: []string{
            "注冊了賬號",
        },
    }

    a, err := json.Marshal(novel)
    if err != nil {
        fmt.Println(err)
    }
    os.Stdout.Write(a)
    fmt.Println("")

    err = json.Unmarshal(a, &novel)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(novel)

    novel1JSON := []byte(`{"ID":2,"Title":"小步慢跑","Chapters":["?( ? )?"]}`)
    var novel1 Novel
    err = json.Unmarshal(novel1JSON, &novel1)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(novel1)

打印的內(nèi)容為:

{"ID":1,"Title":"我與掘金的二三事","Chapters":["注冊了賬號"]}
{1 我與掘金的二三事 [注冊了賬號]}
{2 小步慢跑 [?( ? )?]}

練習(xí)代碼步驟

1.創(chuàng)建一個文件夾并cd到文件夾目錄下:

$ mkdir practice
$ cd practice

2.使用go mod init 初始化模塊,模塊名稱為practice,這會創(chuàng)建一個go.mod文件,這個文件可以用于對代碼進(jìn)行依賴追蹤。

$ go mod init practice

此時go.mod文件的內(nèi)容為:

module practice

go 1.18

3.創(chuàng)建一個屬于main包的文件main.go,當(dāng)運行main包的時候,會默認(rèn)執(zhí)行其中的main函數(shù)。

package main

import "fmt"

func main() {
    fmt.Println("小步慢跑(? ˙o˙)?")
}

main包所在的文件目錄下執(zhí)行go run .,表示編譯和運行當(dāng)前目錄下的main包:

$ go run .

4.導(dǎo)入gorm庫中的包:gorm核心程序,gorm mysql數(shù)據(jù)庫驅(qū)動程序

package main

import (
    "fmt"

    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

//...(此處為省略的使用gorm的代碼)

導(dǎo)入新的包時,執(zhí)行go mod tidy下載新增的包并更新go.mod文件,此時go.mod文件內(nèi)容為:

module todo

go 1.18

require (
    gorm.io/driver/mysql v1.3.5
    gorm.io/gorm v1.23.8
)

require (
    github.com/go-sql-driver/mysql v1.6.0 // indirect
    github.com/jinzhu/inflection v1.0.0 // indirect
    github.com/jinzhu/now v1.1.5 // indirect
)

執(zhí)行go mod vendormain模塊的根目錄下創(chuàng)建一個名為vendor的目錄,其中包含主模塊所需的包的副本。

5.完整的代碼地址:https://github.com/renmo/myBlog/tree/master/2022-07-31-struct

以上就是Go語言學(xué)習(xí)教程之結(jié)構(gòu)體的示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Go語言 結(jié)構(gòu)體的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解在Go語言單元測試中如何解決Redis存儲依賴問題

    詳解在Go語言單元測試中如何解決Redis存儲依賴問題

    在編寫單元測試時,除了?MySQL?這個外部存儲依賴,Redis?應(yīng)該是另一個最為常見的外部存儲依賴了,本文就來講解下如何解決?Redis?外部依賴,文章通過代碼示例介紹的非常詳細(xì),需要的朋友可以參考下
    2023-08-08
  • Golang的命名規(guī)范及最佳實踐(推薦!)

    Golang的命名規(guī)范及最佳實踐(推薦!)

    這篇文章主要給大家介紹了關(guān)于Golang的命名規(guī)范及最佳實踐的相關(guān)資料,命名規(guī)則涉及變量、常量、全局函數(shù)、結(jié)構(gòu)、接口、方法等的命名,文中介紹的非常詳細(xì),需要的朋友可以參考下
    2023-10-10
  • 深入理解go sync.Waitgroup的使用

    深入理解go sync.Waitgroup的使用

    WaitGroup在go語言中,用于線程同步,本文主要介紹了深入理解go sync.Waitgroup的使用,具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • 詳解Go語言中關(guān)于包導(dǎo)入必學(xué)的 8 個知識點

    詳解Go語言中關(guān)于包導(dǎo)入必學(xué)的 8 個知識點

    這篇文章主要介紹了詳解Go語言中關(guān)于包導(dǎo)入必學(xué)的 8 個知識點,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • 詳解Go strconv包

    詳解Go strconv包

    Go語言中strconv包實現(xiàn)了基本數(shù)據(jù)類型和其字符串表示的相互轉(zhuǎn)換。這篇文章主要介紹了Go strconv包的相關(guān)知識,需要的朋友可以參考下
    2020-10-10
  • PHP與Go語言之間的通信詳解

    PHP與Go語言之間的通信詳解

    相信大家都知道不同語言之間的通信方式有很多種,這篇文章詳細(xì)的介紹了PHP與Go語言之間如何通信,有需要的朋友們可以參考借鑒,下面來一起看看吧。
    2016-10-10
  • golang 如何獲取map所有key的方式

    golang 如何獲取map所有key的方式

    這篇文章主要介紹了golang 獲取map所有key的方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • golang實現(xiàn)基于channel的通用連接池詳解

    golang實現(xiàn)基于channel的通用連接池詳解

    這篇文章主要給大家介紹了關(guān)于golang實現(xiàn)基于channel的通用連接池的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-02-02
  • golang實現(xiàn)簡單的tcp數(shù)據(jù)傳輸

    golang實現(xiàn)簡單的tcp數(shù)據(jù)傳輸

    這篇文章主要為大家介紹了golang實現(xiàn)簡單的tcp數(shù)據(jù)傳輸,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • Golang安裝和使用protocol-buffer流程介紹

    Golang安裝和使用protocol-buffer流程介紹

    這篇文章主要介紹了Golang安裝和使用protocol-buffer過程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-09-09

最新評論

溧水县| 梁山县| 九龙县| 东安县| 禹州市| 肥东县| 嘉义市| 泸水县| 普洱| 西林县| 郎溪县| 太保市| 莲花县| 钟祥市| 邮箱| 大余县| 珠海市| 苍梧县| 新河县| 奉贤区| 曲周县| 正安县| 青海省| 隆林| 怀来县| 江北区| 锡林郭勒盟| 高雄市| 湾仔区| 库车县| 巢湖市| 衡南县| 南通市| 阿拉善右旗| 铜鼓县| 宜兰市| 白山市| 华蓥市| 远安县| 乌鲁木齐县| 安图县|