溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

Golang中的Struct怎么定義和使用

發(fā)布時(shí)間:2022-11-18 09:49:11 來(lái)源:億速云 閱讀:135 作者:iii 欄目:編程語(yǔ)言

本篇內(nèi)容主要講解“Golang中的Struct怎么定義和使用”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“Golang中的Struct怎么定義和使用”吧!

Go語(yǔ)言中提供了對(duì)struct的支持;struct,中文翻譯稱(chēng)為結(jié)構(gòu)體,與數(shù)組一樣,屬于復(fù)合類(lèi)型,并非引用類(lèi)型。Go語(yǔ)言的struct,與C語(yǔ)言中的struct或其他面向?qū)ο缶幊陶Z(yǔ)言中的類(lèi)(class)類(lèi)似,可以定義字段(屬性)和方法,但也有很不同的地方,需要深入學(xué)習(xí),才能區(qū)分他們之間的區(qū)別。

注意復(fù)合類(lèi)型與引用類(lèi)型之間的區(qū)別,這應(yīng)該也是值傳遞和引用傳遞的區(qū)別吧。

定義

使用struct關(guān)鍵字可以定義一個(gè)結(jié)構(gòu)體,結(jié)構(gòu)體中的成員,稱(chēng)為結(jié)構(gòu)體的字段或?qū)傩浴?/p>

type Member struct {
    id          int
    name, email string
    gender, age int
}

上面的代碼中,我們定義了一個(gè)包含5個(gè)字段的結(jié)構(gòu)體,可以看到,相同類(lèi)型nameemail、genderage在同一行中定義,但比較好的編程習(xí)慣是每一行只定義一個(gè)字段,如:

type Member struct {
    id     int
    name   string
    email  string
    gender int
    age    int
}

當(dāng)然,結(jié)構(gòu)體也可以不包含任何字段,稱(chēng)為空結(jié)構(gòu)體,struct{}表示一個(gè)空的結(jié)構(gòu)體,注意,直接定義一個(gè)空的結(jié)構(gòu)體并沒(méi)有意義,但在并發(fā)編程中,channel之間的通訊,可以使用一個(gè)struct{}作為信號(hào)量。

ch := make(chan struct{})
ch <- struct{}{}

使用

上面的例子中,我們定義了Member結(jié)構(gòu)體類(lèi)型,接下就可以這個(gè)自定義的類(lèi)型創(chuàng)建變量了。

直接定義變量,這個(gè)使用方式并沒(méi)有為字段賦初始值,因此所有字段都會(huì)被自動(dòng)賦予自已類(lèi)型的零值,比如name的值為空字符串"",age的值為0。

var m1 Member//所有字段均為空值

使用字面量創(chuàng)建變量,這種使用方式,可以在大括號(hào)中為結(jié)構(gòu)體的成員賦初始值,有兩種賦初始值的方式,一種是按字段在結(jié)構(gòu)體中的順序賦值,下面代碼中m2就是使用這種方式,這種方式要求所有的字段都必須賦值,因此如果字段太多,每個(gè)字段都要賦值,會(huì)很繁瑣,另一種則使用字段名為指定字段賦值,如下面代碼中變量m3的創(chuàng)建,使用這種方式,對(duì)于其他沒(méi)有指定的字段,則使用該字段類(lèi)型的零值作為初始化值。

var m2 = Member{1,"小明","xiaoming@163.com",1,18} // 簡(jiǎn)短變量聲明方式:m2 := Member{1,"小明","xiaoming@163.com",1,18}
var m3 = Member{id:2,"name":"小紅"}// 簡(jiǎn)短變量聲明方式:m3 := Member{id:2,"name":"小紅"}

訪問(wèn)字段

通過(guò)變量名,使用逗號(hào)(.),可以訪問(wèn)結(jié)構(gòu)體類(lèi)型中的字段,或?yàn)樽侄钨x值,也可以對(duì)字段進(jìn)行取址(&)操作。

fmt.Println(m2.name)//輸出:小明
m3.name = "小花"
fmt.Println(m3.name)//輸出:小花

age := &m3.age
*age = 20
fmt.Println(m3.age)//20

指針結(jié)構(gòu)體

結(jié)構(gòu)體與數(shù)組一樣,都是值傳遞,比如當(dāng)把數(shù)組或結(jié)構(gòu)體作為實(shí)參傳給函數(shù)的形參時(shí),會(huì)復(fù)制一個(gè)副本,所以為了提高性能,一般不會(huì)把數(shù)組直接傳遞給函數(shù),而是使用切片(引用類(lèi)型)代替,而把結(jié)構(gòu)體傳給函數(shù)時(shí),可以使用指針結(jié)構(gòu)體。

指針結(jié)構(gòu)體,即一個(gè)指向結(jié)構(gòu)體的指針,聲明結(jié)構(gòu)體變量時(shí),在結(jié)構(gòu)體類(lèi)型前加*號(hào),便聲明一個(gè)指向結(jié)構(gòu)體的指針,如:

