Golang中通过参数传递函数与泛型参数类型的应用探讨

Golang中通过参数传递函数与泛型参数类型的应用探讨 大家好,我遇到了一个问题,也许有人能帮忙。我有一个预先定义好的函数,我需要在另一个函数内部执行这个函数,它通过参数传递,并且该函数的其中一个参数的类型可以改变。以下是我在Playground上尝试做的示例:

Go Playground - The Go Programming Language

PS:我无法访问doSomthingAdoSomthingB函数的代码,这些函数是生成的,并且第二个参数arg2的类型名称会改变。

感谢任何形式的帮助。

4 回复

检查类型断言的结果以避免程序崩溃。

更多关于Golang中通过参数传递函数与泛型参数类型的应用探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


要么需要使用泛型:The Gotip Playground

要么传递一个包装函数,而不是直接传递 doSomethingAdoSomethingB,像这样:Go Playground - The Go Programming Language

要么需要使用反射,这可能最慢:Go Playground - The Go Programming Language

我认为问题在于函数 doSomethingAdoSomethingB 的签名。我的意思是,你使用的是 func(a string, b interface{}),而它们的签名是 func doSomethingA(arg1 string, arg2 typeA)doSomethingA(arg1 string, arg2 typeB)

你可以使用泛型、反射,或者直接修改这两个函数的签名,将 arg2 改为 interface{},并相应地修改函数内的代码:

func doSomethingA(arg1 string, arg2 interface{}) {
  fmt.Printf("ARG 1: %s / ARG 2: %s\n", arg1, (arg2.(*typeA)).any)
}

func doSomethingB(arg1 string, arg2 interface{}) {
  fmt.Printf("ARG 1: %s / ARG 2: %s\n", arg1, (arg2.(*typeB)).any)
}

在Go语言中,可以通过接口(interface)或泛型(generics)来解决这个问题。由于你提到无法修改doSomthingAdoSomthingB的代码,且第二个参数类型会变化,这里提供两种解决方案:

方案1:使用接口(推荐,兼容性更好)

package main

import "fmt"

// 定义接口
type Processor interface {
    Process()
}

// 具体类型A
type ArgTypeA struct {
    Value string
}

func (a ArgTypeA) Process() {
    fmt.Printf("Processing A: %s\n", a.Value)
}

// 具体类型B
type ArgTypeB struct {
    Value int
}

func (b ArgTypeB) Process() {
    fmt.Printf("Processing B: %d\n", b.Value)
}

// 预定义的函数(模拟生成的代码)
func doSomthingA(arg1 string, arg2 ArgTypeA) {
    fmt.Printf("doSomthingA called: %s\n", arg1)
    arg2.Process()
}

func doSomthingB(arg1 string, arg2 ArgTypeB) {
    fmt.Printf("doSomthingB called: %s\n", arg1)
    arg2.Process()
}

// 包装函数
func executeFunction(fn interface{}, arg1 string, arg2 Processor) {
    switch f := fn.(type) {
    case func(string, ArgTypeA):
        f(arg1, arg2.(ArgTypeA))
    case func(string, ArgTypeB):
        f(arg1, arg2.(ArgTypeB))
    default:
        panic("unsupported function type")
    }
}

func main() {
    // 使用示例
    argA := ArgTypeA{Value: "test"}
    executeFunction(doSomthingA, "hello", argA)
    
    argB := ArgTypeB{Value: 42}
    executeFunction(doSomthingB, "world", argB)
}

方案2:使用泛型(Go 1.18+)

package main

import "fmt"

// 具体类型
type ArgTypeA struct {
    Value string
}

type ArgTypeB struct {
    Value int
}

// 预定义的函数(模拟生成的代码)
func doSomthingA(arg1 string, arg2 ArgTypeA) {
    fmt.Printf("doSomthingA: %s, %v\n", arg1, arg2.Value)
}

func doSomthingB(arg1 string, arg2 ArgTypeB) {
    fmt.Printf("doSomthingB: %s, %d\n", arg1, arg2.Value)
}

// 泛型包装器
type Executor[T any] struct {
    fn func(string, T)
}

func (e *Executor[T]) Execute(arg1 string, arg2 T) {
    e.fn(arg1, arg2)
}

func NewExecutor[T any](fn func(string, T)) *Executor[T] {
    return &Executor[T]{fn: fn}
}

func main() {
    // 使用示例
    executorA := NewExecutor(doSomthingA)
    executorA.Execute("hello", ArgTypeA{Value: "test"})
    
    executorB := NewExecutor(doSomthingB)
    executorB.Execute("world", ArgTypeB{Value: 42})
}

方案3:使用反射(最灵活,但性能较差)

package main

import (
    "fmt"
    "reflect"
)

type ArgTypeA struct {
    Value string
}

type ArgTypeB struct {
    Value int
}

func doSomthingA(arg1 string, arg2 ArgTypeA) {
    fmt.Printf("doSomthingA: %s, %v\n", arg1, arg2.Value)
}

func doSomthingB(arg1 string, arg2 ArgTypeB) {
    fmt.Printf("doSomthingB: %s, %d\n", arg1, arg2.Value)
}

func executeReflect(fn interface{}, arg1 string, arg2 interface{}) {
    fnValue := reflect.ValueOf(fn)
    args := []reflect.Value{
        reflect.ValueOf(arg1),
        reflect.ValueOf(arg2),
    }
    fnValue.Call(args)
}

func main() {
    executeReflect(doSomthingA, "hello", ArgTypeA{Value: "test"})
    executeReflect(doSomthingB, "world", ArgTypeB{Value: 42})
}

推荐使用方案1(接口),因为它:

  1. 类型安全,编译时检查
  2. 性能较好
  3. 代码清晰易维护
  4. 兼容所有Go版本

如果使用Go 1.18+且需要更强的类型约束,方案2(泛型)也是不错的选择。方案3(反射)应仅在无法使用前两种方案时考虑。

回到顶部