我应该将函数定义为指向结构体的指针还是直接在结构体上定义?

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

Should I define my funcs with a pointer to the struct or just on the struct?

问题

在Go语言中,我似乎有两种选择:

foo := Thing{}
foo.bar()

foo := &Thing{}
foo.bar()
func (self Thing) bar() {
}

func (self *Thing) bar() {
}

使用self Thing还是self *Thing来定义我的函数,哪种方式更好?

编辑:这不是有关方法和函数的重复问题。这个问题与Thing&Thing有关,我认为它足够不同,值得有自己的URL。

英文:

In go I seem to have two options:

foo := Thing{}
foo.bar()

foo := &Thing{}
foo.bar()


func (self Thing) bar() {
}

func (self *Thing) bar() {
}

What's the better way to define my funcs with self Thing or with self *Thing?

Edit: this is not a duplicate of the question about methods and functions. This question has to do with Thing and &Thing and I think it's different enough to warrent it's own url.

答案1

得分: 2

请看一下来自官方FAQ的这个项目:

对于不习惯使用指针的程序员来说,这两个示例之间的区别可能会令人困惑,但实际情况非常简单。在类型上定义方法时,接收器(上述示例中的s)的行为就像它是方法的参数一样。因此,将接收器定义为值还是指针是同一个问题,就像函数参数是值还是指针一样。有几个考虑因素。

首先,也是最重要的,方法是否需要修改接收器?如果需要修改,接收器必须是指针。(切片和映射作为引用,所以它们的情况稍微复杂一些,但是例如要在方法中更改切片的长度,接收器仍然必须是指针。)在上面的示例中,如果pointerMethod修改了s的字段,调用者将看到这些更改,但是valueMethod是使用调用者的参数的副本调用的(这就是传递值的定义),所以它所做的更改对调用者是不可见的。

顺便说一下,指针接收器与Java中的情况完全相同,尽管在Java中指针是隐藏在封面下的;而Go的值接收器才是不寻常的。

其次是效率的考虑。如果接收器很大,比如一个大的结构体,使用指针接收器会更便宜。

接下来是一致性的考虑。如果类型的某些方法必须具有指针接收器,那么其余的方法也应该具有指针接收器,以便无论类型如何使用,方法集都是一致的。有关详细信息,请参阅有关方法集的部分。

对于基本类型、切片和小结构等类型,值接收器非常便宜,因此除非方法的语义要求使用指针,否则值接收器是高效且清晰的。

英文:

Take a look at this item from the official FAQ:

> For programmers unaccustomed to pointers, the distinction between
> these two examples can be confusing, but the situation is actually
> very simple. When defining a method on a type, the receiver (s in the
> above examples) behaves exactly as if it were an argument to the
> method. Whether to define the receiver as a value or as a pointer is
> the same question, then, as whether a function argument should be a
> value or a pointer. There are several considerations.
>
> First, and most important, does the method need to modify the
> receiver? If it does, the receiver must be a pointer. (Slices and maps
> act as references, so their story is a little more subtle, but for
> instance to change the length of a slice in a method the receiver must
> still be a pointer.) In the examples above, if pointerMethod modifies
> the fields of s, the caller will see those changes, but valueMethod is
> called with a copy of the caller's argument (that's the definition of
> passing a value), so changes it makes will be invisible to the caller.
>
> By the way, pointer receivers are identical to the situation in Java,
> although in Java the pointers are hidden under the covers; it's Go's
> value receivers that are unusual.
>
> Second is the consideration of efficiency. If the receiver is large, a
> big struct for instance, it will be much cheaper to use a pointer
> receiver.
>
> Next is consistency. If some of the methods of the type must have
> pointer receivers, the rest should too, so the method set is
> consistent regardless of how the type is used. See the section on
> method sets for details.
>
> For types such as basic types, slices, and small structs, a value
> receiver is very cheap so unless the semantics of the method requires
> a pointer, a value receiver is efficient and clear.

答案2

得分: 1

没有一个明确的答案,但它们是完全不同的。当你不使用指针时,你是"按值传递",这意味着你调用的对象将是不可变的(修改的是副本);当你使用指针时,你是"按引用传递"。我会说更常见的是使用指针的方式,但这完全取决于具体情况,没有"更好的方式"。

如果你看一下各种编程框架/类库,你会看到很多例子,作者故意选择按值或按引用进行操作。例如,在C# .NET中,这是结构体和类之间的根本区别,而像GuidDateTime这样的类型被故意实现为结构体(值类型)。再次强调,我认为指针更常常是更好的选择(如果你浏览一下.NET,几乎所有东西都是类,引用类型),但这确实取决于你希望通过该类型实现什么,或者你希望消费者/其他开发人员如何与之交互。你可能需要考虑性能和并发性(也许你希望所有东西都按值传递,这样就不必担心类型上的并发操作,也许你需要一个指针,因为对象的内存占用很大,复制它会使程序变得过慢或消耗过多资源)。

英文:

There isn't a clear answer but they're completely different. When you don't use a pointer you 'pass by value' meaning the object you called it on will be immutable (modifying a copy), when you use the pointer you 'pass by reference'. I would say more often you use the pointer variety but it is completely situational, there is no 'better way'.

If you look at various programming frameworks/class libraries you will see many examples where the authors have deliberately chosen to do things by value or reference. For example, in C# .NET this is the fundamental difference between a struct and a class and types like Guid and DateTime were deliberately implemented as structs (value type). Again, I think the pointer is more often the better choice (if you look through .NET almost everything is a class, the reference type), but it definitely depends on what you wish to achieve with the type and/or how you want consumers/other developers to interact with it. Your may need to consider performance and concurrency (maybe you want everything to be by value so you don't have to worry about concurrent ops on a type, maybe you need a pointer because the objects memory footprint is large and copying it would make your program too slow or consumptive).

huangapple
  • 本文由 发表于 2016年3月16日 02:53:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/36019754.html
匿名

发表评论

匿名网友

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

确定