goang學習筆記---struct

什麼是結構體

結構體(struct)是用戶自定義的類型,它表明若干字段的集合,能夠用於描述一個實體對象,相似java中的class,是golang面向對象編程的基礎類型。java

如何定義一個結構體

type Coordinate struct {
    X, Y float32
}

語法:type <Name> struct{}
上述代碼定義個一個名爲Coordinate的結構體,裏面包括了兩個float32的變量X,Y,該結構體可用於表示一個平面座標。golang

 

添加對象方法

在go語言中,對象方法在結構體定義的外部添加mongodb

type Coordinate struct {
    X, Y float32
}
//打印座標
func (coo *Coordinate) GetCoordinate() {
    fmt.Printf("(%.2f,%.2f)\n", coo.X, coo.Y)
    return
}

其中,func關鍵字後面的(coo *Coordinate),表示該函數傳入一個指向Coordinate的指針,可經過指針變量coo來操做結構體的值 編程

幾種結構體初始化

1、按原始字段順序經過建立結構體

package main

import (
	"fmt"
)

type Coordinate struct {
	X, Y float32
}

//打印座標
func (coo Coordinate) GetCoordinate() {
	fmt.Printf("(%.6f,%.4f)\n", coo.X, coo.Y)
	return
}

func main() {

	p0 := Coordinate{1, 2}
	p0.GetCoordinate()

}

  

輸出:(1.00,2.00),其中X=1,Y=2json

2、按照自定義字段順序進行初始化

package main

import (
	"fmt"
)

type Coordinate struct {
	X, Y float32
}

func (coo *Coordinate) GetCoordinate() {
	fmt.Printf("X: %.5f Y: %.4f\n", coo.X, coo.Y)
}
func main() {
	p0 := Coordinate{Y: 1, X: 2}
	p0.GetCoordinate()
}

  

輸出:X: 2.00000 Y: 1.0000函數

其中,func關鍵字後面的(coo *Coordinate),表示該函數傳入一個指向Coordinate的指針,可經過指針變量coo來操做結構體的值。ui

 

3、經過new函數建立

package main

import (
	"fmt"
)

type Coordinate struct {
	X, Y float32
}

func (coo *Coordinate) GetCoordinate() {
	fmt.Printf("X: %.5f Y: %.4f\n", coo.X, coo.Y)
}
func main() {
	p0 := new(Coordinate)
	p0.X = 2.012
	p0.Y = 3.2645
	p0.GetCoordinate()
}

  輸出結果:X: 2.01200 Y: 3.26453d

 

比較三種建立方式

其中,第一種與第二種,p0均爲一個類型爲Coordinate的實例,而第三種p0爲一個指向Coordinate的指針,至關於var p0 *Coordinate = new(Coordinate)指針

通常在進行例如type T struct {a, b int}的結構體定義以後
習慣使用t := new(T)給該結構體變量分配內存,它返回指向已分配內存的指針。變量t是一個指向T的指針,此時結構體字段的值是它們所屬類型的零值。
聲明 var t T 也會給 t 分配內存,並零值化內存,可是這個時候 t是類型T。在這兩種方式中,t 一般被稱作類型T的一個實例(instance)或對象(Object)。var t *T = new(T)等價於t := new(T)。

 

package main

import (
	"fmt"
)

type Coordinate struct {
	X, Y float32
}

func (coo *Coordinate) GetCoordinate() {
	fmt.Printf("X: %.5f Y: %.4f\n", coo.X, coo.Y)
}
func main() {
	//p0是類型Coordinate的一個實例
	p0 := Coordinate{1, 2}

	//給該結構體p2變量分配內存,它返回指向已分配內存的指針
	p2 := new(Coordinate)
	p2.X = 1
	p2.Y = 2
	p3 := &Coordinate{X: 1, Y: 2}
	p4 := &Coordinate{1, 2}

	fmt.Println("-------輸出p0-------")
	fmt.Printf("%v\n%T\n", p0, p0)
	fmt.Println("-------輸出p2-------")
	fmt.Printf("%v\n%T\n", p2, p2)
	fmt.Println("-------輸出p3-------")
	fmt.Printf("%v\n%T\n", p3, p3)
	fmt.Println("-------輸出p4-------")
	fmt.Printf("%v\n%T\n", p4, p4)

}

  結果輸出:code

-------輸出p0-------
{1 2}
main.Coordinate
-------輸出p2-------
&{1 2}
*main.Coordinate
-------輸出p3-------
&{1 2}
*main.Coordinate
-------輸出p4-------
&{1 2}
*main.Coordinate

