Golang中匿名函数的巧妙用法:不仅限于并发场景

Golang中匿名函数的巧妙用法:不仅限于并发场景 我突然想到,我几乎只将匿名函数用作编写并发和并行代码的简便方式,极少情况下会从另一个函数返回一个函数。在 Go 中,还有哪些场景适合使用匿名函数?

6 回复

谢谢!

更多关于Golang中匿名函数的巧妙用法:不仅限于并发场景的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


也许可以作为一个回调函数:

package main

import (
	"fmt"
	"time"
)

type callback func()

func main() {
	doSth(func() {
		fmt.Print("Jobs done")
	})
}

func doSth(c callback) {
	time.Sleep(time.Second * 2) // 模拟一些任务
	c()
}

我最常在 sort.Slice 中使用匿名函数作为 less 参数。sort package - sort - Go Packages

func main() {
    fmt.Println("hello world")
}

你好 @ashinnv

匿名函数存在于定义它的函数的上下文中。(这就是为什么匿名函数也被称为“闭包”。)

闭包绑定到其外部作用域。也就是说,闭包可以访问定义该闭包的函数中定义的变量。这样,你就可以创建携带某种状态的函数,例如一个计数器,如 Go by Example: Closures 所示。

Jon Calhoun 描述了 Go 中闭包的 5 种有用用法

我也将它们用作 C# 局部函数 的等价物。我能想到的一个例子是,如果我需要为类型构建一些元数据,例如:

type typeData struct {
    // 一些我希望为每个类型保存的数据,可能是该类型的字段
    // 到获取或设置值的函数等的映射。
}

var typeDataByReflectType = sync.Map{}

func getTypeData(rt reflect.Type) *typeData {
    // newTypeData 是 getTypeData 的"局部"函数,因为你不应该
    // 能够在 getTypeData 之外调用它,即使是在同一个包中的其他函数里。
    newTypeData := func(rt reflect.Type) (td *typeData) {
        // 执行创建这些 *typeData 结构体所需的任何操作
        return
    }
    v, loaded := typeDataByReflectType.Load(rt)
    if loaded {
        return v.(*typeData)
    }
    td := newTypeData(rt)
    if v, loaded = typeDataByReflectType.LoadOrStore(rt, td); loaded {
        return v.(*typeData)
    }
    return td
}

在Go中,匿名函数确实远不止并发场景。以下是一些典型用例:

1. 延迟执行与资源管理

func processFile(filename string) error {
    f, err := os.Open(filename)
    if err != nil {
        return err
    }
    defer func() {
        if err := f.Close(); err != nil {
            log.Printf("关闭文件失败: %v", err)
        }
    }()
    
    // 文件处理逻辑
    return nil
}

2. 实现闭包捕获状态

func counter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

func main() {
    c := counter()
    fmt.Println(c()) // 1
    fmt.Println(c()) // 2
    fmt.Println(c()) // 3
}

3. 回调函数与事件处理

type EventHandler func(string)

func registerEvent(name string, handler EventHandler) {
    // 注册逻辑
    handler("事件触发: " + name)
}

func main() {
    registerEvent("click", func(msg string) {
        fmt.Println("处理点击:", msg)
    })
}

4. 排序与比较逻辑

type Person struct {
    Name string
    Age  int
}

func main() {
    people := []Person{
        {"Alice", 30},
        {"Bob", 25},
        {"Charlie", 35},
    }
    
    sort.Slice(people, func(i, j int) bool {
        return people[i].Age < people[j].Age
    })
    
    fmt.Println(people) // [{Bob 25} {Alice 30} {Charlie 35}]
}

5. 中间件与装饰器模式

func loggingMiddleware(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        next(w, r)
        log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
    }
}

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello")
}

func main() {
    http.HandleFunc("/", loggingMiddleware(handler))
    http.ListenAndServe(":8080", nil)
}

6. 错误处理包装

func withRetry(fn func() error, maxRetries int) error {
    var err error
    for i := 0; i < maxRetries; i++ {
        err = fn()
        if err == nil {
            return nil
        }
        time.Sleep(time.Second * time.Duration(i+1))
    }
    return fmt.Errorf("重试失败: %v", err)
}

func main() {
    err := withRetry(func() error {
        // 可能失败的操作
        return nil
    }, 3)
}

7. 配置选项模式

type Server struct {
    addr string
    timeout time.Duration
}

type Option func(*Server)

func WithTimeout(d time.Duration) Option {
    return func(s *Server) {
        s.timeout = d
    }
}

func NewServer(addr string, opts ...Option) *Server {
    s := &Server{addr: addr, timeout: 30 * time.Second}
    for _, opt := range opts {
        opt(s)
    }
    return s
}

func main() {
    srv := NewServer(":8080", WithTimeout(60*time.Second))
}

8. 测试中的模拟函数

func TestProcess(t *testing.T) {
    mockDB := func(query string) ([]string, error) {
        return []string{"result1", "result2"}, nil
    }
    
    results, err := processWithDB(mockDB, "SELECT * FROM users")
    // 断言逻辑
}

匿名函数在Go中的核心价值在于其能够捕获上下文、延迟执行、以及作为一等公民的函数式编程能力。这些特性使其在资源管理、配置、测试和中间件等场景中极为实用。

回到顶部