hager
2022/05/17阅读:29主题:橙心
golang 可变参数
go基础之可变参数
之前文章中,已经对方法、接口、结构体等做了简单的记录。本篇将对于方法中一种特殊参数进行分析和记录。
我们知道定义方法或者函数时,包含方法或者说函数的名称、参数、以及最后的返回值类型。大概格式如下:

那么,对于可变参数该如何理解和使用呢?
其实,如果你有过编程经验,其他一些语言中都有这个所谓的可变参数,比如Java(...)、python(*)、C#(params)等
今天我们主要来学习go中的可变参数,看到之后你或许就会直呼,so easy了~
语法
func hello(a int, b ...int) {
}
[函数]通常只接受固定数量的参数。*可变参数函数是接受可变数量参数的函数。如果函数定义的最后一个参数以省略号**...*为前缀,则该函数可以接受该参数的任意数量的参数。
这里需要注意的是,必须是最后一个参数是可变参数,否则会有问题。
如果拿上面的语法示例中的**hello
函数来讲,参数b
**就是可变参数。
hello(1, 2) // a=1,b=2
hello(5, 6, 7, 8, 9) //a=5,b=6,7,8,9
可能你比较好奇,假如说语法中参数a,b换一下位置,会变成什么样呢?
其实,IDE会直接告诉你一个类似这样的错误:上述函数将无法编译并出现错误
syntax error: cannot use ... with non-final parameter b`
示例和理解可变参数函数的工作原理
让我们创建自己的可变参数函数。我们将编写一个简单的程序来查找整数输入列表中是否存在整数。
package main
import (
"fmt"
)
func find(num int, nums ...int) {
fmt.Printf("type of nums is %T\n", nums)
found := false
// 这里你会发现,nums其实是 []int的slice类型,即 切片类型
for i, v := range nums {
if v == num {
fmt.Println(num, "found at index", i, "in", nums)
found = true
}
}
if !found {
fmt.Println(num, "not found in ", nums)
}
fmt.Printf("\n")
}
func main() {
find(89, 89, 90, 95)
find(45, 56, 67, 45, 90, 109)
find(78, 38, 56, 98)
find(87)
}
我们了解到函数的可变参数实际上是转换为切片的。那么当我们可以使用切片实现相同的功能时,为什么还需要可变参数函数呢?现在我们直接用切片slice直接改写上述示例,大概如下:
package main
import (
"fmt"
)
func find(num int, nums []int) {
fmt.Printf("type of nums is %T\n", nums)
found := false
for i, v := range nums {
if v == num {
fmt.Println(num, "found at index", i, "in", nums)
found = true
}
}
if !found {
fmt.Println(num, "not found in ", nums)
}
fmt.Printf("\n")
}
func main() {
find(89, []int{89, 90, 95})
find(45, []int{56, 67, 45, 90, 109})
find(78, []int{38, 56, 98})
find(87, []int{})
}
其实,我们日常程序开发中,用到的可变参数还是挺多的,比如组合数组对象时,用到的append就是一个可变参数定义
// append定义 /Go/src/builtin/builtin.go:144
func append(slice []Type, elems ...Type) []Type
// GetUserList 根据搜索条件查询,返回usermodel 集合
func GetUserList(model model.SearchModel) []UserModel {
fmt.Printf("入参:%d \n", model)
var userList []UserModel
for i := 0; i < 8; i++ {
item := UserModel{
UserId: 0,
UserName: "",
Birthday: time.Now(),
}
userList = append(userList, item)
}
return userList
}
切片传递给可变参数会怎样?
其实,是可以的,只不过,需要借助于一种特殊魔法:
有一种语法糖可用于将切片传递给可变参数函数。您必须使用省略号为切片添加后缀。...
如果这样做,切片将直接传递给函数,而无需创建新切片。
package main
import (
"fmt"
)
func change(s ...string) {
s[0] = "Go"
}
func main() {
welcome := []string{"hello", "world"}
change(welcome...)
fmt.Println(welcome)
}
如果不在welcome后面加三个点,IDE直接就会报错提示:

总结
对于可变参数和切片,一点小区别:
1、无需在每次函数调用期间创建切片,这点可以看上述改造成slice的示例中,main方法中调用find是的区别。
2、假如,你不想传递第二个参数时,可变参数可以直接忽略;而slice则需要声明一个空切片才能满足函数对参数的要求。
3、可读性方面,可能可变参数更加直观些。而切片参数给人一种必须传递的赶脚。
至于日常工作中,你如何使用,根据自己喜好选择即可。这里不做过多的“规定”。
references
https://golangbot.com/variadic-functions/
作者介绍