可选参数?

dev*_*vyn 411 overloading go

Can Go有可选参数吗?或者我可以只定义两个具有相同名称和不同数量的参数的函数?

And*_*are 391

Go没有可选参数,也不支持方法重载:

如果不需要进行类型匹配,则简化方法调度.使用其他语言的经验告诉我们,使用具有相同名称但签名不同的各种方法偶尔会有用,但在实践中它也可能令人困惑和脆弱.仅根据名称进行匹配并要求在类型中保持一致性是Go类型系统中的主要简化决策.

  • 我打算走出困境,不同意这个选择.语言设计者基本上已经说过,"我们需要函数重载来设计我们想要的语言,因此make,range等基本上都是重载的,但如果你想要函数重载来设计你想要的API,那就太难了." 一些程序员滥用语言功能这一事实并不是摆脱该功能的理由. (68认同)
  • @ Mk12`make`是一种语言结构,上面提到的规则不适用.请参阅[此相关问题](http://stackoverflow.com/q/18512781/1643939). (57认同)
  • 那么`make`是一个特例吗?或者它甚至没有真正实现为功能...... (53认同)
  • @Tom 他们认为函数重载是滥用行为,但 goto 就很好......(╯°□°)╯︵┻━┻ (32认同)
  • 方法过载 - 理论上的一个好主意,当实施良好时是优秀的.但是,我在实践中目睹了垃圾难以理解的超载,因此会同意谷歌的决定 (13认同)
  • 在这个意义上,`range`与`make`的情况相同 (7认同)
  • @Tom 与泛型相同。让你感觉有点像二流开发者。 (4认同)
  • 我需要在这里发泄我的愤怒——毫无意义,但对我自己的理智有用。方法重载和可选参数可能并且已经被滥用来产生不可读的混乱,这一事实“不是”不实现它们的正当理由。 (4认同)
  • @NotX - 是的。关于 Go 中泛型的讨论都归结为编译器开发人员说:“仅仅因为过去二十年中开发的所有其他理智的语言都有泛型类型,并不意味着我们必须承认泛型类型在逻辑上、物理上、哲学上或道德上都是如此可能的。” (3认同)
  • @Cyber​​ience 也许吧。但解决方案有好的也有坏的,而将坏的解决方案强加给我们的语言应该被认为是病态的。将“interface{}”作为类型传递与在 C 中传递“void*”并没有多大区别,而且几乎是一个糟糕的主意。 (3认同)
  • @Cyber​​ience 认真的吗?它的问题在于你放弃了编译时类型检查(你确定正确的类型被作为接口{}传递吗?每次调用该函数时?)并且必须编写大量样板代码来实现它。 (2认同)
  • @Tom,对于 GoLang 来说,“简单性”是一个特性(而且是一个非常重要的特性),因此要摆脱一个很少使用的会产生复杂性的特性;对很多人来说都很有意义。 (2认同)
  • @DewanAhmed - 不确定我是否认为传递 `...interface{}` 比强类型可选参数更简单。当然,对于编译器实现者来说,它更简单,但对于使用该语言的人来说,它会导致函数顶部出现长度检查和类型断言的丑陋混乱。AFAICT 让编译器实现者的生活变得更简单,但以用户丑陋且容易出错的样板文件为代价,这从来都不是一种语言特性,而是一种缺陷。 (2认同)
  • 我使用 Go 的时间越长,我就越看不到它所宣传的简单性。这不仅是缺乏许多可以减少冗长从而提高可读性的功能,而且还存在诸如高度不可靠的“nil”检查以及频繁需要反射您希望开箱即用的信息等问题,特别是如果您必须使用“interface{}”或指针。我的意思是,只需检查一下如何从“sql.Row”读取数据...这只是冰山一角。 (2认同)

Fer*_*uzz 193

实现类似可选参数的好方法是使用可变参数args.该函数实际上接收您指定的任何类型的切片.

func foo(params ...int) {
    fmt.Println(len(params))
}

func main() {
    foo()
    foo(1)
    foo(1,2,3)
}
Run Code Online (Sandbox Code Playgroud)

  • 但仅适用于相同类型的参数:( (68认同)
  • @JuandeParras嗯,你仍然可以使用像... interface {}这样的东西. (11认同)
  • 这让我觉得完美的语言并不存在。喜欢关于 Go 的一切,但是这个:( (6认同)
  • 在上面的例子中,`params`是一个整数 (3认同)
  • 对于......类型,您没有传达各个选项的含义.请改用结构....类型对于在调用之前必须放入数组中的值很方便. (3认同)

dea*_*mon 153

您可以使用包含参数的结构:

type Params struct {
  a, b, c int
}

func doIt(p Params) int {
  return p.a + p.b + p.c 
}

// you can call it without specifying all parameters
doIt(Params{a: 1, c: 9})
Run Code Online (Sandbox Code Playgroud)

  • @lytnus,我讨厌分裂头发,但是省略值的字段默认为其类型的"零值"; 零是一种不同的动物.如果省略字段的类型恰好是指针,则零值将为零. (38认同)
  • 如果结构在这里可以有默认值,那将是很好的; 用户省略的任何内容默认为该类型的nil值,该值可能是也可能不是函数的合适默认参数. (10认同)
  • 我认为需要考虑并使用这样的选项,这一事实凸显了拥有可选和默认参数可能会更好。至少,如果我们拥有它们,那么目的是明确的,而不是隐藏在人为构造后面,这些构造掩盖了开发人员的意图,并且这些构造本身最终可能会被滥用,超出其预期用途。 (7认同)
  • @burfl 是的,除了“零值”的概念对于 int/float/string 类型绝对没用,因为这些值是有意义的,所以如果从结构中省略了值,或者零值是故意通过。 (3认同)
  • @keymone,我不同意你的看法。我只是对上面的陈述而之以鼻,该陈述是用户忽略的值默认为“该类型的零值”,这是不正确的。它们默认为零值(取决于类型是否为指针),该值可以为nil,也可以为nil。 (2认同)

Del*_*ace 106

对于任意的,可能大量的可选参数,一个很好的习惯是使用功能选项.

对于您的类型Foobar,首先只写一个构造函数:

func NewFoobar(options ...func(*Foobar) error) (*Foobar, error){
  fb := &Foobar{}
  // ... (write initializations with default values)...
  for _, op := range options{
    err := op(fb)
    if err != nil {
      return nil, err
    }
  }
  return fb, nil
}
Run Code Online (Sandbox Code Playgroud)

其中每个选项都是一个改变Foobar的函数.然后为用户提供方便的方式来使用或创建标准选项,例如:

func OptionReadonlyFlag(fb *Foobar) error {
  fb.mutable = false
  return nil
}

func OptionTemperature(t Celsius) func(*Foobar) error {
  return func(fb *Foobar) error {
    fb.temperature = t
    return nil
  }
}
Run Code Online (Sandbox Code Playgroud)

操场

为简明起见,您可以为选项类型(Playground)命名:

type OptionFoobar func(*Foobar) error
Run Code Online (Sandbox Code Playgroud)

如果需要必需参数,请在变量数据之前将它们添加为构造函数的第一个参数options.

功能选项习惯用语的主要好处是:

  • 您的API可以在不破坏现有代码的情况下随着时间的推移而增长,因为当需要新选项时,构造器签名保持不变.
  • 它使默认用例最简单:完全没有参数!
  • 它提供了对复杂值初始化的精细控制.

这种技术是由Rob Pike创造的,Dave Cheney也证明了这一点.

  • 聪明,但太复杂了.Go的哲学是以直接的方式编写代码.只需传递一个结构并测试默认值. (12认同)
  • 只是FYI,这个成语的原作者,至少是第一个被引用的出版商,是指挥官Rob Pike,我认为他对Go哲学有足够的权威性.链接 - https://commandcenter.blogspot.bg/2014/01/self-referential-functions-and-design.html.还搜索"简单是复杂的". (7认同)
  • 来源:http://commandcenter.blogspot.com.au/2014/01/self-referential-functions-and-design.html,http://dave.cheney.net/2014/10/17/functional-options-对于友好的API (6认同)
  • #JMTCW,但我发现这种方法很难推理。我更愿意传入一个值结构,如果需要,其属性可以是`func()`s,而不是让我的大脑围绕这种方法。每当我不得不使用这种方法时,例如使用 Echo 库时,我发现我的大脑陷入了抽象的兔子洞中。#fwiw (2认同)

pet*_*rSO 16

Go中既不支持可选参数也不支持函数重载.Go确实支持可变数量的参数:将参数传递给...参数


Max*_*728 9

所以我觉得我参加这个聚会已经太晚了,但我一直在寻找是否有比我已经做的更好的方法。这有点解决了您想要做的事情,同时也给出了可选参数的概念。

package main

import "fmt"

type FooOpts struct {
    // optional arguments
    Value string
}

func NewFoo(mandatory string) {
    NewFooWithOpts(mandatory, &FooOpts{})
}

func NewFooWithOpts(mandatory string, opts *FooOpts) {
    if (&opts) != nil {
        fmt.Println("Hello " + opts.Value)
    } else {
        fmt.Println("Hello")
    }
}

func main() {
    NewFoo("make it work please")

    NewFooWithOpts("Make it work please", &FooOpts{Value: " World"})
}
Run Code Online (Sandbox Code Playgroud)

更新1:

添加了一个功能示例来展示功能与示例

  • 与其他选择相比,我更喜欢这个。这也是我在许多库中看到的一种模式,当某些东西有不同的选项并且可以重用时,您可以创建一个结构来表示这些选项并通过参数传递选项,或者您可以“nil”要使用的选项默认值。此外,选项可以记录在它们自己的结构中,您可以创建预定义的选项集。我在 GitHub 客户端库和 go-cache 库等中看到了这一点。 (2认同)

nob*_*bar 8

您可以使用地图传递任意命名参数。aType = map[key].(*foo.type)如果参数具有非统一类型,则必须使用“ ”来断言类型。

type varArgs map[string]interface{}

func myFunc(args varArgs) {

    arg1 := "default"
    if val, ok := args["arg1"]; ok {
        arg1 = val.(string)
    }

    arg2 := 123
    if val, ok := args["arg2"]; ok {
        arg2 = val.(int)
    }

    fmt.Println(arg1, arg2)
}

func Test_test() {
    myFunc(varArgs{"arg1": "value", "arg2": 1234})
}
Run Code Online (Sandbox Code Playgroud)


Sum*_*mer 7

Go 不支持可选参数默认值函数重载,但您可以使用一些技巧来实现相同的功能。

\n

分享一个示例,您可以在一个函数中使用不同数量和类型的参数。它\xe2\x80\x99是一个简单的代码,为了便于理解,您需要添加错误处理和一些逻辑。

\n
func student(StudentDetails ...interface{}) (name string, age int, area string) {\n    age = 10 //Here Age and area are optional params set to default values\n    area = "HillView Singapore"\n\n    for index, val := range StudentDetails {\n        switch index {\n            case 0: //the first mandatory param\n                name, _ = val.(string)\n            case 1: // age is optional param\n                age, _ = val.(int)\n            case 2: //area is optional param\n                area, _ = val.(string)\n        }\n    }\n    return\n}\n\nfunc main() {\n    fmt.Println(student("Aayansh"))\n    fmt.Println(student("Aayansh", 11))\n    fmt.Println(student("Aayansh", 15, "Bukit Gombak, Singapore"))\n}\n
Run Code Online (Sandbox Code Playgroud)\n

  • 呃,那太可怕了。 (14认同)

Sky*_*n42 6

如果您不想使用指针,则可以使用指针并将它们保留为零:

func getPosts(limit *int) {
  if optParam != nil {
    // fetch posts with limit 
  } else {
    // fetch all posts
  }
}

func main() {
  // get Posts, limit by 2
  limit := 2
  getPosts(&limit)

  // get all posts
  getPosts(nil)
}
Run Code Online (Sandbox Code Playgroud)

  • 完全同意。有时将 nil 作为参数可能比其他更改简单得多。 (2认同)

Ale*_*lli 5

不-都不。根据Go for C ++程序员文档,

Go不支持函数重载,也不支持用户定义的运算符。

我找不到一个同样明确的说法,即不支持可选参数,但也不支持它们。

  • “此[可选参数]目前没有计划。” Ian Lance Taylor,Go语言团队。http://groups.google.com/group/golang-nuts/msg/030e63e7e681fd3e (8认同)

Jam*_*ser 5

您可以将其很好地封装在类似于以下内容的 func 中。

package main

import (
        "bufio"
        "fmt"
        "os"
)

func main() {
        fmt.Println(prompt())
}

func prompt(params ...string) string {
        prompt := ": "
        if len(params) > 0 {
                prompt = params[0]
        }
        reader := bufio.NewReader(os.Stdin)
        fmt.Print(prompt)
        text, _ := reader.ReadString('\n')
        return text
}
Run Code Online (Sandbox Code Playgroud)

在这个例子中,提示默认有一个冒号,前面有一个空格。. .

: 
Run Code Online (Sandbox Code Playgroud)

. . . 但是,您可以通过向提示函数提供参数来覆盖它。

prompt("Input here -> ")
Run Code Online (Sandbox Code Playgroud)

这将导致如下提示。

Input here ->
Run Code Online (Sandbox Code Playgroud)