Go中的函数和方法有啥区别?

Posted

技术标签:

【中文标题】Go中的函数和方法有啥区别?【英文标题】:Whats the difference of functions and methods in Go?Go中的函数和方法有什么区别? 【发布时间】:2012-01-06 00:08:12 【问题描述】:

我正在尝试开始使用 Go,documentation 非常好。我在文档中没有找到的是函数和方法之间的区别。

据我目前的理解:函数是“全局的”,这意味着我不必导入包来使用函数,它们总是存在的。方法绑定到包。这是正确的吗?

【问题讨论】:

术语“方法”和“函数”只是历史惯例。简单的语言暗示“如何”与“什么”,事实并非如此,因此令人困惑。我们可能认为“函数”是数学的,但事实并非如此,因此令人困惑。事实上,“方法”只是从面向对象语言中采用的与特定类型相关联的,而“函数”是从函数是独立的过程语言中采用的。我想你可以在 go 中看到那些简单的解释:在类型上调用方法,而不是函数。 【参考方案1】:

据我目前的理解:函数是“全局的”,这意味着我不必导入包来使用函数,它们总是存在的。方法绑定到包。这是正确的吗?

不,这是不正确的。 builtin 包中只有几个函数始终可用。其他所有内容都需要导入。

“方法”一词是由面向对象编程提出的。在 OOP 语言(例如 C++)中,您可以定义一个“类”,它封装了属于一起的数据和函数。类中的那些函数称为“方法”,您需要该类的实例来调用这样的方法。

在 Go 中,术语基本相同,尽管 Go 不是经典意义上的 OOP 语言。在 Go 中,接受接收者的函数通常称为方法(可能只是因为人们仍然习惯于 OOP 的术语)。

所以,例如:

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)

【讨论】:

我不会说“Go 不是 OOP 语言”。 Go 恕我直言,它非常喜欢组合而不是继承,它为前者提供了额外的语法糖,并完全摆脱了后者。 我不确定。恕我直言,OOP 范式的一部分也是子类化 + 类型层次结构。这在 Go 中是不可能的(幸运的是!)。当然,Go 提供了其他方法(即嵌入)来解决类似问题,但它们不是 1:1 的替代品。但是让我们停止使用术语-bikeshedding :D 谢谢,我终于明白了!一个缺失的部分是,方法总是与类型结合使用。 @tux21b 谢谢你的例子。一个问题,你的方法包含一个invalid operation: a + b (mismatched types MyInteger and int)。为了使其工作,您需要将a 转换为int 或将b 转换为MyInteger。例如:return int(a) + breturn a + MyInteger(b)。在第二种情况下,您需要将方法签名中的返回值类型更改为MyInteger 而不是int。 playground 我要指出这句话说:“Go 不是经典意义上的 OOP 语言”,并不是说它不是 OOP 语言【参考方案2】:

Tux 的答案很棒,但我想通过使用 Go 的方法和 structs 来扩充它(因为这是我经常使用它的地方)。因此,让我们假设您想要构建一些东西来计算三角形的各种方法。你从struct开始:

type Triangle struct 
    a, b, c float64

然后你想添加一些函数来计算周长和正方形:

func valid(t *Triangle) error 
    if t.a + t.b > t.c && t.a + t.c > t.b && t.b + t.c > t.a 
        return nil
    
    return errors.New("Triangle is not valid")


func perimeter(t *Triangle) (float64, error) 
    err := valid(t)
    if err != nil 
        return -1, err
    

    return t.a + t.b + t.c, nil


func square(t *Triangle) (float64, error) 
    p, err := perimeter(t)
    if err != nil 
        return -1, err
    

    p /= 2
    s := p * (p - t.a) * (p - t.b) * (p - t.c)
    return math.Sqrt(s), nil

现在你得到了你的工作程序Go Playground。在这种情况下,您的函数接受一个参数(指向三角形的指针)并执行某些操作。在 OOP 中,人们可能已经创建了一个类,然后添加了方法。我们可以将我们的结构体视为一种带有字段的类,现在我们添加方法:

func (t *Triangle) valid() error 
    if t.a + t.b > t.c && t.a + t.c > t.b && t.b + t.c > t.a 
        return nil
    
    return errors.New("Triangle is not valid")


func (t *Triangle) perimeter() (float64, error) 
    err := t.valid()
    if err != nil 
        return -1, err
    

    return t.a + t.b + t.c, nil


func (t *Triangle) square() (float64, error) 
    p, err := t.perimeter()
    if err != nil 
        return -1, err
    

    p /= 2
    s := p * (p - t.a) * (p - t.b) * (p - t.c)
    return math.Sqrt(s), nil

我们有一个完整的 working example

请注意,它看起来真的很像对象的方法。

【讨论】:

【参考方案3】:

这里有详细解释-https://anil.cloud/2017/01/26/golang-functions-methods-simplified/

Go 中的函数遵循以下语法:

func FunctionName(Parameters...) ReturnTypes...

例子:

func add(x int, y int) int

执行:

  add(2,3) 

方法类似于函数,但附加到类型(称为接收器)。官方指南指出“方法是具有特殊接收器参数的函数”。接收者出现在 func 关键字和方法名之间。方法的语法是:

func (t ReceiverType) FunctionName(Parameters...) ReturnTypes...

例子:

func (t MyType) add(int x, int y) int

执行:

type MyType string
t1 := MyType("sample")
t1.add(1,2)

现在让我们将指针带入表中。 Go lang 是按值传递的,这意味着将参数的新副本传递给每个函数/方法调用。要通过引用传递它们,您可以使用指针。

参数/参数列表中带有指针的函数的语法。

func FunctionName(*Pointers...,Parameters...) ReturnTypes...

例子

func add(t *MyType, x int, y int) int

执行:

type MyType string
t1 := MyType("sample")
add(&t1,4,5)

对于方法,接收者类型也可以是指针。带指针的方法语法(作为接收者)

func (*Pointer) FunctionName(Parameters...) ReturnTypes...

例子

func (t *MyType) add(x int, y int) int

执行:

type MyType string
t1 := MyType("sample")
t1.add(2,3)

请注意,我们仍然可以编写 t1.add() 来执行带有指针接收器的方法(即使“t1”不是指针),Go 会将其解释为 (&t1).add()。类似地,也可以使用指针调用具有值接收器的方法,在这种情况下,Go 会将 p.add() 解释为 (*p).add()(其中“p”是指针)。这仅适用于方法,不适用于函数。

带有指针接收器的方法对于获得类似“Java”的行为非常有用,其中该方法实际上修改的是接收器指向的值,而不是它的副本。

【讨论】:

只是想知道,这会破坏目的,因为关于不变性的整个论点不是修改值?

以上是关于Go中的函数和方法有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

在 C# 中,类中的析构函数和 Finalize 方法有啥区别?

重载和重写有啥区别

Go 中的 path 和 path.filepath 包有啥区别

php静态方法-“静态函数”和“公共静态函数”有啥区别?

为啥 Go 中有两种声明变量的方式,有啥区别,用哪一种?

js中的事件和方法有啥区别