zoukankan      html  css  js  c++  java
  • goang学习笔记---struct

    什么是结构体

    结构体(struct)是用户自定义的类型,它代表若干字段的集合,可以用于描述一个实体对象,类似java中的class,是golang面向对象编程的基础类型。

    如何定义一个结构体

    type Coordinate struct {
        X, Y float32
    }

    语法:type <Name> struct{}
    上述代码定义个一个名为Coordinate的结构体,里面包括了两个float32的变量X,Y,该结构体可用于表示一个平面坐标。

    添加对象方法

    在go语言中,对象方法在结构体定义的外部添加

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

    其中,func关键字后面的(coo *Coordinate),表示该函数传入一个指向Coordinate的指针,可通过指针变量coo来操作结构体的值 

    几种结构体初始化

    一、按原始字段顺序通过创建结构体

    package main
    
    import (
    	"fmt"
    )
    
    type Coordinate struct {
    	X, Y float32
    }
    
    //打印坐标
    func (coo Coordinate) GetCoordinate() {
    	fmt.Printf("(%.6f,%.4f)
    ", coo.X, coo.Y)
    	return
    }
    
    func main() {
    
    	p0 := Coordinate{1, 2}
    	p0.GetCoordinate()
    
    }
    

      

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

    二、按照自定义字段顺序进行初始化

    package main
    
    import (
    	"fmt"
    )
    
    type Coordinate struct {
    	X, Y float32
    }
    
    func (coo *Coordinate) GetCoordinate() {
    	fmt.Printf("X: %.5f Y: %.4f
    ", 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来操作结构体的值。

    三、通过new函数创建

    package main
    
    import (
    	"fmt"
    )
    
    type Coordinate struct {
    	X, Y float32
    }
    
    func (coo *Coordinate) GetCoordinate() {
    	fmt.Printf("X: %.5f Y: %.4f
    ", coo.X, coo.Y)
    }
    func main() {
    	p0 := new(Coordinate)
    	p0.X = 2.012
    	p0.Y = 3.2645
    	p0.GetCoordinate()
    }
    

      输出结果:X: 2.01200 Y: 3.2645

    比较三种创建方式

    其中,第一种与第二种,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
    ", 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
    %T
    ", p0, p0)
    	fmt.Println("-------输出p2-------")
    	fmt.Printf("%v
    %T
    ", p2, p2)
    	fmt.Println("-------输出p3-------")
    	fmt.Printf("%v
    %T
    ", p3, p3)
    	fmt.Println("-------输出p4-------")
    	fmt.Printf("%v
    %T
    ", p4, p4)
    
    }
    

      结果输出:

    -------输出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
    ", 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
    %T
    ", 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
    %T
    ", j, j)
    	fmt.Println(string(k))
    	fmt.Printf("%s
    %T
    ", 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
    %T
    ", j, j)
    	fmt.Println(string(k))
    	fmt.Printf("%s
    %T
    ", 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
    ", 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输出出来
    	}
    }
    

      

     

  • 相关阅读:
    MapReduce Shuffle 和 Spark Shuffle 原理概述
    Kafka生产消费API JAVA实现
    Kylin引入Spark引擎
    CentOS 下安装 Cmake 步骤
    TP5 使用验证码功能
    连接树莓派中的MySQL服务器
    用 PHP 函数变量数组改变代码结构
    表的优化
    Wamp 下运行 CGI 笔记
    PHP 中的关于 trait 的简单
  • 原文地址:https://www.cnblogs.com/saryli/p/11671370.html
Copyright © 2011-2022 走看看