Go 學習:那些不一樣的知識點(上)

在學習 Go 語言的過程中,會涉及到語法、數(shù)據(jù)結(jié)構(gòu)、和一些 Go 特有的東西,其中有不少現(xiàn)在還印象深刻,本文就說說 Go 語言中那些不一樣的地方。
1、變量和函數(shù)的聲明
package?main
import?"fmt"
func?main()?{
?var?name?string?//使用?var?關鍵字定義一個名為?name?的字符串類型變量
?name?=?"oec2003"?//給變量賦值
?age:=18?//使用?:=?來定義變量,會自動進行類型推導,這種方式也是常用的方式
?fmt.Println(name,age)
}
上面示例是變量的聲明方式,可以看到跟 C# 不同的是,類型是寫在變量名之后的,而在 Go 中的函數(shù)也遵循這個原則:
func?getName(name?string)?string?{
?return?"hello,"?+?name
}
- 函數(shù)的定義使用 func 關鍵字;
- 參數(shù)也是類型寫在了參數(shù)名的后面;
- 函數(shù)的返回值寫在了方法名的后面。
2、不支持類型的隱式轉(zhuǎn)換
為了提高代碼的可讀性和避免一些隱藏的錯誤,在 Go 中不支持類型的隱式轉(zhuǎn)換。
在 C# 中下面的代碼可以正常編譯和運行:
static?void?Main(string[]?args)
{
????int?a?=?18;
????long?b?=?a;
????Console.WriteLine(b);
}
而在 Go 中下面的代碼是會有編譯錯誤的:
func?main()?{
?var?a?int16=10
?var?b?int32=a
?fmt.Println(b)
}
int16 和 int32 之間不能隱式轉(zhuǎn)換,只能進行顯示轉(zhuǎn)換,如下:
func?main()?{
?var?a?int16?=?10
?var?b?int32?=?int32(a)
?fmt.Println(b)
}
3、運算符
為了提高生產(chǎn)力,避免在語言中犯錯,Go 有很強的約束性,不支持前置 ++ 和 -- :
func?main()?{
?a:=10
?a++?//可以支持后置?++?和?--
?++a?//不支持前置?++?和?--
}
對數(shù)組的比較和 C# 也有區(qū)別,比如在 C# 中比較兩個長度相同、值相同的數(shù)組,返回的是 False :
static?void?Main(string[]?args)
{
????int[]?a?=?new[]?{1,?2,?3};
????int[]?b?=?new[]?{1,?2,?3};
????Console.WriteLine(a==b);?//?返回?False
}
但在 Go 中會有不一樣的結(jié)果:
func?main()?{
?a?:=?[...]int{1,?2,?3,?4}
?b?:=?[...]int{1,?2,?3,?4}
?fmt.Println(a?==?b)?//?長度相同、內(nèi)容相同、值順序相同?返回?ture
?b?=?[...]int{1,?2,?3,?5}
?fmt.Println(a?==?b)?//?長度不同?返回?false
?b?=?[...]int{1,?2,?4,?3}
?fmt.Println(a?==?b)?//?長度相同、內(nèi)容相同、值順序不相同?返回?false
}
4、map 的值可以是一個函數(shù)
Go 中的 map 和 C# 中的 Dictionary 有點像,map 的值可以是普通的數(shù)據(jù)類型外,也可以是一個函數(shù):
package?main
import?(
?"fmt"
)
func?aa(num?int)?int?{
?return?num?*?num
}
func?main()?{
?m?:=?map[int]func(a?int,?b?int)?int{}
?m[1]?=?func(a?int,?b?int)?int?{?return?a?+?b?}
?m[2]?=?func(a?int,?b?int)?int?{?return?a?-?b?}
?m[3]?=?func(a?int,?b?int)?int?{?return?a?*?b?}
?m[4]?=?func(a?int,?b?int)?int?{?return?a?*?b?}
?m[1](12,?3)
?for?_,?f?:=?range?m?{
??fmt.Println(f(12,?3))
?}
}
//運行結(jié)果如下:
15
9
36
36
5、條件判斷
在 Go 中的條件判斷也有 if 和 switch 兩種。
if 可以支持在條件的表達式中進行變量賦值:
func?main()?{
?m?:=?map[int]string{1:?"oec2003",?2:?"fengwei"}
?if?v,?isExist?:=?m[2];?isExist?{
??fmt.Println(v)
?}?else?{
??fmt.Println("不存在")
?}
}
- if 后的表達式分成了兩段,第一段對變量進行賦值;第二段進行條件的判斷,兩段之間用分號分隔;
- 第二段的條件判斷必須返回布爾值。
switch 的使用也有些不一樣的地方:
- 單個 case 中可以支持多個值,用逗號分隔;
- 不需要顯示添加 break 來退出 case;
- switch 后面可以不加任何表達式,就跟 if 類似了。
func?main()?{
?switch?os?:=?runtime.GOOS;?os?{
?case?"darwin":
??fmt.Println("system?is?mac")
?case?"linux":
??fmt.Println("system?is?linux")
?default:
??fmt.Println("其他")
?}
}
//類似?if?else
name:="oec2003"
switch{
????case?name=="oec2003":
????fmt.Println("name?is?oec2003")
????default:
????fmt.Println("name?is?undefine")
}
//case?后面支持多個值
name?:=?"fengwei"
switch?name?{
????case?"oec2003",?"fengwei":
????fmt.Println("都是我的名字")
????default:
????fmt.Println("不認識")
}
6、循環(huán)
在 Go 語言中,關鍵字非常少,因此,循環(huán)處理只有一個關鍵字 for
//相當于?C#?中的?while
n?:=?0
for?n?<?5?{
????fmt.Println(n)
????n++
}
//相當于?C#中的?while(true)
for{
????fmt.Println("這是一個死循環(huán)")
}
//?相當于?C#?中的正常的?for
for?i?:=?0;?i?<?10;?i++?{
????fmt.Println(i)
}
//?相當于?C#?中的?foreach
a?:=?[...]int{1,?2,?3,?4}
for?v?:=?range?a?{
????fmt.Println(v)
}
- 上面使用一個關鍵字包含了 C# 中所有遍歷的方式;
- for 后面的表達式不需要寫括號。
7、特別的 main 函數(shù)
一個簡單完整的 Go 程序如下所示:
package?main
import?"fmt"
func?main()?{
?fmt.Println("hello?oec2003!")
}
可以看出 Go 的 main 函數(shù)沒有返回值和參數(shù),如果需要在 main 函數(shù)中使用參數(shù),需要引入 os 包:
package?main
import?(
?"fmt"
?"os"
)
func?main()?{
????if?len(os.Args)?>?1?{
????????//這里需要注意,第一個參數(shù)下標為?1
????????fmt.Println("hello",?os.Args[1])
?}
????
????//遍歷所有的參數(shù)
?for?_,?arg?:=?range?os.Args?{
??fmt.Println(arg)
?}
}
- os.Args 是一個字符串的數(shù)組;
- 使用 range 可以用來遍歷數(shù)組;
- 在 for中使用 _ 可以忽略第一個參數(shù)。
8、初始化函數(shù)
我們知道在 Go 語言中,main 函數(shù)是程序的入口函數(shù),但 main 函數(shù)并不是第一個執(zhí)行的函數(shù),在 main 之前還有 init 函數(shù)會先被執(zhí)行,在 init 函數(shù)中可以做一些初始化的工作:
package?main
import?(
?"fmt"
)
func?init()?{
?fmt.Println("在這里做初始化工作")
}
func?main()?{
?fmt.Println("程序開始執(zhí)行")
}
9、函數(shù)支持多返回值
在 C# 中函數(shù)支持多返回值的方式有很多種,比如:返回一個對象、參數(shù)使用 out ,還有就是在 C# 7.0 添加的新功能元組,下面為 C# 中用元組的方式返回多值:
class?Program
{?
????static?void?Main(string[]?args)
????{
????????var?userInfo?=?GetUserInfo();
????????var?name?=?userInfo.name;
????????var?age?=?userInfo.age;
????????Console.WriteLine(name+age);
????}
????static?(string?name,?int?age)?GetUserInfo()
????{
?????return?("oec2003",?18);
????}
}
Go 語言的函數(shù)返回多值和 C# 的元組的方式比較像:
package?main
import?(
?"fmt"
)
func?getUserInfo()?(string,?int)?{
?return?"oec2003",?18
}
func?main()?{
?name,?age?:=?getUserInfo()
?fmt.Println(name,?age)
}
在接收值時,如果某些值不需要,可以使用 _ 進行忽略:
func?getUserInfo()?(string,?int)?{
?return?"oec2003",?18
}
func?main()?{
?name,_?:=?getUserInfo()
?fmt.Println(name)
}
10、函數(shù)延遲執(zhí)行
在 Go 中使用 defer 關鍵字可以讓函數(shù)延遲執(zhí)行,可以用來做釋放資源,釋放鎖等,先看下面的代碼,clear 函數(shù)前面添加了 defer 關鍵字,雖然在第 main 函數(shù)第一行,但會最后調(diào)用:
package?main
import?(
?"fmt"
)
func?clear()?{
?fmt.Println("清理資源")
}
func?main()?{
?defer?clear()
?fmt.Println("程序開始")
}
//程序執(zhí)行結(jié)果為
程序開始
清理資源
使用 panic 使程序崩潰,會發(fā)現(xiàn)被 defer 定義的函數(shù)依然會在最后執(zhí)行:
package?main
import?(
?"fmt"
)
func?clear()?{
?fmt.Println("清理資源")
}
func?main()?{
?defer?clear()
?fmt.Println("程序開始")
?panic("程序崩潰啦")
}
程序開始
清理資源
panic:?程序崩潰啦
這有點像是在 C# 中使用 try catch ,異常捕獲之后,finally 塊中的代碼還是會被執(zhí)行,我認為 Go 語言的這種處理更加靈活,以組合的方式來達到目的,這也符合 Go 語言的設計哲學。
相關閱讀:
