如何在最近的Go weekly中比较两个函数的指针相等性?

如何在最近的Go weekly中比较两个函数的指针相等性?,go,function-pointers,go-reflect,Go,Function Pointers,Go Reflect,在Go中,有没有办法比较两个非nil函数指针以测试相等性?我的平等标准是指针平等。如果不允许,是否有任何特殊原因导致不允许指针相等 到目前为止,如果我尝试以直截了当的方式进行此操作: package main import "fmt" func SomeFun() { } func main() { fmt.Println(SomeFun == SomeFun) } 我明白了 据我所知,这种行为是最近才出现的 我使用reflect软件包找到了答案;然而,Atom在下面指出,这实

在Go中,有没有办法比较两个非nil函数指针以测试相等性?我的平等标准是指针平等。如果不允许,是否有任何特殊原因导致不允许指针相等

到目前为止,如果我尝试以直截了当的方式进行此操作:

package main

import "fmt"

func SomeFun() {
}

func main() {
    fmt.Println(SomeFun == SomeFun)
}
我明白了

据我所知,这种行为是最近才出现的


我使用reflect软件包找到了答案;然而,Atom在下面指出,这实际上会产生未定义的行为。有关更多信息和可能的替代解决方案,请参见Atom的帖子

package main

import "fmt"
import "reflect"

func SomeFun() { }

func AnotherFun() { }

func main() {
    sf1 := reflect.ValueOf(SomeFun)
    sf2 := reflect.ValueOf(SomeFun)
    fmt.Println(sf1.Pointer() == sf2.Pointer())

    af1 := reflect.ValueOf(AnotherFun)
    fmt.Println(sf1.Pointer() == af1.Pointer())
}
产出:

true
false

现在不允许进行映射和函数值比较(除了 根据Go 1计划,与nil进行比较。函数相等是 在某些上下文中有问题,映射相等比较指针,而不是 地图的内容

函数相等在闭包存在时是有问题的(当 两个闭包相等吗?)


解决方法取决于具体情况。我不得不改变几个比较函数的地方。有一次,我只是做了一些不同的事情,所以我不需要再比较它们了。在另一个例子中,我使用一个struct将函数与可比较的字符串相关联,比如

type nameFunc struct {
    name string
    fval func()
}

我只需要比较两个函数,因此最简单的方法是保留这些结构的一个片段,并根据需要扫描该片段,比较name字段并分派fval。如果你有很多,你可以用地图代替。如果函数具有不同的签名,则可以使用接口,依此类推。

请注意,等式和标识之间存在差异。运算符
==
=正在比较等价值(比较通道时除外),而不是标识值。因为这些运算符试图不混合相等和标识,所以Go1在这方面比前Go1更一致

函数相等不同于函数恒等式


不允许
==
的一个原因=是性能。例如,以下闭包未使用其环境中的任何变量:

f := func(){fmt.Println("foo")}
不允许比较函数使编译器能够为闭包生成单个实现,而不需要运行时(在运行时)创建新的闭包。因此,从性能的角度来看,不允许函数比较的决定是一个好的决定


关于使用
reflect
包确定函数标识,代码如下

func SomeFun()    {}
func AnotherFun() {}

func main() {
    sf1 := reflect.ValueOf(SomeFun)
    sf2 := reflect.ValueOf(SomeFun)
    fmt.Println(sf1.Pointer() == sf2.Pointer())  // Prints true

    af1 := reflect.ValueOf(AnotherFun)
    fmt.Println(sf1.Pointer() == af1.Pointer())  // Prints false
}
依赖于未定义的行为。对于程序将打印什么,没有任何保证。编译器可能决定将
SomeFun
另一个fun
合并到一个实现中,在这种情况下,第二个print语句将打印
true
。事实上,绝对不能保证第一个print语句将打印
true
(它可能在其他一些Go1编译器和运行时下打印
false


对原始问题的正确答案是:

package main

import "fmt"

func F1() {}
func F2() {}

var F1_ID = F1  // Create a *unique* variable for F1
var F2_ID = F2  // Create a *unique* variable for F2

func main() {
    f1 := &F1_ID  // Take the address of F1_ID
    f2 := &F2_ID  // Take the address of F2_ID

    // Compare pointers
    fmt.Println(f1 == f1)  // Prints true
    fmt.Println(f1 == f2)  // Prints false
}

我认为这并不能真正回答我的问题。我知道我使用的方式不允许进行函数值比较;我想知道的是是否有任何解决办法。我还想知道为什么函数指针相等有问题。@BurntSushi5这是个好问题。当然,有些语言确实有闭包,您可以在其中比较函数指针。在这些语言中,在不同变量上关闭的同一函数的闭包测试结果并不相等。这似乎只是在回避代表权问题。这在围棋中是不允许的,没有根本的根本原因。它可以以明智、可靠和一致的方式实施;他们只是选择不这样做。@peterSO回应您的编辑:这并不能真正回答为什么不能使用函数指针相等。如果两个函数指针指向相同的内存位置,那么它们应该相等。它不会像函数相等的一般形式那样有用,但也不会无用。@tchrist我完全理解。然而,我要问的是指针相等。那里不应该有任何肮脏的封闭;两个函数指针是否指向同一内存位置。它不像你所建议的那么复杂,但它确实有点意思。reflect软件包就是我想要的。我已经用答案更新了我的OP。太好了。我也通过改变我的方法来解决这个问题。感谢您提供有关保留函数的字符串表示形式的提示。对于我正在做的事情(允许在库中使用任意回调函数),它不会很好地工作,但它可能会很有用。我已经用答案更新了我的OP。反应很好。非常感谢。你回答“为什么”肯定会得到表扬,但我对你对我最初问题的回答感到困惑。它似乎在测试变量F1_ID和F2_ID的标识,而不是函数F1和F2的标识。例如,如果我有'var F1_ID2=F1',那么如果我们测试函数标识,&F1_ID==&F1_ID2将返回true;但结果是错误的。而且,你对我使用reflect的方法的批评让我担心。你不是在暗示在Go中测试函数标识是不可能保证的吗?注释1:最后一段代码中的假设是某个特定函数有一个ID。注释2:reflect
包不是语言规范的一部分,因此,使用它不能保证函数标识,但是
Pointer()
方法确实在当前实现中工作得很好Go1中的函数没有标识,因此编译器没有义务跟踪函数标识。我想说的是,语言规范意味着,如果
package main

import "fmt"

func F1() {}
func F2() {}

var F1_ID = F1  // Create a *unique* variable for F1
var F2_ID = F2  // Create a *unique* variable for F2

func main() {
    f1 := &F1_ID  // Take the address of F1_ID
    f2 := &F2_ID  // Take the address of F2_ID

    // Compare pointers
    fmt.Println(f1 == f1)  // Prints true
    fmt.Println(f1 == f2)  // Prints false
}