Golang中的Struct怎么定义和使用


本篇内容主要讲解“Golang中的Struct怎么定义和使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Golang中的Struct怎么定义和使用”吧!Go语言中提供了对struct的支持;struct,中文翻译称为结构体,与数组一样,属于复合类型,并非引用类型。Go语言的struct,与C语言中的struct或其他面向对象编程语言中的类(class)类似,可以定义字段(属性)和方法,但也有很不同的地方,需要深入学习,才能区分他们之间的区别。注意复合类型与引用类型之间的区别,这应该也是值传递和引用传递的区别吧。使用struct关键字可以定义一个结构体,结构体中的成员,称为结构体的字段或属性。

typeMemberstruct{
idint
name,emailstring
gender,ageint
}

上面的代码中,我们定义了一个包含5个字段的结构体,可以看到,相同类型nameemailgenderage在同一行中定义,但比较好的编程习惯是每一行只定义一个字段,如:

typeMemberstruct{
idint
namestring
emailstring
genderint
ageint
}

当然,结构体也可以不包含任何字段,称为空结构体,struct{}表示一个空的结构体,注意,直接定义一个空的结构体并没有意义,但在并发编程中,channel之间的通讯,可以使用一个struct{}作为信号量。

ch:=make(chanstruct{})
ch

上面的代码中,我们定义了一个包含5个字段的结构体,可以看到,相同类型nameemailgenderage在同一行中定义,但比较好的编程习惯是每一行只定义一个字段,如:
当然,结构体也可以不包含任何字段,称为空结构体,struct{}表示一个空的结构体,注意,直接定义一个空的结构体并没有意义,但在并发编程中,channel之间的通讯,可以使用一个struct{}作为信号量。
上面的例子中,我们定义了Member结构体类型,接下就可以这个自定义的类型创建变量了。直接定义变量,这个使用方式并没有为字段赋初始值,因此所有字段都会被自动赋予自已类型的零值,比如name的值为空字符串””,age的值为0。

varm1Member//所有字段均为空值

使用字面量创建变量,这种使用方式,可以在大括号中为结构体的成员赋初始值,有两种赋初始值的方式,一种是按字段在结构体中的顺序赋值,下面代码中m2就是使用这种方式,这种方式要求所有的字段都必须赋值,因此如果字段太多,每个字段都要赋值,会很繁琐,另一种则使用字段名为指定字段赋值,如下面代码中变量m3的创建,使用这种方式,对于其他没有指定的字段,则使用该字段类型的零值作为初始化值。

varm2=Member{1,"小明","xiaoming@163.com",1,18}//简短变量声明方式:m2:=Member{1,"小明","xiaoming@163.com",1,18}
varm3=Member{id:2,"name":"小红"}//简短变量声明方式:m3:=Member{id:2,"name":"小红"}

通过变量名,使用逗号(.),可以访问结构体类型中的字段,或为字段赋值,也可以对字段进行取址(&)操作。

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

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

结构体与数组一样,都是值传递,比如当把数组或结构体作为实参传给函数的形参时,会复制一个副本,所以为了提高性能,一般不会把数组直接传递给函数,而是使用切片(引用类型)代替,而把结构体传给函数时,可以使用指针结构体。指针结构体,即一个指向结构体的指针,声明结构体变量时,在结构体类型前加*号,便声明一个指向结构体的指针,如:注意,指针类型为引用类型,声明结构体指针时,如果未初始化,则初始值为nil,只有初始化后,才能访问字段或为字段赋值。

varm1*Member
m1.name="小明"//错误用法,未初始化,m1为nil

m1=&Member{}
m1.name="小明"//初始化后,结构体指针指向某个结构体地址,才能访问字段,为字段赋值。

另外,使用Go内置new()函数,可以分配内存来初始化结构休,并返回分配的内存指针,因为已经初始化了,所以可以直接访问字段。

varm2=new(Member)
m2.name="小红"

另外,使用Go内置new()函数,可以分配内存来初始化结构休,并返回分配的内存指针,因为已经初始化了,所以可以直接访问字段。
我们知道,如果将结构体转给函数,只是复制结构体的副本,如果在函数内修改结构体字段值,外面的结构体并不会受影响,而如果将结构体指针传给函数,则在函数中使用指针对结构体所做的修改,都会影响到指针指向的结构体。

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

funcChange(m1Member,m2*Member){
m1.Name="小明"
m2.Name="小红"
}

上面的例子中,我们定义结构体字段名首字母是小写的,这意味着这些字段在包外不可见,因而无法在其他包中被访问,只允许包内访问。下面的例子中,我们将Member声明在member包中,而后在main包中创建一个变量,但由于结构体的字段包外不可见,因此无法为字段赋初始值,无法按字段还是按索引赋值,都会引发panic错误。

packagemember
typeMemberstruct{
idint
namestring
emailstring
genderint
ageint
}

packagemain

funmain(){
varm=member.Member{1,"小明","xiaoming@163.com",1,18}//会引发panic错误
}

因此,如果想在一个包中访问另一个包中结构体的字段,则必须是大写字母开头的变量,即可导出的变量,如:

typeMemberstruct{
Idint
Namestring
Emailstring
Genderint
Ageint
}

在定义结构体字段时,除字段名称和数据类型外,还可以使用反引号为结构体字段声明元信息,这种元信息称为Tag,用于编译阶段关联到字段当中,如我们将上面例子中的结构体修改为:

