This commit is contained in:
2025-08-24 13:01:09 +08:00
parent 61e51ad014
commit f028913eb8
36 changed files with 10420 additions and 70 deletions

View File

@@ -0,0 +1,967 @@
/*
01-reflection.go - Go 语言反射详解
学习目标:
1. 理解反射的概念和原理
2. 掌握 reflect 包的基本使用
3. 学会反射的类型和值操作
4. 了解反射的应用场景
5. 掌握反射的最佳实践和注意事项
知识点:
- 反射的基本概念
- reflect.Type 和 reflect.Value
- 类型检查和转换
- 结构体字段和方法的反射
- 反射的性能考虑
- 反射的实际应用
*/
package main
import (
"fmt"
"reflect"
"strconv"
"strings"
"time"
)
func main() {
fmt.Println("=== Go 语言反射详解 ===\\n")
// 演示反射的基本概念
demonstrateReflectionBasics()
// 演示类型反射
demonstrateTypeReflection()
// 演示值反射
demonstrateValueReflection()
// 演示结构体反射
demonstrateStructReflection()
// 演示方法反射
demonstrateMethodReflection()
// 演示反射的实际应用
demonstratePracticalApplications()
// 演示反射的最佳实践
demonstrateBestPractices()
}
// demonstrateReflectionBasics 演示反射的基本概念
func demonstrateReflectionBasics() {
fmt.Println("1. 反射的基本概念:")
// 反射的基本概念
fmt.Printf(" 反射的基本概念:\\n")
fmt.Printf(" - 反射是程序在运行时检查自身结构的能力\\n")
fmt.Printf(" - Go 通过 reflect 包提供反射功能\\n")
fmt.Printf(" - 反射基于接口的动态类型信息\\n")
fmt.Printf(" - 主要包括类型反射和值反射\\n")
fmt.Printf(" - 反射遵循 Go 的类型系统规则\\n")
// 反射的核心类型
fmt.Printf(" 反射的核心类型:\\n")
fmt.Printf(" - reflect.Type: 表示类型信息\\n")
fmt.Printf(" - reflect.Value: 表示值信息\\n")
fmt.Printf(" - reflect.Kind: 表示基础类型种类\\n")
// 基本反射示例
fmt.Printf(" 基本反射示例:\\n")
var x interface{} = 42
// 获取类型信息
t := reflect.TypeOf(x)
fmt.Printf(" 类型: %v\\n", t)
fmt.Printf(" 类型名称: %s\\n", t.Name())
fmt.Printf(" 类型种类: %v\\n", t.Kind())
// 获取值信息
v := reflect.ValueOf(x)
fmt.Printf(" 值: %v\\n", v)
fmt.Printf(" 值的类型: %v\\n", v.Type())
fmt.Printf(" 值的种类: %v\\n", v.Kind())
fmt.Printf(" 值的接口: %v\\n", v.Interface())
// 不同类型的反射
fmt.Printf(" 不同类型的反射:\\n")
values := []interface{}{
42,
"hello",
3.14,
true,
[]int{1, 2, 3},
map[string]int{"a": 1},
Person{Name: "Alice", Age: 30},
}
for i, val := range values {
t := reflect.TypeOf(val)
v := reflect.ValueOf(val)
fmt.Printf(" 值%d: %v (类型: %v, 种类: %v)\\n",
i+1, val, t, v.Kind())
}
fmt.Println()
}
// demonstrateTypeReflection 演示类型反射
func demonstrateTypeReflection() {
fmt.Println("2. 类型反射:")
// 基本类型反射
fmt.Printf(" 基本类型反射:\\n")
var i int = 42
var s string = "hello"
var f float64 = 3.14
var b bool = true
types := []interface{}{i, s, f, b}
for _, val := range types {
t := reflect.TypeOf(val)
fmt.Printf(" %v: 名称=%s, 种类=%v, 大小=%d字节\\n",
val, t.Name(), t.Kind(), t.Size())
}
// 复合类型反射
fmt.Printf(" 复合类型反射:\\n")
// 切片类型
slice := []int{1, 2, 3}
sliceType := reflect.TypeOf(slice)
fmt.Printf(" 切片类型: %v\\n", sliceType)
fmt.Printf(" 元素类型: %v\\n", sliceType.Elem())
fmt.Printf(" 是否为切片: %t\\n", sliceType.Kind() == reflect.Slice)
// 映射类型
m := map[string]int{"a": 1, "b": 2}
mapType := reflect.TypeOf(m)
fmt.Printf(" 映射类型: %v\\n", mapType)
fmt.Printf(" 键类型: %v\\n", mapType.Key())
fmt.Printf(" 值类型: %v\\n", mapType.Elem())
// 指针类型
var p *int = &i
ptrType := reflect.TypeOf(p)
fmt.Printf(" 指针类型: %v\\n", ptrType)
fmt.Printf(" 指向类型: %v\\n", ptrType.Elem())
fmt.Printf(" 是否为指针: %t\\n", ptrType.Kind() == reflect.Ptr)
// 函数类型
fn := func(int, string) bool { return true }
fnType := reflect.TypeOf(fn)
fmt.Printf(" 函数类型: %v\\n", fnType)
fmt.Printf(" 参数个数: %d\\n", fnType.NumIn())
fmt.Printf(" 返回值个数: %d\\n", fnType.NumOut())
for i := 0; i < fnType.NumIn(); i++ {
fmt.Printf(" 参数%d类型: %v\\n", i, fnType.In(i))
}
for i := 0; i < fnType.NumOut(); i++ {
fmt.Printf(" 返回值%d类型: %v\\n", i, fnType.Out(i))
}
// 通道类型
ch := make(chan int)
chType := reflect.TypeOf(ch)
fmt.Printf(" 通道类型: %v\\n", chType)
fmt.Printf(" 通道方向: %v\\n", chType.ChanDir())
fmt.Printf(" 元素类型: %v\\n", chType.Elem())
fmt.Println()
}
// demonstrateValueReflection 演示值反射
func demonstrateValueReflection() {
fmt.Println("3. 值反射:")
// 基本值操作
fmt.Printf(" 基本值操作:\\n")
var x interface{} = 42
v := reflect.ValueOf(x)
fmt.Printf(" 原始值: %v\\n", v.Interface())
fmt.Printf(" 整数值: %d\\n", v.Int())
fmt.Printf(" 是否有效: %t\\n", v.IsValid())
fmt.Printf(" 是否为零值: %t\\n", v.IsZero())
// 值的修改
fmt.Printf(" 值的修改:\\n")
var y int = 100
v = reflect.ValueOf(&y) // 需要传递指针才能修改
if v.Kind() == reflect.Ptr && v.Elem().CanSet() {
v.Elem().SetInt(200)
fmt.Printf(" 修改后的值: %d\\n", y)
}
// 字符串值操作
var str string = "hello"
v = reflect.ValueOf(&str)
if v.Kind() == reflect.Ptr && v.Elem().CanSet() {
v.Elem().SetString("world")
fmt.Printf(" 修改后的字符串: %s\\n", str)
}
// 切片值操作
fmt.Printf(" 切片值操作:\\n")
slice := []int{1, 2, 3}
v = reflect.ValueOf(slice)
fmt.Printf(" 切片长度: %d\\n", v.Len())
fmt.Printf(" 切片容量: %d\\n", v.Cap())
// 访问切片元素
for i := 0; i < v.Len(); i++ {
elem := v.Index(i)
fmt.Printf(" 元素[%d]: %v\\n", i, elem.Interface())
}
// 映射值操作
fmt.Printf(" 映射值操作:\\n")
m := map[string]int{"a": 1, "b": 2, "c": 3}
v = reflect.ValueOf(m)
fmt.Printf(" 映射长度: %d\\n", v.Len())
// 遍历映射
for _, key := range v.MapKeys() {
value := v.MapIndex(key)
fmt.Printf(" %v: %v\\n", key.Interface(), value.Interface())
}
// 设置映射值
v.SetMapIndex(reflect.ValueOf("d"), reflect.ValueOf(4))
fmt.Printf(" 添加元素后: %v\\n", m)
// 创建新值
fmt.Printf(" 创建新值:\\n")
// 创建新的整数值
newInt := reflect.New(reflect.TypeOf(0))
newInt.Elem().SetInt(999)
fmt.Printf(" 新创建的整数: %v\\n", newInt.Elem().Interface())
// 创建新的切片
sliceType := reflect.SliceOf(reflect.TypeOf(0))
newSlice := reflect.MakeSlice(sliceType, 3, 5)
for i := 0; i < newSlice.Len(); i++ {
newSlice.Index(i).SetInt(int64(i * 10))
}
fmt.Printf(" 新创建的切片: %v\\n", newSlice.Interface())
fmt.Println()
}
// demonstrateStructReflection 演示结构体反射
func demonstrateStructReflection() {
fmt.Println("4. 结构体反射:")
// 结构体类型信息
fmt.Printf(" 结构体类型信息:\\n")
person := Person{
Name: "Alice",
Age: 30,
Email: "alice@example.com",
}
t := reflect.TypeOf(person)
v := reflect.ValueOf(person)
fmt.Printf(" 结构体名称: %s\\n", t.Name())
fmt.Printf(" 字段数量: %d\\n", t.NumField())
// 遍历结构体字段
fmt.Printf(" 遍历结构体字段:\\n")
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
fmt.Printf(" 字段%d: %s (类型: %v, 值: %v)\\n",
i, field.Name, field.Type, value.Interface())
// 检查字段标签
if tag := field.Tag; tag != "" {
fmt.Printf(" 标签: %s\\n", tag)
if jsonTag := tag.Get("json"); jsonTag != "" {
fmt.Printf(" JSON标签: %s\\n", jsonTag)
}
}
}
// 按名称访问字段
fmt.Printf(" 按名称访问字段:\\n")
nameField := v.FieldByName("Name")
if nameField.IsValid() {
fmt.Printf(" Name字段值: %v\\n", nameField.Interface())
}
// 修改结构体字段
fmt.Printf(" 修改结构体字段:\\n")
// 需要使用指针才能修改
v = reflect.ValueOf(&person)
if v.Kind() == reflect.Ptr {
v = v.Elem() // 获取指针指向的值
nameField = v.FieldByName("Name")
if nameField.CanSet() {
nameField.SetString("Bob")
fmt.Printf(" 修改后的Name: %s\\n", person.Name)
}
ageField := v.FieldByName("Age")
if ageField.CanSet() {
ageField.SetInt(35)
fmt.Printf(" 修改后的Age: %d\\n", person.Age)
}
}
// 嵌套结构体
fmt.Printf(" 嵌套结构体:\\n")
employee := Employee{
Person: Person{Name: "Charlie", Age: 28},
Title: "Developer",
Salary: 80000,
}
t = reflect.TypeOf(employee)
v = reflect.ValueOf(employee)
fmt.Printf(" 嵌套结构体字段数: %d\\n", t.NumField())
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
fmt.Printf(" 字段%d: %s (类型: %v)\\n", i, field.Name, field.Type)
// 如果是嵌套结构体,进一步展开
if field.Type.Kind() == reflect.Struct {
fmt.Printf(" 嵌套字段:\\n")
nestedValue := value
nestedType := field.Type
for j := 0; j < nestedType.NumField(); j++ {
nestedField := nestedType.Field(j)
nestedFieldValue := nestedValue.Field(j)
fmt.Printf(" %s: %v\\n", nestedField.Name, nestedFieldValue.Interface())
}
} else {
fmt.Printf(" 值: %v\\n", value.Interface())
}
}
fmt.Println()
}
// demonstrateMethodReflection 演示方法反射
func demonstrateMethodReflection() {
fmt.Println("5. 方法反射:")
// 方法信息
fmt.Printf(" 方法信息:\\n")
person := &Person{Name: "David", Age: 25}
t := reflect.TypeOf(person)
v := reflect.ValueOf(person)
fmt.Printf(" 方法数量: %d\\n", t.NumMethod())
// 遍历方法
for i := 0; i < t.NumMethod(); i++ {
method := t.Method(i)
fmt.Printf(" 方法%d: %s (类型: %v)\\n", i, method.Name, method.Type)
}
// 调用方法
fmt.Printf(" 调用方法:\\n")
// 按名称获取方法
greetMethod := v.MethodByName("Greet")
if greetMethod.IsValid() {
// 调用无参数方法
result := greetMethod.Call(nil)
if len(result) > 0 {
fmt.Printf(" Greet方法返回: %v\\n", result[0].Interface())
}
}
// 调用带参数的方法
setAgeMethod := v.MethodByName("SetAge")
if setAgeMethod.IsValid() {
args := []reflect.Value{reflect.ValueOf(30)}
setAgeMethod.Call(args)
fmt.Printf(" 调用SetAge后年龄: %d\\n", person.Age)
}
// 调用带返回值的方法
getInfoMethod := v.MethodByName("GetInfo")
if getInfoMethod.IsValid() {
result := getInfoMethod.Call(nil)
if len(result) > 0 {
fmt.Printf(" GetInfo方法返回: %v\\n", result[0].Interface())
}
}
// 函数值的反射调用
fmt.Printf(" 函数值的反射调用:\\n")
fn := func(a, b int) int {
return a + b
}
fnValue := reflect.ValueOf(fn)
args := []reflect.Value{
reflect.ValueOf(10),
reflect.ValueOf(20),
}
result := fnValue.Call(args)
fmt.Printf(" 函数调用结果: %v\\n", result[0].Interface())
// 方法集的检查
fmt.Printf(" 方法集的检查:\\n")
// 检查类型是否实现了某个接口
stringerType := reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
personType := reflect.TypeOf(person)
fmt.Printf(" Person是否实现Stringer接口: %t\\n",
personType.Implements(stringerType))
fmt.Println()
}
// demonstratePracticalApplications 演示反射的实际应用
func demonstratePracticalApplications() {
fmt.Println("6. 反射的实际应用:")
// 应用1: JSON 序列化
fmt.Printf(" 应用1 - JSON 序列化:\\n")
person := Person{
Name: "Eve",
Age: 32,
Email: "eve@example.com",
}
jsonStr := structToJSON(person)
fmt.Printf(" JSON序列化结果: %s\\n", jsonStr)
// 应用2: 结构体复制
fmt.Printf(" 应用2 - 结构体复制:\\n")
original := Person{Name: "Frank", Age: 40}
copied := copyStruct(original).(Person)
fmt.Printf(" 原始结构体: %+v\\n", original)
fmt.Printf(" 复制结构体: %+v\\n", copied)
// 应用3: 结构体比较
fmt.Printf(" 应用3 - 结构体比较:\\n")
person1 := Person{Name: "Grace", Age: 28}
person2 := Person{Name: "Grace", Age: 28}
person3 := Person{Name: "Henry", Age: 30}
fmt.Printf(" person1 == person2: %t\\n", deepEqual(person1, person2))
fmt.Printf(" person1 == person3: %t\\n", deepEqual(person1, person3))
// 应用4: 配置映射
fmt.Printf(" 应用4 - 配置映射:\\n")
config := map[string]interface{}{
"Name": "Iris",
"Age": 26,
"Email": "iris@example.com",
}
var configPerson Person
mapToStruct(config, &configPerson)
fmt.Printf(" 配置映射结果: %+v\\n", configPerson)
// 应用5: 验证器
fmt.Printf(" 应用5 - 验证器:\\n")
validPerson := Person{Name: "Jack", Age: 25, Email: "jack@example.com"}
invalidPerson := Person{Name: "", Age: -5, Email: "invalid"}
fmt.Printf(" 有效Person验证: %t\\n", validateStruct(validPerson))
fmt.Printf(" 无效Person验证: %t\\n", validateStruct(invalidPerson))
// 应用6: ORM 映射
fmt.Printf(" 应用6 - ORM 映射:\\n")
tableName := getTableName(Person{})
columns := getColumns(Person{})
fmt.Printf(" 表名: %s\\n", tableName)
fmt.Printf(" 列名: %v\\n", columns)
fmt.Println()
}
// demonstrateBestPractices 演示反射的最佳实践
func demonstrateBestPractices() {
fmt.Println("7. 反射的最佳实践:")
// 最佳实践原则
fmt.Printf(" 最佳实践原则:\\n")
fmt.Printf(" 1. 谨慎使用反射,优先考虑类型安全的方案\\n")
fmt.Printf(" 2. 缓存反射结果以提高性能\\n")
fmt.Printf(" 3. 进行充分的错误检查\\n")
fmt.Printf(" 4. 避免在热点路径使用反射\\n")
fmt.Printf(" 5. 使用接口而不是反射来实现多态\\n")
// 性能考虑
fmt.Printf(" 性能考虑:\\n")
// 性能测试示例
person := Person{Name: "Test", Age: 30}
// 直接访问 vs 反射访问
start := time.Now()
for i := 0; i < 100000; i++ {
_ = person.Name // 直接访问
}
directTime := time.Since(start)
start = time.Now()
v := reflect.ValueOf(person)
nameField := v.FieldByName("Name")
for i := 0; i < 100000; i++ {
_ = nameField.Interface() // 反射访问
}
reflectTime := time.Since(start)
fmt.Printf(" 直接访问耗时: %v\\n", directTime)
fmt.Printf(" 反射访问耗时: %v\\n", reflectTime)
fmt.Printf(" 性能差异: %.2fx\\n", float64(reflectTime)/float64(directTime))
// 错误处理
fmt.Printf(" 错误处理:\\n")
// 安全的反射操作
safeReflectOperation := func(obj interface{}, fieldName string) (interface{}, error) {
v := reflect.ValueOf(obj)
if v.Kind() != reflect.Struct {
return nil, fmt.Errorf("对象不是结构体")
}
field := v.FieldByName(fieldName)
if !field.IsValid() {
return nil, fmt.Errorf("字段 %s 不存在", fieldName)
}
return field.Interface(), nil
}
// 测试安全操作
result, err := safeReflectOperation(person, "Name")
if err != nil {
fmt.Printf(" 错误: %v\\n", err)
} else {
fmt.Printf(" 安全获取Name字段: %v\\n", result)
}
result, err = safeReflectOperation(person, "NonExistent")
if err != nil {
fmt.Printf(" 预期错误: %v\\n", err)
}
// 反射的替代方案
fmt.Printf(" 反射的替代方案:\\n")
fmt.Printf(" 1. 接口: 使用接口实现多态\\n")
fmt.Printf(" 2. 类型断言: 处理已知的有限类型集合\\n")
fmt.Printf(" 3. 代码生成: 编译时生成类型安全的代码\\n")
fmt.Printf(" 4. 泛型: Go 1.18+ 支持泛型编程\\n")
// 何时使用反射
fmt.Printf(" 何时使用反射:\\n")
fmt.Printf(" ✓ 序列化/反序列化库\\n")
fmt.Printf(" ✓ ORM 框架\\n")
fmt.Printf(" ✓ 配置映射\\n")
fmt.Printf(" ✓ 测试框架\\n")
fmt.Printf(" ✓ 依赖注入容器\\n")
fmt.Printf("\\n")
fmt.Printf(" ✗ 简单的类型转换\\n")
fmt.Printf(" ✗ 已知类型的操作\\n")
fmt.Printf(" ✗ 性能敏感的代码\\n")
fmt.Printf(" ✗ 可以用接口解决的问题\\n")
fmt.Println()
}
// ========== 类型定义 ==========
// Person 人员结构体
type Person struct {
Name string `json:"name" db:"name" validate:"required"`
Age int `json:"age" db:"age" validate:"min=0,max=150"`
Email string `json:"email" db:"email" validate:"email"`
}
// Greet 问候方法
func (p *Person) Greet() string {
return fmt.Sprintf("Hello, I'm %s", p.Name)
}
// SetAge 设置年龄
func (p *Person) SetAge(age int) {
p.Age = age
}
// GetInfo 获取信息
func (p *Person) GetInfo() string {
return fmt.Sprintf("Name: %s, Age: %d, Email: %s", p.Name, p.Age, p.Email)
}
// String 实现 Stringer 接口
func (p Person) String() string {
return fmt.Sprintf("Person{Name: %s, Age: %d}", p.Name, p.Age)
}
// Employee 员工结构体
type Employee struct {
Person
Title string `json:"title" db:"title"`
Salary float64 `json:"salary" db:"salary"`
}
// ========== 辅助函数 ==========
// structToJSON 将结构体转换为 JSON 字符串
func structToJSON(obj interface{}) string {
v := reflect.ValueOf(obj)
t := reflect.TypeOf(obj)
if v.Kind() != reflect.Struct {
return "{}"
}
var parts []string
for i := 0; i < v.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
// 获取 JSON 标签
jsonTag := field.Tag.Get("json")
if jsonTag == "" {
jsonTag = strings.ToLower(field.Name)
}
// 根据类型格式化值
var valueStr string
switch value.Kind() {
case reflect.String:
valueStr = fmt.Sprintf("\\\"%s\\\"", value.String())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
valueStr = fmt.Sprintf("%d", value.Int())
case reflect.Float32, reflect.Float64:
valueStr = fmt.Sprintf("%.2f", value.Float())
case reflect.Bool:
valueStr = fmt.Sprintf("%t", value.Bool())
default:
valueStr = fmt.Sprintf("\\\"%v\\\"", value.Interface())
}
parts = append(parts, fmt.Sprintf("\\\"%s\\\": %s", jsonTag, valueStr))
}
return "{" + strings.Join(parts, ", ") + "}"
}
// copyStruct 复制结构体
func copyStruct(src interface{}) interface{} {
srcValue := reflect.ValueOf(src)
srcType := reflect.TypeOf(src)
if srcValue.Kind() != reflect.Struct {
return src
}
// 创建新的结构体实例
newValue := reflect.New(srcType).Elem()
// 复制所有字段
for i := 0; i < srcValue.NumField(); i++ {
srcField := srcValue.Field(i)
newField := newValue.Field(i)
if newField.CanSet() {
newField.Set(srcField)
}
}
return newValue.Interface()
}
// deepEqual 深度比较两个值
func deepEqual(a, b interface{}) bool {
va := reflect.ValueOf(a)
vb := reflect.ValueOf(b)
if va.Type() != vb.Type() {
return false
}
switch va.Kind() {
case reflect.Struct:
for i := 0; i < va.NumField(); i++ {
if !deepEqual(va.Field(i).Interface(), vb.Field(i).Interface()) {
return false
}
}
return true
case reflect.Slice:
if va.Len() != vb.Len() {
return false
}
for i := 0; i < va.Len(); i++ {
if !deepEqual(va.Index(i).Interface(), vb.Index(i).Interface()) {
return false
}
}
return true
case reflect.Map:
if va.Len() != vb.Len() {
return false
}
for _, key := range va.MapKeys() {
aVal := va.MapIndex(key)
bVal := vb.MapIndex(key)
if !bVal.IsValid() || !deepEqual(aVal.Interface(), bVal.Interface()) {
return false
}
}
return true
default:
return va.Interface() == vb.Interface()
}
}
// mapToStruct 将 map 映射到结构体
func mapToStruct(m map[string]interface{}, dst interface{}) error {
dstValue := reflect.ValueOf(dst)
if dstValue.Kind() != reflect.Ptr || dstValue.Elem().Kind() != reflect.Struct {
return fmt.Errorf("dst 必须是结构体指针")
}
dstValue = dstValue.Elem()
dstType := dstValue.Type()
for i := 0; i < dstValue.NumField(); i++ {
field := dstType.Field(i)
fieldValue := dstValue.Field(i)
if !fieldValue.CanSet() {
continue
}
// 从 map 中获取对应的值
if mapValue, ok := m[field.Name]; ok {
mapValueReflect := reflect.ValueOf(mapValue)
// 类型转换
if mapValueReflect.Type().ConvertibleTo(fieldValue.Type()) {
fieldValue.Set(mapValueReflect.Convert(fieldValue.Type()))
}
}
}
return nil
}
// validateStruct 验证结构体
func validateStruct(obj interface{}) bool {
v := reflect.ValueOf(obj)
t := reflect.TypeOf(obj)
if v.Kind() != reflect.Struct {
return false
}
for i := 0; i < v.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
// 获取验证标签
validateTag := field.Tag.Get("validate")
if validateTag == "" {
continue
}
// 解析验证规则
rules := strings.Split(validateTag, ",")
for _, rule := range rules {
if !validateField(value, strings.TrimSpace(rule)) {
return false
}
}
}
return true
}
// validateField 验证单个字段
func validateField(value reflect.Value, rule string) bool {
switch rule {
case "required":
return !value.IsZero()
case "email":
if value.Kind() == reflect.String {
email := value.String()
return strings.Contains(email, "@") && strings.Contains(email, ".")
}
return false
default:
// 处理 min=0, max=150 等规则
if strings.HasPrefix(rule, "min=") {
minStr := strings.TrimPrefix(rule, "min=")
if min, err := strconv.Atoi(minStr); err == nil {
if value.Kind() == reflect.Int {
return value.Int() >= int64(min)
}
}
}
if strings.HasPrefix(rule, "max=") {
maxStr := strings.TrimPrefix(rule, "max=")
if max, err := strconv.Atoi(maxStr); err == nil {
if value.Kind() == reflect.Int {
return value.Int() <= int64(max)
}
}
}
}
return true
}
// getTableName 获取表名
func getTableName(obj interface{}) string {
t := reflect.TypeOf(obj)
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
// 将结构体名转换为表名(简单的复数形式)
name := t.Name()
return strings.ToLower(name) + "s"
}
// getColumns 获取列名
func getColumns(obj interface{}) []string {
t := reflect.TypeOf(obj)
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
var columns []string
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
// 获取数据库标签
dbTag := field.Tag.Get("db")
if dbTag != "" {
columns = append(columns, dbTag)
} else {
columns = append(columns, strings.ToLower(field.Name))
}
}
return columns
}
/*
运行这个程序:
go run 01-reflection.go
学习要点:
1. 反射是程序在运行时检查自身结构的能力
2. Go 通过 reflect 包提供反射功能
3. 反射主要包括类型反射和值反射
4. 反射可以用于序列化、ORM、配置映射等场景
5. 反射有性能开销,应该谨慎使用
反射的核心概念:
1. reflect.Type: 表示类型信息
2. reflect.Value: 表示值信息
3. reflect.Kind: 表示基础类型种类
4. 反射遵循 Go 的类型系统规则
5. 反射可以检查和修改值
类型反射:
1. 获取类型信息reflect.TypeOf()
2. 类型名称和种类Name(), Kind()
3. 复合类型Elem(), Key(), In(), Out()
4. 结构体字段NumField(), Field()
5. 方法信息NumMethod(), Method()
值反射:
1. 获取值信息reflect.ValueOf()
2. 值的访问Interface(), Int(), String()
3. 值的修改Set(), SetInt(), SetString()
4. 集合操作Len(), Index(), MapIndex()
5. 创建新值New(), MakeSlice(), MakeMap()
结构体反射:
1. 字段访问Field(), FieldByName()
2. 字段信息Name, Type, Tag
3. 字段修改CanSet(), Set()
4. 标签解析Tag.Get()
5. 嵌套结构体处理
方法反射:
1. 方法信息Method(), MethodByName()
2. 方法调用Call()
3. 方法类型Type, NumIn(), NumOut()
4. 接口检查Implements()
5. 方法集分析
实际应用场景:
1. JSON 序列化/反序列化
2. ORM 数据库映射
3. 配置文件映射
4. 结构体验证
5. 依赖注入
6. 测试框架
7. 代码生成工具
性能考虑:
1. 反射比直接访问慢很多
2. 缓存反射结果可以提高性能
3. 避免在热点路径使用反射
4. 考虑使用接口替代反射
5. 编译时代码生成是更好的选择
最佳实践:
1. 谨慎使用反射,优先考虑类型安全
2. 进行充分的错误检查
3. 缓存反射操作的结果
4. 使用接口实现多态
5. 考虑性能影响
6. 提供清晰的文档说明
注意事项:
1. 反射会破坏类型安全
2. 反射代码难以理解和维护
3. 反射错误只能在运行时发现
4. 反射会影响代码的可读性
5. 反射操作可能导致 panic
*/

