0

Go 中函数变量的正常使用允许它们仅与 nil 进行比较,而不是相互比较。这样做的原因(正如我向我解释的那样)是因为 Go 有闭包,所以相等的定义是模糊的。如果我有两个不同的闭包,它们具有绑定到局部变量的不同值,但它们使用相同的底层函数,它们应该被视为相等还是不相等?

但是,我确实希望能够进行这样的比较。特别是,我有这样的代码(除了,在我的真实代码中,检查实际上是必要的 - 这只是一个虚拟示例),我将函数指针与函数文字进行比较:

func getFunc(which bool) (func ()) {
    if which {
        return func1
    } else {
        return func2
    }
}

func func1() { }
func func2() { }

f := getFunc(true)
if f == func1 {
    fmt.Println("func1")
} else {
    fmt.Println("func2")
}

有什么办法,例如使用反射不安全包,让它工作?

4

3 回答 3

4

您可以按名称比较函数:

f := getFunc(true)
f1 := runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
f2 := runtime.FuncForPC(reflect.ValueOf(func1).Pointer()).Name()
if f1 == f2 {
    fmt.Println("func1")
} else {
    fmt.Println("func2")
}

但这依赖于reflectruntime包。这样做可能不是一个好主意。

你真的需要比较功能吗?如果可能的话,我会考虑另一种选择。

于 2013-08-05T20:51:36.773 回答
2

扩展@Luke's answer,看来我可以直接测试指针相等性。请注意,这真的很不确定。引用reflect.Value.Pointer()文档:

如果 v 的 Kind 是 Func,则返回的指针是底层代码指针,但不一定足以唯一标识单个函数。唯一的保证是当且仅当 v 是一个 nil func 值时结果为零。

也就是说,您可以执行以下操作:

f := getFunc(true)
f1 := reflect.ValueOf(f).Pointer()
f2 := reflect.ValueOf(func1).Pointer()
eq := f1 == f2

请注意,我确实针对这个新版本进行了一系列测试(我曾经用这些测试对@Luke 的答案产生的代码进行回归测试),并且它们都通过了,这让我相信reflect文档中发出的警告可能可以忽略,但是,忽略文档真的不是一个好主意......

于 2013-08-08T01:23:22.503 回答
1

如果要比较的所有函数都具有相同的签名,则可以执行以下操作:

type cmpFunc struct {
    f func()
    id uint64
}

func (c *cmpFunc) call() { c.f() }
func (c *cmpFunc) equals(other *cmpFunc) { return c.id == other.id }

makeComparable(f func()) *cmpFunc {
    return &cmpFunc{f, get_uniq_id()}
}

get_uniq_id盒子上说的在哪里。这有点难看,因为 Go 没有()重载,如果你想为一般函数执行此操作,没有泛型或多或少是不可能的。但这应该可以很好地满足您的目的。

于 2013-08-05T21:51:22.230 回答