你可以使用reflect.Value
由 Uvelichitel 提出,或者函数地址作为string
获得者fmt.Sprint()或地址为uintptr
获得者reflect.Value.Pointer()(更多内容在答案中如何比较 Go 中的两个函数?),但我建议不要这样做。
由于语言规范不允许比较函数值,也不允许获取他们的地址,您无法保证程序中一次有效的东西将始终有效,包括特定的运行,并包括不同的(未来)Go 编译器。我不会使用它。
由于规范对此很严格,这意味着编译器可以生成例如在运行时更改函数地址的代码(例如卸载未使用的函数,然后在需要时再次加载)。我目前不知道这样的行为,但这并不意味着未来的 Go 编译器不会利用这样的东西。
如果您存储函数地址(以任何格式),则该值不再算作保留函数值。如果没有其他人再“拥有”该函数值,则生成的代码(以及 Go 运行时)将可以“自由”修改/重新定位该函数(从而更改其地址)——而不会违反规范和 Go 的类型安全。所以你不能理所当然地对编译器感到愤怒和责备,而只能对你自己感到愤怒和责怪。
如果您想检查是否重用,可以使用接口值。
假设您需要带有签名的函数:
func(p ParamType) RetType
创建一个接口:
type EventResponse interface {
Do(p ParamType) RetType
}
例如,您可能有一个未导出的struct
类型,并且指向它的指针可以实现您的EventResponse
界面。创建导出函数以返回单个值,因此不会创建新值。
E.g.:
type myEvtResp struct{}
func (m *myEvtResp) Do(p ParamType) RetType {
// Your logic comes here
}
var single = &myEvtResp{}
func Get() EventResponse { return single }
是否真的需要将实现隐藏在包中,并且只创建和“发布”单个实例?不幸的是,是的,因为否则你可以创造其他价值,比如&myEvtResp{}
这可能是不同的指针但仍然具有相同的Do()
方法,但接口包装值可能不相等:
接口值具有可比性。如果两个接口值相等,则它们具有完全相同的动态类型和相等的动态值或者两者都有值nil
.
[...和...]
指针值具有可比性。如果两个指针值指向同一个变量或者两者的值为 nil,则它们相等。指向不同的指针零尺寸变量可能相等也可能不相等。
方式*myEvtResp
实施EventResponse
所以你可以注册它的值(唯一的值,可以通过Get()
)。你可以有一张类型的地图map[EventResponse]bool
您可以在其中存储注册的处理程序、接口值作为键,以及true
作为价值观。使用不在映射中的键对映射进行索引会产生映射值类型的零值。所以如果地图的值类型是bool
,使用不存在的键对其进行索引将导致false
– 告诉它不在地图上。已注册的索引EventResponse
(现有的密钥)将导致存储的值 –true
– 告诉它在地图上,它已经注册了。
您可以简单地检查是否已经注册:
type EventResponseSet map[*EventResponse]bool
func (ers EventResponseSet) Add(r EventResponse) {
if ers[r] {
// warn here
return
}
ers[r] = true
}
Closing:为了避免重复使用,这似乎有点太麻烦了。我同意,而且我不会这么做。但如果你想...