注意,指針類(lèi)型為引用類(lèi)型,聲明結(jié)構(gòu)體指針時(shí),如果未初始化,則初始值為nil,只有初始化后,才能訪問(wèn)字段或?yàn)樽侄钨x值。

var m1 *Member
m1.name = "小明"//錯(cuò)誤用法,未初始化,m1為nil

m1 = &Member{}
m1.name = "小明"//初始化后,結(jié)構(gòu)體指針指向某個(gè)結(jié)構(gòu)體地址,才能訪問(wèn)字段,為字段賦值。

另外,使用Go內(nèi)置new()函數(shù),可以分配內(nèi)存來(lái)初始化結(jié)構(gòu)休,并返回分配的內(nèi)存指針,因?yàn)橐呀?jīng)初始化了,所以可以直接訪問(wèn)字段。

var m2 = new(Member)
m2.name = "小紅"

我們知道,如果將結(jié)構(gòu)體轉(zhuǎn)給函數(shù),只是復(fù)制結(jié)構(gòu)體的副本,如果在函數(shù)內(nèi)修改結(jié)構(gòu)體字段值,外面的結(jié)構(gòu)體并不會(huì)受影響,而如果將結(jié)構(gòu)體指針傳給函數(shù),則在函數(shù)中使用指針對(duì)結(jié)構(gòu)體所做的修改,都會(huì)影響到指針指向的結(jié)構(gòu)體。

func main() {
    m1 := Member{}
    m2 := new(Member)
    Change(m1,m2)
    fmt.Println(m1,m2)
}

func Change(m1 Member,m2 *Member){
    m1.Name = "小明"
    m2.Name = "小紅"
}

可見(jiàn)性

上面的例子中,我們定義結(jié)構(gòu)體字段名首字母是小寫(xiě)的,這意味著這些字段在包外不可見(jiàn),因而無(wú)法在其他包中被訪問(wèn),只允許包內(nèi)訪問(wèn)。

下面的例子中,我們將Member聲明在member包中,而后在main包中創(chuàng)建一個(gè)變量,但由于結(jié)構(gòu)體的字段包外不可見(jiàn),因此無(wú)法為字段賦初始值,無(wú)法按字段還是按索引賦值,都會(huì)引發(fā)panic錯(cuò)誤。

package member
type Member struct {
    id     int
    name   string
    email  string
    gender int
    age    int
}

package main

fun main(){
    var m = member.Member{1,"小明","xiaoming@163.com",1,18}//會(huì)引發(fā)panic錯(cuò)誤
}

因此,如果想在一個(gè)包中訪問(wèn)另一個(gè)包中結(jié)構(gòu)體的字段,則必須是大寫(xiě)字母開(kāi)頭的變量,即可導(dǎo)出的變量,如:

type Member struct {
    Id     int
    Name   string
    Email  string
    Gender int
    Age    int
}

Tags

在定義結(jié)構(gòu)體字段時(shí),除字段名稱(chēng)和數(shù)據(jù)類(lèi)型外,還可以使用反引號(hào)為結(jié)構(gòu)體字段聲明元信息,這種元信息稱(chēng)為T(mén)ag,用于編譯階段關(guān)聯(lián)到字段當(dāng)中,如我們將上面例子中的結(jié)構(gòu)體修改為:

type Member struct {
    Id     int    `json:"id,-"`
    Name   string `json:"name"`
    Email  string `json:"email"`
    Gender int    `json:"gender,"`
    Age    int    `json:"age"`
}

上面例子演示的是使用encoding/json包編碼或解碼結(jié)構(gòu)體時(shí)使用的Tag信息。

Tag由反引號(hào)括起來(lái)的一系列用空格分隔的key:"value"鍵值對(duì)組成,如:

Id int `json:"id" gorm:"AUTO_INCREMENT"`

特性

下面總結(jié)幾點(diǎn)結(jié)構(gòu)體的相關(guān)特性:

值傳遞

結(jié)構(gòu)體與數(shù)組一樣,是復(fù)合類(lèi)型,無(wú)論是作為實(shí)參傳遞給函數(shù)時(shí),還是賦值給其他變量,都是值傳遞,即復(fù)一個(gè)副本。

沒(méi)有繼承

Go語(yǔ)言是支持面向?qū)ο缶幊痰?,但卻沒(méi)有繼承的概念,在結(jié)構(gòu)體中,可以通過(guò)組合其他結(jié)構(gòu)體來(lái)構(gòu)建更復(fù)雜的結(jié)構(gòu)體。

結(jié)構(gòu)體不能包含自己

一個(gè)結(jié)構(gòu)體,并沒(méi)有包含自身,比如Member中的字段不能是Member類(lèi)型,但卻可能是*Member。

方法