View File

@@ -0,0 +1,561 @@
/*
02-generics.go - Go 语言泛型详解
学习目标:
1. 理解泛型的概念和优势
2. 掌握类型参数的定义和使用
3. 学会类型约束的应用
4. 了解泛型函数和泛型类型
5. 掌握泛型的最佳实践
知识点:
- 泛型的基本概念
- 类型参数和类型约束
- 泛型函数和泛型类型
- 内置约束和自定义约束
- 类型推断
- 泛型的实际应用
注意:此示例需要 Go 1.18 或更高版本
*/
package main
import (
"fmt"
"sort"
"strconv"
"strings"
)
func main() {
fmt.Println("=== Go 语言泛型详解 ===\\n")
// 演示泛型的基本概念
demonstrateGenericsBasics()
// 演示泛型函数
demonstrateGenericFunctions()
// 演示泛型类型
demonstrateGenericTypes()
// 演示类型约束
demonstrateTypeConstraints()
// 演示内置约束
demonstrateBuiltinConstraints()
// 演示泛型的实际应用
demonstratePracticalApplications()
// 演示泛型的最佳实践
demonstrateBestPractices()
}
// demonstrateGenericsBasics 演示泛型的基本概念
func demonstrateGenericsBasics() {
fmt.Println("1. 泛型的基本概念:")
// 泛型的基本概念
fmt.Printf(" 泛型的基本概念:\\n")
fmt.Printf(" - 泛型允许编写可重用的代码\\n")
fmt.Printf(" - 类型参数在编译时确定具体类型\\n")
fmt.Printf(" - 提供类型安全的抽象\\n")
fmt.Printf(" - Go 1.18+ 支持泛型\\n")
fmt.Printf(" - 减少代码重复和类型断言\\n")
// 泛型语法
fmt.Printf(" 泛型语法:\\n")
fmt.Printf(" 函数泛型: func Name[T any](param T) T\\n")
fmt.Printf(" 类型泛型: type Name[T any] struct { field T }\\n")
fmt.Printf(" 约束语法: func Name[T Constraint](param T) T\\n")
// 简单泛型函数示例
fmt.Printf(" 简单泛型函数示例:\\n")
// 使用泛型函数
fmt.Printf(" 整数最大值: %d\\n", Max(10, 20))
fmt.Printf(" 浮点数最大值: %.2f\\n", Max(3.14, 2.71))
fmt.Printf(" 字符串最大值: %s\\n", Max("apple", "banana"))
// 类型推断
fmt.Printf(" 类型推断:\\n")
fmt.Printf(" Go 编译器可以自动推断类型参数\\n")
// 显式指定类型参数
result1 := Max[int](5, 8)
fmt.Printf(" 显式指定类型: %d\\n", result1)
// 自动类型推断
result2 := Max(5, 8)
fmt.Printf(" 自动类型推断: %d\\n", result2)
// 泛型的优势
fmt.Printf(" 泛型的优势:\\n")
fmt.Printf(" 1. 类型安全:编译时检查类型\\n")
fmt.Printf(" 2. 代码复用:一套代码支持多种类型\\n")
fmt.Printf(" 3. 性能优化:避免运行时类型断言\\n")
fmt.Printf(" 4. 可读性:减少重复代码\\n")
fmt.Printf(" 5. 维护性:统一的逻辑实现\\n")
fmt.Println()
}
// demonstrateGenericFunctions 演示泛型函数
func demonstrateGenericFunctions() {
fmt.Println("2. 泛型函数:")
// 基本泛型函数
fmt.Printf(" 基本泛型函数:\\n")
// 交换函数
a, b := 10, 20
fmt.Printf(" 交换前: a=%d, b=%d\\n", a, b)
a, b = Swap(a, b)
fmt.Printf(" 交换后: a=%d, b=%d\\n", a, b)
str1, str2 := "hello", "world"
fmt.Printf(" 交换前: str1=%s, str2=%s\\n", str1, str2)
str1, str2 = Swap(str1, str2)
fmt.Printf(" 交换后: str1=%s, str2=%s\\n", str1, str2)
// 查找函数
fmt.Printf(" 查找函数:\\n")
numbers := []int{1, 2, 3, 4, 5}
index := Find(numbers, 3)
fmt.Printf(" 在 %v 中查找 3: 索引 %d\\n", numbers, index)
words := []string{"apple", "banana", "cherry"}
index = Find(words, "banana")
fmt.Printf(" 在 %v 中查找 banana: 索引 %d\\n", words, index)
// 映射函数
fmt.Printf(" 映射函数:\\n")
// 数字平方
squares := Map([]int{1, 2, 3, 4, 5}, func(x int) int {
return x * x
})
fmt.Printf(" 数字平方: %v\\n", squares)
// 字符串长度
lengths := Map([]string{"go", "rust", "python"}, func(s string) int {
return len(s)
})
fmt.Printf(" 字符串长度: %v\\n", lengths)
// 过滤函数
fmt.Printf(" 过滤函数:\\n")
// 过滤偶数
evens := Filter([]int{1, 2, 3, 4, 5, 6}, func(x int) bool {
return x%2 == 0
})
fmt.Printf(" 偶数: %v\\n", evens)
// 过滤长字符串
longWords := Filter([]string{"go", "rust", "python", "javascript"}, func(s string) bool {
return len(s) > 4
})
fmt.Printf(" 长单词: %v\\n", longWords)
// 归约函数
fmt.Printf(" 归约函数:\\n")
// 求和
sum := Reduce([]int{1, 2, 3, 4, 5}, 0, func(acc, x int) int {
return acc + x
})
fmt.Printf(" 数组求和: %d\\n", sum)
// 字符串连接
concat := Reduce([]string{"Hello", " ", "World", "!"}, "", func(acc, s string) string {
return acc + s
})
fmt.Printf(" 字符串连接: %s\\n", concat)
// 多类型参数函数
fmt.Printf(" 多类型参数函数:\\n")
pairs := Zip([]int{1, 2, 3}, []string{"a", "b", "c"})
fmt.Printf(" 配对结果: %v\\n", pairs)
fmt.Println()
}
// demonstrateGenericTypes 演示泛型类型
func demonstrateGenericTypes() {
fmt.Println("3. 泛型类型:")
// 泛型栈
fmt.Printf(" 泛型栈:\\n")
// 整数栈
intStack := NewStack[int]()
intStack.Push(1)
intStack.Push(2)
intStack.Push(3)
fmt.Printf(" 整数栈大小: %d\\n", intStack.Size())
for !intStack.IsEmpty() {
value, _ := intStack.Pop()
fmt.Printf(" 弹出: %d\\n", value)
}
// 字符串栈
strStack := NewStack[string]()
strStack.Push("first")
strStack.Push("second")
strStack.Push("third")
fmt.Printf(" 字符串栈内容:\\n")
for !strStack.IsEmpty() {
value, _ := strStack.Pop()
fmt.Printf(" %s\\n", value)
}
// 泛型队列
fmt.Printf(" 泛型队列:\\n")
queue := NewQueue[string]()
queue.Enqueue("first")
queue.Enqueue("second")
queue.Enqueue("third")
fmt.Printf(" 队列大小: %d\\n", queue.Size())
for !queue.IsEmpty() {
value, _ := queue.Dequeue()
fmt.Printf(" 出队: %s\\n", value)
}
// 泛型映射
fmt.Printf(" 泛型映射:\\n")
cache := NewCache[string, int]()
cache.Set("apple", 5)
cache.Set("banana", 3)
cache.Set("cherry", 8)
if value, ok := cache.Get("apple"); ok {
fmt.Printf(" apple 的值: %d\\n", value)
}
fmt.Printf(" 缓存大小: %d\\n", cache.Size())
fmt.Printf(" 所有键: %v\\n", cache.Keys())
// 泛型链表
fmt.Printf(" 泛型链表:\\n")
list := NewLinkedList[int]()
list.Add(1)
list.Add(2)
list.Add(3)
fmt.Printf(" 链表大小: %d\\n", list.Size())
fmt.Printf(" 链表内容: %v\\n", list.ToSlice())
if list.Contains(2) {
fmt.Printf(" 链表包含 2\\n")
}
list.Remove(2)
fmt.Printf(" 删除 2 后: %v\\n", list.ToSlice())
fmt.Println()
}
// demonstrateTypeConstraints 演示类型约束
func demonstrateTypeConstraints() {
fmt.Println("4. 类型约束:")
// 基本约束
fmt.Printf(" 基本约束:\\n")
fmt.Printf(" any: 任意类型\\n")
fmt.Printf(" comparable: 可比较类型\\n")
// 自定义约束
fmt.Printf(" 自定义约束:\\n")
// 数值类型约束
fmt.Printf(" 数值类型约束:\\n")
fmt.Printf(" 整数求和: %d\\n", Sum([]int{1, 2, 3, 4, 5}))
fmt.Printf(" 浮点数求和: %.2f\\n", Sum([]float64{1.1, 2.2, 3.3}))
// 有序类型约束
fmt.Printf(" 有序类型约束:\\n")
intSlice := []int{3, 1, 4, 1, 5, 9}
fmt.Printf(" 排序前: %v\\n", intSlice)
SortSlice(intSlice)
fmt.Printf(" 排序后: %v\\n", intSlice)
strSlice := []string{"banana", "apple", "cherry"}
fmt.Printf(" 排序前: %v\\n", strSlice)
SortSlice(strSlice)
fmt.Printf(" 排序后: %v\\n", strSlice)
// 字符串化约束
fmt.Printf(" 字符串化约束:\\n")
fmt.Printf(" 整数转字符串: %s\\n", ToString(42))
fmt.Printf(" 浮点数转字符串: %s\\n", ToString(3.14))
fmt.Printf(" 布尔值转字符串: %s\\n", ToString(true))
// 接口约束
fmt.Printf(" 接口约束:\\n")
shapes := []Shape{
Rectangle{Width: 5, Height: 3},
Circle{Radius: 4},
}
totalArea := CalculateTotalArea(shapes)
fmt.Printf(" 总面积: %.2f\\n", totalArea)
// 类型集合约束
fmt.Printf(" 类型集合约束:\\n")
fmt.Printf(" 整数绝对值: %d\\n", Abs(-42))
fmt.Printf(" 浮点数绝对值: %.2f\\n", Abs(-3.14))
fmt.Println()
}
// demonstrateBuiltinConstraints 演示内置约束
func demonstrateBuiltinConstraints() {
fmt.Println("5. 内置约束:")
// comparable 约束
fmt.Printf(" comparable 约束:\\n")
// 可比较类型的相等检查
fmt.Printf(" 整数相等: %t\\n", Equal(5, 5))
fmt.Printf(" 字符串相等: %t\\n", Equal("hello", "hello"))
fmt.Printf(" 布尔值相等: %t\\n", Equal(true, false))
// 可比较类型的去重
duplicateInts := []int{1, 2, 2, 3, 3, 3, 4}
uniqueInts := Unique(duplicateInts)
fmt.Printf(" 整数去重: %v -> %v\\n", duplicateInts, uniqueInts)
duplicateStrs := []string{"a", "b", "b", "c", "c", "c"}
uniqueStrs := Unique(duplicateStrs)
fmt.Printf(" 字符串去重: %v -> %v\\n", duplicateStrs, uniqueStrs)
// any 约束
fmt.Printf(" any 约束:\\n")
// 任意类型的容器
container := NewContainer[any]()
container.Add(42)
container.Add("hello")
container.Add(3.14)
container.Add(true)
fmt.Printf(" 容器大小: %d\\n", container.Size())
fmt.Printf(" 容器内容:\\n")
for i := 0; i < container.Size(); i++ {
item := container.Get(i)
fmt.Printf(" [%d]: %v (%T)\\n", i, item, item)
}
// 类型断言与泛型
fmt.Printf(" 类型断言与泛型:\\n")
values := []any{42, "hello", 3.14, true}
// 提取特定类型
strings := ExtractType[string](values)
fmt.Printf(" 提取字符串: %v\\n", strings)
numbers := ExtractType[int](values)
fmt.Printf(" 提取整数: %v\\n", numbers)
fmt.Println()
}
// demonstratePracticalApplications 演示泛型的实际应用
func demonstratePracticalApplications() {
fmt.Println("6. 泛型的实际应用:")
// 应用1: 泛型数据结构
fmt.Printf(" 应用1 - 泛型数据结构:\\n")
// 优先队列
pq := NewPriorityQueue[int]()
pq.Push(3, 3)
pq.Push(1, 1)
pq.Push(4, 4)
pq.Push(2, 2)
fmt.Printf(" 优先队列出队顺序:\\n")
for !pq.IsEmpty() {
item, priority := pq.Pop()
fmt.Printf(" 项目: %v, 优先级: %d\\n", item, priority)
}
// 应用2: 泛型算法
fmt.Printf(" 应用2 - 泛型算法:\\n")
// 二分查找
sortedInts := []int{1, 3, 5, 7, 9, 11, 13}
index := BinarySearch(sortedInts, 7)
fmt.Printf(" 二分查找 7 在 %v 中的位置: %d\\n", sortedInts, index)
sortedStrs := []string{"apple", "banana", "cherry", "date"}
index = BinarySearch(sortedStrs, "cherry")
fmt.Printf(" 二分查找 cherry 在 %v 中的位置: %d\\n", sortedStrs, index)
// 应用3: 泛型工具函数
fmt.Printf(" 应用3 - 泛型工具函数:\\n")
// 切片操作
original := []int{1, 2, 3, 4, 5}
// 反转
reversed := Reverse(original)
fmt.Printf(" 反转: %v -> %v\\n", original, reversed)
// 分块
chunks := Chunk(original, 2)
fmt.Printf(" 分块(大小2): %v -> %v\\n", original, chunks)
// 去重并排序
unsorted := []int{3, 1, 4, 1, 5, 9, 2, 6, 5}
uniqueSorted := UniqueAndSort(unsorted)
fmt.Printf(" 去重排序: %v -> %v\\n", unsorted, uniqueSorted)
// 应用4: 泛型缓存
fmt.Printf(" 应用4 - 泛型缓存:\\n")
lruCache := NewLRUCache[string, string](3)
lruCache.Put("a", "apple")
lruCache.Put("b", "banana")
lruCache.Put("c", "cherry")
fmt.Printf(" 缓存状态: %v\\n", lruCache.Keys())
// 访问会更新顺序
if value, ok := lruCache.Get("a"); ok {
fmt.Printf(" 获取 a: %s\\n", value)
}
// 添加新项会淘汰最久未使用的
lruCache.Put("d", "date")
fmt.Printf(" 添加 d 后: %v\\n", lruCache.Keys())
// 应用5: 泛型验证器
fmt.Printf(" 应用5 - 泛型验证器:\\n")
validator := NewValidator[User]()
// 添加验证规则
validator.AddRule(\"name\", func(u User) bool {
return len(u.Name) > 0
}, \"姓名不能为空\")
validator.AddRule(\"age\", func(u User) bool {
return u.Age >= 0 && u.Age <= 150
}, \"年龄必须在0-150之间\")
validator.AddRule(\"email\", func(u User) bool {
return strings.Contains(u.Email, \"@\")
}, \"邮箱格式不正确\")
// 验证用户
validUser := User{Name: \"Alice\", Age: 25, Email: \"alice@example.com\"}
invalidUser := User{Name: \"\", Age: -5, Email: \"invalid\"}
if errors := validator.Validate(validUser); len(errors) == 0 {
fmt.Printf(" 有效用户验证通过\\n")
} else {
fmt.Printf(" 有效用户验证失败: %v\\n", errors)
}
if errors := validator.Validate(invalidUser); len(errors) > 0 {
fmt.Printf(" 无效用户验证失败: %v\\n", errors)
}
fmt.Println()
}
// demonstrateBestPractices 演示泛型的最佳实践
func demonstrateBestPractices() {
fmt.Println("7. 泛型的最佳实践:")
// 最佳实践原则
fmt.Printf(" 最佳实践原则:\\n")
fmt.Printf(" 1. 优先使用具体类型必要时才使用泛型\\n")
fmt.Printf(" 2. 使用有意义的类型参数名称\\n")
fmt.Printf(" 3. 合理使用类型约束\\n")
fmt.Printf(" 4. 避免过度泛型化\\n")
fmt.Printf(" 5. 考虑编译时间和代码复杂度\\n")
// 命名约定
fmt.Printf(" 命名约定:\\n")
fmt.Printf(" - T: 通用类型参数\\n")
fmt.Printf(" - K, V: 键值对类型\\n")
fmt.Printf(" - E: 元素类型\\n")
fmt.Printf(" - R: 结果类型\\n")
fmt.Printf(" - 使用描述性名称: TKey, TValue, TElement\\n")
// 约束设计
fmt.Printf(" 约束设计:\\n")
fmt.Printf(" - 使用最小必要约束\\n")
fmt.Printf(" - 优先使用内置约束\\n")
fmt.Printf(" - 自定义约束要有明确语义\\n")
fmt.Printf(" - 避免过于复杂的约束\\n")
// 性能考虑
fmt.Printf(" 性能考虑:\\n")
// 泛型 vs 接口性能对比
fmt.Printf(" 泛型 vs 接口性能对比:\\n")
// 泛型版本
start := time.Now()
genericSum := 0
for i := 0; i < 1000000; i++ {
genericSum = Add(genericSum, 1)
}
genericTime := time.Since(start)
// 接口版本
start = time.Now()
var interfaceSum Addable = IntValue(0)
for i := 0; i < 1000000; i++ {
interfaceSum = interfaceSum.Add(IntValue(1))
}
interfaceTime := time.Since(start)
fmt.Printf(" 泛型版本耗时: %v\\n", genericTime)
fmt.Printf(" 接口版本耗时: %v\\n", interfaceTime)
fmt.Printf(" 性能提升: %.2fx\\n", float64(interfaceTime)/float64(genericTime))
// 何时使用泛型
fmt.Printf(" 何时使用泛型:\\n")
fmt.Printf(" 数据结构实现\\n")
fmt.Printf(" 算法函数\\n")
fmt.Printf(" 工具函数\\n")
fmt.Printf(" 类型安全的容器\\n")
fmt.Printf(" 减少代码重复\\n")
fmt.Printf("\\n")
fmt.Printf(" 简单的业务逻辑\\n")
fmt.Printf(" 只有一种类型的场景\\n")
fmt.Printf(" 过度抽象的设计\\n")
fmt.Printf(" 性能敏感且类型固定的代码\\n")
// 迁移策略
fmt.Printf(" 迁移策略:\\n")
fmt.Printf(" 1. 识别重复代码模式\\n")
fmt.Printf(" 2. 从简单场景开始\\n")
fmt.Printf(" 3. 逐步替换类型断言\\n")
fmt.Printf(" 4. 保持向后兼容\\n")
fmt.Printf(" 5. 充分测试泛型代码\\n")
fmt.Println()
}

View File

@@ -0,0 +1,765 @@
/*
03-context.go - Go 语言 Context 包详解
学习目标:
1. 理解 Context 的概念和作用
2. 掌握 Context 的创建和使用方法
3. 学会在并发程序中传递取消信号
4. 了解 Context 的超时和截止时间机制
5. 掌握 Context 的最佳实践
知识点:
- Context 接口和实现
- WithCancel, WithTimeout, WithDeadline
- WithValue 传递请求范围的数据
- Context 在 HTTP 服务中的应用
- Context 的传播和继承
- 避免 Context 的常见陷阱
*/
package main
import (
"context"
"fmt"
"net/http"
"sync"
"time"
)
func main() {
fmt.Println("=== Go 语言 Context 包详解 ===\n")
// 演示 Context 的基本概念
demonstrateContextBasics()
// 演示取消 Context
demonstrateCancellation()
// 演示超时 Context
demonstrateTimeout()
// 演示截止时间 Context
demonstrateDeadline()
// 演示 Context 传递值
demonstrateWithValue()
// 演示 Context 在并发中的应用
demonstrateConcurrencyWithContext()
// 演示 Context 在 HTTP 中的应用
demonstrateHTTPContext()
// 演示 Context 的最佳实践
demonstrateContextBestPractices()
}
// demonstrateContextBasics 演示 Context 的基本概念
func demonstrateContextBasics() {
fmt.Println("1. Context 的基本概念:")
// Context 的概念
fmt.Printf(" Context 的概念:\n")
fmt.Printf(" - Context 是 Go 语言中用于传递请求范围数据的标准方式\n")
fmt.Printf(" - 提供取消信号、超时控制和请求范围值传递\n")
fmt.Printf(" - 在 goroutine 之间传递取消信号和截止时间\n")
fmt.Printf(" - 避免 goroutine 泄漏和资源浪费\n")
// Context 接口
fmt.Printf(" Context 接口:\n")
fmt.Printf(" type Context interface {\n")
fmt.Printf(" Deadline() (deadline time.Time, ok bool)\n")
fmt.Printf(" Done() <-chan struct{}\n")
fmt.Printf(" Err() error\n")
fmt.Printf(" Value(key interface{}) interface{}\n")
fmt.Printf(" }\n")
// 创建根 Context
fmt.Printf(" 创建根 Context:\n")
// Background Context
bgCtx := context.Background()
fmt.Printf(" Background Context: %v\n", bgCtx)
fmt.Printf(" - 通常用作根 Context\n")
fmt.Printf(" - 永远不会被取消,没有值,没有截止时间\n")
// TODO Context
todoCtx := context.TODO()
fmt.Printf(" TODO Context: %v\n", todoCtx)
fmt.Printf(" - 当不确定使用哪个 Context 时使用\n")
fmt.Printf(" - 通常在重构时作为占位符\n")
// Context 的方法
fmt.Printf(" Context 的方法:\n")
deadline, ok := bgCtx.Deadline()
fmt.Printf(" Deadline(): %v, %t (是否有截止时间)\n", deadline, ok)
fmt.Printf(" Done(): %v (取消通道)\n", bgCtx.Done())
fmt.Printf(" Err(): %v (错误信息)\n", bgCtx.Err())
fmt.Printf(" Value(key): %v (获取值)\n", bgCtx.Value("key"))
fmt.Println()
}
// demonstrateCancellation 演示取消 Context
func demonstrateCancellation() {
fmt.Println("2. 取消 Context:")
// WithCancel 的使用
fmt.Printf(" WithCancel 的使用:\n")
fmt.Printf(" ctx, cancel := context.WithCancel(context.Background())\n")
fmt.Printf(" defer cancel() // 确保释放资源\n")
// 创建可取消的 Context
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// 启动一个 goroutine 监听取消信号
fmt.Printf(" 启动监听取消信号的 goroutine:\n")
go func() {
select {
case <-ctx.Done():
fmt.Printf(" goroutine 收到取消信号: %v\n", ctx.Err())
case <-time.After(5 * time.Second):
fmt.Printf(" goroutine 超时退出\n")
}
}()
// 模拟一些工作
time.Sleep(100 * time.Millisecond)
fmt.Printf(" 执行取消操作...\n")
cancel() // 发送取消信号
// 等待 goroutine 处理取消信号
time.Sleep(100 * time.Millisecond)
// 检查 Context 状态
fmt.Printf(" Context 状态:\n")
fmt.Printf(" Done(): %v\n", ctx.Done() != nil)
fmt.Printf(" Err(): %v\n", ctx.Err())
// 演示取消传播
fmt.Printf(" 取消传播:\n")
parentCtx, parentCancel := context.WithCancel(context.Background())
childCtx, childCancel := context.WithCancel(parentCtx)
defer parentCancel()
defer childCancel()
// 取消父 Context
parentCancel()
// 检查子 Context 是否也被取消
select {
case <-childCtx.Done():
fmt.Printf(" 子 Context 也被取消了: %v\n", childCtx.Err())
case <-time.After(100 * time.Millisecond):
fmt.Printf(" 子 Context 没有被取消\n")
}
fmt.Println()
}
// demonstrateTimeout 演示超时 Context
func demonstrateTimeout() {
fmt.Println("3. 超时 Context:")
// WithTimeout 的使用
fmt.Printf(" WithTimeout 的使用:\n")
fmt.Printf(" ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)\n")
fmt.Printf(" defer cancel()\n")
// 创建超时 Context
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
defer cancel()
// 模拟长时间运行的操作
fmt.Printf(" 模拟长时间操作:\n")
start := time.Now()
select {
case <-time.After(500 * time.Millisecond):
fmt.Printf(" 操作完成,耗时: %v\n", time.Since(start))
case <-ctx.Done():
fmt.Printf(" 操作被超时取消,耗时: %v错误: %v\n", time.Since(start), ctx.Err())
}
// 演示超时处理函数
fmt.Printf(" 超时处理函数示例:\n")
result, err := doWorkWithTimeout(300 * time.Millisecond)
if err != nil {
fmt.Printf(" 工作超时: %v\n", err)
} else {
fmt.Printf(" 工作完成: %s\n", result)
}
result, err = doWorkWithTimeout(100 * time.Millisecond)
if err != nil {
fmt.Printf(" 工作超时: %v\n", err)
} else {
fmt.Printf(" 工作完成: %s\n", result)
}
// 演示 HTTP 请求超时
fmt.Printf(" HTTP 请求超时示例:\n")
err = makeHTTPRequestWithTimeout("https://httpbin.org/delay/1", 500*time.Millisecond)
if err != nil {
fmt.Printf(" HTTP 请求失败: %v\n", err)
} else {
fmt.Printf(" HTTP 请求成功\n")
}
fmt.Println()
}
// demonstrateDeadline 演示截止时间 Context
func demonstrateDeadline() {
fmt.Println("4. 截止时间 Context:")
// WithDeadline 的使用
fmt.Printf(" WithDeadline 的使用:\n")
deadline := time.Now().Add(200 * time.Millisecond)
fmt.Printf(" deadline := time.Now().Add(200 * time.Millisecond)\n")
fmt.Printf(" ctx, cancel := context.WithDeadline(context.Background(), deadline)\n")
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel()
// 检查截止时间
ctxDeadline, ok := ctx.Deadline()
fmt.Printf(" Context 截止时间:\n")
fmt.Printf(" 截止时间: %v\n", ctxDeadline)
fmt.Printf(" 有截止时间: %t\n", ok)
fmt.Printf(" 距离截止时间: %v\n", time.Until(ctxDeadline))
// 等待截止时间到达
fmt.Printf(" 等待截止时间到达:\n")
start := time.Now()
<-ctx.Done()
fmt.Printf(" Context 在 %v 后被取消,错误: %v\n", time.Since(start), ctx.Err())
// 演示截止时间检查
fmt.Printf(" 截止时间检查示例:\n")
checkDeadline(context.Background())
deadlineCtx, deadlineCancel := context.WithDeadline(context.Background(), time.Now().Add(100*time.Millisecond))
defer deadlineCancel()
checkDeadline(deadlineCtx)
fmt.Println()
}
// demonstrateWithValue 演示 Context 传递值
func demonstrateWithValue() {
fmt.Println("5. Context 传递值:")
// WithValue 的使用
fmt.Printf(" WithValue 的使用:\n")
fmt.Printf(" ctx := context.WithValue(context.Background(), \\\"userID\\\", 12345)\n")
// 创建带值的 Context
ctx := context.WithValue(context.Background(), "userID", 12345)
ctx = context.WithValue(ctx, "requestID", "req-abc-123")
ctx = context.WithValue(ctx, "traceID", "trace-xyz-789")
// 获取值
fmt.Printf(" 获取 Context 中的值:\n")
if userID := ctx.Value("userID"); userID != nil {
fmt.Printf(" 用户ID: %v\n", userID)
}
if requestID := ctx.Value("requestID"); requestID != nil {
fmt.Printf(" 请求ID: %v\n", requestID)
}
if traceID := ctx.Value("traceID"); traceID != nil {
fmt.Printf(" 追踪ID: %v\n", traceID)
}
// 值不存在的情况
if sessionID := ctx.Value("sessionID"); sessionID != nil {
fmt.Printf(" 会话ID: %v\n", sessionID)
} else {
fmt.Printf(" 会话ID: 不存在\n")
}
// 演示类型安全的键
fmt.Printf(" 类型安全的键:\n")
type contextKey string
const (
userIDKey contextKey = "userID"
requestIDKey contextKey = "requestID"
)
safeCtx := context.WithValue(context.Background(), userIDKey, 67890)
safeCtx = context.WithValue(safeCtx, requestIDKey, "req-def-456")
// 使用类型安全的方式获取值
if userID := safeCtx.Value(userIDKey); userID != nil {
fmt.Printf(" 安全获取用户ID: %v\n", userID)
}
// 演示值的传播
fmt.Printf(" 值的传播:\n")
processRequest(ctx)
// 演示值的最佳实践
fmt.Printf(" 值的最佳实践:\n")
fmt.Printf(" 1. 只存储请求范围的数据\n")
fmt.Printf(" 2. 使用类型安全的键\n")
fmt.Printf(" 3. 不要存储可选参数\n")
fmt.Printf(" 4. 键应该是不可导出的\n")
fmt.Println()
}
// demonstrateConcurrencyWithContext 演示 Context 在并发中的应用
func demonstrateConcurrencyWithContext() {
fmt.Println("6. Context 在并发中的应用:")
// 演示工作池模式
fmt.Printf(" 工作池模式:\n")
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
// 创建工作通道
jobs := make(chan int, 10)
results := make(chan string, 10)
// 启动工作者
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go worker(ctx, i, jobs, results, &wg)
}
// 发送工作
go func() {
for i := 1; i <= 8; i++ {
select {
case jobs <- i:
fmt.Printf(" 发送工作 %d\n", i)
case <-ctx.Done():
fmt.Printf(" 停止发送工作: %v\n", ctx.Err())
close(jobs)
return
}
time.Sleep(100 * time.Millisecond)
}
close(jobs)
}()
// 收集结果
go func() {
wg.Wait()
close(results)
}()
// 打印结果
fmt.Printf(" 工作结果:\n")
for result := range results {
fmt.Printf(" %s\n", result)
}
// 演示扇出扇入模式
fmt.Printf(" 扇出扇入模式:\n")
fanOutFanIn()
fmt.Println()
}
// demonstrateHTTPContext 演示 Context 在 HTTP 中的应用
func demonstrateHTTPContext() {
fmt.Println("7. Context 在 HTTP 中的应用:")
// HTTP 请求中的 Context
fmt.Printf(" HTTP 请求中的 Context:\n")
fmt.Printf(" - 每个 HTTP 请求都有一个关联的 Context\n")
fmt.Printf(" - 可以通过 r.Context() 获取\n")
fmt.Printf(" - 请求取消时 Context 也会被取消\n")
// 模拟 HTTP 处理器
fmt.Printf(" HTTP 处理器示例:\n")
// 创建模拟请求
req, _ := http.NewRequest("GET", "/api/data", nil)
// 添加超时
ctx, cancel := context.WithTimeout(req.Context(), 1*time.Second)
defer cancel()
req = req.WithContext(ctx)
// 模拟处理请求
fmt.Printf(" 处理请求...\n")
handleRequest(req)
// 演示中间件模式
fmt.Printf(" 中间件模式:\n")
fmt.Printf(" func middleware(next http.Handler) http.Handler {\n")
fmt.Printf(" return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n")
fmt.Printf(" ctx := context.WithValue(r.Context(), \\\"requestID\\\", generateID())\n")
fmt.Printf(" next.ServeHTTP(w, r.WithContext(ctx))\n")
fmt.Printf(" })\n")
fmt.Printf(" }\n")
// 演示数据库查询超时
fmt.Printf(" 数据库查询超时:\n")
queryCtx, queryCancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer queryCancel()
err := queryDatabase(queryCtx, "SELECT * FROM users WHERE id = ?", 123)
if err != nil {
fmt.Printf(" 数据库查询失败: %v\n", err)
} else {
fmt.Printf(" 数据库查询成功\n")
}
fmt.Println()
}
// demonstrateContextBestPractices 演示 Context 的最佳实践
func demonstrateContextBestPractices() {
fmt.Println("8. Context 的最佳实践:")
// 最佳实践列表
fmt.Printf(" Context 的最佳实践:\n")
fmt.Printf(" 1. 不要将 Context 存储在结构体中\n")
fmt.Printf(" 2. Context 应该作为函数的第一个参数\n")
fmt.Printf(" 3. 不要传递 nil Context使用 context.TODO()\n")
fmt.Printf(" 4. Context.Value 只用于请求范围的数据\n")
fmt.Printf(" 5. 使用 defer cancel() 确保资源释放\n")
fmt.Printf(" 6. 不要忽略 Context 的取消信号\n")
fmt.Printf(" 7. 使用类型安全的键\n")
fmt.Printf(" 8. 避免在 Context 中存储可选参数\n")
// 正确的函数签名
fmt.Printf(" 正确的函数签名:\n")
fmt.Printf(" func DoSomething(ctx context.Context, arg string) error\n")
fmt.Printf(" func (s *Service) Process(ctx context.Context, data []byte) (*Result, error)\n")
// 错误的用法
fmt.Printf(" 避免的错误用法:\n")
fmt.Printf(" // 错误:不要在结构体中存储 Context\n")
fmt.Printf(" type Server struct {\n")
fmt.Printf(" ctx context.Context // 错误\n")
fmt.Printf(" }\n")
fmt.Printf(" \n")
fmt.Printf(" // 错误:不要传递 nil Context\n")
fmt.Printf(" DoSomething(nil, \\\"data\\\") // 错误\n")
// 正确的用法
fmt.Printf(" 正确的用法示例:\n")
ctx := context.Background()
// 正确的取消处理
processWithCancel(ctx)
// 正确的超时处理
processWithTimeout(ctx)
// 正确的值传递
processWithValue(ctx)
// Context 的性能考虑
fmt.Printf(" 性能考虑:\n")
fmt.Printf(" 1. Context.Value 的查找是 O(n) 的\n")
fmt.Printf(" 2. 避免在热路径中频繁创建 Context\n")
fmt.Printf(" 3. 合理使用 Context 的层次结构\n")
fmt.Printf(" 4. 及时调用 cancel 函数释放资源\n")
// 常见陷阱
fmt.Printf(" 常见陷阱:\n")
fmt.Printf(" 1. 忘记调用 cancel 函数导致资源泄漏\n")
fmt.Printf(" 2. 在循环中创建过多的 Context\n")
fmt.Printf(" 3. 将 Context 用作可选参数的容器\n")
fmt.Printf(" 4. 不检查 Context 的取消信号\n")
fmt.Println()
}
// ========== 辅助函数 ==========
// doWorkWithTimeout 带超时的工作函数
func doWorkWithTimeout(timeout time.Duration) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
// 模拟工作
select {
case <-time.After(200 * time.Millisecond):
return "工作完成", nil
case <-ctx.Done():
return "", ctx.Err()
}
}
// makeHTTPRequestWithTimeout 带超时的 HTTP 请求
func makeHTTPRequestWithTimeout(url string, timeout time.Duration) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return err
}
// 模拟 HTTP 请求
select {
case <-time.After(300 * time.Millisecond):
return nil // 请求成功
case <-ctx.Done():
return ctx.Err()
}
}
// checkDeadline 检查截止时间
func checkDeadline(ctx context.Context) {
if deadline, ok := ctx.Deadline(); ok {
fmt.Printf(" 有截止时间: %v剩余时间: %v\n", deadline, time.Until(deadline))
} else {
fmt.Printf(" 没有截止时间\n")
}
}
// processRequest 处理请求
func processRequest(ctx context.Context) {
if userID := ctx.Value("userID"); userID != nil {
fmt.Printf(" 处理用户 %v 的请求\n", userID)
}
if requestID := ctx.Value("requestID"); requestID != nil {
fmt.Printf(" 请求ID: %v\n", requestID)
}
}
// worker 工作者函数
func worker(ctx context.Context, id int, jobs <-chan int, results chan<- string, wg *sync.WaitGroup) {
defer wg.Done()
for {
select {
case job, ok := <-jobs:
if !ok {
fmt.Printf(" 工作者 %d 退出(通道关闭)\n", id)
return
}
// 模拟工作
select {
case <-time.After(200 * time.Millisecond):
results <- fmt.Sprintf("工作者 %d 完成工作 %d", id, job)
case <-ctx.Done():
fmt.Printf(" 工作者 %d 被取消: %v\n", id, ctx.Err())
return
}
case <-ctx.Done():
fmt.Printf(" 工作者 %d 被取消: %v\n", id, ctx.Err())
return
}
}
}
// fanOutFanIn 扇出扇入模式
func fanOutFanIn() {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
// 输入通道
input := make(chan int, 5)
// 启动多个处理器(扇出)
c1 := process(ctx, input)
c2 := process(ctx, input)
c3 := process(ctx, input)
// 合并结果(扇入)
output := merge(ctx, c1, c2, c3)
// 发送数据
go func() {
defer close(input)
for i := 1; i <= 6; i++ {
select {
case input <- i:
case <-ctx.Done():
return
}
}
}()
// 收集结果
for result := range output {
fmt.Printf(" 扇出扇入结果: %s\n", result)
}
}
// process 处理函数
func process(ctx context.Context, input <-chan int) <-chan string {
output := make(chan string)
go func() {
defer close(output)
for {
select {
case n, ok := <-input:
if !ok {
return
}
select {
case output <- fmt.Sprintf("处理 %d", n*n):
case <-ctx.Done():
return
}
case <-ctx.Done():
return
}
}
}()
return output
}
// merge 合并多个通道
func merge(ctx context.Context, channels ...<-chan string) <-chan string {
var wg sync.WaitGroup
output := make(chan string)
// 为每个输入通道启动一个 goroutine
multiplex := func(c <-chan string) {
defer wg.Done()
for {
select {
case s, ok := <-c:
if !ok {
return
}
select {
case output <- s:
case <-ctx.Done():
return
}
case <-ctx.Done():
return
}
}
}
wg.Add(len(channels))
for _, c := range channels {
go multiplex(c)
}
// 等待所有 goroutine 完成
go func() {
wg.Wait()
close(output)
}()
return output
}
// handleRequest 处理 HTTP 请求
func handleRequest(req *http.Request) {
ctx := req.Context()
// 模拟处理
select {
case <-time.After(500 * time.Millisecond):
fmt.Printf(" 请求处理完成\n")
case <-ctx.Done():
fmt.Printf(" 请求被取消: %v\n", ctx.Err())
}
}
// queryDatabase 查询数据库
func queryDatabase(ctx context.Context, query string, args ...interface{}) error {
// 模拟数据库查询
select {
case <-time.After(300 * time.Millisecond):
return nil // 查询成功
case <-ctx.Done():
return ctx.Err()
}
}
// processWithCancel 带取消的处理
func processWithCancel(ctx context.Context) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
// 模拟处理
go func() {
time.Sleep(100 * time.Millisecond)
cancel() // 取消操作
}()
select {
case <-time.After(200 * time.Millisecond):
fmt.Printf(" 处理完成\n")
case <-ctx.Done():
fmt.Printf(" 处理被取消\n")
}
}
// processWithTimeout 带超时的处理
func processWithTimeout(ctx context.Context) {
ctx, cancel := context.WithTimeout(ctx, 50*time.Millisecond)
defer cancel()
select {
case <-time.After(100 * time.Millisecond):
fmt.Printf(" 处理完成\n")
case <-ctx.Done():
fmt.Printf(" 处理超时\n")
}
}
// processWithValue 带值传递的处理
func processWithValue(ctx context.Context) {
ctx = context.WithValue(ctx, "operation", "process")
if op := ctx.Value("operation"); op != nil {
fmt.Printf(" 执行操作: %v\n", op)
}
}
/*
运行这个程序:
go run 03-context.go
Context 的核心概念:
1. Context 是 Go 语言中处理请求范围数据的标准方式
2. 提供取消信号、超时控制和值传递功能
3. 在 goroutine 之间传递取消信号和截止时间
4. 避免 goroutine 泄漏和资源浪费
Context 的主要类型:
1. context.Background(): 根 Context通常用作起点
2. context.TODO(): 当不确定使用哪个 Context 时使用
3. context.WithCancel(): 创建可取消的 Context
4. context.WithTimeout(): 创建带超时的 Context
5. context.WithDeadline(): 创建带截止时间的 Context
6. context.WithValue(): 创建带值的 Context
Context 的最佳实践:
1. 不要将 Context 存储在结构体中
2. Context 应该作为函数的第一个参数
3. 不要传递 nil Context使用 context.TODO()
4. Context.Value 只用于请求范围的数据
5. 使用 defer cancel() 确保资源释放
6. 不要忽略 Context 的取消信号
7. 使用类型安全的键
8. 避免在 Context 中存储可选参数
Context 的应用场景:
1. HTTP 请求处理
2. 数据库查询超时
3. 并发任务协调
4. 微服务调用链
5. 长时间运行的任务控制
注意事项:
1. Context 是并发安全的
2. Context 的取消会传播到所有子 Context
3. Context.Value 的查找是 O(n) 的
4. 及时调用 cancel 函数释放资源
5. 不要在 Context 中存储可选参数
常见错误:
1. 忘记调用 cancel 函数
2. 将 Context 存储在结构体中
3. 传递 nil Context
4. 不检查 Context 的取消信号
5. 在 Context 中存储过多数据
*/

View File

@@ -0,0 +1,919 @@
/*
04-testing.go - Go 语言测试详解
学习目标:
1. 理解 Go 语言测试的基本概念
2. 掌握单元测试的编写方法
3. 学会基准测试和性能分析
4. 了解测试覆盖率和测试工具
5. 掌握测试的最佳实践
知识点:
- 测试函数的命名和签名
- testing 包的使用
- 表驱动测试模式
- 基准测试和内存分析
- 测试覆盖率
- 测试工具和技巧
*/
package main
import (
"fmt"
"sort"
"strings"
"time"
)
// 这个文件演示了如何编写各种类型的测试
// 注意:实际的测试文件应该以 _test.go 结尾
func main() {
fmt.Println("=== Go 语言测试详解 ===\n")
// 演示测试的基本概念
demonstrateTestingBasics()
// 演示单元测试
demonstrateUnitTesting()
// 演示表驱动测试
demonstrateTableDrivenTests()
// 演示基准测试
demonstrateBenchmarkTesting()
// 演示示例测试
demonstrateExampleTesting()
// 演示测试工具和技巧
demonstrateTestingTools()
// 演示测试的最佳实践
demonstrateTestingBestPractices()
}
// demonstrateTestingBasics 演示测试的基本概念
func demonstrateTestingBasics() {
fmt.Println("1. 测试的基本概念:")
// 测试的重要性
fmt.Printf(" 测试的重要性:\n")
fmt.Printf(" - 验证代码的正确性\n")
fmt.Printf(" - 防止回归错误\n")
fmt.Printf(" - 提高代码质量\n")
fmt.Printf(" - 便于重构和维护\n")
fmt.Printf(" - 作为代码的文档\n")
// Go 测试的特点
fmt.Printf(" Go 测试的特点:\n")
fmt.Printf(" - 内置测试框架\n")
fmt.Printf(" - 简单的测试语法\n")
fmt.Printf(" - 强大的工具支持\n")
fmt.Printf(" - 并行测试支持\n")
fmt.Printf(" - 基准测试集成\n")
// 测试文件的组织
fmt.Printf(" 测试文件的组织:\n")
fmt.Printf(" 源文件: calculator.go\n")
fmt.Printf(" 测试文件: calculator_test.go\n")
fmt.Printf(" \n")
fmt.Printf(" 源文件: utils.go\n")
fmt.Printf(" 测试文件: utils_test.go\n")
// 测试函数的类型
fmt.Printf(" 测试函数的类型:\n")
fmt.Printf(" 1. 单元测试: func TestXxx(*testing.T)\n")
fmt.Printf(" 2. 基准测试: func BenchmarkXxx(*testing.B)\n")
fmt.Printf(" 3. 示例测试: func ExampleXxx()\n")
fmt.Printf(" 4. 模糊测试: func FuzzXxx(*testing.F) (Go 1.18+)\n")
// 运行测试的命令
fmt.Printf(" 运行测试的命令:\n")
fmt.Printf(" go test # 运行当前包的测试\n")
fmt.Printf(" go test ./... # 运行所有子包的测试\n")
fmt.Printf(" go test -v # 详细输出\n")
fmt.Printf(" go test -run TestAdd # 运行特定测试\n")
fmt.Printf(" go test -bench=. # 运行基准测试\n")
fmt.Printf(" go test -cover # 显示覆盖率\n")
fmt.Println()
}
// demonstrateUnitTesting 演示单元测试
func demonstrateUnitTesting() {
fmt.Println("2. 单元测试:")
// 基本测试函数
fmt.Printf(" 基本测试函数:\n")
fmt.Printf(" func TestAdd(t *testing.T) {\n")
fmt.Printf(" result := Add(2, 3)\n")
fmt.Printf(" expected := 5\n")
fmt.Printf(" if result != expected {\n")
fmt.Printf(" t.Errorf(\\\"Add(2, 3) = %%d; want %%d\\\", result, expected)\n")
fmt.Printf(" }\n")
fmt.Printf(" }\n")
// 模拟运行测试
fmt.Printf(" 模拟运行测试:\n")
mockT := &MockT{}
// 测试 Add 函数
fmt.Printf(" 测试 Add 函数:\n")
TestAdd(mockT)
if mockT.failed {
fmt.Printf(" ❌ 测试失败: %s\n", mockT.errorMsg)
} else {
fmt.Printf(" ✅ 测试通过\n")
}
// 测试 Divide 函数
fmt.Printf(" 测试 Divide 函数:\n")
mockT = &MockT{}
TestDivide(mockT)
if mockT.failed {
fmt.Printf(" ❌ 测试失败: %s\n", mockT.errorMsg)
} else {
fmt.Printf(" ✅ 测试通过\n")
}
// 测试 IsPrime 函数
fmt.Printf(" 测试 IsPrime 函数:\n")
mockT = &MockT{}
TestIsPrime(mockT)
if mockT.failed {
fmt.Printf(" ❌ 测试失败: %s\n", mockT.errorMsg)
} else {
fmt.Printf(" ✅ 测试通过\n")
}
// testing.T 的方法
fmt.Printf(" testing.T 的方法:\n")
fmt.Printf(" t.Error(args ...) # 记录错误但继续测试\n")
fmt.Printf(" t.Errorf(format, args ...) # 格式化错误信息\n")
fmt.Printf(" t.Fatal(args ...) # 记录错误并停止测试\n")
fmt.Printf(" t.Fatalf(format, args ...) # 格式化致命错误\n")
fmt.Printf(" t.Log(args ...) # 记录日志信息\n")
fmt.Printf(" t.Logf(format, args ...) # 格式化日志信息\n")
fmt.Printf(" t.Skip(args ...) # 跳过测试\n")
fmt.Printf(" t.Helper() # 标记为辅助函数\n")
fmt.Println()
}
// demonstrateTableDrivenTests 演示表驱动测试
func demonstrateTableDrivenTests() {
fmt.Println("3. 表驱动测试:")
// 表驱动测试的优势
fmt.Printf(" 表驱动测试的优势:\n")
fmt.Printf(" - 减少重复代码\n")
fmt.Printf(" - 易于添加新的测试用例\n")
fmt.Printf(" - 提高测试的可维护性\n")
fmt.Printf(" - 清晰的测试数据结构\n")
// 表驱动测试示例
fmt.Printf(" 表驱动测试示例:\n")
fmt.Printf(" func TestAddTable(t *testing.T) {\n")
fmt.Printf(" tests := []struct {\n")
fmt.Printf(" name string\n")
fmt.Printf(" a, b int\n")
fmt.Printf(" expected int\n")
fmt.Printf(" }{\n")
fmt.Printf(" {\\\"positive\\\", 2, 3, 5},\n")
fmt.Printf(" {\\\"negative\\\", -1, -2, -3},\n")
fmt.Printf(" {\\\"zero\\\", 0, 5, 5},\n")
fmt.Printf(" }\n")
fmt.Printf(" \n")
fmt.Printf(" for _, tt := range tests {\n")
fmt.Printf(" t.Run(tt.name, func(t *testing.T) {\n")
fmt.Printf(" result := Add(tt.a, tt.b)\n")
fmt.Printf(" if result != tt.expected {\n")
fmt.Printf(" t.Errorf(\\\"got %%d, want %%d\\\", result, tt.expected)\n")
fmt.Printf(" }\n")
fmt.Printf(" })\n")
fmt.Printf(" }\n")
fmt.Printf(" }\n")
// 模拟运行表驱动测试
fmt.Printf(" 模拟运行表驱动测试:\n")
mockT := &MockT{}
TestAddTable(mockT)
// 字符串处理的表驱动测试
fmt.Printf(" 字符串处理的表驱动测试:\n")
mockT = &MockT{}
TestStringOperations(mockT)
// 错误处理的表驱动测试
fmt.Printf(" 错误处理的表驱动测试:\n")
mockT = &MockT{}
TestValidateInput(mockT)
fmt.Println()
}
// demonstrateBenchmarkTesting 演示基准测试
func demonstrateBenchmarkTesting() {
fmt.Println("4. 基准测试:")
// 基准测试的概念
fmt.Printf(" 基准测试的概念:\n")
fmt.Printf(" - 测量代码的性能\n")
fmt.Printf(" - 比较不同实现的效率\n")
fmt.Printf(" - 识别性能瓶颈\n")
fmt.Printf(" - 验证优化效果\n")
// 基准测试函数
fmt.Printf(" 基准测试函数:\n")
fmt.Printf(" func BenchmarkStringConcat(b *testing.B) {\n")
fmt.Printf(" for i := 0; i < b.N; i++ {\n")
fmt.Printf(" _ = \\\"hello\\\" + \\\"world\\\"\n")
fmt.Printf(" }\n")
fmt.Printf(" }\n")
// 模拟运行基准测试
fmt.Printf(" 模拟运行基准测试:\n")
// 字符串连接基准测试
fmt.Printf(" 字符串连接性能比较:\n")
runBenchmark("StringConcat", BenchmarkStringConcat)
runBenchmark("StringBuffer", BenchmarkStringBuffer)
runBenchmark("StringBuilder", BenchmarkStringBuilder)
// 排序算法基准测试
fmt.Printf(" 排序算法性能比较:\n")
runBenchmark("BubbleSort", BenchmarkBubbleSort)
runBenchmark("QuickSort", BenchmarkQuickSort)
runBenchmark("StandardSort", BenchmarkStandardSort)
// 基准测试的运行参数
fmt.Printf(" 基准测试的运行参数:\n")
fmt.Printf(" go test -bench=. # 运行所有基准测试\n")
fmt.Printf(" go test -bench=BenchmarkAdd # 运行特定基准测试\n")
fmt.Printf(" go test -bench=. -benchmem # 显示内存分配\n")
fmt.Printf(" go test -bench=. -benchtime=10s # 设置运行时间\n")
fmt.Printf(" go test -bench=. -count=5 # 运行多次\n")
// 基准测试结果解读
fmt.Printf(" 基准测试结果解读:\n")
fmt.Printf(" BenchmarkAdd-8 1000000000 0.50 ns/op 0 B/op 0 allocs/op\n")
fmt.Printf(" │ │ │ │ │\n")
fmt.Printf(" │ │ │ │ └─ 每次操作的分配次数\n")
fmt.Printf(" │ │ │ └─ 每次操作分配的字节数\n")
fmt.Printf(" │ │ └─ 每次操作的平均时间\n")
fmt.Printf(" │ └─ 执行次数\n")
fmt.Printf(" └─ 基准测试名称和CPU核心数\n")
fmt.Println()
}
// demonstrateExampleTesting 演示示例测试
func demonstrateExampleTesting() {
fmt.Println("5. 示例测试:")
// 示例测试的概念
fmt.Printf(" 示例测试的概念:\n")
fmt.Printf(" - 提供函数使用示例\n")
fmt.Printf(" - 验证输出结果\n")
fmt.Printf(" - 生成文档\n")
fmt.Printf(" - 确保示例代码正确\n")
// 示例测试函数
fmt.Printf(" 示例测试函数:\n")
fmt.Printf(" func ExampleAdd() {\n")
fmt.Printf(" result := Add(2, 3)\n")
fmt.Printf(" fmt.Println(result)\n")
fmt.Printf(" // Output: 5\n")
fmt.Printf(" }\n")
// 运行示例测试
fmt.Printf(" 运行示例测试:\n")
fmt.Printf(" Add 函数示例:\n")
ExampleAdd()
fmt.Printf(" Divide 函数示例:\n")
ExampleDivide()
fmt.Printf(" StringReverse 函数示例:\n")
ExampleStringReverse()
// 示例测试的特点
fmt.Printf(" 示例测试的特点:\n")
fmt.Printf(" 1. 函数名以 Example 开头\n")
fmt.Printf(" 2. 使用 // Output: 注释指定期望输出\n")
fmt.Printf(" 3. 可以使用 // Unordered output: 处理无序输出\n")
fmt.Printf(" 4. 会在 go doc 中显示\n")
fmt.Printf(" 5. 可以通过 go test 验证\n")
fmt.Println()
}
// demonstrateTestingTools 演示测试工具和技巧
func demonstrateTestingTools() {
fmt.Println("6. 测试工具和技巧:")
// 测试覆盖率
fmt.Printf(" 测试覆盖率:\n")
fmt.Printf(" go test -cover # 显示覆盖率\n")
fmt.Printf(" go test -coverprofile=cover.out # 生成覆盖率文件\n")
fmt.Printf(" go tool cover -html=cover.out # 生成HTML报告\n")
fmt.Printf(" go tool cover -func=cover.out # 显示函数覆盖率\n")
// 测试辅助函数
fmt.Printf(" 测试辅助函数:\n")
fmt.Printf(" func assertEqual(t *testing.T, got, want interface{}) {\n")
fmt.Printf(" t.Helper() // 标记为辅助函数\n")
fmt.Printf(" if got != want {\n")
fmt.Printf(" t.Errorf(\\\"got %%v, want %%v\\\", got, want)\n")
fmt.Printf(" }\n")
fmt.Printf(" }\n")
// 模拟使用辅助函数
fmt.Printf(" 使用辅助函数:\n")
mockT := &MockT{}
TestWithHelpers(mockT)
if mockT.failed {
fmt.Printf(" ❌ 测试失败: %s\n", mockT.errorMsg)
} else {
fmt.Printf(" ✅ 测试通过\n")
}
// 子测试
fmt.Printf(" 子测试:\n")
fmt.Printf(" t.Run(\\\"subtest\\\", func(t *testing.T) {\n")
fmt.Printf(" // 子测试代码\n")
fmt.Printf(" })\n")
// 并行测试
fmt.Printf(" 并行测试:\n")
fmt.Printf(" func TestParallel(t *testing.T) {\n")
fmt.Printf(" t.Parallel() // 标记为并行测试\n")
fmt.Printf(" // 测试代码\n")
fmt.Printf(" }\n")
// 测试设置和清理
fmt.Printf(" 测试设置和清理:\n")
fmt.Printf(" func TestMain(m *testing.M) {\n")
fmt.Printf(" setup() // 全局设置\n")
fmt.Printf(" code := m.Run()\n")
fmt.Printf(" teardown() // 全局清理\n")
fmt.Printf(" os.Exit(code)\n")
fmt.Printf(" }\n")
// 跳过测试
fmt.Printf(" 跳过测试:\n")
fmt.Printf(" if testing.Short() {\n")
fmt.Printf(" t.Skip(\\\"跳过长时间运行的测试\\\")\n")
fmt.Printf(" }\n")
// 临时目录
fmt.Printf(" 临时目录:\n")
fmt.Printf(" tmpDir := t.TempDir() // 创建临时目录\n")
fmt.Printf(" // 测试结束后自动清理\n")
fmt.Println()
}
// demonstrateTestingBestPractices 演示测试的最佳实践
func demonstrateTestingBestPractices() {
fmt.Println("7. 测试的最佳实践:")
// 测试命名
fmt.Printf(" 测试命名:\n")
fmt.Printf(" 1. 使用描述性的测试名称\n")
fmt.Printf(" 2. 包含被测试的功能和场景\n")
fmt.Printf(" 3. 使用一致的命名约定\n")
fmt.Printf(" \n")
fmt.Printf(" 好的命名:\n")
fmt.Printf(" TestAdd_PositiveNumbers\n")
fmt.Printf(" TestDivide_ByZero_ReturnsError\n")
fmt.Printf(" TestUser_Create_ValidInput\n")
// 测试组织
fmt.Printf(" 测试组织:\n")
fmt.Printf(" 1. 每个源文件对应一个测试文件\n")
fmt.Printf(" 2. 按功能分组测试\n")
fmt.Printf(" 3. 使用子测试组织复杂场景\n")
fmt.Printf(" 4. 保持测试的独立性\n")
// 测试数据
fmt.Printf(" 测试数据:\n")
fmt.Printf(" 1. 使用表驱动测试处理多个用例\n")
fmt.Printf(" 2. 包含边界条件和异常情况\n")
fmt.Printf(" 3. 使用有意义的测试数据\n")
fmt.Printf(" 4. 避免硬编码的魔法数字\n")
// 断言
fmt.Printf(" 断言:\n")
fmt.Printf(" 1. 使用清晰的错误消息\n")
fmt.Printf(" 2. 一个测试只验证一个行为\n")
fmt.Printf(" 3. 优先使用 Errorf 而不是 Error\n")
fmt.Printf(" 4. 在必要时使用 Fatal 停止测试\n")
// 测试覆盖率
fmt.Printf(" 测试覆盖率:\n")
fmt.Printf(" 1. 追求高覆盖率但不盲目追求100%%\n")
fmt.Printf(" 2. 关注重要的业务逻辑\n")
fmt.Printf(" 3. 测试边界条件和错误路径\n")
fmt.Printf(" 4. 使用覆盖率工具识别遗漏\n")
// 性能考虑
fmt.Printf(" 性能考虑:\n")
fmt.Printf(" 1. 避免在测试中进行复杂计算\n")
fmt.Printf(" 2. 使用并行测试提高速度\n")
fmt.Printf(" 3. 合理使用测试缓存\n")
fmt.Printf(" 4. 避免不必要的外部依赖\n")
// 测试维护
fmt.Printf(" 测试维护:\n")
fmt.Printf(" 1. 保持测试代码的简洁\n")
fmt.Printf(" 2. 定期重构测试代码\n")
fmt.Printf(" 3. 及时更新过时的测试\n")
fmt.Printf(" 4. 删除无用的测试\n")
// 常见陷阱
fmt.Printf(" 常见陷阱:\n")
fmt.Printf(" 1. 测试依赖外部状态\n")
fmt.Printf(" 2. 测试之间相互依赖\n")
fmt.Printf(" 3. 忽略错误处理的测试\n")
fmt.Printf(" 4. 过度复杂的测试逻辑\n")
fmt.Printf(" 5. 不稳定的测试flaky tests\n")
fmt.Println()
}
// ========== 被测试的函数 ==========
// Add 计算两个整数的和
func Add(a, b int) int {
return a + b
}
// Divide 计算两个数的商
func Divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("division by zero")
}
return a / b, nil
}
// IsPrime 判断是否为质数
func IsPrime(n int) bool {
if n < 2 {
return false
}
for i := 2; i*i <= n; i++ {
if n%i == 0 {
return false
}
}
return true
}
// StringReverse 反转字符串
func StringReverse(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
// Capitalize 将字符串首字母大写
func Capitalize(s string) string {
if len(s) == 0 {
return s
}
return strings.ToUpper(s[:1]) + strings.ToLower(s[1:])
}
// ValidateInput 验证输入
func ValidateInput(input string) error {
if len(input) == 0 {
return fmt.Errorf("input cannot be empty")
}
if len(input) > 100 {
return fmt.Errorf("input too long")
}
return nil
}
// ========== 模拟的 testing.T 和 testing.B ==========
// MockT 模拟 testing.T 类型
type MockT struct {
failed bool
errorMsg string
logs []string
}
func (m *MockT) Error(args ...interface{}) {
m.failed = true
m.errorMsg = fmt.Sprint(args...)
}
func (m *MockT) Errorf(format string, args ...interface{}) {
m.failed = true
m.errorMsg = fmt.Sprintf(format, args...)
}
func (m *MockT) Fatal(args ...interface{}) {
m.failed = true
m.errorMsg = fmt.Sprint(args...)
}
func (m *MockT) Fatalf(format string, args ...interface{}) {
m.failed = true
m.errorMsg = fmt.Sprintf(format, args...)
}
func (m *MockT) Log(args ...interface{}) {
m.logs = append(m.logs, fmt.Sprint(args...))
}
func (m *MockT) Logf(format string, args ...interface{}) {
m.logs = append(m.logs, fmt.Sprintf(format, args...))
}
func (m *MockT) Helper() {
// 标记为辅助函数
}
func (m *MockT) Run(name string, f func(*MockT)) bool {
fmt.Printf(" 运行子测试: %s\n", name)
subT := &MockT{}
f(subT)
if subT.failed {
fmt.Printf(" ❌ 失败: %s\n", subT.errorMsg)
return false
} else {
fmt.Printf(" ✅ 通过\n")
return true
}
}
// MockB 模拟 testing.B 类型
type MockB struct {
N int
}
func (b *MockB) ResetTimer() {}
func (b *MockB) ReportAllocs() {}
// ========== 测试函数示例 ==========
// TestAdd 测试 Add 函数
func TestAdd(t *MockT) {
result := Add(2, 3)
expected := 5
if result != expected {
t.Errorf("Add(2, 3) = %d; want %d", result, expected)
}
}
// TestDivide 测试 Divide 函数
func TestDivide(t *MockT) {
// 正常情况
result, err := Divide(10, 2)
if err != nil {
t.Errorf("Divide(10, 2) returned error: %v", err)
return
}
if result != 5 {
t.Errorf("Divide(10, 2) = %f; want 5", result)
}
// 除零情况
_, err = Divide(10, 0)
if err == nil {
t.Error("Divide(10, 0) should return error")
}
}
// TestIsPrime 测试 IsPrime 函数
func TestIsPrime(t *MockT) {
tests := []struct {
n int
expected bool
}{
{2, true},
{3, true},
{4, false},
{17, true},
{25, false},
{1, false},
{0, false},
{-1, false},
}
for _, tt := range tests {
result := IsPrime(tt.n)
if result != tt.expected {
t.Errorf("IsPrime(%d) = %t; want %t", tt.n, result, tt.expected)
}
}
}
// TestAddTable 表驱动测试示例
func TestAddTable(t *MockT) {
tests := []struct {
name string
a, b int
expected int
}{
{"positive numbers", 2, 3, 5},
{"negative numbers", -1, -2, -3},
{"mixed numbers", -1, 2, 1},
{"zero", 0, 5, 5},
}
for _, tt := range tests {
t.Run(tt.name, func(subT *MockT) {
result := Add(tt.a, tt.b)
if result != tt.expected {
subT.Errorf("Add(%d, %d) = %d; want %d", tt.a, tt.b, result, tt.expected)
}
})
}
}
// TestStringOperations 字符串操作测试
func TestStringOperations(t *MockT) {
tests := []struct {
name string
input string
expected string
}{
{"normal string", "hello", "Hello"},
{"empty string", "", ""},
{"single char", "a", "A"},
{"already capitalized", "Hello", "Hello"},
}
for _, tt := range tests {
t.Run(tt.name, func(subT *MockT) {
result := Capitalize(tt.input)
if result != tt.expected {
subT.Errorf("Capitalize(%q) = %q; want %q", tt.input, result, tt.expected)
}
})
}
}
// TestValidateInput 输入验证测试
func TestValidateInput(t *MockT) {
tests := []struct {
name string
input string
wantError bool
}{
{"valid input", "hello", false},
{"empty input", "", true},
{"too long input", strings.Repeat("a", 101), true},
{"max length input", strings.Repeat("a", 100), false},
}
for _, tt := range tests {
t.Run(tt.name, func(subT *MockT) {
err := ValidateInput(tt.input)
if (err != nil) != tt.wantError {
subT.Errorf("ValidateInput(%q) error = %v; wantError %t", tt.input, err, tt.wantError)
}
})
}
}
// TestWithHelpers 使用辅助函数的测试
func TestWithHelpers(t *MockT) {
assertEqual := func(t *MockT, got, want interface{}) {
t.Helper()
if got != want {
t.Errorf("got %v, want %v", got, want)
}
}
// 使用辅助函数进行断言
assertEqual(t, Add(2, 3), 5)
assertEqual(t, Capitalize("hello"), "Hello")
result, err := Divide(10, 2)
if err != nil {
t.Errorf("unexpected error: %v", err)
return
}
assertEqual(t, result, 5.0)
}
// ========== 基准测试函数 ==========
// BenchmarkStringConcat 字符串连接基准测试
func BenchmarkStringConcat(b *MockB) {
for i := 0; i < b.N; i++ {
_ = "hello" + "world" + "golang" + "benchmark"
}
}
// BenchmarkStringBuffer 字符串缓冲区基准测试
func BenchmarkStringBuffer(b *MockB) {
for i := 0; i < b.N; i++ {
var buffer strings.Builder
buffer.WriteString("hello")
buffer.WriteString("world")
buffer.WriteString("golang")
buffer.WriteString("benchmark")
_ = buffer.String()
}
}
// BenchmarkStringBuilder 字符串构建器基准测试
func BenchmarkStringBuilder(b *MockB) {
for i := 0; i < b.N; i++ {
parts := []string{"hello", "world", "golang", "benchmark"}
_ = strings.Join(parts, "")
}
}
// BenchmarkBubbleSort 冒泡排序基准测试
func BenchmarkBubbleSort(b *MockB) {
data := []int{64, 34, 25, 12, 22, 11, 90}
for i := 0; i < b.N; i++ {
bubbleSort(append([]int(nil), data...))
}
}
// BenchmarkQuickSort 快速排序基准测试
func BenchmarkQuickSort(b *MockB) {
data := []int{64, 34, 25, 12, 22, 11, 90}
for i := 0; i < b.N; i++ {
quickSort(append([]int(nil), data...))
}
}
// BenchmarkStandardSort 标准排序基准测试
func BenchmarkStandardSort(b *MockB) {
data := []int{64, 34, 25, 12, 22, 11, 90}
for i := 0; i < b.N; i++ {
sort.Ints(append([]int(nil), data...))
}
}
// ========== 示例测试函数 ==========
// ExampleAdd Add 函数的示例
func ExampleAdd() {
result := Add(2, 3)
fmt.Println(result)
// Output: 5
}
// ExampleDivide Divide 函数的示例
func ExampleDivide() {
result, err := Divide(10, 2)
if err != nil {
fmt.Printf("Error: %v", err)
return
}
fmt.Printf("%.1f", result)
// Output: 5.0
}
// ExampleStringReverse StringReverse 函数的示例
func ExampleStringReverse() {
result := StringReverse("hello")
fmt.Println(result)
// Output: olleh
}
// ========== 辅助函数 ==========
// runBenchmark 运行基准测试
func runBenchmark(name string, benchFunc func(*MockB)) {
b := &MockB{N: 1000000}
start := time.Now()
benchFunc(b)
duration := time.Since(start)
nsPerOp := duration.Nanoseconds() / int64(b.N)
fmt.Printf(" %s: %d ns/op\n", name, nsPerOp)
}
// bubbleSort 冒泡排序
func bubbleSort(arr []int) {
n := len(arr)
for i := 0; i < n-1; i++ {
for j := 0; j < n-i-1; j++ {
if arr[j] > arr[j+1] {
arr[j], arr[j+1] = arr[j+1], arr[j]
}
}
}
}
// quickSort 快速排序
func quickSort(arr []int) {
if len(arr) < 2 {
return
}
pivot := partition(arr)
quickSort(arr[:pivot])
quickSort(arr[pivot+1:])
}
// partition 分区函数
func partition(arr []int) int {
pivot := arr[len(arr)-1]
i := -1
for j := 0; j < len(arr)-1; j++ {
if arr[j] <= pivot {
i++
arr[i], arr[j] = arr[j], arr[i]
}
}
arr[i+1], arr[len(arr)-1] = arr[len(arr)-1], arr[i+1]
return i + 1
}
/*
运行这个程序:
go run 04-testing.go
实际测试文件示例calculator_test.go
package calculator
import "testing"
func TestAdd(t *testing.T) {
result := Add(2, 3)
expected := 5
if result != expected {
t.Errorf("Add(2, 3) = %d; want %d", result, expected)
}
}
func TestAddTable(t *testing.T) {
tests := []struct {
name string
a, b int
expected int
}{
{"positive numbers", 2, 3, 5},
{"negative numbers", -1, -2, -3},
{"mixed numbers", -1, 2, 1},
{"zero", 0, 5, 5},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := Add(tt.a, tt.b)
if result != tt.expected {
t.Errorf("got %d, want %d", result, tt.expected)
}
})
}
}
func BenchmarkStringConcat(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = "hello" + "world"
}
}
func ExampleAdd() {
result := Add(2, 3)
fmt.Println(result)
// Output: 5
}
运行测试命令:
go test # 运行当前包的所有测试
go test -v # 详细输出
go test -run TestAdd # 运行特定测试
go test -bench=. # 运行基准测试
go test -cover # 显示覆盖率
go test ./... # 运行所有子包的测试
测试的核心概念:
1. 单元测试:验证单个函数或方法的正确性
2. 表驱动测试:使用测试数据表驱动测试执行
3. 基准测试:测量代码的性能和内存使用
4. 示例测试:提供函数使用示例并验证输出
测试的最佳实践:
1. 使用描述性的测试名称
2. 保持测试的独立性
3. 测试边界条件和错误情况
4. 使用表驱动测试处理多个用例
5. 编写清晰的错误消息
6. 保持高的测试覆盖率
7. 定期重构和维护测试代码
注意事项:
1. 测试文件必须以 _test.go 结尾
2. 测试函数必须以 Test 开头
3. 基准测试函数必须以 Benchmark 开头
4. 示例测试函数必须以 Example 开头
5. 使用 t.Helper() 标记辅助函数
6. 合理使用并行测试提高效率
*/

View File

@@ -1,15 +1,24 @@
# 第九章:高级特性
本章将学习 Go 语言的一些高级特性,包括反射、泛型、上下文和测试
本章将学习 Go 语言的高级特性,这些特性能帮助你编写更强大和灵活的程序
## 学习目标
- 了解反射的基本概念和用法
- 理解泛型的语法和应用
- 掌握 context 包的使用
- 学会编写单元测试
- 掌握反射的基本使用和应用场景
- 理解泛型的概念和语法
- 学会使用 context 包进行上下文管理
- 掌握单元测试的编写方法
## 文件列表
- `01-reflection.go` - 反射
- `02-generics.go` - 泛型
- `03-context.go` - Context
- `04-testing.go` - 测试
- `01-reflection.go` - 反射机制
- `02-generics.go` - 泛型编程
- `03-context.go` - 上下文管理
- `04-testing.go` - 单元测试
## 学习建议
1. 反射是强大但复杂的特性,需要谨慎使用
2. 泛型是 Go 1.18+ 的新特性,能提高代码复用性
3. Context 是并发编程中的重要工具
4. 测试是保证代码质量的重要手段