Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/go/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Go中的可选参数?_Go_Overloading - Fatal编程技术网

Go中的可选参数?

Go中的可选参数?,go,overloading,Go,Overloading,Go可以有可选参数吗?或者我可以用相同的名称和不同数量的参数定义两个函数吗?Go没有可选参数: 方法调度可以简化,如果 不需要像以前那样进行类型匹配 好。有其他语言的经验 告诉我们有各种各样的 具有相同名称但 偶尔会有不同的签名 有用,但也可能有用 在实践中令人困惑和脆弱。 仅按名称和要求匹配 类型的一致性是一个主要问题 Go型决策的简化 系统 没有,也没有。根据文件 Go不支持函数 重载,不支持用户 定义的运算符 我找不到一个同样明确的说法,即不支持可选参数,但它们也不受支持。Go中既不支持可

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

Go没有可选参数:

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

没有,也没有。根据文件

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


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

Go中既不支持可选参数,也不支持函数重载。Go确实支持数量可变的参数:

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

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

实现可选参数之类的功能的一个好方法是使用可变参数。函数实际上接收您指定的任何类型的片

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

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

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

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

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

如果需要强制参数,请将它们作为构造函数的第一个参数添加到variadic
选项之前

“功能选项”习惯用法的主要好处是:

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

这项技术是由发明的,也由演示

我最终使用了params和variadic args结构的组合。这样,我就不必更改多个服务使用的现有接口,并且我的服务能够根据需要传递额外的参数。golang Playedy中的示例代码:

您可以将其很好地封装在类似于下面内容的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
}
在本例中,默认情况下提示前面有冒号和空格

: 
。但是,您可以通过向prompt函数提供一个参数来覆盖该参数

prompt("Input here -> ")
这将导致如下所示的提示

Input here ->

我有点晚了,但如果您喜欢fluent接口,您可以为链式调用设计setter,如下所示:

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)
  }
}
然后这样称呼它:

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 answer上介绍的函数选项习惯用法,具有相同的优点,但也有一些缺点:

  • 如果发生错误,它不会立即中止,因此,如果您希望构造函数经常报告错误,那么它的效率会稍低一些
  • 您必须花费一行时间声明
    err
    变量并将其归零

  • 然而,有一个可能的小优势,这种类型的函数调用应该更容易让编译器内联,但我不是专家。

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

    您可以通过映射传递任意命名的参数。如果参数具有非统一类型,则必须使用“
    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})
    }
    

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

    例如:

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

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

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

    Go语言团队的Ian Lance Taylor说:“目前还没有这个[可选参数]的计划。”。没有用户定义的运算符是一个糟糕的决定,因为它是任何光滑数学库背后的核心,如三维图形中经常使用的线性代数的点积或叉积;用户省略的任何内容都默认为该类型的nil值,这可能是函数的合适默认参数,也可能不是。@lytnus,我不喜欢吹毛求疵,但省略值的字段的类型默认为“零值”;尼尔是另一种动物。如果省略字段的类型恰好是指针,则零值将为零。@burfl是的,除了“零值”的概念对int/float/string类型绝对没有用处之外,因为这些值是有意义的,所以如果从结构中省略了值,或者是故意传递了零值,您就无法分辨它们之间的区别。@keymone,我不同意您的看法。我只是对上面的语句有点迂腐,用户默认将值省略为“该类型的nil值”,这是不正确的。它们默认为零值,可能为零,也可能不为零,这取决于类型是否为指针。那么
    make
    是否为特例?或者它甚至不是真正实现为一个函数…@Mk12
    make
    是一种语言构造,上面提到的规则不适用于