微软Go学习教程(上半部分)

Posted Harris-H

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微软Go学习教程(上半部分)相关的知识,希望对你有一定的参考价值。

微软Go学习教程(上半部分)

0.下载Go

go version #查看go版本,是否安装成功.
go env #查看环境变量

1.Hello World

package main
import "fmt"

func main() 
	fmt.Println("Hello World!")

go run main.go 编译并执行Go应用。

go build main.go编译并生成可执行文件

2.声明和使用变量

package main

import "fmt"

func main()
	var a string = "Harris"	//变量 名称 类型 初始化
	var aa,bb int = 12,13
	var (
		b = 12  //自动推断类型
		c = "Jack"
	)
	age := 123	//最常用的声明和初始化方式
	name := "Lisa"
	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c,aa,bb)
	fmt.Println(age,name)

Harris
12
Jack

需要记住的重要一点是,在 Go 中,当你声明一个变量但不使用它时,Go 会抛出错误,而不是像某些其他编程语言一样抛出警告。

声明常量

常量和变量之间既有相似之处,也有一些重要差异。 例如,你可以在不使用常量的情况下声明常量。 你不会收到错误消息。 不能使用冒号等于号来声明常量。 如果采用这种方式,Go 会发出警告。

const (
    StatusOK              = 0
    StatusConnectionReset = 1
    StatusOtherError      = 2
)

3.数据类型

Go 是一种强类型语言。 这意味着你声明的每个变量都绑定到特定的数据类型,并且只接受与此类型匹配的值。

Go 有四类数据类型:

  • 基本类型:数字、字符串和布尔值
  • 聚合类型:数组和结构
  • 引用类型:指针、切片、映射、函数和通道
  • 接口类型:接口

整型数字

一般来说,定义整数类型的关键字是 int。 但 Go 还提供了 int8int16int32int64 类型,其大小分别为 8、16、32 或 64 位的整数。 使用 32 位操作系统时,如果只是使用 int,则大小通常为 32 位。 在 64 位系统上,int 大小通常为 64 位。 但是,此行为可能因计算机而不同。 可以使用 uint。 但是,只有在出于某种原因需要将值表示为无符号数字的情况下,才使用此类型。 此外,Go 还提供 uint8uint16uint32uint64 类型。

var integer8 int8 = 127
var integer16 int16 = 32767
var integer32 int32 = 2147483647
var integer64 int64 = 9223372036854775807
fmt.Println(integer8, integer16, integer32, integer64)

大多数情况下,你将使用 int,但需要了解其他整数类型,因为在 Go 中,intint32 不同,即使整数的自然大小为 32 位也是如此。 换句话说,需要强制转换时,你需要进行显式转换。 如果尝试在不同类型之间执行数学运算,将会出现错误。 例如,假定你具有下面的代码:

运行该程序时,你会收到以下错误:

invalid operation: integer16 + integer32 (mismatched types int16 and int32)

如你所见,在 Go 中将值从一种类型转换为另一种类型时,需要显式声明新类型。 我们将在本模块结束时讨论如何正确地强制转换类型。

在学习 Go 过程中,你可能会收到有关 runes 的信息。 rune 只是 int32 数据类型的别名。 它用于表示 Unicode 字符(或 Unicode 码位)。 例如,假设有以下代码:

rune := 'G'
fmt.Println(rune)

运行前面的代码片段时,你可能会在命令提示符下看到程序打印符 G。 不过,你还会看到数字 71,它表示 G 的 Unicode 字符。 我们将在后续模块中详细介绍 runes。

内置builtin.go 查看数据类型

https://go.dev/src/builtin/builtin.go

浮点数

var float32 float32 = 2147483647
var float64 float64 = 9223372036854775807
fmt.Println(float32, float64)
package main

import (
	"fmt"
	"math"
)

func main() 
	fmt.Println(math.MaxFloat32, math.MaxFloat64)

布尔型

