英文:
Function signatures as type
问题
我有一个函数签名的类型定义如下:
type MessageListener func(msg *Message) bool
在一个结构体中,我定义了以下函数,它基本上使用上面类型定义的函数签名注册一个监听器:
func (wm *WM) AddListener(listener *MessageListener) error {
...
}
我的服务结构体如下:
type Service struct {
...
}
func (service *Service) receiveMessage(msg *Message) bool {
...
}
为什么当我执行以下操作时会出现不兼容的类型错误:
wm.AddListener(&service.receiveMessage)
难道 service.receiveMessage
不是 MessageListener
类型吗?
英文:
I have a type for a function signature defined as
type MessageListener func(msg *Message) bool
In a struct, I have the following function defined which basically registers a Listener with the function signature defined by the type above
func (wm *WM) AddListener(listener *MessageListener) error {
...
}
My service struct
type Service struct {
...
}
func (service *Service) receiveMessage(msg *Message) bool {
...
}
Why am I getting an incompatible type error when I do
wm.AddListener(&service.receiveMessage)
Doesn't service.receiveMessage
have the type MessageListener
?
答案1
得分: 2
因为该函数是一个引用,所以你不需要使用任何指针或尝试获取函数的地址。
只需声明:
func (wm *WM) AddListener(listener MessageListener) error {
...
}
// 并且去掉获取函数地址的部分
wm.AddListener(service.receiveMessage)
英文:
Because the function is a reference you do not need to use any pointers or try to take an address of the function.
Just declare
func (wm *WM) AddListener(listener MessageListener) error {
...
}
// and get rid of taken address of function
wm.AddListener(service.receiveMessage)
答案2
得分: 1
MessageListener
已经是一个函数指针,所以你可以简单地将AddListener
定义为:
func (wm *WM) AddListener(listener MessageListener) error {
//...
}
然后可以通过调用wm.AddListener(service.receiveMessage)
来注册一个监听器。
根据地址运算符的规范:
> 对于类型为T的操作数x,地址操作&x生成一个类型为*T的指针,指向x。操作数必须是可寻址的,也就是说,要么是一个变量、指针间接引用或切片索引操作;或者是可寻址结构操作数的字段选择器;或者是可寻址数组的数组索引操作。作为对可寻址要求的例外,x也可以是一个(可能带括号的)复合字面量。如果对x的求值会导致运行时恐慌,那么对&x的求值也会导致恐慌。
因此,你不能获取函数/方法的地址。然而,你可以获取存储函数/方法的变量的地址。可以将监听器声明为:
func (wm *WM) AddListener(listener *MessageListener) error {
//...
}
但是在注册监听器时,需要进行以下操作:
var fn MessageListener = service.receiveMessage
wm.AddListener(&fn)
在调用监听器时:
msg := Message{//...}
(*wm.listener)(&msg)
这样做会增加不必要的代码,将函数变量转换为指针,然后再转换回函数变量。
英文:
MessageListener
is already a function pointer so you can simply define AddListener
as
func (wm *WM) AddListener(listener MessageListener) error {
//...
}
A listener then can be registered by calling wm.AddListener(service.receiveMessage)
.
From specifications of Address operator:
> For an operand x of type T, the address operation &x generates a pointer of type *T to x. The operand must be addressable, that is, either a variable, pointer indirection, or slice indexing operation; or a field selector of an addressable struct operand; or an array indexing operation of an addressable array. As an exception to the addressability requirement, x may also be a (possibly parenthesized) composite literal. If the evaluation of x would cause a run-time panic, then the evaluation of &x does too.
So you can't takes the address of a function/method. However, you can takes the address of a variable that stores function/method. It is valid to declare the listener as
func (wm *WM) AddListener(listener *MessageListener) error {
//...
}
but then when registering a listener, do the following:
var fn MessageListener = service.receiveMessage
wm.AddListener(&fn)
and when calling the listener:
msg := Message{//...}
(*wm.listener)(&msg)
which adds unnecessary codes for converting function variable to pointer then converting back to function variable.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论