函数声明语法:函数名前括号内的东西

Posted

技术标签:

【中文标题】函数声明语法:函数名前括号内的东西【英文标题】:Function declaration syntax: things in parenthesis before function name 【发布时间】:2016-03-06 01:09:53 【问题描述】:

很抱歉,我无法在问题标题中更具体,但我正在阅读一些 Go 代码,并且遇到了这种形式的函数声明:

func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) 
    ...

来自https://github.com/mattermost/platform/blob/master/api/context.go

func (s *GracefulServer) BlockingClose() bool 
    ...

来自https://github.com/braintree/manners/blob/master/server.go

括号之间的(h handler)(s *GracefulServer) 是什么意思?整个函数声明是什么意思,考虑到括号之间的东西的含义?

编辑

这不是Whats the difference of functions and methods in Go?的重复:这个问题来找我是因为我不知道函数名前括号中的东西是什么,而不是因为我想知道函数和方法之间有什么区别......如果我知道这个声明是一种方法,我一开始就不会提出这个问题。如果有一天有人和我有同样的疑问,我不相信她会去寻找“golang方法”,因为她不知道是这样的。这就像想知道数学表达式之前的字母“sigma”是什么意思(不知道它的意思是求和),有人说它是求和与其他事物之间区别的重复。

此外,对这个问题(“它是一个接收器”)的简短回答不能回答“函数和方法之间有什么区别”。

【问题讨论】:

@Volker 然后发表免责声明,称 *** 上的 Go 人员只回答不在 Tour of Go 上的问题。在 Haskell 社区中,人们可以提出How can I get nth element from the list in Haskell? 之类的问题,该问题位于Learn you a Haskell for Great Good 的简介中,并且可以毫不费力地回答他们的问题。 当我有这个问题时,我首先去了Go Tour。我检查了所有的“功能”标题,但没有一个例子涵盖这一点。 tour.golang.org/basics/4 tour.golang.org/basics/5 如果你不知道扩展方法和接口,你不会看到“方法就是函数”的标题。这个问题对谷歌索引是有效的和伟大的。重复的旗帜***者需要点亮。 感谢您没有具体说明您的问题,因为这足以帮助我找到答案! 你问的正是我搜索的内容,这是一个有效的问题。谢谢你。我阅读了所有类型的函数定义,但没有人解释这一点。我仍然尝试写我的 nube 问题并找到了这个。 非常感谢您提出这个问题并以这种方式表达它! 【参考方案1】:

这称为“接收器”。在第一种情况下(h handler) 它是一个值类型,在第二种情况下(s *GracefulServer) 它是一个指针。这在 Go 中的工作方式可能与其他一些语言有所不同。然而,接收类型的工作方式或多或少类似于大多数面向对象编程中的类。这是您调用该方法的东西,就像我将一些方法 A 放在某个类 Person 中,那么我需要一个 Person 类型的实例才能调用 A(假设它是一个实例方法而不是静态的!)。

这里的一个问题是接收者像其他参数一样被推入调用堆栈,所以如果接收者是一个值类型,比如handler,那么你将处理你调用的东西的副本返回调用范围后,h.Name = "Evan" 之类的方法不会持续存在。出于这个原因,任何期望改变接收者状态的东西都需要使用指针或返回修改后的值(如果你正在寻找它,它会提供更多的不可变类型范例)。

这是规范中的相关部分; https://golang.org/ref/spec#Method_sets

【讨论】:

很好的解释和额外的业力点,用于链接到相关规范 golang 之旅也有一些非常有用的例子tour.golang.org/methods/1 也可能值得注意:s.BlockingClose() 等价于(&s).BlockingClose()。这是因为 Go 识别(从方法 BlockingClose 的声明)接收者 s 应该是一个指针并将其视为指针。 我认为这是对如何将方法声明为函数的更好解释,例如:golang.org/ref/spec#Method_declarations【参考方案2】:

这意味着ServeHTTP 不是一个独立的函数。函数名前的括号是定义这些函数将操作的对象的 Go 方式。所以,ServeHTTP 本质上是一个 handler 类型的方法,可以使用任何 handler 类型的对象,比如 h 来调用。

h.ServeHTTP(w, r)

它们也被称为接收器。 There 是定义它们的两种方式。如果要修改接收器,请使用如下指针:

func (s *MyStruct) pointerMethod()   // method on pointer

如果您不需要修改接收器,您可以将接收器定义为如下值:

func (s MyStruct)  valueMethod()     // method on value

来自 Go 游乐场的This 示例演示了这一概念。

package main

import "fmt"

type Mutatable struct 
    a int
    b int


func (m Mutatable) StayTheSame() 
    m.a = 5
    m.b = 7


func (m *Mutatable) Mutate() 
    m.a = 5
    m.b = 7


func main() 
    m := &Mutatable0, 0
    fmt.Println(m)
    m.StayTheSame()
    fmt.Println(m)
    m.Mutate()
    fmt.Println(m)

上述程序的输出是:

&0 0
&0 0
&5 7

【讨论】:

如果你的结构类似于type Logger struct lgr *zap.Logger ,操作这个结构/对象的函数(它们将调用记录器)是否应该有指针或值的接收器?【参考方案3】:

如果你熟悉c# extension methods,

go 方法 (a function with a special receiver argument) 例如

func (v Vertex) Abs() float64

类似于c#扩展方法

static float Abs( this Vertex v);

值类型和指针的区别见evanmcdonnal’s answer

【讨论】:

以上是关于函数声明语法:函数名前括号内的东西的主要内容,如果未能解决你的问题,请参考以下文章

Scala中调用函数或者方法为啥有时候需要括号,有时候不需要括号

JavaScript箭头函数

C++学习_C++概述

js 箭头函数

javascript篇-----函数

JavaScript定义函数