hager

V1

2022/05/17阅读:29主题:橙心

golang 可变参数

go基础之可变参数

之前文章中,已经对方法、接口、结构体等做了简单的记录。本篇将对于方法中一种特殊参数进行分析和记录。

我们知道定义方法或者函数时,包含方法或者说函数的名称、参数、以及最后的返回值类型。大概格式如下:

go-function
go-function

那么,对于可变参数该如何理解和使用呢?

其实,如果你有过编程经验,其他一些语言中都有这个所谓的可变参数,比如Java(...)、python(*)、C#(params)等

今天我们主要来学习go中的可变参数,看到之后你或许就会直呼,so easy了~

语法

func hello(a int, b ...int) {  
}

[函数]通常只接受固定数量的参数。*可变参数函数是接受可变数量参数的函数。如果函数定义的最后一个参数以省略号**...*为前缀,则该函数可以接受该参数的任意数量的参数。

这里需要注意的是,必须是最后一个参数是可变参数,否则会有问题。

如果拿上面的语法示例中的**hello函数来讲,参数b**就是可变参数。

hello(12// a=1,b=2 
hello(56789//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(89899095)
    find(4556674590109)
    find(78385698)
    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{899095})
    find(45, []int{56674590109})
    find(78, []int{385698})
    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直接就会报错提示:

image-20220517155040699
image-20220517155040699

总结

对于可变参数和切片,一点小区别:

1、无需在每次函数调用期间创建切片,这点可以看上述改造成slice的示例中,main方法中调用find是的区别

2、假如,你不想传递第二个参数时,可变参数可以直接忽略;而slice则需要声明一个空切片才能满足函数对参数的要求。

3、可读性方面,可能可变参数更加直观些。而切片参数给人一种必须传递的赶脚。

至于日常工作中,你如何使用,根据自己喜好选择即可。这里不做过多的“规定”。

references

https://golangbot.com/variadic-functions/

分类:

后端

标签:

Golang

作者介绍

hager
V1