在Go語(yǔ)言中,將函數(shù)綁定到具體的類(lèi)型中,則稱(chēng)該函數(shù)是該類(lèi)型的方法,其定義的方式是在func與函數(shù)名稱(chēng)之間加上具體類(lèi)型變量,這個(gè)類(lèi)型變量稱(chēng)為方法接收器,如:

注意,并不是只有結(jié)構(gòu)體才能綁定方法,任何類(lèi)型都可以綁定方法,只是我們這里介紹將方法綁定到結(jié)構(gòu)體中。

func setName(m Member,name string){//普通函數(shù)
    m.Name = name
}

func (m Member)setName(name string){//綁定到Member結(jié)構(gòu)體的方法
    m.Name = name
}

從上面的例子中,我們可以看出,通過(guò)方法接收器可以訪問(wèn)結(jié)構(gòu)體的字段,這類(lèi)似其他編程語(yǔ)言中的this關(guān)鍵詞,但在Go語(yǔ)言中,只是一個(gè)變量名而已,我們可以任意命名方法接收器。

調(diào)用結(jié)構(gòu)體的方法,與調(diào)用字段一樣:

m := Member{}
m.setName("小明")
fmt.Println(m.Name)//輸出為空

上面的代碼中,我們會(huì)很奇怪,不是調(diào)用setName()方法設(shè)置了字段Name的值了嗎?為什么還是輸出為空呢?

這是因?yàn)?,結(jié)構(gòu)體是值傳遞,當(dāng)我們調(diào)用setName時(shí),方法接收器接收到是只是結(jié)構(gòu)體變量的一個(gè)副本,通過(guò)副本對(duì)值進(jìn)行修復(fù),并不會(huì)影響調(diào)用者,因此,我們可以將方法接收器定義為指針變量,就可達(dá)到修改結(jié)構(gòu)體的目的了。

func (m *Member)setName(name string){/將Member改為*Member
    m.Name = name
}

m := Member{}
m.setName("小明")
fmt.Println(m.Name)//小明

方法和字段一樣,如果首字母為小寫(xiě),則只允許在包內(nèi)可見(jiàn),在其他包中是無(wú)法訪問(wèn)的,因此,如果要在其他包中訪問(wèn)setName,則應(yīng)該將方法名改為SetName

組合

我們知道,結(jié)構(gòu)體中并沒(méi)有繼承的概念,其實(shí),在Go語(yǔ)言中也沒(méi)有繼承的概念,Go語(yǔ)言的編程哲學(xué)里,推薦使用組合的方式來(lái)達(dá)到代碼復(fù)用效果。

什么是組合

組合,可以理解為定義一個(gè)結(jié)構(gòu)體中,其字段可以是其他的結(jié)構(gòu)體,這樣,不同的結(jié)構(gòu)體就可以共用相同的字段。

注意,在記得我們前面提過(guò)的,結(jié)構(gòu)體不能包含自身,但可能包含指向自身的結(jié)構(gòu)體指針。

例如,我們定義了一個(gè)名為Animal表示動(dòng)物,如果我們想定義一個(gè)結(jié)構(gòu)體表示貓,如:

type Animal struct {
    Name   string  //名稱(chēng)
    Color  string  //顏色
    Height float32 //身高
    Weight float32 //體重
    Age    int     //年齡
}
//奔跑
func (a Animal)Run() {
    fmt.Println(a.Name + "is running")
}
//吃東西
func (a Animal)Eat() {
    fmt.Println(a.Name + "is eating")
}

type Cat struct {
    a Animal
}

func main() {
    var c = Cat{
	    a: Animal{
            Name:   "貓貓",
            Color:  "橙色",
            Weight: 10,
            Height: 30,
            Age:    5,
        },
    }
    fmt.Println(c.a.Name)
    c.a.Run()
}

可以看到,我們定義Cat結(jié)構(gòu)體時(shí),可以把Animal結(jié)構(gòu)體作為Cat的字段。

匿名字段

上面的例子,我們看到,把Animal結(jié)構(gòu)體作為Cat的字段時(shí),其變量名為a,所以我們?cè)L問(wèn)Animal的方法時(shí),語(yǔ)法為c.a.Run(),這種通過(guò)葉子屬性訪問(wèn)某個(gè)字段類(lèi)型所帶的方法和字段用法非常繁瑣。

Go語(yǔ)言支持直接將類(lèi)型作為結(jié)構(gòu)體的字段,而不需要取變量名,這種字段叫匿名字段,如:

type Lion struct {
	Animal //匿名字段
}

func main(){
    var lion = Lion{
        Animal{
            Name:  "小獅子",
            Color: "灰色",
        },
    }
    lion.Run()
    fmt.Println(lion.Name)
}

通過(guò)上面例子,可以看到,通過(guò)匿名字段組合其他類(lèi)型,而后訪問(wèn)匿名字段類(lèi)型所帶的方法和字段時(shí),不需要使用葉子屬性,非常方便。

到此,相信大家對(duì)“Golang中的Struct怎么定義和使用”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢(xún),關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI