Go中的可选参数?

huangapple go评论83阅读模式
英文:

Optional Parameters in Go?

问题

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

英文:

Can Go have optional parameters? Or can I just define two different functions with the same name and a different number of arguments?

答案1

得分: 650

Go语言没有可选参数也不支持方法重载

如果方法调度不需要进行类型匹配,那么方法调度就会变得简单。我们从其他语言的经验中得知,拥有多个具有相同名称但不同签名的方法有时很有用,但在实践中可能会令人困惑和脆弱。在Go语言的类型系统中,只通过名称进行匹配并要求类型一致是一个重要的简化决策。

英文:

Go does not have optional parameters nor does it support method overloading:

> Method dispatch is simplified if it
> doesn't need to do type matching as
> well. Experience with other languages
> told us that having a variety of
> methods with the same name but
> different signatures was occasionally
> useful but that it could also be
> confusing and fragile in practice.
> Matching only by name and requiring
> consistency in the types was a major
> simplifying decision in Go's type
> system.

答案2

得分: 340

一个实现类似可选参数的好方法是使用可变参数。函数实际上接收一个你指定类型的切片。

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

func main() {
    foo()
    foo(1)
    foo(1,2,3)
}
英文:

A nice way to achieve something like optional parameters is to use variadic args. The function actually receives a slice of whatever type you specify.

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

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

答案3

得分: 239

你可以使用一个包含参数的结构体:

type Params struct {
  a, b, c int
}

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

// 你可以在调用时不指定所有参数
doIt(Params{a: 1, c: 9})

与省略号(params ...SomeType)相比,主要优势是可以使用参数结构体来处理不同类型的参数

英文:

You can use a struct which includes the parameters:

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

The main advantage over an ellipsis (params ...SomeType) is that you can use the param struct with different parameter types.

答案4

得分: 194

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

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

func NewFoobar(options ...func(*Foobar) error) (*Foobar, error){
  fb := &Foobar{}
  // ...(使用默认值进行初始化)...
  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演示过。

英文:

For arbitrary, potentially large number of optional parameters, a nice idiom is to use Functional options.

For your type Foobar, first write only one constructor:

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
}

where each option is a function which mutates the Foobar. Then provide convenient ways for your user to use or create standard options, for example :

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

For conciseness, you may give a name to the type of the options (Playground) :

type OptionFoobar func(*Foobar) error

If you need mandatory parameters, add them as first arguments of the constructor before the variadic options.

The main benefits of the Functional options idiom are :

  • your API can grow over time without breaking existing code, because the constuctor signature stays the same when new options are needed.
  • it enables the default use case to be its simplest: no arguments at all!
  • it provides fine control over the initialization of complex values.

This technique was coined by Rob Pike and also demonstrated by Dave Cheney.

答案5

得分: 24

Go语言不支持可选参数和函数重载。但是,Go语言支持可变数量的参数:将参数传递给...参数

英文:

Neither optional parameters nor function overloading are supported in Go. Go does support a variable number of parameters: Passing arguments to ... parameters

答案6

得分: 11

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

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

我找不到一个同样清晰的声明说可选参数不受支持,但它们也不被支持。

英文:

No -- neither. Per the Go for C++ programmers docs,

> Go does not support function
> overloading and does not support user
> defined operators.

I can't find an equally clear statement that optional parameters are unsupported, but they are not supported either.

答案7

得分: 11

你可以使用一个map来传递任意命名的参数。如果参数具有非统一的类型,你将需要使用“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})
}
英文:

You can pass arbitrary named parameters with a map. You will have to assert types with "aType = map[key].(*foo.type)" if the parameters have non-uniform types.

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

答案8

得分: 8

所以我觉得我来晚了,但我正在搜索是否有比我已经做的更好的方法。这种方法解决了你试图做的事情,同时还提供了可选参数的概念。

package main

import "fmt"

type FooOpts struct {
	// 可选参数
	Value string
}

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

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

func main() {
	NewFoo("请让它工作")

	NewFooWithOpts("请让它工作", &FooOpts{Value: " 世界"})
}

更新 1:

添加了一个功能示例,以展示功能与示例的区别。

英文:

So I feel like I'm way late to this party but I was searching to see if there was a better way to do this than what I already do. This kinda solves what you were trying to do while also giving the concept of an optional argument.

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

Update 1:

Added a functional example to show functionality versus the sample

答案9

得分: 7

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

下面是一个示例,你可以在一个函数中使用不同数量和类型的参数。这只是一个简单的代码,为了方便理解,你需要添加错误处理和一些逻辑。

func student(StudentDetails ...interface{}) (name string, age int, area string) {
    age = 10 // 这里的Age和area是可选参数,设置为默认值
    area = "HillView Singapore"

    for index, val := range StudentDetails {
        switch index {
            case 0: // 第一个必需的参数
                name, _ = val.(string)
            case 1: // age是可选参数
                age, _ = val.(int)
            case 2: // area是可选参数
                area, _ = val.(string)
        }
    }
    return
}

func main() {
    fmt.Println(student("Aayansh"))
    fmt.Println(student("Aayansh", 11))
    fmt.Println(student("Aayansh", 15, "Bukit Gombak, Singapore"))
}

请注意,这只是一个示例,你可以根据自己的需求进行修改和扩展。

英文:

Go doesn’t support optional parameters , default values and function overloading but you can use some tricks to implement the same.

Sharing one example where you can have different number and type of arguments in one function. It’s a plain code for easy understanding you need to add error handling and some logic.

func student(StudentDetails ...interface{}) (name string, age int, area string) {
    age = 10 //Here Age and area are optional params set to default values
    area = "HillView Singapore"

    for index, val := range StudentDetails {
        switch index {
            case 0: //the first mandatory param
		        name, _ = val.(string)
            case 1: // age is optional param
                age, _ = val.(int)
            case 2: //area is optional param
                area, _ = val.(string)
        }
    }
	return
}

func main() {
	fmt.Println(student("Aayansh"))
	fmt.Println(student("Aayansh", 11))
	fmt.Println(student("Aayansh", 15, "Bukit Gombak, Singapore"))
}

答案10

得分: 5

你可以将这段代码封装在一个类似下面的函数中。

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("在这里输入 -> ")

这将导致一个类似下面的提示符。

在这里输入 ->
英文:

You can encapsulate this quite nicely in a func similar to what is below.

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
}

In this example, the prompt by default has a colon and a space in front of it . . .

: 

. . . however you can override that by supplying a parameter to the prompt function.

prompt("Input here -> ")

This will result in a prompt like below.

Input here ->

答案11

得分: 5

你可以使用指针,并在不使用它们时将其设置为nil:

func getPosts(limit *int) {
  if limit != nil {
    // 根据限制获取帖子
  } else {
    // 获取所有帖子
  }
}

func main() {
  // 获取帖子,限制为2
  limit := 2
  getPosts(&limit)

  // 获取所有帖子
  getPosts(nil)
}
英文:

You could use pointers and leave them nil if you don't want to use them:

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

答案12

得分: 4

Go语言不支持方法重载,但是你可以使用可变参数(variadic args)来模拟可选参数的功能,同时你也可以使用interface{}作为参数,但这并不是一个好的选择。

英文:

Go language does not support method overloading, but you can use variadic args just like optional parameters, also you can use interface{} as parameter but it is not a good choice.

答案13

得分: 3

我有点晚了,但如果你喜欢流畅的接口,你可以像这样设计你的setter方法以实现链式调用:

type myType struct {
  s string
  a, b int
}

func New(s string, err *error) *myType {
  if s == "" {
    *err = errors.New(
      "必填参数`s`不能为空!")
  }
  return &myType{s: s}
}

func (this *myType) setA (a int, err *error) *myType {
  if *err == nil {
    if a == 42 {
      *err = errors.New("42不是答案!")
    } 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("失败:", err)
  } else {
    fmt.Println(instance)
  }
}

这与@Ripounet的答案中介绍的函数选项习惯用法类似,并享有相同的好处,但也有一些缺点:

  1. 如果发生错误,它不会立即中止,因此,如果你期望构造函数经常报告错误,它会稍微低效一些。
  2. 你需要花一行代码声明一个err变量并将其置零。

然而,这种函数调用方式可能有一个小优势,编译器应该更容易内联它们,但我真的不是专家。

英文:

I am a little late, but if you like fluent interface you might design your setters for chained calls like this:

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
}

And then call it like 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)
  }
}

This is similar to the Functional options idiom presented on @Ripounet answer and enjoys the same benefits but has some drawbacks:

  1. If an error occurs it will not abort immediately, thus, it would be slightly less efficient if you expect your constructor to report errors often.
  2. You'll have to spend a line declaring an err variable and zeroing it.

There is, however, a possible small advantage, this type of function calls should be easier for the compiler to inline but I am really not a specialist.

答案14

得分: 2

我最终使用了参数结构和可变参数的组合。这样,我就不需要改变已被多个服务使用的现有接口,而我的服务可以根据需要传递额外的参数。在golang playground中有示例代码:https://play.golang.org/p/G668FA97Nu

英文:

I ended up using a combination of a structure of params and variadic args. This way, I didn't have to change the existing interface which was consumed by several services and my service was able to pass additional params as needed. Sample code in golang playground: https://play.golang.org/p/G668FA97Nu

答案15

得分: 0

另一种可能性是使用一个结构体,其中包含一个字段来指示其是否有效。像 NullString 这样的 SQL 空类型非常方便。不需要定义自己的类型也是很好的,但是如果你需要一个自定义的数据类型,你总是可以遵循相同的模式。我认为函数定义中的可选性是清晰的,而且额外的代码或工作量很少。

举个例子:

func Foo(bar string, baz sql.NullString){
  if !baz.Valid {
		baz.String = "defaultValue"
  }
  // 其余的实现
}
英文:

Another possibility would be to use a struct which with a field to indicate whether its valid. The null types from sql such as NullString are convenient. Its nice to not have to define your own type, but in case you need a custom data type you can always follow the same pattern. I think the optional-ness is clear from the function definition and there is minimal extra code or effort.

As an example:

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

huangapple
  • 本文由 发表于 2010年1月9日 10:38:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/2032149.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定