布尔类型仅可能有两个值:truefalse。 你可以使用关键字 bool 声明布尔类型。 Go 不同于其他编程语言,在 Go 中,你不能将布尔类型隐式转换为 0 或 1。 你必须显式执行此操作。

因此,你可以按如下方式声明布尔变量:

var featureFlag bool = true

最后,让我们看一下编程语言中最常见的数据类型:string。 在 Go 中,关键字 string 用于表示字符串数据类型。 若要初始化字符串变量,你需要在双引号(")中定义值。 单引号(')用于单个字符(以及 runes,正如我们在上一节所述)。

例如,下面的代码演示了声明和初始化字符串变量的两种方法:

package main

import (
	"fmt"
)

func main() 
	var firstName string = "John"
	lastName := "Doe"
	fmt.Println(firstName, lastName)

有时,你需要对字符进行转义。 为此,在 Go 中,请在字符之前使用反斜杠 (\\)。 例如,下面是使用转义字符的最常见示例:

  • \\n:新行
  • \\r:回车符
  • \\t:选项卡
  • \\':单引号
  • \\":双引号
  • \\\\:反斜杠

默认值

到目前为止,几乎每次声明变量时,都使用值对其进行了初始化。 但与在其他编程语言中不同的是,在 Go 中,如果你不对变量初始化,所有数据类型都有默认值。 此功能非常方便,因为在使用之前,你无需检查变量是否已初始化。

下面列出了我们目前浏览过类型的几个默认值:

  • int 类型的 0(及其所有子类型,如 int64
  • float32float64 类型的 +0.000000e+000
  • bool 类型的 false
  • string 类型的空值

类型转换

在上一节中,我们确认在 Go 中隐式强制转换不起作用。 接下来,需要显式强制转换。 Go 提供了将一种数据类型转换为另一种数据类型的一些本机方法。 例如,一种方法是对每个类型使用内置函数,如下所示:

package main

import (
	"fmt"
)

func main() 
	var integer16 int16 = 127
	var integer32 int32 = 32767
	fmt.Println(int32(integer16) + integer32)

Go 的另一种转换方法是使用 strconv 包。 例如,若要将 string 转换为 int,可以使用以下代码,反之亦然:

package main

import (
	"fmt"
	"strconv"
)

func main() 
	i, _ := strconv.Atoi("-42")
	s := strconv.Itoa(-42)
	fmt.Println(i, s)

main函数

与之交互的函数是 main() 函数。 Go 中的所有可执行程序都具有此函数,因为它是程序的起点。 你的程序中只能有一个 main() 函数。 如果创建的是 Go 包,则无需编写 main() 函数。 我们将在后续模块中介绍如何创建包。

在深入了解如何创建自定义函数的基本知识之前,让我们看看 main() 函数的一个重要特性。 你可能留意到,main() 函数没有任何参数,并且不返回任何内容。 但这并不意味着其不能从用户读取值,如命令行参数。 如要访问 Go 中的命令行参数,可以使用用于保存传递到程序的所有参数的 os 包os.Args 变量来执行操作。

下面的代码从命令行读取两个数字,并为其求和:

package main

import (
    "fmt"
    "os"
    "strconv"
)

func main() 
    number1, _ := strconv.Atoi(os.Args[1])
    number2, _ := strconv.Atoi(os.Args[2])
    fmt.Println("Sum:", number1+number2)

os.Args 变量包含传递给程序的每个命令行参数。 由于这些值的类型为 string,因此需要将它们转换为 int 以进行求和。

若要运行程序,请使用以下命令:

go run main.go 3 5

4.自定义函数

func name(parameters) (results) 
    body-content

请注意,使用 func 关键字来定义函数,然后为其指定名称。 在命名后,指定函数的参数列表。 你可以指定零个或多个参数。 你还可以定义函数的返回类型,该函数也可以是零个或多个。 (我们将在下一节中讨论如何返回多个值)。在定义所有这些值之后,你可以编写函数的正文内容。

若要练习此技巧,我们将重构上一节的代码,为自定义函数中的数字求和。 我们将使用以下代码:

package main

import (
    "fmt"
    "os"
    "strconv"
)

func main() 
    sum := sum(os.Args[1], os.Args[2])
    fmt.Println("Sum:", sum)


func sum(number1 string, number2 string) int 
    int1, _ := strconv.Atoi(number1)
    int2, _ := strconv.Atoi(number2)
    return int1 + int2

在 Go 中,你还可以为函数的返回值设置名称,将其当作一个变量。 例如,你可以重构如下 sum 函数:

func sum(number1 string, number2 string) (result int) 
    int1, _ := strconv.Atoi(number1)
    int2, _ := strconv.Atoi(number2)
    result = int1 + int2
    return

请注意,你现在需要将函数的结果值括在括号中。 你还可以在函数中使用该变量,并且只需在末尾添加 return 行。 Go 将返回这些返回变量的当前值。 在函数末尾编写 return 关键字非常简单方便,尤其是在有多个返回值时。 我们不建议使用此方法。 可能不确定函数将返回什么。

返回多个值

func calc(number1 string, number2 string) (sum int, mul int) 
    int1, _ := strconv.Atoi(number1)
    int2, _ := strconv.Atoi(number2)
    sum = int1 + int2
    mul = int1 * int2
    return

Go 的另一个有趣功能是,如果不需要函数的某个返回值,可以通过将返回值分配给 _ 变量来放弃该函数。 _ 变量是 Go 忽略返回值的惯用方式。 它允许程序进行编译。 因此,如果只需要求和,则可以使用以下代码:

package main

import "fmt"

func main() 
    sum, _ := calc(os.Args[1], os.Args[2])
    fmt.Println("Sum:", sum)

更改函数参数值

将值传递给函数时,该函数中的每个更改都不会影响调用方。 Go 是“按值传递”编程语言。 每次向函数传递值时,Go 都会使用该值并创建本地副本(内存中的新变量)。 在函数中对该变量所做的更改都不会影响你向函数发送的更改。

例如,假设你创建了一个用于更新人员姓名的函数。 请注意,运行此代码时会发生的变化:

package main

import "fmt"

func main() 
    firstName := "John"
    updateName(firstName)
    fmt.Println(firstName)


func updateName(name string) 
    name = "David"

即使你在函数中将该名称更改为 David,输出仍为 John。 由于 updateName 函数中的更改仅会修改本地副本,因此输出不会发生变化。 Go 传递变量的值,而不是变量本身。

如果你希望在 updateName 函数中进行的更改会影响 main 函数中的 firstName 变量,则需要使用指针。 指针 是包含另一个变量的内存地址的变量。 当你发送指向某个函数的指针时,不会传递值,而是传递地址内存。 因此,对该变量所做的每个更改都会影响调用方。

在 Go 中,有两个运算符可用于处理指针:

  • & 运算符使用其后对象的地址。
  • * 运算符取消引用指针。 也就是说,你可以前往指针中包含的地址访问其中的对象。

让我们修改前面的示例,以阐明指针的工作方式:

package main

import "fmt"

func main() 
    firstName := "John"
    updateName(&firstName)
    fmt.Println(firstName)


func updateName(name *string) 
    *name = "David"

首先要做的就是修改函数的签名,以指明你要接收指针。 为此,请将参数类型从 string 更改为 *string。 (后者仍是字符串,但现在它是指向字符串 的 指针。)然后,将新值分配给该变量时,需要在该变量的左侧添加星号 (*) 以暂停该变量的值。 调用 updateName 函数时,系统不会发送值,而是发送变量的内存地址。 这就是前面的代码在变量左侧带有 & 符号的原因。

引用本地包

calculator包下初始化go.mod

go mod init github.com/myuser/calculator

文件目录结构

src/
  calculator/
    go.mod
    sum.go
  var_use/
    main.go

var_use下初始化go.mod

go mod init var_use
module var_use

go 1.17

require github.com/myuser/calculator v0.0.0

replace github.com/myuser/calculator => ../calculator //本地路径下的位置

总结

我们介绍了开始在 Go 中构建更复杂应用程序所需的基础知识。 现在,你可以通过几种方法来声明和初始化变量。 你还了解 Go 提供的各种数据类型。 你已经使用最基本的数据类型,

并且还了解到如何创建函数来组织代码,并使代码更易于维护。 你已经了解到,Go 是“按值传递”语言,但它亦支持指针。 我们将在后续的部分模块中使用指针。

最后,你已经了解包在 Go 中的工作原理,以及如何在需要与其他开发人员共享代码时创建模块。 你已经了解到,如果只需要创建独立的应用程序,则所有代码都需要成为 main 包的一部分。 你已经了解到,该程序的起点是 main() 函数。 现在,你已了解如何引用本地和第三方模块。 构建 Go 程序时,你将会使用这些模块。

5.条件语句

package main

import "fmt"

func givemeanumber() int 
    return -1


func main() 
    if num := givemeanumber(); num < 0 
        fmt.Println(num, "is negative")
     else if num < 10 
        fmt.Println(num, "has only one digit")
     else 
        fmt.Println(num, "has multiple digits")
    

在 Go 中,在 if 块内声明变量是惯用的方式。 这是一种使用在 Go 中常见的约定进行高效编程的方式。

switch

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() 
	sec := time.Now().Unix()
	rand.Seed(sec)
	i := rand.Int31n(3)

	switch i 
	case 0:
		fmt.Print("zero...")
	case 1:
		fmt.Print("one...")
	case 2:
		fmt.Print("two...")
	

	fmt.Println("ok")

如果多次运行前面的代码,则每次都会看到不同的输出。 (但是,如果在 Go Playground 中运行代码,则每次都会获得相同的结果。 这是此服务的局限性之一。)

Go 会执行 switch 语句的每个用例,直到找到条件的匹配项。 但请注意,前面的代码未涵盖 num 变量值的所有可能情况。 如果 num 最终为 5,则程序的输出为 ok

也可让默认用例更加具体,像下面这样包含它:

switch i 
case 0:
    fmt.Print("zero...")
case 1:
    fmt.Print("one...")
case 2:
    fmt.Print("two...")
default:
    fmt.Print("no match...")

请注意,对于 default 用例,不要编写验证表达式, 只需包含 i 变量即可,因为你将在 case 语句中验证其值。

使用多个表达式

有时,多个表达式仅与一个 case 语句匹配。 在 Go 中,如果希望 case 语句包含多个表达式,请使用逗号 (,) 来分隔表达式。 此方法可避免代码重复。

以下代码示例演示了如何包含多个表达式。

package main

import "fmt"

func location(city string) (string, string) 
    var region string
    var continent string
    switch city 
    case "Delhi", "Hyderabad", "Mumbai", "Chennai", "Kochi":
        region, continent = "India", "Asia"
    case "Lafayette", "Louisville", "Boulder":
        region, continent = "Colorado", "USA"
    case "Irvine", "Los Angeles", "San Diego":
        region, continent = "California", "USA"
    default:
        region, continent = "Unknown", "Unknown"
    
    return region, continent

func main() 
    region, continent := location("Irvine")
    fmt.Printf("John works in %s, %s\\n", region, continent)

调用函数

switch 还可以调用函数。 在该函数中,可以针对可能的返回值编写 case 语句。 例如,以下代码调用 time.Now() 函数。 它提供的输出取决于当前工作日。

package main

import (
	"fmt"
	"time"
)

func main() 
	switch time.Now().Weekday().String() 
	case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday":
		fmt.Println("It's time to learn some Go.")
	default:
		fmt.Println("It's weekend, time to rest!")
	

	fmt.Println(time.Now().Weekday().String())

switch 语句调用函数时,无需更改表达式即可修改其逻辑,因为你始终会验证函数返回的内容。

此外,还可以从 case 语句调用函数。 例如,使用此方法可以通过正则表达式来匹配特定模式。 下面是一个示例:

package main

import "fmt"

import "regexp"

func main() 
	var email = regexp.MustCompile(`^[^@]+@[^@.]+\\.[^@.]+`)
	var phone = regexp.MustCompile(`^[(]?[0-9][0-9][0-9][). \\-]*[0-9][0-9][0-9][.\\-]?[0-9][0-9][0-9][0-9]`)

	contact := "foo@bar.com"

	switch 
	case email.MatchString(contact):
		fmt.Println(contact, "is an email")
	case phone.MatchString(contact):
		fmt.Println(contact, "is a phone number")
	default:
		fmt.Println(contact, "is not recognized")
	

请注意,switch 块没有任何验证表达式。 我们将在下一部分讨论该概念。

在 Go 中,可以在 switch 语句中省略条件,就像在 if 语句中那样。 此模式类似于比较 true 值,就像强制 switch 语句一直运行一样。

下面是一个示例,说明了如何编写不带条件的 switch 语句:

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() 
    rand.Seed(time.Now().Unix())
    r := rand.Float64()
    switch 
    case r > 0.1:
        fmt.Println("Common case, 90% of the time")
    default:
        fmt.Println("10% of the time")
    

该程序始终运行这种类型的 switch 语句,因为条件始终为 true。 一个条件 switch 块比一长串的 ifelse if 语句更易于维护。

使逻辑进入到下一个 case

在某些编程语言中,你会在每个 case 语句末尾写一个 break 关键字。 但在 Go 中,当逻辑进入某个 case 时,它会退出 switch 块,除非你显式停止它。 若要使逻辑进入到下一个紧邻的 case,请使用 fallthrough 关键字。

若要更好地了解此模式,请查看以下代码示例。

package main

import (
    "fmt"
)

func main() 
    switch num := 15; 
    case num < 50:
        fmt.Printf("%d is less than 50\\n", num)
        fallthrough
    case num > 100:
        fmt.Printf("%d is greater than 100\\n", num)
        fallthrough
    case num < 200:
        fmt.Printf("%d is less than 200", num)
    

15 is less than 50
15 is greater than 100
15 is less than 200

请注意,由于 num 为 15(小于 50),因此它与第一个 case 匹配。 但是,num 不大于 100。 由于第一个 case 语句包含 fallthrough 关键字,因此逻辑会立即转到下一个 case 语句,而不会对该 case 进行验证。 因此,在使用 fallthrough 关键字时必须谨慎。 该代码产生的行为可能不是你想要的。

for循环

func main() 
    sum := 0
    for i := 1; i <= 100; i++ 
        sum += i
    
    fmt.Println("sum of 1..100 is", sum)

在某些编程语言中,可以使用 while 关键字编写循环模式,在这些模式中,只有条件表达式是必需的。 Go 没有 while 关键字。 但是,你可以改用 for 循环。 此预配使预处理语句和后处理语句变得可选。

使用以下代码片段确认是否可以在不使用预处理语句和后处理语句的情况下使用 for 循环。

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() 
    var num int64
    rand.Seed(time.Now().Unix())
    for num != 5 
        num = rand.Int63n以上是关于微软Go学习教程(上半部分)的主要内容,如果未能解决你的问题,请参考以下文章

真香!微软出Go语言教程了(中文版)

Azure 机器人微软Azure Bot 编辑器系列 : 机器人/用户提问回答模式,机器人从API获取响应并组织答案 (The Bot Framework Composer tutorial(代码片段

如何从(和替换)TabLayout 和 ViewPager 移动到新片段?

golang代码片段(摘抄)

client-go系列之5---Informer

Go 系列教程 —— 17. 方法