首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
复杂数据类型
值类型和指针类型
值类型和指针类型的存储结构
为什么要区分值类型和指针类型
关于引用类型
slice(切片)的使用及实现原理
切片如何实现大小可变
切片的声明和定义
切片长度的扩展
切片容量的扩展
切片参数的复制
利用数组创建切片
利用切片创建切片
切片元素的修改
切片的循环处理
切片索引越界
总结切片操作的底层原理
map(映射)的使用及实现原理
声明和创建map
遍历map中的元素
元素查找与避免二义性
删除元素
map的存储结构解析
map元素的定位原理解析
map的容量扩展原理解析
channel(通道)的使用及实现原理
channel的使用
channel的实现原理
channel与消息队列、协程通信的对比
自定义结构体
自定义数据类型和自定义结构体
自定义结构体的使用
利用new创建实例
从自定义结构体看访问权限控制
自描述的访问权限
编程范例——结构体使用实例
利用自定义结构体实现bitmap
利用timer.Ticker实现定时任务
流程控制
分支控制
if语句实现分支控制
switch语句实现分支控制
分支控制的本质是向下跳转
避免多层if嵌套的技巧
循环控制
for循环
for-range循环
循环控制的本质是向上跳转
循环和递归的区别
跳转控制
goto关键字的使用
goto的本质是任意跳转
编程范例——流程控制的灵活使用
for循环的误区
switch-case的灵活使用
当前位置:
首页>>
技术小册>>
深入浅出Go语言核心编程(二)
小册名称:深入浅出Go语言核心编程(二)
### 自定义结构体的使用 在Go语言中,结构体(Struct)是一种复合数据类型,它允许你将多个不同类型的变量组合成一个单一的类型。这种能力使得Go语言在处理复杂数据时非常灵活和强大。在《深入浅出Go语言核心编程(二)》中,我们深入探讨自定义结构体的使用,包括其基本定义、字段访问、方法定义、嵌套结构体、以及结构体的高级应用,如接口实现、反射等。 #### 一、结构体基础 ##### 1.1 定义结构体 在Go中,你可以通过`type`关键字和`struct`标签来定义一个结构体。结构体中的每个字段都有一个名称和一个类型。例如,定义一个表示人的结构体`Person`: ```go type Person struct { Name string Age int Address string } ``` 这里,`Person`结构体有三个字段:`Name`(字符串类型)、`Age`(整型)和`Address`(字符串类型)。 ##### 1.2 初始化结构体 结构体的初始化可以通过直接赋值或使用`new`关键字结合类型断言来完成。但更常见的是使用字面量语法: ```go // 直接使用字面量初始化 p1 := Person{ Name: "Alice", Age: 30, Address: "123 Wonderland St", } // 使用字段名指定初始化(可选字段更灵活) p2 := Person{ Name: "Bob", Age: 25, // Address未指定,将使用其类型的零值,即空字符串 } ``` ##### 1.3 访问结构体字段 访问结构体字段使用点操作符(`.`): ```go fmt.Println(p1.Name) // 输出: Alice p2.Address = "456 Imagination Ave" fmt.Println(p2.Address) // 输出: 456 Imagination Ave ``` #### 二、结构体方法 结构体方法是一种特殊类型的函数,它们被绑定到特定的结构体类型上。这意味着,你可以为结构体定义一组操作这些结构体实例的函数。 ##### 2.1 定义结构体方法 结构体方法的定义看起来很像普通的函数,但在函数名之前有一个接收者(receiver)参数,该参数的类型是结构体类型(或指向结构体的指针)。 ```go // 为Person定义一个方法,计算年龄是否大于18 func (p Person) IsAdult() bool { return p.Age >= 18 } // 使用指针接收者的版本,可以修改接收者的状态 func (p *Person) SetAge(age int) { p.Age = age } ``` ##### 2.2 调用结构体方法 调用结构体方法与调用普通函数类似,但你需要通过结构体实例来调用: ```go fmt.Println(p1.IsAdult()) // 输出: true p1.SetAge(31) // 不能直接修改p1的Age,因为SetAge使用值接收者 fmt.Println(p1.IsAdult()) // 仍然是使用修改前的p1,输出: true // 使用指针接收者 p2Ptr := &p2 p2Ptr.SetAge(26) fmt.Println(p2Ptr.IsAdult()) // 输出: true ``` 注意,如果方法使用指针接收者,则可以直接修改接收者指向的结构体实例的状态。 #### 三、嵌套结构体 Go允许你定义嵌套的结构体,即一个结构体的字段可以是另一个结构体类型。 ```go type ContactInfo struct { Email string Phone string } type PersonDetail struct { Person ContactInfo } // 初始化嵌套结构体 pd := PersonDetail{ Person: Person{ Name: "Charlie", Age: 28, }, ContactInfo: ContactInfo{ Email: "charlie@example.com", Phone: "123-456-7890", }, } // 访问嵌套结构体字段 fmt.Println(pd.Name) // 输出: Charlie fmt.Println(pd.Email) // 直接访问嵌套字段 ``` 在上面的例子中,`PersonDetail`结构体包含了`Person`和`ContactInfo`两个结构体作为字段。这种嵌套允许你构建更复杂的数据模型。 #### 四、结构体的高级应用 ##### 4.1 结构体与接口 Go的接口(Interface)是一种类型,它定义了对象的行为。结构体可以通过实现接口中的方法来隐式地满足接口。这使得结构体可以与其他需要该接口类型的代码无缝协作。 ```go type Speaker interface { Speak() } // Person实现Speaker接口 func (p Person) Speak() { fmt.Printf("Hello, my name is %s and I am %d years old.\n", p.Name, p.Age) } // 使用接口 func MakeItSpeak(s Speaker) { s.Speak() } MakeItSpeak(Person{Name: "David", Age: 35}) ``` ##### 4.2 结构体与反射 Go的反射(Reflection)提供了一种在运行时检查、修改和操作对象的能力。虽然反射在Go中不常用且可能影响性能,但在某些情况下(如序列化/反序列化、通用库开发等)非常有用。 ```go import "reflect" func PrintStructFields(v interface{}) { rv := reflect.ValueOf(v) if rv.Kind() == reflect.Struct { for i := 0; i < rv.NumField(); i++ { f := rv.Type().Field(i) val := rv.Field(i).Interface() fmt.Printf("%s: %v\n", f.Name, val) } } } PrintStructFields(p1) ``` 在这个例子中,`PrintStructFields`函数接受任意类型的参数,并使用反射来检查它是否为结构体,并打印其所有字段的名称和值。 #### 五、总结 自定义结构体的使用是Go语言编程中的基础且强大的特性之一。通过结构体,你可以构建复杂的数据模型,并通过方法定义来封装与这些数据相关的行为。此外,结构体与接口、反射等高级特性的结合,使得Go语言在构建大型、可扩展的软件系统时更加灵活和强大。在《深入浅出Go语言核心编程(二)》中,我们深入探讨了结构体的各个方面,希望这些内容能帮助你更好地理解和使用Go语言中的结构体。
上一篇:
自定义数据类型和自定义结构体
下一篇:
利用new创建实例
该分类下的相关小册推荐:
深入浅出Go语言核心编程(四)
go编程权威指南(一)
go编程权威指南(四)
深入浅出Go语言核心编程(五)
Go 组件设计与实现
深入浅出Go语言核心编程(三)
Go-Web编程实战
GO面试指南
go编程权威指南(三)
深入浅出Go语言核心编程(八)
WebRTC音视频开发实战
深入解析go语言