能夠看出來,p2,p3,p4均爲一個指針變量

 

添加值拷貝的對象方法

剛纔說到了,添加一個對象方法,能夠經過func (t *T) functionname()來建立,其中t爲一個指針變量。咱們也能夠經過值拷貝的方式,添加一個對象方法,語法爲func(t T) functionname()

package main

import (
	"fmt"
)

type Coordinate struct {
	X, Y float32
}

func (coo *Coordinate) GetCoordinate() {
	fmt.Printf("X,Y is : %.4f %.5f\n", coo.X, coo.Y)
}

//值拷貝對象方法
func (coo Coordinate) SetPosition01(a float32, b float32) {
	coo.X = a
	coo.Y = b
}

//指針變量對象方法
func (coo *Coordinate) SetPosition02(a float32, b float32) {
	coo.X = a
	coo.Y = b
}
func main() {
	p0 := Coordinate{1, 2}
	fmt.Print("SetPosition02調用前:")
	p0.GetCoordinate()
	
	p0.SetPosition02(0, 0)
	fmt.Print("SetPosition02調用後:")
	p0.GetCoordinate()
	
	p0.SetPosition01(-1.22, 1.44)
	fmt.Print("SetPosition01調用後:")
	p0.GetCoordinate()
}

  結果輸出:

SetPosition02調用前:X,Y is : 1.0000 2.00000
SetPosition02調用後:X,Y is : 0.0000 0.00000
SetPosition01調用後:X,Y is : 0.0000 0.00000

 從程序輸出中能夠看出,調用SetPosition01方法,發生了值拷貝,即便在方法內改變了coo的值,外部的p0的值沒有被改變。而SetPosition02方法中,coo爲指向p0地址的指針,因爲是經過指針變量修改了X,Y的值,因此調用完畢後,外部p0的值會被修改成(0,0)

匿名結構體

package main

import (
	"fmt"
)

func main() {
	p_3d := struct {
		X, Y, Z float32
	}{1, 2, 3}
	fmt.Println("-------輸出p_3d-------")
	fmt.Printf("%v\n%T\n", p_3d, p_3d)
	
}

輸出:

-------輸出p_3d-------
{1 2 3}
struct { X float32; Y float32; Z float32 }

p_3d爲一個包含X,Y,Z三個變量的匿名結構體

golang構造函數?

在Go語言中沒有構造函數的概念,對象的建立一般交由一個全局的建立函數來完成,以 NewXXX來命名,表示「構造函數」:
這一切很是天然,開發者也不須要分析在使用了new以後到底背後發生了多少事情。在Go語言中,一切要發生的事情都直接能夠看到。
—— 《Go語言編程》

 

func NewRect(x, y, width, height float64) *Rect { 
    return &Rect{x, y, width, height}
}

 

變量、方法可見性

Go語言對關鍵字的增長很是吝嗇,其中沒有privateprotectedpublic這樣的關鍵 字。要使某個符號對其餘包(package)可見(便可以訪問),須要將該符號定義爲以大寫字母開頭,如:

 type Rect struct { 
  X, Y float64
  Width, Height float64 
}

這樣,Rect類型的成員變量就所有被導出了,能夠被全部其餘引用了Rect所在包的代碼訪問到。 成員方法的可訪問性遵循一樣的規則,例如:

func (r *Rect) area() float64 { 
    return r.Width * r.Height
} 

  

這樣,Rectarea()方法只能在該類型所在的包內使用。
須要注意的一點是,Go語言中符號的可訪問性是包一級的而不是類型一級的。在上面的例 子中,儘管area()Rect的內部方法,但同一個包中的其餘類型也均可以訪問到它。這樣的可訪問性控制很粗曠,很特別,可是很是實用。若是Go語言符號的可訪問性是類型一級的,少不 了還要加上friend這樣的關鍵字,以表示兩個類是朋友關係,能夠訪問彼此的私有成員。

 

struct Tag

在處理json格式字符串的時候,常常會看到聲明struct結構的時候,屬性的右側還有反引號括起來的內容。形如

type User struct {
    UserId   int    `json:"user_id" bson:"user_id"`
    UserName string `json:"user_name" bson:"user_name"`
}

struct成員變量標籤(Tag)說明

要比較詳細的瞭解這個,要先了解一下golang的基礎,在golang中,命名都是推薦都是用駝峯方式,而且在首字母大小寫有特殊的語法含義:包外沒法引用。可是由常常須要和其它的系統進行數據交互,例如轉成json格式,存儲到mongodb啊等等。這個時候若是用屬性名來做爲鍵值可能不必定會符合項目要求。

