Default value in Go's method

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

Default value in Go's method

问题

在Go语言中,函数的参数不能指定默认值。这意味着你不能在函数定义时为参数设置默认值。如果你想要实现类似的功能,可以通过函数重载或者使用可选参数的方式来实现。

英文:

Is there a way to specify default value in Go's function? I am trying to find this in the documentation but I can't find anything that specifies that this is even possible.

func SaySomething(i string = "Hello")(string){
...
}

答案1

得分: 243

不,但有一些其他选项可以实现默认值。关于这个主题有一些很好的博客文章,但这里有一些具体的示例。

选项1: 调用者选择使用默认值

// 两个参数都是可选的,使用空字符串作为默认值
func Concat1(a string, b int) string {
  if a == "" {
    a = "default-a"
  }
  if b == 0 {
    b = 5
  }

  return fmt.Sprintf("%s%d", a, b)
}

选项2: 在末尾使用单个可选参数

// a 是必需的,b 是可选的。
// 只使用 b_optional 中的第一个值。
func Concat2(a string, b_optional ...int) string {
  b := 5
  if len(b_optional) > 0 {
    b = b_optional[0]
  }

  return fmt.Sprintf("%s%d", a, b)
}

选项3: 使用配置结构体

// 声明默认值的语法
// 空值将被替换为默认值
type Parameters struct {
  A string `default:"default-a"` // 这只适用于字符串
  B string // 默认值为 5
}

func Concat3(prm Parameters) string {
  typ := reflect.TypeOf(prm)

  if prm.A == "" {
    f, _ := typ.FieldByName("A")
    prm.A = f.Tag.Get("default")
  }

  if prm.B == 0 {
    prm.B = 5
  }

  return fmt.Sprintf("%s%d", prm.A, prm.B)
}

选项4: 完全可变参数解析(类似 JavaScript)

func Concat4(args ...interface{}) string {
  a := "default-a"
  b := 5

  for _, arg := range args {
    switch t := arg.(type) {
      case string:
        a = t
      case int:
        b = t
      default:
        panic("Unknown argument")
    }
  }

  return fmt.Sprintf("%s%d", a, b)
}
英文:

NO,but there are some other options to implement default value. There are some good blog posts on the subject, but here are some specific examples.

Option 1: The caller chooses to use default values

// Both parameters are optional, use empty string for default value
func Concat1(a string, b int) string {
  if a == "" {
    a = "default-a"
  }
  if b == 0 {
    b = 5
  }

  return fmt.Sprintf("%s%d", a, b)
}

Option 2: A single optional parameter at the end

// a is required, b is optional.
// Only the first value in b_optional will be used.
func Concat2(a string, b_optional ...int) string {
  b := 5
  if len(b_optional) > 0 {
    b = b_optional[0]
  }

  return fmt.Sprintf("%s%d", a, b)
}

Option 3: A config struct

// A declarative default value syntax
// Empty values will be replaced with defaults
type Parameters struct {
  A string `default:"default-a"` // this only works with strings
  B string // default is 5
}

func Concat3(prm Parameters) string {
  typ := reflect.TypeOf(prm)

  if prm.A == "" {
    f, _ := typ.FieldByName("A")
    prm.A = f.Tag.Get("default")
  }

  if prm.B == 0 {
    prm.B = 5
  }

  return fmt.Sprintf("%s%d", prm.A, prm.B)
}

Option 4: Full variadic argument parsing (javascript style)

func Concat4(args ...interface{}) string {
  a := "default-a"
  b := 5

  for _, arg := range args {
    switch t := arg.(type) {
      case string:
        a = t
      case int:
        b = t
      default:
        panic("Unknown argument")
    }
  }

  return fmt.Sprintf("%s%d", a, b)
}

答案2

得分: 171

不,谷歌的管理层选择不支持那个。

英文:

No, the powers that be at Google chose not to support that.

https://groups.google.com/forum/#!topic/golang-nuts/-5MCaivW0qQ

答案3

得分: 19

不,没有办法指定默认值。我相信这是有意为之的,以提高可读性,代价是需要更多的时间(以及希望是思考)来编写代码。

我认为正确的方法是创建一个新的函数,该函数为更通用的函数提供默认值。有了这个函数,你的代码在意图上变得更清晰。例如:

func SaySomething(say string) {
    // 所有涉及说话的复杂部分
}

func SayHello() {
    SaySomething("Hello")
}

只需很少的努力,我就创建了一个执行常见操作并重用通用函数的函数。你可以在许多库中看到这一点,例如 fmt.Println 只是在 fmt.Print 的基础上添加了一个换行符。然而,当阅读别人的代码时,通过调用的函数可以清楚地知道他们的意图。如果有默认值,我就不知道应该发生什么,除非还要去函数中查看默认值是什么。

