golang给函数参数设置默认值的几种方式小结(函数参数默认值
学亮编程手记 人气:0前言
这个问题相当麻烦,根据golang-nuts/google groups中的这篇文章,golang现在与将来都不会支持参数默认值。Go始终在使得自己变得尽可能的简单,而增加这种额外的支持会使parser变得更复杂。
设置参数值的好处:
- 可以缺省部分参数。
- 可以提供一种默认的,行之有效的配置。
但是参考资料中提到了几种实现默认值的方法:
强制改变
// 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) }
go的一大特点就是所有的变量都必须经过初始化。那如果在函数内部读取到参数值为初始化值,即可进行对应的操作。 但是这种方法无法解决设置参数默认值时所经常应对的场景,即参数缺省问题。
使用可变参数语法糖
// 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) }
上述方法中,a是必须的,而b是可选的。 此时b的默认值时5,如果b_optional中存在数据,则将其读取。
利用结构体的config
// 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) }
虽然也能做到缺省参数(如果不设置A属性,则该属性将被默认初始化),但是这种方式只对字符串管用。
转换函数的全部参数
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) }
相当泛用的方法,但是对于不同的类型就不可行了。
补充知识:Golang中设置函数默认参数的优雅实现
在Golang中,我们经常碰到要设置一个函数的默认值,或者说我定义了参数值,但是又不想传递值,这个在python或php一类的语言中很好实现,但Golang中好像这种方法又不行。今天在看Grpc源码时,发现了一个方法可以很优雅的实现,叫做 Functional Options Patter.通过定义函数的方式来实现
比如我们以如下的构造函数为例说明下,用这个的好处
func NewClient(address string,timeout,trynums int){}
如果我们要实例化这个函数,timeout,trynums这个是必须要传的,那如果我不想传呢,一般可能是通过传对象(struct,map)或定义多个func,感觉都不太方便。
func NewClient(address string){} func NewClientNoTimeout(address string,trynums int){}
另一种传一个对象
type Options struct{ timeout int, trynums int } func NewClient(address string,opts Options){}
用对象的形式,还得检查参数的合法性。比如传递了不存在的参数等。
那么,我们看下用Functional Options Patter的方式,我写了一个简单的例子。
package main import "fmt" //如何向func传递默认值 type dialOption struct { Username string Password string Service string } type DialOption interface { apply(*dialOption) } type funcOption struct { f func(*dialOption) } func(fdo *funcOption) apply(do *dialOption){ fdo.f(do) } func newFuncOption(f func(*dialOption))*funcOption{ return &funcOption{ f:f, } } func withUserName(s string) DialOption{ return newFuncOption(func(o *dialOption){ o.Username = s }) } func withPasswordd(s string) DialOption{ return newFuncOption(func(o *dialOption){ o.Password = s }) } func withService(s string) DialOption{ return newFuncOption(func(o *dialOption){ o.Service = s }) } //默认参数 func defaultOptions() dialOption{ return dialOption{ Service:"test", } } type clientConn struct { timeout int dopts dialOption } func NewClient(address string, opts ...DialOption){ cc :=&clientConn{ timeout:30, dopts:defaultOptions(), } //循环调用opts for _,opt := range opts { opt.apply(&cc.dopts) } fmt.Printf("%+v",cc.dopts) } func main(){ NewClient("127.0.0.1",withPasswordd("654321"),withService("habox")) NewClient("127.0.0.1",withService("habox")) }
实例化时,通过func的方式来传递参数,也可以定义一些默认参数。如果以后要加,只需要更改很少的代码。
而且,这种方式也不会传递不相关的参数,因为参数都在通过func的方式来修改的。
唯一不好的地方可能是代码量相应的增加了。但是为了更优雅,这种做法还是值得的。
总结
加载全部内容