函数签名作为类型

huangapple go评论96阅读模式
英文:

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.

huangapple
  • 本文由 发表于 2017年8月1日 14:15:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/45430207.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定