英文:

No, there is no way to specify defaults. I believer this is done on purpose to enhance readability, at the cost of a little more time (and, hopefully, thought) on the writer's end.

I think the proper approach to having a "default" is to have a new function which supplies that default to the more generic function. Having this, your code becomes clearer on your intent. For example:

func SaySomething(say string) {
	// All the complicated bits involved in saying something
}

func SayHello() {
	SaySomething("Hello")
}

With very little effort, I made a function that does a common thing and reused the generic function. You can see this in many libraries, fmt.Println for example just adds a newline to what fmt.Print would otherwise do. When reading someone's code, however, it is clear what they intend to do by the function they call. With default values, I won't know what is supposed to be happening without also going to the function to reference what the default value actually is.

答案4

得分: 2

默认函数参数通常为客户端代码提供一个常见的参数值,同时允许客户端代码覆盖它。通过将函数转换为结构体的方法,并将默认参数转换为该结构体的字段,可以实现类似的结果。

让我们以一个基于你的示例的函数为例(它无法编译):

func SaySomething(msg string = "Hello") string {
    return fmt.Sprintf("I say %q", msg)
}

首先,定义一个结构体来保存默认参数:

type SomethingSayer struct {
    msg string
}

其次,将函数SaySomething转换为结构体SomethingSayer的方法。原始函数参数msg被替换为SomethingSayer的字段s.msg

func (s SomethingSayer) SaySomething() string {
    return fmt.Sprintf("I say %q", s.msg)
}

你还可以为SomethingSayer结构体定义一个构造函数。这将使用原始的默认参数设置字段msg

func NewSaySomethingSayer() SomethingSayer {
    return SomethingSayer {
        msg: "Hello", // 默认参数
    }
}

最后,定义一个方便的包装函数,与原始函数类似,但没有默认参数的参数:

func SaySomething() string {
    return NewSaySomethingSayer().SaySomething()
}

这样,当默认值适用时,客户端代码只需调用SaySomething()即可。如果不适用默认值,仍然可以构造自己的SomethingSayer并调用SaySomething()方法:

fmt.Println(SaySomething())    // I say "Hello"

s := SomethingSayer {
    msg: "Bye",
}
fmt.Println(s.SaySomething())  // I say "Bye"

请注意,标准库中的http.Gethttp.Headhttp.Post也遵循了这种方法。也就是说,这些函数允许你执行一个GET请求而无需显式构造一个http.Client:它使用一个全局可用的http.Client值(http.DefaultClient)并在其上调用其Get方法。类似地,客户端代码仍然可以通过构造自己的http.Client并调用其Get方法来覆盖默认参数(例如超时)。

英文:

A default function argument usually provides the client code with a very common argument value while still allowing the client code to override it. A similar result can be achieved by turning the function into a method of a struct and the default argument into a field of that struct.

Let's see it with a function based on your example (it doesn't compile):

func SaySomething(msg string = "Hello") string {
	return fmt.Sprintf("I say %q", msg)
}

First, define a struct that will hold the default argument:

type SomethingSayer struct {
	msg string
}

Second, turn the function SaySomething into a method on the struct SomethingSayer. The original function parameter msg is replaced by s.msg, a SomethingSayer's field:

func (s SomethingSayer) SaySomething() string {
	return fmt.Sprintf("I say %q", s.msg)
}

You can also define a constructor for the SomethingSayer struct. This will set the field msg with the original default argument:

func NewSaySomethingSayer() SomethingSayer {
	return SomethingSayer {
		msg: "Hello", // the default argument
	}
}

Finally, define a convenience wrapping function that is like the original one but without the parameter that had the default argument:

func SaySomething() string {
	return NewSaySomethingSayer().SaySomething()
}

This way, the client code can just call SaySomething() when the defaults are suitable. If that's not the case, it can still construct its own SomethingSayer and then call the SaySomething() method:

fmt.Println(SaySomething())    // I say "Hello"

s := SomethingSayer {
	msg: "Bye",
}
fmt.Println(s.SaySomething())  // I say "Bye"

Note that this approach is also followed by the standard library in http.Get, http.Head, and http.Post. That is, this function allows you to perform a GET request without explicitly constructing a http.Client: it uses a globally available http.Client value (http.DefaultClient) and calls its Get method on it. Similarly, client code can still override the default arguments (e.g., the timeout) by constructing its own http.Client and calling Get on it.

huangapple
  • 本文由 发表于 2013年10月27日 06:10:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/19612449.html
匿名

发表评论

匿名网友

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

确定