golang:可变参数

可变参数类型

可变参数是指函数传入的参数个数是可变的,为了做到这点,首先需要将函数定义为可以接受可变参数的类型:

func myfunc(args ...int) {
    for _, arg := range args {
        fmt.Println(arg)
    }
}

上面这段代码的意思是,函数 myfunc() 接受不定数量的参数,这些参数的类型全部是 int,所以它可以用如下方式调用:

myfunc(2, 3, 4)
myfunc(1, 3, 7, 13)

形如…type格式的类型只能作为函数的参数类型存在,并且必须是最后一个参数,它是一个语法糖(syntactic sugar),即这种语法对语言的功能并没有影响,但是更方便程序员使用,通常来说,使用语法糖能够增加程序的可读性,从而减少程序出错的可能。

从内部实现机理上来说,类型…type本质上是一个数组切片,也就是[]type,这也是为什么上面的参数 args 可以用 for 循环来获得每个传入的参数。

假如没有…type这样的语法糖,开发者将不得不这么写:

func myfunc2(args []int) {
    for _, arg := range args {
        fmt.Println(arg)
    }
}

从函数的实现角度来看,这没有任何影响,该怎么写就怎么写,但从调用方来说,情形则完全不同:

myfunc2([]int{1, 3, 7, 13})

任意类型的可变参数

之前的例子中将可变参数类型约束为 int,如果你希望传任意类型,可以指定类型为 interface{},下面是Go语言标准库中 fmt.Printf() 的函数原型:

func Printf(format string, args ...interface{}) {
    // ...
}

用 interface{} 传递任意类型数据是Go语言的惯例用法,使用 interface{} 仍然是类型安全的,这和 C/C++ 不太一样,下面通过示例来了解一下如何分配传入 interface{} 类型的数据。

package main
import "fmt"
func MyPrintf(args ...interface{}) {
    for _, arg := range args {
        switch arg.(type) {
            case int:
                fmt.Println(arg, "is an int value.")
            case string:
                fmt.Println(arg, "is a string value.")
            case int64:
                fmt.Println(arg, "is an int64 value.")
            default:
                fmt.Println(arg, "is an unknown type.")
        }
    }
}
func main() {
    var v1 int = 1
    var v2 int64 = 234
    var v3 string = "hello"
    var v4 float32 = 1.234
    MyPrintf(v1, v2, v3, v4)
}

在这里插入图片描述

遍历可变参数列表——获取每一个参数的值

可变参数列表的数量不固定,传入的参数是一个切片,如果需要获得每一个参数的具体值时,可以对可变参数变量进行遍历,参见下面代码:

package main
import (
	"bytes"
	"fmt"
)
// 定义一个函数, 参数数量为0~n, 类型约束为字符串
func joinStrings(slist ...string) string {
	// 定义一个字节缓冲, 快速地连接字符串
	var b bytes.Buffer
	// 遍历可变参数列表slist, 类型为[]string
	for _, s := range slist {
		// 将遍历出的字符串连续写入字节数组
		b.WriteString(s)
	}
	// 将连接好的字节数组转换为字符串并输出
	return b.String()
}
func main() {
	// 输入3个字符串, 将它们连成一个字符串
	fmt.Println(joinStrings("pig ", " and ", " rat"))
	fmt.Println(joinStrings("hammer", " mom ", " and", " hawk"))
}

在这里插入图片描述
如果要获取可变参数的数量,可以使用 len() 函数对可变参数变量对应的切片进行求长度操作,以获得可变参数数量。

获得可变参数类型——获得每一个参数的类型

当可变参数为 interface{} 类型时,可以传入任何类型的值,此时,如果需要获得变量的类型,可以通过 switch 获得变量的类型,下面的代码演示将一系列不同类型的值传入 printTypeValue() 函数,该函数将分别为不同的参数打印它们的值和类型的详细描述。

打印类型及值:

package main
import (
	"bytes"
	"fmt"
)
func printTypeValue(slist ...interface{}) string {
	// 字节缓冲作为快速字符串连接
	var b bytes.Buffer
	// 遍历参数
	for _, s := range slist {
		// 将interface{}类型格式化为字符串
		str := fmt.Sprintf("%v", s)
		// 类型的字符串描述
		var typeString string
		// 对s进行类型断言
		switch s.(type) {
		case bool:    // 当s为布尔类型时
			typeString = "bool"
		case string:    // 当s为字符串类型时
			typeString = "string"
		case int:    // 当s为整型类型时
			typeString = "int"
		}
		// 写字符串前缀
		b.WriteString("value: ")
		// 写入值
		b.WriteString(str)
		// 写类型前缀
		b.WriteString(" type: ")
		// 写类型字符串
		b.WriteString(typeString)
		// 写入换行符
		b.WriteString("\n")
	}
	return b.String()
}
func main() {
	// 将不同类型的变量通过printTypeValue()打印出来
	fmt.Println(printTypeValue(100, "str", true))
}

在这里插入图片描述

在多个可变参数函数中传递参数

可变参数变量是一个包含所有参数的切片,如果要将这个含有可变参数的变量传递给下一个可变参数函数,可以在传递时给可变参数变量后面添加…,这样就可以将切片中的元素进行传递,而不是传递可变参数变量本身。

下面的例子模拟 print() 函数及实际调用的 rawPrint() 函数,两个函数都拥有可变参数,需要将参数从 print 传递到 rawPrint 中。

package main
import "fmt"
// 实际打印的函数
func rawPrint(rawList ...interface{}) {
    // 遍历可变参数切片
    for _, a := range rawList {
        // 打印参数
        fmt.Println(a)
    }
}
// 打印函数封装
func print(slist ...interface{}) {
    // 将slist可变参数切片完整传递给下一个函数
    rawPrint(slist...)
}
func main() {
    print(1, 2, 3)
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值