新聞中心
在C語言時代大家一般都用過 printf() 函數(shù),從那個時候開始其實已經(jīng)在感受可變參數(shù)的魅力和價值,如同C語言中的 printf() 函數(shù),Go語言標(biāo)準(zhǔn)庫中的 fmt.Println() 等函數(shù)的實現(xiàn)也依賴于語言的可變參數(shù)功能。

本節(jié)我們將介紹可變參數(shù)的用法。合適地使用可變參數(shù),可以讓代碼簡單易用,尤其是輸入輸出類函數(shù),比如日志函數(shù)等。
可變參數(shù)類型
可變參數(shù)是指函數(shù)傳入的參數(shù)個數(shù)是可變的,為了做到這點,首先需要將函數(shù)定義為可以接受可變參數(shù)的類型:
func myfunc(args ...int) {
for _, arg := range args {
fmt.Println(arg)
}
}上面這段代碼的意思是,函數(shù) myfunc() 接受不定數(shù)量的參數(shù),這些參數(shù)的類型全部是 int,所以它可以用如下方式調(diào)用:
myfunc(2, 3, 4)
myfunc(1, 3, 7, 13)
形如
...type格式的類型只能作為函數(shù)的參數(shù)類型存在,并且必須是最后一個參數(shù),它是一個語法糖(syntactic sugar),即這種語法對語言的功能并沒有影響,但是更方便程序員使用,通常來說,使用語法糖能夠增加程序的可讀性,從而減少程序出錯的可能。
從內(nèi)部實現(xiàn)機(jī)理上來說,類型
...type本質(zhì)上是一個數(shù)組切片,也就是
[]type,這也是為什么上面的參數(shù) args 可以用 for 循環(huán)來獲得每個傳入的參數(shù)。
假如沒有
...type這樣的語法糖,開發(fā)者將不得不這么寫:
func myfunc2(args []int) {
for _, arg := range args {
fmt.Println(arg)
}
}從函數(shù)的實現(xiàn)角度來看,這沒有任何影響,該怎么寫就怎么寫,但從調(diào)用方來說,情形則完全不同:
myfunc2([]int{1, 3, 7, 13})
大家會發(fā)現(xiàn),我們不得不加上
[]int{}來構(gòu)造一個數(shù)組切片實例,但是有了
...type這個語法糖,我們就不用自己來處理了。
任意類型的可變參數(shù)
之前的例子中將可變參數(shù)類型約束為 int,如果你希望傳任意類型,可以指定類型為 interface{},下面是Go語言標(biāo)準(zhǔn)庫中 fmt.Printf() 的函數(shù)原型:
func Printf(format string, args ...interface{}) {
// ...
}
用 interface{} 傳遞任意類型數(shù)據(jù)是Go語言的慣例用法,使用 interface{} 仍然是類型安全的,這和 C/ C++ 不太一樣,下面通過示例來了解一下如何分配傳入 interface{} 類型的數(shù)據(jù)。
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)
}該程序的輸出結(jié)果為:
1 is an int value.
234 is an int64 value.
hello is a string value.
1.234 is an unknown type.
遍歷可變參數(shù)列表——獲取每一個參數(shù)的值
可變參數(shù)列表的數(shù)量不固定,傳入的參數(shù)是一個切片,如果需要獲得每一個參數(shù)的具體值時,可以對可變參數(shù)變量進(jìn)行遍歷,參見下面代碼:
package main
import (
"bytes"
"fmt"
)
// 定義一個函數(shù), 參數(shù)數(shù)量為0~n, 類型約束為字符串
func joinStrings(slist ...string) string {
// 定義一個字節(jié)緩沖, 快速地連接字符串
var b bytes.Buffer
// 遍歷可變參數(shù)列表slist, 類型為[]string
for _, s := range slist {
// 將遍歷出的字符串連續(xù)寫入字節(jié)數(shù)組
b.WriteString(s)
}
// 將連接好的字節(jié)數(shù)組轉(zhuǎn)換為字符串并輸出
return b.String()
}
func main() {
// 輸入3個字符串, 將它們連成一個字符串
fmt.Println(joinStrings("pig ", "and", " rat"))
fmt.Println(joinStrings("hammer", " mom", " and", " hawk"))
}代碼輸出如下:
pig and rat
hammer mom and hawk
代碼說明如下:
- 第 8 行,定義了一個可變參數(shù)的函數(shù),slist 的類型為 []string,每一個參數(shù)的類型都是 string,也就是說,該函數(shù)只接受字符串類型作為參數(shù)。
- 第 11 行,bytes.Buffer 在這個例子中的作用類似于 StringBuilder,可以高效地進(jìn)行字符串連接操作。
- 第 13 行,遍歷 slist 可變參數(shù),s 為每個參數(shù)的值,類型為 string。
- 第 15 行,將每一個傳入?yún)?shù)放到 bytes.Buffer 中。
- 第 19 行,將 bytes.Buffer 中的數(shù)據(jù)轉(zhuǎn)換為字符串作為函數(shù)返回值返回。
- 第 24 行,輸入 3 個字符串,使用 joinStrings() 函數(shù)將參數(shù)連接為字符串輸出。
- 第 25 行,輸入 4 個字符串,連接后輸出。
如果要獲取可變參數(shù)的數(shù)量,可以使用 len() 函數(shù)對可變參數(shù)變量對應(yīng)的切片進(jìn)行求長度操作,以獲得可變參數(shù)數(shù)量。
獲得可變參數(shù)類型——獲得每一個參數(shù)的類型
當(dāng)可變參數(shù)為 interface{} 類型時,可以傳入任何類型的值,此時,如果需要獲得變量的類型,可以通過 switch 獲得變量的類型,下面的代碼演示將一系列不同類型的值傳入 printTypeValue() 函數(shù),該函數(shù)將分別為不同的參數(shù)打印它們的值和類型的詳細(xì)描述。
打印類型及值:
package main
import (
"bytes"
"fmt"
)
func printTypeValue(slist ...interface{}) string {
// 字節(jié)緩沖作為快速字符串連接
var b bytes.Buffer
// 遍歷參數(shù)
for _, s := range slist {
// 將interface{}類型格式化為字符串
str := fmt.Sprintf("%v", s)
// 類型的字符串描述
var typeString string
// 對s進(jìn)行類型斷言
switch s.(type) {
case bool: // 當(dāng)s為布爾類型時
typeString = "bool"
case string: // 當(dāng)s為字符串類型時
typeString = "string"
case int: // 當(dāng)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))
}代碼輸出如下:
value: 100 type: int
value: str type: string
value: true type: bool
代碼說明如下:
- 第 8 行,printTypeValue() 輸入不同類型的值并輸出類型和值描述。
- 第 11 行,bytes.Buffer 字節(jié)緩沖作為快速字符串連接。
- 第 14 行,遍歷 slist 的每一個元素,類型為 interface{}。
- 第 17 行,使用 fmt.Sprintf 配合
%v動詞,可以將 interface{} 格式的任意值轉(zhuǎn)為字符串。 - 第 20 行,聲明一個字符串,作為變量的類型名。
- 第 23 行,switch s.(type) 可以對 interface{} 類型進(jìn)行類型斷言,也就是判斷變量的實際類型。
- 第 24~29 行為 s 變量可能的類型,將每種類型的對應(yīng)類型字符串賦值到 typeString 中。
- 第 33~42 行為寫輸出格式的過程。
在多個可變參數(shù)函數(shù)中傳遞參數(shù)
可變參數(shù)變量是一個包含所有參數(shù)的切片,如果要將這個含有可變參數(shù)的變量傳遞給下一個可變參數(shù)函數(shù),可以在傳遞時給可變參數(shù)變量后面添加
...,這樣就可以將切片中的元素進(jìn)行傳遞,而不是傳遞可變參數(shù)變量本身。
下面的例子模擬 print() 函數(shù)及實際調(diào)用的 rawPrint() 函數(shù),兩個函數(shù)都擁有可變參數(shù),需要將參數(shù)從 print 傳遞到 rawPrint 中。
可變參數(shù)傳遞:
package main
import "fmt"
// 實際打印的函數(shù)
func rawPrint(rawList ...interface{}) {
// 遍歷可變參數(shù)切片
for _, a := range rawList {
// 打印參數(shù)
fmt.Println(a)
}
}
// 打印函數(shù)封裝
func print(slist ...interface{}) {
// 將slist可變參數(shù)切片完整傳遞給下一個函數(shù)
rawPrint(slist...)
}
func main() {
print(1, 2, 3)
}代碼輸出如下:
1
2
3
對代碼的說明:
- 第 9~13 行,遍歷 rawPrint() 的參數(shù)列表 rawList 并打印。
- 第 20 行,將變量在 print 的可變參數(shù)列表中添加
...后傳遞給 rawPrint()。 - 第 25 行,傳入 1、2、3 這 3 個整型值并進(jìn)行打印。
如果嘗試將第 20 行修改為:
rawPrint("fmt", slist)再次執(zhí)行代碼,將輸出:
[1 2 3]
此時,slist(類型為 []interface{})將被作為一個整體傳入 rawPrint(),rawPrint() 函數(shù)中遍歷的變量也就是 slist 的切片值。
可變參數(shù)使用
...進(jìn)行傳遞與切片間使用 append 連接是同一個特性。
新聞標(biāo)題:創(chuàng)新互聯(lián)GO教程:Go語言可變參數(shù)(變參函數(shù))
網(wǎng)站鏈接:http://fisionsoft.com.cn/article/cohcpsj.html


咨詢
建站咨詢
