Go中的可选参数?

问题描述 投票:409回答:11

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

go overloading
11个回答
390
投票

Go没有可选参数nor does it support method overloading

如果简化方法分发,不需要做类型匹配好。有其他语言的经验告诉我们,有各种各样的具有相同名称的方法偶尔会有不同的签名有用,但也可能是在实践中令人困惑和脆弱。仅按名称匹配并要求类型的一致性是一个主要问题简化Go类型的决策系统。


2
投票

Go语言不支持方法重载,但是您可以像可选参数一样使用可变参数args,也可以使用interface {}作为参数,但这不是一个好选择。


2
投票

您可以通过地图传递任意命名的参数。

type varArgs map[string]interface{}

func myFunc(args varArgs) {

    arg1 := "default" // optional default value
    if val, ok := args["arg1"]; ok {
        // value override or other action
        arg1 = val.(string) // runtime panic if wrong type
    }

    arg2 := 123 // optional default value
    if val, ok := args["arg2"]; ok {
        // value override or other action
        arg2 = val.(int) // runtime panic if wrong type
    }

    fmt.Println(arg1, arg2)
}

func Test_test() {
    myFunc(varArgs{"arg1": "value", "arg2": 1234})
}

193
投票

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

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

func main() {
    foo()
    foo(1)
    foo(1,2,3)
}

152
投票

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

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})

105
投票

对于任意的,可能潜在的大量可选参数,一个好习惯是使用Functional options

对于您的类型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
}

其中每个选项都是使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
  }
}

Playground

为简洁起见,您可以为选项的类型(Playground)指定名称:

type OptionFoobar func(*Foobar) error

如果需要强制性参数,请将其添加为可变参数options之前的构造函数的第一个参数。

功能选项惯用语的主要优点是:

  • 您的API可以随着时间的增长而不会破坏现有代码,因为当需要新选项时,构造器签名将保持不变。
  • 它使默认用例变得最简单:根本没有参数!
  • 它对复数值的初始化提供了很好的控制。

[该技术由Rob Pike创造,也由Dave Cheney证明。


16
投票

Go既不支持可选参数,也不支持函数重载。 Go确实支持可变数量的参数:Passing arguments to ... parameters


5
投票

不-都不。根据Go for C++ programmers文档,

Go不支持功能重载,不支持用户定义的运算符。

我找不到同样清楚的声明,不支持可选参数,但也不支持。


4
投票

您可以将其很好地封装在类似于下面的函数中。

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
}

在此示例中,提示默认情况下在前面带有一个冒号和一个空格。 。 。

: 

。 。 。但是,您可以通过为提示功能提供参数来覆盖它。

prompt("Input here -> ")

这将导致如下所示的提示。

Input here ->

3
投票

我最终使用了参数和可变参数的结构的组合。这样,我不必更改多个服务使用的现有接口,并且我的服务能够根据需要传递其他参数。 golang游乐场中的示例代码:https://play.golang.org/p/G668FA97Nu


2
投票

我来晚了一点,但是如果您喜欢流畅的界面,则可以像下面这样设计链式呼叫的设置器:

type myType struct {
  s string
  a, b int
}

func New(s string, err *error) *myType {
  if s == "" {
    *err = errors.New(
      "Mandatory argument `s` must not be empty!")
  }
  return &myType{s: s}
}

func (this *myType) setA (a int, err *error) *myType {
  if *err == nil {
    if a == 42 {
      *err = errors.New("42 is not the answer!")
    } else {
      this.a = a
    }
  }
  return this
}

func (this *myType) setB (b int, _ *error) *myType {
  this.b = b
  return this
}

然后这样称呼它:

func main() {
  var err error = nil
  instance :=
    New("hello", &err).
    setA(1, &err).
    setB(2, &err)

  if err != nil {
    fmt.Println("Failed: ", err)
  } else {
    fmt.Println(instance)
  }
}

这类似于@Ripounet答案上出现的Functional options惯用语,虽然具有相同的优点,但有一些缺点:

  1. 如果发生错误,它将不会立即中止,因此,如果您希望构造函数经常报告错误,则效率会稍低。
  2. 您将不得不花费一行声明一个err变量并将其清零。

但是,这可能有一个小的优势,这种类型的函数调用应该更易于编译器内联,但我实际上不是专家。

© www.soinside.com 2019 - 2024. All rights reserved.