反引號中的內容在golang中叫標籤(Tag),在轉換成其它數據格式的時候,會使用其中特定的字段做爲鍵值。例如上例在轉成json格式:

u := &User{UserId: 1, UserName: "tony"}

j, _ := json.Marshal(u)

fmt.Println(string(j))

// 輸出內容:{"user_id":1,"user_name":"tony"}

  

 完整示例:

package main

import (
	"encoding/json"
	"fmt"
)

type User struct {
	UserId   int    `json:"user_id" bson:"user_id"`
	UserName string `json:"user_name" bson:"user_name"`
}

func main() {

	u := &User{UserId: 1, UserName: "tony"}

	j, _ := json.Marshal(u)

	k, _ := json.MarshalIndent(u, "", "	")

	fmt.Println(string(j))
	fmt.Printf("%s\n%T\n", j, j)
	fmt.Println(string(k))
	fmt.Printf("%s\n%T\n", k, k)
}

  輸出:

{"user_id":1,"user_name":"tony"}
{"user_id":1,"user_name":"tony"}
[]uint8
{
	"user_id": 1,
	"user_name": "tony"
}
{
	"user_id": 1,
	"user_name": "tony"
}
[]uint8

  

 

若是在屬性中不增長標籤說明

package main

import (
	"encoding/json"
	"fmt"
)

type User struct {
	UserId   int
	UserName string
}

func main() {

	u := &User{UserId: 1, UserName: "tony"}

	j, _ := json.Marshal(u)

	k, _ := json.MarshalIndent(u, "", "	")

	fmt.Println(string(j))
	fmt.Printf("%s\n%T\n", j, j)
	fmt.Println(string(k))
	fmt.Printf("%s\n%T\n", k, k)
}

  則輸出:

{"UserId":1,"UserName":"tony"}
{"UserId":1,"UserName":"tony"}
[]uint8
{
	"UserId": 1,
	"UserName": "tony"
}
{
	"UserId": 1,
	"UserName": "tony"
}
[]uint8

 

能夠看到直接用struct的屬性名作鍵值。

其中還有一個bson的聲明,這個是用在將數據存儲到mongodb使用的。

struct成員變量標籤(Tag)獲取

那麼當咱們須要本身封裝一些操做,須要用到Tag中的內容時,如何去獲取呢?這邊可使用反射包(reflect)中的方法來獲取:

t := reflect.TypeOf(u)
field := t.Elem().Field(0)
fmt.Println(field.Tag.Get("json"))
fmt.Println(field.Tag.Get("bson"))

 

完整示例:

package main

import (
	"encoding/json"
	"fmt"
	"reflect"
)

func main() {
	type User struct {
		UserId   int    `json:"user_id" bson:"userid"`
		UserName string `json:"user_name" bson:"username"`
	}
	// 輸出json格式
	u := &User{UserId: 1, UserName: "tony"}
	j, _ := json.Marshal(u) //return ([]byte, error)
	fmt.Printf("%c\n", j)
	fmt.Println(string(j))

	// 輸出內容:{"user_id":1,"user_name":"tony"}

	// 獲取tag中的內容
	t := reflect.TypeOf(u)
	field := t.Elem().Field(0)
	fmt.Println(field.Tag.Get("json"))
	// 輸出:user_id
	fmt.Println(field.Tag.Get("bson"))
	// 輸出:user_id

}

  結果輸出:

[{ " u s e r _ i d " : 1 , " u s e r _ n a m e " : " t o n y " }]
{"user_id":1,"user_name":"tony"}
user_id
userid

  

beego的ORM中也經過tag來定義參數的。

package main

import (
	"fmt"
	"reflect"
)

type Job struct {
	AlarmStatus *string `json:"alarm_status" name:"alarmstatus"`
	CPUTopology string  `json:"cpu_topology" name:"cputopology"`
}

func main() {
	a := "abc"
	s := Job{&a, "hello"}
	st := reflect.TypeOf(s)
	field := st.Field(1)
	fmt.Println(field.Tag.Get("json"), field.Tag.Get("name"))
}

  

package main

import (
	"fmt"
	"reflect" // 這裏引入reflect模塊
)

type User struct {
	Name   string "user name" //這引號裏面的就是tag
	Passwd string "user passsword"
}

func main() {
	user := &User{"chronos", "pass"}
	s := reflect.TypeOf(user).Elem() //經過反射獲取type定義
	for i := 0; i < s.NumField(); i++ {
		fmt.Println(s.Field(i).Tag) //將tag輸出出來
	}
}
相關文章
相關標籤/搜索