golang 中函数和方法的区别

1,916 阅读2分钟

函数和方法

Go 语言中同时有函数和方法。一个方法就是一个包含了接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针。所有给定类型的方法属于该类型的方法集。

函数的定义和使用可以如下

func MyFunction(a, b int) int {
  return a + b
}
// Usage:
// MyFunction(1, 2)

方法的定义和使用可以如下

type MyInteger int
func (a MyInteger) MyMethod(b int) int {
  return a + b
}
// Usage:
// var x MyInteger = 1
// x.MyMethod(2)

区别

照我的理解,两者应该是不一致的,因为一个包括了接受者,一个不包括接受者。直到最近看项目github.com/gogf/gf中的时间轮源码的时候,看到了一个比较有意思的操作。

首先定义了一个JobFunc类型,无参数和无返回值的函数

type JobFunc = func()

后面的代码把wheelporceed方法作为JobFunc传入了函数

func (w *wheel) proceed() {
	...
}
func (w *wheel) addEntry(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Entry {...}

t.wheels[i-1].addEntry(n, w.proceed, false, gDEFAULT_TIMES, STATUS_READY)

也就是说,*wheel.proceed作为JobFunc类型,传入到了函数addEntry之中,这个就和我预期的不一致了(所以时间轮的源码,花了些时间才看明白)。

整一个可运行的 demo 如下:

package main

import "fmt"

type Process func()

type Person struct {
	name string
}

func (p *Person) changeName() {
	p.name = "zhangsan"
}

func main() {
	p := new(Person)
	var fn1 Process = p.changeName
	fn1()
    	fmt.Println(p.name)
    
    	var fn2 Process = func() {
		fmt.Println("haha")
	}
	fn2()
}

在 demo 中,方法和函数都是可以作为Process类型来进行运行。通过这个 demo,可以明白:虽然通过定义上来说,两者是有区别的,但是实际上方法也是一种函数,可以作为普通的函数来进行传递和使用

困惑的缘由

之所以潜意识里认为函数和方法有区分的另外一个原因是曾经看过一篇介绍defer的博客中说道: 结构体的方法作为 defer的执行操作时,执行了一种如下的变换

Image for post

现在看来这个仅仅是作者的类比行为,并不是实际发生的操作。