typeMemberstruct{
Idint`json:"id,-"`
Namestring`json:"name"`
Emailstring`json:"email"`
Genderint`json:"gender,"`
Ageint`json:"age"`
}

上面例子演示的是使用encoding/json包编码或解码结构体时使用的Tag信息。

上面例子演示的是使用encoding/json包编码或解码结构体时使用的Tag信息。
Tag由反引号括起来的一系列用空格分隔的key:”value”键值对组成,如:

Idint`json:"id"gorm:"AUTO_INCREMENT"`

下面总结几点结构体的相关特性:结构体与数组一样,是复合类型,无论是作为实参传递给函数时,还是赋值给其他变量,都是值传递,即复一个副本。Go语言是支持面向对象编程的,但却没有继承的概念,在结构体中,可以通过组合其他结构体来构建更复杂的结构体。一个结构体,并没有包含自身,比如Member中的字段不能是Member类型,但却可能是*Member。在Go语言中,将函数绑定到具体的类型中,则称该函数是该类型的方法,其定义的方式是在func与函数名称之间加上具体类型变量,这个类型变量称为方法接收器,如:注意,并不是只有结构体才能绑定方法,任何类型都可以绑定方法,只是我们这里介绍将方法绑定到结构体中。

funcsetName(mMember,namestring){//普通函数
m.Name=name
}

func(mMember)setName(namestring){//绑定到Member结构体的方法免费云主机域名
m.Name=name
}

从上面的例子中,我们可以看出,通过方法接收器可以访问结构体的字段,这类似其他编程语言中的this关键词,但在Go语言中,只是一个变量名而已,我们可以任意命名方法接收器

从上面的例子中,我们可以看出,通过方法接收器可以访问结构体的字段,这类似其他编程语言中的this关键词,但在Go语言中,只是一个变量名而已,我们可以任意命名方法接收器
调用结构体的方法,与调用字段一样:

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

上面的代码中,我们会很奇怪,不是调用setName()方法设置了字段Name的值了吗?为什么还是输出为空呢?

上面的代码中,我们会很奇怪,不是调用setName()方法设置了字段Name的值了吗?为什么还是输出为空呢?
这是因为,结构体是值传递,当我们调用setName时,方法接收器接收到是只是结构体变量的一个副本,通过副本对值进行修复,并不会影响调用者,因此,我们可以将方法接收器定义为指针变量,就可达到修改结构体的目的了。

func(m*Member)setName(namestring){/将Member改为*Member
m.Name=name
}

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

方法和字段一样,如果首字母为小写,则只允许在包内可见,在其他包中是无法访问的,因此,如果要在其他包中访问setName,则应该将方法名改为SetName

方法和字段一样,如果首字母为小写,则只允许在包内可见,在其他包中是无法访问的,因此,如果要在其他包中访问setName,则应该将方法名改为SetName
我们知道,结构体中并没有继承的概念,其实,在Go语言中也没有继承的概念,Go语言的编程哲学里,推荐使用组合的方式来达到代码复用效果。组合,可以理解为定义一个结构体中,其字段可以是其他的结构体,这样,不同的结构体就可以共用相同的字段。注意,在记得我们前面提过的,结构体不能包含自身,但可能包含指向自身的结构体指针。例如,我们定义了一个名为Animal表示动物,如果我们想定义一个结构体表示猫,如:

typeAnimalstruct{
Namestring//名称
Colorstring//颜色
Heightfloat32//身高
Weightfloat32//体重
Ageint//年龄
}
//奔跑
func(aAnimal)Run(){
fmt.Println(a.Name+"isrunning")
}
//吃东西
func(aAnimal)Eat(){
fmt.Println(a.Name+"iseating")
}

typeCatstruct{
aAnimal
}

funcmain(){
varc=Cat{
	a:Animal{
Name:"猫猫",
Color:"橙色",
Weight:10,
Height:30,
Age:5,
},
}
fmt.Println(c.a.Name)
c.a.Run()
}

可以看到,我们定义Cat结构体时,可以把Animal结构体作为Cat的字段。

可以看到,我们定义Cat结构体时,可以把Animal结构体作为Cat的字段。
上面的例子,我们看到,把Animal结构体作为Cat的字段时,其变量名为a,所以我们访问Animal的方法时,语法为c.a.Run(),这种通过叶子属性访问某个字段类型所带的方法和字段用法非常繁琐。Go语言支持直接将类型作为结构体的字段,而不需要取变量名,这种字段叫匿名字段,如:

typeLionstruct{
	Animal//匿名字段
}

funcmain(){
varlion=Lion{
Animal{
Name:"小狮子",
Color:"灰色",
},
}
lion.Run()
fmt.Println(lion.Name)
}

通过上面例子,可以看到,通过匿名字段组合其他类型,而后访问匿名字段类型所带的方法和字段时,不需要使用叶子属性,非常方便。到此,相信大家对“Golang中的Struct怎么定义和使用”有了更深的了解,不妨来实际操作一番吧!这里是百云主机网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

相关推荐: Java8新特性之Lambda表达式的语法是什么

这篇文章主要介绍了Java8新特性之Lambda表达式的语法是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java8新特性之Lambda表达式的语法是什么文章都会有所收获,下面我们一起来看看吧。#Lambd表达式 Lambd…

免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。

Like (0)
Donate 微信扫一扫 微信扫一扫
Previous 02/22 15:00
Next 02/22 15:00

相关推荐