可选参数?

时间:2010-01-09 02:38:58

标签: go overloading

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

14 个答案:

答案 0 :(得分:354)

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

  

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

答案 1 :(得分:176)

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

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

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

答案 2 :(得分:139)

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

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

答案 3 :(得分:100)

对于任意的,可能有大量的可选参数,一个不错的习惯是使用功能选项

对于类型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也证明了这一点。

答案 4 :(得分:16)

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

答案 5 :(得分:5)

不 - 不。根据{{​​3}}文档,

  

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

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

答案 6 :(得分: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 ->

答案 7 :(得分:2)

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

答案 8 :(得分:2)

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

答案 9 :(得分: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})
}

答案 10 :(得分:1)

我有点晚了,但是如果你喜欢流畅的界面,你可能会为这样的链式调用设计你的setter:

<div id="block">
  <div id="next"></div>
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
  <div id="prev"></div>
</div>

然后像这样称呼它:

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
}

这类似于@Ripounet回答中提供的功能选项习惯用法,享有同样的好处但有一些缺点:

  1. 如果发生错误,它将不会立即中止,因此,如果您希望构造函数经常报告错误,效率会稍微降低。
  2. 您必须花一行来声明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) } } 变量并将其归零。
  3. 然而,有一个可能的小优点,这种类型的函数调用应该更容易编译器内联,但我真的不是专家。

答案 11 :(得分:1)

另一种可能性是使用带有字段的结构以指示其是否有效。来自sql的空类型(例如NullString)很方便。不必定义自己的类型很不错,但是如果您需要自定义数据类型,则可以始终遵循相同的模式。我认为从函数定义中可以清楚地看出可选性,并且几乎没有额外的代码或工作。

例如:

func Foo(bar string, baz sql.NullString){
  if !baz.Valid {
        baz.String = "defaultValue"
  }
  // the rest of the implementation
}

答案 12 :(得分:0)

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

type FooOpts struct {
  // optional arguments
  Value string
}

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

func NewFooWithOpts(mandatory string, opts *FooOpts) {
  // implement some empty checks
  defaultOpts := false // or just declare (var defaultOpts bool) to use zero 
                       // default of false. I just prefer to explicity set the value.
  if (*opts) == FooOpts{} {
    // Assume that this was not provided and continue with default process
    defaultOpts = true
  }

  // utilizing the optionals from this point would be below
  if !defaultOpts {
    fmt.Println(*opts.Value)
  }
}

答案 13 :(得分:0)

如果你不想使用它们,你可以使用指针并让它们为零:

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