在结构上调用函数的正确用法是什么?

时间:2016-05-25 19:57:57

标签: function go struct idioms

我是新手(来自python和ruby)并且想知道在结构上调用函数的惯用方法是什么?

大多数情况下,我想知道是否应该使用点运算符来调用函数或使用我的类型作为参数之一。是否更好地使用指针?

  • 指针vs无指针?
  • dot vs argument? *

我可以这样做吗?

package main

import "fmt"

func main() {
    me := Person{firstname: "John", lastname: "Doe", age: 40}
    fmt.Println(me.fullname())
}

type Person struct {
    firstname string
    lastname  string
    age       int
}

func (p Person) fullname() string {
    return p.firstname + p.lastname
}

或者这样

package main

import "fmt"

func main() {
    me := Person{firstname: "John", lastname: "Doe", age: 40}
    fmt.Println(fullname(&me))
}

type Person struct {
    firstname string
    lastname  string
    age       int
}

func fullname(p *Person) string {
    return p.firstname + p.lastname
}

1 个答案:

答案 0 :(得分:2)

没有一种正确的方法可以做到这一点。理解两者之间的差异并在适当的时候应用它们非常重要。此外,您的功能并不具有可比性。在一种情况下,您已经使用类型为Person的接收器定义了一个函数,并且它通过值传递,这意味着实例的副本被生成并推送到堆栈上。在另一个实例中,该函数是独立的,并且您将Person引用传递给它。您还可以通过使用“实例方法”通过引用来传递接收类型指针。

所以在第一种情况下;

func (p Person) fullname() string {
    return p.firstname + p.lastname
}

你将函数称为p.fullname(),并将p的副本压入堆栈。如果您在此函数中进行了赋值,则不会修改p,则会修改范围中的实例。因此,对于setter或任何旨在更改对象状态的函数,这根本不是一个选项。

详细说明我所讨论的替代方案,你可以这样做;

func (p *Person) fullname() string {
    return p.firstname + p.lastname
}

仍允许您在p.fullname()之类的实例上调用它,但是,传递给该函数的是*Person类型的引用。因此,这将是实现在结构上设置值的函数的典型选择。

现在是你的第二个例子;

func fullname(p *Person) string {
    return p.firstname + p.lastname
}

没有接收器并传递指针。这意味着你称之为fullname(p)。该函数未导出,因此它仅在声明它的包中可用,在本例中为main。为了与(可能)更熟悉的语言相似,这就像在C ++中定义一个函数,其他两个函数在类中定义'方法'或函数。另外两个将与“通过价值传递”或“通过参考传递”进行比较。这通过引用传递,与任何类型无关。

在您的示例中,出于性能原因,我总是使用func (p *Person) fullname() string。在我看来,使用pass by value alternative func (p Person) fullname() string的最合适的时间是你想要强制实现不变性。如果你想进行操作,最好是生成一个新对象而不是修改现有对象(很多集合库都是这样运行的,例如C#中的LINQ总是生成一个新集合,并尝试在查询期间修改集合将导致异常)然后你可能想要一个值类型接收器。

希望有所帮助。如果其中任何一个仍然是造成混淆的原因,我可以用更多信息进行更新。

相关问题