当前位置: 首页 > 科技观察

传参有坑,我在蚌埠!

时间:2023-03-13 05:16:10 科技观察

最近在工作中发现了一个坑。关键是坑不报错,所以记录在这里,顺便把相关的知识点总结一下。关于这个坑,还得从“不确定参数”说起。IndeterminateparametersUndeterminateparameters,顾名思义,是golang在参数个数不确定的情况下支持的一种机制。比如我们要实现一个累加多个数的方法。既然有多个数,那么数的个数肯定是不确定的,所以我们可以这样写。funcNumberSum(args...int)(resint){//使用可变参数的例子i:=rangeargs{res+=i}returnres}funcmain(){total:=NumberSum(1,2,3,4,5,6)fmt.Println(total)}//输出结果15通过上面的例子我们可以看出不确定参数的使用还是很简单的,只要定义好参数,在前面加上"输入三个点”就足够了。但是,在使用不确定参数时还有其他问题需要注意。所有可变参数类型必须相同。不确定参数必须是函数的最后一个参数。不确定参数相当于函数体中的一个切片,对切片的操作也适用于对不确定参数的操作。(例如,在上面的示例中,可以对args执行“forrange”操作)。切片也可以作为参数传递给不确定的参数,在切片名称后添加“...”。前三点其实还是比较容易理解的。第四点看下面的例子,也是“容易出问题”的地方,本文要说的“坑”也与此有关。funcNumberSum(args...int)(resint){fori:=rangeargs{res+=i}returnres}funcmain(){//定义一个slice,准备传给可变参数numSelice:=[]int{1,2,3,4,5,6}//切片传给可变参数时,需要加上...total:=NumberSum(numSelice...)fmt.Println(total)}通过上面的例子,我们应该已经对可变参数有了一定的掌握。接下来,我们来看一个特殊的场景。如果“可变参数的参数类型不同”怎么办?这种场景其实很常见,使用接口类型就可以解决。比如我们要封装一个生成rediskey的方法。funcMakeRedisKey(keystring,args...interface{})(redisKeystring){redisKey=fmt.Sprintf(key,args)returnredisKey}funcmain(){ucid:=123name:="test"key:="key_id_%d_name_%s"redisKey:=MakeRedisKey(key,ucid,name)fmt.Println(redisKey)}//输出key_id_[123%!d(string=test)]_name_%!s(MISSING)上面的代码好像没问题,但是输出结果好像是乱码。为什么?下面分析一下我们在main函数中调用“MakeRedisKey”函数时传入的“ucid和name”。不确定的参数。“在MakeRedisKey函数中”,打印出来的args的类型是“slice”类型,即[]interface{}。让我们看看“fmt.Sprintf”函数的定义。funcSprintf(formatstring,a...interface{})string{.........returns的内容是什么并不重要}我们会发现,Sprintf的第二个参数也是一个“不确定的”范围”。这相当于在“MakeRedisKey”中,我们将“切片类型”的参数args作为参数传递给一个“不确定参数”。在参数后面加上...**这个规律,上面的代码可以改成下面这样就正常了。funcMakeRedisKey(keystring,args...interface{})(redisKeystring){redisKey=fmt.Sprintf(key,args...)//Add...returnredisKey}funcmain(){ucid:=123name:="test"key:="key_id_%d_name_%s"redisKey:=MakeRedisKey(key,ucid,name)fmt.Println(redisKey)}//输出结果key_id_123_name_test这样我们就可以正常搞定了。最后总结一下,“当我们使用不确定的参数时,尤其是涉及到多个pass的时候,千万不要想当然。中间pass的时候记得在参数后面加上...”。