在方法声明中允许可变数量的返回值

jrk*_*rkt 4 go

我有一个函数可以解决 Go 不允许在方法声明中设置默认值的问题。我想通过允许可变数量的返回变量来使它更好一点。我知道我可以允许接口数组作为返回类型,然后创建一个包含所有要返回的变量的接口数组,如下所示:

func SetParams(params []interface{}, args ...interface{}) (...[]interface{}) {
    var values []interface{}
    for i := range params {
            var value interface{}
            paramType := reflect.TypeOf(params[i])
            if len(args) < (i + 1) {
                    value = params[i]
            } else {
                    argType := reflect.TypeOf(args[i])
                    if paramType != argType {
                            value = params[i]
                    }
                    value = args[i]
            }
            values = append(values, value)
    }

    return values
}
Run Code Online (Sandbox Code Playgroud)

这是您要为其定义默认值的方法的示例。您将其构建为可变参数函数(允许可变数量的参数),然后定义您在函数内部而不是在声明行中查找的特定参数的默认值。

func DoSomething(args ...interface{}) {
    //setup default values
    str := "default string 1"
    num := 1
    str2 := "default string 2"


    //this is fine
    values := SetParams([]interface{str, num, str2}, args)
    str = values[0].(string)
    num = values[1].(int)
    str = values[2].(string)


    //I'd rather support this
    str, num, str2 = SetParams(params, args)
}
Run Code Online (Sandbox Code Playgroud)

我明白那个

[]接口{str, num, str2}

上面的例子在语法上是不正确的。我这样做是为了简化我的帖子。但是,它代表了另一个构建接口数组的函数。

我想支持这一点:

str, num, str2 = SetParams(params, args)
Run Code Online (Sandbox Code Playgroud)

而不是必须这样做:

values := SetParams([]interface{str, num, str2}, args)
str = values[0].(string)
num = values[1].(int)
str = values[2].(string)
Run Code Online (Sandbox Code Playgroud)

有什么建议吗?帮助?

kos*_*tix 6

请不要写可怕的(由于reflect)代码来解决不存在的问题。正如评论中指出的那样,在转换后将一种语言变成你以前的语言之一确实很有吸引力,但这会适得其反。

相反,最好使用该语言提供的习语、方法和最佳实践——即使您不喜欢它们(但也许)。

对于这种特殊情况,您可以像这样滚动:

  • 使想要接受具有默认值的参数列表的函数接受自定义struct类型的单个值。

  • 首先,这种类型的任何变量在分配时,其所有字段都使用适合其各自类型的所谓“零值”进行初始化。

    如果这足够了,您可以停在那里:您将能够struct通过直接在调用站点上通过文字生成它们将您的类型的值传递给您的函数 - 仅初始化您需要的字段。

    否则有预处理/后处理代码,它会为您需要的字段提供您自己的“零值”。

2016-08-29 更新:

使用struct类型来模拟可选参数,使用其字段被分配默认值,这些默认值恰好是 Go 各自数据类型的本机零值:

package main

import (
    "fmt"
)

type params struct {
    foo string
    bar int
    baz float32
}

func myfun(params params) {
    fmt.Printf("%#v\n", params)
}

func main() {
    myfun(params{})
    myfun(params{bar: 42})
    myfun(params{foo: "xyzzy", baz: 0.3e-2})
}
Run Code Online (Sandbox Code Playgroud)

输出:

main.params{foo:"", bar:0, baz:0}
main.params{foo:"", bar:42, baz:0}
main.params{foo:"xyzzy", bar:0, baz:0.003}
Run Code Online (Sandbox Code Playgroud)

如您所见,params除非我们在定义文字时指定自己的值,否则Go 会使用适合其各自类型的零值初始化我们类型的字段。

游乐场链接。

可以通过对用户提交的复合类型的值进行预处理或后处理来为我们的自定义类型的字段提供不是 Go-native 零值的默认值。

后期处理:

package main

import (
    "fmt"
)

type params struct {
    foo string
    bar int
    baz float32
}

func (pp *params) setDefaults() {
    if pp.foo == "" {
        pp.foo = "ahem"
    }
    if pp.bar == 0 {
        pp.bar = -3
    }
    if pp.baz == 0 { // Can't really do this to FP numbers; for demonstration purposes only
        pp.baz = 0.5
    }
}

func myfun(params params) {
    params.setDefaults()
    fmt.Printf("%#v\n", params)
}

func main() {
    myfun(params{})
    myfun(params{bar: 42})
    myfun(params{foo: "xyzzy", baz: 0.3e-2})
}
Run Code Online (Sandbox Code Playgroud)

输出:

main.params{foo:"ahem", bar:-3, baz:0.5}
main.params{foo:"ahem", bar:42, baz:0.5}
main.params{foo:"xyzzy", bar:-3, baz:0.003}
Run Code Online (Sandbox Code Playgroud)

游乐场链接。

预处理相当于创建一个“构造函数”函数,该函数将返回一个所需类型的值,该值预先填充了您为其字段选择的默认值——如下所示:

func newParams() params {
    return params{
        foo: "ahem",
        bar: -3,
        baz: 0.5,
    }
}
Run Code Online (Sandbox Code Playgroud)

以便您的函数的调用者可以调用newParams(),根据需要调整其字段,然后将结果值传递给您的函数:

myfunc(newParams())

ps := newParams()
ps.foo = "xyzzy"
myfunc(ps)
Run Code Online (Sandbox Code Playgroud)

这种方法可能比后处理更健壮一些,但它排除了使用文字来构造要在调用站点传递给函数的值,这不太“整洁”。