如何在最新的Go Week中比较指针相等的两个函数?

Bur*_*hi5 26 function-pointers go go-reflect

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

截至目前,如果我试图以直截了当的方式做到这一点:

package main

import "fmt"

func SomeFun() {
}

func main() {
    fmt.Println(SomeFun == SomeFun)
}
Run Code Online (Sandbox Code Playgroud)

我明白了

./func-pointers.go:12: invalid operation: SomeFun == SomeFun (func can only be compared to nil)
Run Code Online (Sandbox Code Playgroud)

我的理解是这种行为最近被引入.


我用反射包找到了答案; 但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())
}
Run Code Online (Sandbox Code Playgroud)

输出:

true
false
Run Code Online (Sandbox Code Playgroud)

小智 43

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

功能平等与功能标识不同.


不允许==!=使用函数类型的一个原因是性能.例如,以下闭包不使用其环境中的任何变量:

f := func(){fmt.Println("foo")}
Run Code Online (Sandbox Code Playgroud)

不允许比较函数使编译器能够为闭包生成单个实现,而不是要求运行时创建新的闭包(在运行时).因此,从性能角度来看,禁止功能比较的决定是一个很好的决定.


关于使用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
}
Run Code Online (Sandbox Code Playgroud)

依赖于未定义的行为.无法保证程序将打印什么.编译器可以决定将合并SomeFun,并AnotherFun成一个单一的实现,在这种情况下,第二个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
}
Run Code Online (Sandbox Code Playgroud)


Son*_*nia 5

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

type nameFunc struct {
    name string
    fval func()
}
Run Code Online (Sandbox Code Playgroud)

我只需要比较几个函数,因此最简单的方法是保留这些结构的切片并根据需要扫描切片,比较 name 字段并调度 fval。如果你有很多,你可以使用地图代替。如果你的函数有不同的签名,你可以使用接口,等等。