主頁 > 後端開發 > Go語言基礎

Go語言基礎

2021-04-23 06:21:45 後端開發

GO開發

  • Golang被譽為21世紀的C語言

    • 2012.3 - 2020.2 1.0 - 1.14版本
  • 為什么選擇GO

    • 繼承python的簡潔 & C語言的性能于一身
  • 環境搭建

  • 執行golang代碼,

    • go run **.go
  • 或者go檔案中 go build 會生成一個檔案 在執行可執行檔案

    • 再或者 go install,會將可執行檔案放到bin目錄
  • 創建目錄

目錄結構如下:
xxx 
- bin
- pkg 
- src  //用于存放專案代碼的目錄
  • 環境變數
GOROOT, GO編譯器安裝目錄
GOPATH, 用于存放專案代碼, 編譯后的可執行檔案, 編譯后的包檔案(go 1.11版本后 ==> go.mod).
GOBIN, 編譯后的可執行的檔案存放的目錄
  • 開發工具

    • goland ide
    • vscode 編輯器
  • 配置

    • 字體
    • 引數提示
  • 專案開發

    • 新專案

    • 打開已經存在的專案

      注意: 專案放在$GOPATH/src目錄,

GO語法的使用

  • Go包管理初識 : 知道專案中檔案和檔案 檔案和檔案夾之間的關系

  • 輸出 , 寫代碼 在go編譯器運行時會在螢屏顯示內容

  • Go的資料型別

    • 整型
    • 字串
    • 布爾型別
  • 變數 & 常量,當作是昵稱 別名

  • 輸入

  • 條件陳述句 if else elif

1. GO包管理

  • 一個檔案夾可以稱為一個包
  • 在檔案夾(包)中可以創建多個檔案
  • 在同一個包下的每個檔案中必須指定包名稱 且必須相同

重點:

- main包,如果是main包 必須寫一個main函式  此函式就是專案的入口(main)    編譯生成可生成可以執行進制檔案
- 非main包  

2. 輸出

在終端將想要展示的資料顯示出來, 例如歡迎登錄, 請輸入用戶名等

  • 內置函式
    • print
    • println
  • fmt包(推薦使用)
    • fmt.print
    • fmt.println

擴展: 行程中有 stdin/stdout/stderr

fmt格式化輸出

fmt.printf()
占位符
%s 字串
%d 整型
%f 小數
%.2f 保留兩位小數

注釋: 單行注釋 // 多行注釋 /**/

3. 資料型別

  • 整型
  • 字串
  • 布爾型 false true

4. 變數var

就是給各種型別的資料起別名 為了方便參考,還可以暫時存盤資料

  • 變數名必須只包含:字母、數字、下劃線

  • 變數不能以數字開頭

  • 不能使用golang中的內置變數 關鍵字

5. 輸入

fmt.scanf()

6. 變數簡寫

var name string = "ha"
var name = "ha"
name := "ha"



因式分解
var(
	name = "zj"
  age = 18
  hobby = "sing"

)
go編譯器會認為如果宣告或者宣告且賦值的變數沒有進行參考, 就要被洗掉掉

7. 變數作用域 (namespace名稱空間)

如果我們定義了大括號, 那么在大括號中定義的變數

  • 不能被上級使用
  • 可以在同級中參考使用
  • 子級可以參考上級的變數
  • 函式內的方法都是區域變數
  • 定義全域變數不能使用 := 簡寫

全域變數和區域變數(都可以進行因式分解):

  • 全域變數: 在函式外的定義的非簡寫變數稱為全域變數
  • 區域變數: 在{}內定義的變數為區域變數

8. 變數的賦值及記憶體相關

name := "zj"
fmt.println(name, &name) //列印變數內容及記憶體地址

9. 常量const

不可修改的變數

	// 常量 - 不可修改的變數
	const age = 12
	//age = 13
	fmt.Println(age)

	const (
		va = 123
		vb = "111"
	)

10. iota

可有可無的東西 可以理解為計數器

// iota 計數累加器 從0開始
const (
		v1 = iota
		v2
		v3
		v4

	)
	fmt.Println(v1, v2, v3, v4)


11. 輸入

// 讓用戶輸入資料, 完成資料互動
fmt.scanf
fmt.scan
fmt.scanln


Scan
	
	var name string
	var age int
	fmt.Println("請輸入用戶名,姓名:")

	_, error := fmt.Scan(&name, &age)  // _是count輸入的總值 , error 是報錯nil報錯是輸入爭取 如果不是就是報錯的
	//fmt.Println(name, age)
	//fmt.Println(error)
	if error == nil {
		fmt.Println(name, age)
	} else {
		fmt.Println("輸入值錯誤", error)
	}



  • 常用的為scanln
    • 但是scanln的問題在于如果輸入的變數存在空格 默認取空格之前的問題 所以我們要使用os.stdin標準輸入
	// os.stdin標準輸入
	fmt.Println("請隨便輸入點東西:")
	reader := bufio.NewReader(os.Stdin)
	// line 從stdin中讀取一行的資料 資料型別為byte 可以轉換為字串
	// reader默認一次只能讀取4096個位元組  如果讀取的位元組數小于4096 isprefix=false
	line, _, _ := reader.ReadLine()
	// 通過string可以將byte型別的資料換換成字串 類似于python的decode
	fmt.Println(string(line))

12. 條件陳述句

  • 最基本的條件陳述句

    if 條件{
      	條件成立執行陳述句
      
    } else {
      	條件不成立執行陳述句
      
    }
    
    
    // 例子
    	var (
    		username string
    		passwd string
    	)
    	fmt.Print("username:")
    
    	fmt.Scanln(&username)
    	fmt.Print("password:")
    	fmt.Scanln(&passwd)
    
    	if username == "zj" && passwd == "123456"{
    		fmt.Printf("用戶名%s登錄成功", username)
    	}else {
    		fmt.Println("用戶名或密碼錯誤")
    
    	}
    
  • 多條件判斷

if {
  
} else if {
  
} else {
  
}
  • 嵌套
if {
  if {
    
  } else {
    
  }
  
} else {
  
}
  • Switch case 陳述句 類似于shell中的switch case 陳述句

  • for回圈陳述句

  • goto 語法 , 不建議使用

  • 字串的格式化 ,

  • 運算子的優先級

1.switch case 陳述句

package main

import "fmt"

func main() {
	var number int
	_, err := fmt.Scanln(&number)
	if err == nil {

		switch number {
		case 1 :
			fmt.Println("1111")
		case 2:
			fmt.Println("2222")

		case 3:
			fmt.Println("3333")

		default:
			fmt.Println("無法識別你輸入的內容")
		}

	}
}


switch 資料型別必須一致

2.for 回圈




	//1.死回圈 for 或者 for true
	//fmt.Println("開始")
	//for true {
	//	fmt.Println("123")
	//	time.Sleep(time.Second * 2) // 一秒為單位 time.second
	//	break
	//}
	//fmt.Println("end")


	number := 1

	for number < 5 {
		fmt.Println(number)
		number += 1
	}

	fmt.Println("end")
// for 回圈條件判斷 

for i:=1; i<10{
  
}

// 回圈10次 進行++
	for i:=1; i<10; i++{
		fmt.Println(i)

	}







	count := 0
	for i:=1; i<100; i++{
		fmt.Println(i)
		count += i

	}
	print(count)
  • continue
// 退出本次回圈
	for i := 1; i <= 10; i++ {
		if i == 7 {
			continue
		}
		fmt.Println(i)
	}


  • break
//跳出整個回圈
	for i := 1; i < 5; i++ {
		if i == 4 {
			fmt.Println("bye")
			break
		}
		fmt.Println(i)

	}


*對 for 進行打標簽, 然后可以通過break和continue就可以時間多層回圈的跳出和中止

test1:
	for i := 1; i < 5; i++ {

		for j := 1; j < 3; j++ {
			if j == 2 {

				//continue test1
				break test1
			}
			fmt.Println(i, j)

		}

	}

goto 陳述句
//跳躍到指定的行向下執行代碼
package main

import "fmt"

func main() {

shibai:
	var name string
	fmt.Print("請輸入用戶名:")

	_, err := fmt.Scanln(&name)
	fmt.Println(name)
	if len(name) < 1 {
		goto shibai
	}
	if err == nil {
		if name == "zj" {
			fmt.Println("牛皮")
		} else {
			fmt.Println("弟弟")
			goto shibai
		}

	}

}


  • 字串格式化
// 格式化字串
package main

import "fmt"

func main() {

	var name string
	var age int
	var score float64
	fmt.Println("please input name")
	fmt.Scanln(&name)

	fmt.Println("please input name")
	fmt.Scanln(&age)

	fmt.Println("please input name")
	fmt.Scanln(&score)

	result := fmt.Sprintf("name is %s, age is %d, score is %.2f", name, age, score)
	fmt.Println(result)


}




必備基礎知識


- 進制
- 單位
- 字符編碼 

golang 資料型別

1.整型
var v1 int8 = 10
var v2 int16 = 20 

v3 := int16(v1) + v2

注意: 
  - 低位轉高位是沒有問題
	- 但是高位轉成低位 是很有可能出問題的




整型轉換成字串型別:Itoa
	v1 := 19
	result := strconv.Itoa(v1)
	
	fmt.Println(result, reflect.TypeOf(result))
2.字串轉換成整形Atoi


	v2 := "19"
	res, err := strconv.Atoi(v2)
	if err == nil {
		fmt.Println(res)
	} else {
		fmt.Println("轉換失敗請檢查原資料")
	}
  • 進制轉換

go:

	- 10進制是以整型的方式存在
	- 其他進制 都是以字串形式存在的
  • 整型 : 10進制數 轉換成其他進制 (FormatInt)
// 使用方法進行進制轉換


v1 := 9
// 通過strconv.FormatInt方法進行進制數轉換 int型別必須是int64 所以必須進行宣告  第二個引數代表的是轉換成幾進制
// 10進制轉換成其他進制
v2 := strconv.FormatInt(int64(v1), 8)
fmt.Println(v2, reflect.TypeOf(v2))







  • 其他進制轉換成10進制parseint
	// 其他進制轉換成整形=> 10進制
	// parseint(其他進制資料, 原本的進制型別, 要轉換成的進制型別 )
	// 只要轉換成功 轉化出來的資料型別就是int64型別
	data := "10001000"
	res, err := strconv.ParseInt(data, 2, 10)
	//fmt.Println(err)
	if err == nil {
		fmt.Println(res, reflect.TypeOf(res))
	}

- 自己出的小練習

將2進制的10001000轉換成8進制數
思路:因為2進制和8進制代碼層面不能相互轉換 所以我們先將2進制轉換成10進制 在將10進制裝換成8進行


	data := "10001000"
	res, err := strconv.ParseInt(data, 2, 10)
	//fmt.Println(err)
	if err == nil {
		fmt.Println(res, reflect.TypeOf(res))
		test := strconv.FormatInt(int64(res), 8)
		fmt.Println(test)
	}


![image-20201228175014934](/Users/mac/Library/Application Support/typora-user-images/image-20201228175014934.png)

常見的資料運算

math 函式
math abs 
math.Pow(2 ,5)  // 2的五次方
math.max(1, 2)  // 兩個值相比較 取大值
math.min(1, 2)  // 兩個值相比較 取小值


指標/nil/宣告變數/ new

  • 宣告變數
 
### 指標/nil/宣告變數/new
* 宣告變數
        
        var v1 int
        v2 := 999


* 指標 (作用就是節省記憶體)
        var v3 *int
        v4 := new(int)
                          nil  
            v3 ->      ^
                         0x0
                        0
            v4 ->    ^
                        0x0

* new 關鍵字 
    * new用于創建記憶體并進行內部資料的初始化,并回傳一個指標型別
* nil
    * nil指go語言中的空值
    * var v6 *int
    * var v7 * int32



* 超大整型
        var v1 big.int
        var v2 *big.int
        var new(big.int)

        v1.setint64(1000)
        

    
var v1 big.Int
v1.SetString("123456767898786898092355262738453726315", 2)
fmt.Println(v1.String(), reflect.TypeOf(v1.String()))




go Array陣列

陣列是固定長度的特定型別元素組成的序列
一個數字由零個或多個元素組成
陣列的長度是固定的,因此Go更常用slice(切片, 動態的增長或收縮序列)
陣列是值型別, 用索引下標訪問每一個元素, 范圍是0 - len-1, 訪問超出陣列長度范圍 會panic例外



// Go Array 陣列中沒有復制的陣列 會有相應的默認值

// 宣告陣列 , 并且個陣列中的元素賦值
var intArr [5]int
fmt.Println(intArr)
intArr[0] = 12
intArr[1] = 34

fmt.Println(intArr)



// 宣告陣列 并且直接賦值 
var namestr [5]string = [5]string{"1", "2"}
fmt.Println(namestr)

var namestr2 = [5]string{"1"}
fmt.Println(namestr2)


// 取陣列最后一個元素  順便展示指定索引賦值
var namestr2 = [5]string{"1", 4: "124124"}
fmt.Println(namestr2, namestr2[len(namestr2)-1])


// 自適應陣列大小[...]
var namestr3 = [...]string{"zj", "zjj", "zjjj"}
fmt.Println(namestr3)


// 資料結構題型別陣列

var namestr5 = [...]struct{
  name string
  age int
}{
  {"zj", 18},
  {"ccn", 18},
}
fmt.Println(namestr5)


// 陣列回圈

for i:=0; i < len(namestr3);i++{
  fmt.Println("for " + namestr3[i])
}

for index, value := range namestr3{
  fmt.Println(index, value)
}



** 陣列注意事項:
	陣列是多個相同資料的組合, 且長度固定, 無法擴容 [5]int

陣列使用步驟:
	1.宣告陣列
	2.給陣列元素賦值
	3.使用陣列
	4.陣列索引從0開始 不能index of range
	5.Go陣列是值型別, 變數傳遞默認是值傳遞, 因此會進行值拷貝
	6.修改原本的陣列, 可以使用參考傳遞(指標)





  • 字串的本質

計算機中所有的操作和資料最終都是二進制 : 010101010...

package main

import (
	"fmt"
	"strconv"
	"unicode/utf8"
)

func main() {
	var name string = "張無忌"

	fmt.Println(name[1], strconv.FormatInt(int64(name[1]), 2))

	fmt.Println(len(name))

	age := 123
	res := strconv.Itoa(age)
	// len位元組長度
	fmt.Println(len(res + ""))

	// 字串轉換為一個 "位元組集合"
	byteset := []byte(name)
	fmt.Println(byteset) // [229 188 160 230 151 160 229 191 140]

	// 位元組集合轉換成字串string
	fmt.Println(string(byteset))

	// rune集合
	testName := "11223ddd"
	runeset := []rune(testName)
	fmt.Println(runeset,)
	fmt.Println(string(runeset[:5]))

	// 字串長度獲取
	// len方法獲取的是位元組的長度
	test2Name := "122333zghang張鑒"
	runeset2 := []rune(test2Name)
	fmt.Println(len(runeset2))
	// or 使用utf8的方法獲取長度

	test2Namelength := utf8.RuneCountInString(test2Name)
	fmt.Println(test2Namelength)
}



字串常見的使用方法


// 獲取長度
	var name string = "張無極"

	fmt.Println(name)
	fmt.Println(utf8.RuneCountInString(name))

	// 是否以xx開頭
	name2 := "張無忌"
	reslut := strings.HasPrefix(name2, "張")
	fmt.Println(reslut)

	// 是否以xx結尾
	result := strings.HasSuffix(name2, "無忌")
	fmt.Println(result)

	// 是否包含 類似 python in
	result2 := strings.Contains(name2, "無")
	fmt.Println(result2)

	fmt.Println("===============")
	// 全變大寫 類似 python upper
	stringTest1 := "fff afa fUUUU 漲"
	fmt.Println(strings.ToUpper(stringTest1))
	// 全變小寫
	fmt.Println(strings.ToLower(stringTest1))

	// 替換所有是-1  是左到右第一個 是1
	fmt.Println(strings.Replace(stringTest1, "f", "", 1))
	fmt.Println(strings.Replace(stringTest1, "f", "", 2))
	fmt.Println(strings.Replace(stringTest1, "f", "", -1))

	// 分割 split
	splitTest := strings.Split(stringTest1, " ")
	fmt.Println(splitTest[len(splitTest)-1])

	// 拼接
	mes := "你好" + "我好" // 不建議使用
	fmt.Println(utf8.RuneCountInString(mes))

	// 高效率的字串拼接方法 非常推薦使用
	var builder strings.Builder
	builder.WriteString("我愛你")
	builder.WriteString("中國")
	value := builder.String()
	fmt.Println(value, utf8.RuneCountInString(value))

	// 字串轉換成int
	var num int = 12
	fmt.Println(strconv.Itoa(num), reflect.TypeOf(strconv.Itoa(num)))

	textStr := "2141414141414"
	fmt.Println(strconv.Atoi(textStr))

	textStr2 := "0101010010"

	fmt.Println(strconv.ParseInt(textStr2, 2 ,10))
	fmt.Println(strconv.FormatInt(int64(num), 16))


	// 字串 轉換
	v1 := string(100)
	fmt.Println(v1)
	v2, size := utf8.DecodeRuneInString("d")
	fmt.Println(v2, size)


  • 索引切片和回圈

package main

import (
	"fmt"
)

func main() {

	var name3 string = "你好你好"
	// 位元組索引 切片
	vs1 := name3[0:3]
	fmt.Println(vs1)

	// 手動回圈所有的字符 range
	for index, item := range name3 {
		if index == 6 || index == 3 {
			fmt.Println(index, string(item))
		}
		//fmt.Println(index, string(item))

	}

	// 轉換成rune集合
	dataList := []rune(name3)
	fmt.Println(dataList, string(dataList))

}

  • 陣列

陣列, 定長且元素型別一致的資料集合 (型別必須相同)

	// 宣告加賦值自適應變數
	var numbers = [...]int{1, 2, 3}
	numbers[0] = 2
	fmt.Println(numbers)

	// 宣告定長的陣列 不足的用空格代替 len為5
	var numbers = [5]string{"test", "alex", "didi"}	
	fmt.Println(numbers)

	

	// 指定索引賦值
	var namestr2 = [5]string{"1", 4: "124124"}
	fmt.Println(namestr2, namestr2[len(namestr2)-1])


	// 宣告指標型別的陣列 (指標型別, 不會開辟記憶體初始化陣列中的值)
	var numbers *[]int
	
	

陣列的特性(可變和拷貝)

  • 元素值是可以被修改的 但是陣列一旦宣告開辟了記憶體空間之后 陣列的大小就不可以改變
  • 可以進行copy
// 修改重新賦值
var numbers = [...]int{1, 2, 3}
numbers[0] = 2
fmt.Println(numbers)


  • 長度切片[0:2]
  • 陣列的嵌套

二維陣列
// 含義為 陣列中有3個元素 每個元素是一個含有2個int元素的陣列
var nestData [3][2]int 
var nestData = https://www.cnblogs.com/zjaiccn/archive/2021/04/22/[3][2]int{{1, 2}, {3, 4}}

nestData[2] = [2]int{11, 22}
nestData[2][1] = 6666

fmt.Println(nestData)


最后三種資料結構概覽

  • 切片
  • 字典
  • 指標

#切片

切片, 動態陣列

切片是Go中重要的資料型別,每個切片物件內部都維護著 : 陣列指標,切片長度,切片容量 三個資料

  • 創建切片
// 每一個切片內部都存盤了三個資料 
陣列指標*array 、 切片長度len 切片容量[:3]


再向切片中追加資料個數大于容量時, 內部會自動擴容且每次擴容都是當前容量的2倍 [:3] -> [:6]


// 創建切片
var nums []int
// 創建切片 并賦值
var nums = []int{1,2,3}

// 創建切片
// make只用于 切片、字典、channel
// 推薦使用
int 型別 默認長度為2 容量為3 cap()
var users = make([]int,2,3)




  • 自動擴容

v1 := make([]int,1,3)
v2 := append(v1, 123)
注意: 擴容前和擴容后的切片記憶體地址 是不同的


  • 切片追加高級用法
// 基本相當于python中的extend 擴展串列

arrayList = append(arrayList, []int{100,200,300,400}...)
fmt.Println(arrayList)



  • 切片洗掉
// 切片資料實際上是沒有洗掉操作的 我們可以使用append來拼接生成新的切片 代替洗掉
newAarrayList := append(arrayList[:6])
newAarrayList = append(newAarrayList, arrayList[7:]...)
fmt.Println(newAarrayList)

// or 

newAarrayList := append(arrayList[:6], arrayList[7:]...)
fmt.Println(newAarrayList)

  • 切片插入
newAarrayList := append(arrayList[:6])
newAarratList = append(arrayList, 123356)
newAarrayList = append(newAarrayList, arrayList[7:]...)
fmt.Println(newAarrayList)




// 洗掉插入 效率低下 不使用,應該使用鏈表

  • 切片嵌套
// 嵌套

testList := [][]int{[]int{1, 2}, []int{66, 77, 99}}
fmt.Println(testList)

目前為止 上面所學的資料型別中, 在修改切片的內部元素時, 會造成所有的賦值的變數同時修改 (不擴容的前提下)

字典型別(Map)

  • 任何編程語言中都會存在字典或者映射 , 同python中的字典 是以鍵值對的形式存在的資料集合
{
  'name' : 'zj',
  
  'age': 15,
  
}


  • 字典的特性map
    • 鍵不能重復
    • 鍵必須可hash(切片和map都不可hash所以不能做鍵)
    • 無序
  • map宣告
	
// 第一個string代表鍵的型別 第二個string代表值的型別
userInfo := map[string]string{
  "name": "zj",
  "age": "18",

}
fmt.Println(userInfo["name"])
//or
// dataInfo := make(map[string]string, 10)
dataInfo := make(map[string]string)
dataInfo["name"] = "ccn"
fmt.Println(dataInfo["name"])



  • map 長度和容量
len(userInfo)
dataInfo := make(map[string]string, 10)
// 根據穿參10 計算出合適的容量
// 一個map 中會包含很多筒,每個筒可以存放8個鍵值對

  • map 增刪改查
userInfo := map[string]string{
  "name": "zj",
  "age": "18",

}
// 增
userInfo["test"] = "123ff"
// 改

userInfo["name"] = "ccc"
// 刪
delete(userInfo, "name")
// 查
for key, value := range userInfo {
  fmt.Println(key, value)
  
}
fmt.Println(userInfo["name"])


  • map變數賦值

...

注意 : 無論是否存在擴容都指向同一個地址

  • map初始化詳解
info = make(map[int]int, 10)
- 第一步 創建一個hmap的結構體物件
- 第二部 生成一個hash因子 hash0 并賦值到hmap物件中(用于后續為key創建hash值)
- 第三部 根據hint=10, 并根據演算法來創建 2的B次方筒數,當前應該是1 所以就是創建兩筒



  • map擴容原理
再向map中添加資料時, 當達到某一個條件, 則會引發字典擴容
擴容的條件:
- map中資料總個數/筒個數 > 6.5時, 便會引發翻倍擴容


指標資料

什么是指標 一個指標變數指向了一個記憶體地址, 類似于變數和常量, 在使用指標前需要宣告指標,指標宣告格式如下

// 指標型別生來就是用來節省記憶體的 

// 宣告指標型別的資料只需要在宣告資料型別前加* 即可宣告為指標型別
package main

import "fmt"

func main() {
	//var a int = 10
	//fmt.Printf("%x" , &a)

	var name string = "yunZhOngKeXin"
	// 查看記憶體地址為&符號
	fmt.Printf("name的記憶體地址: %v\n", &name)

	// 指標變數, 存的是記憶體地址
	// ptr 指標變數指向變數name的記憶體地址
	var ptr *string
	ptr = &name
	fmt.Printf("指標變數ptr的記憶體地址: %v \n", ptr)

	// 獲取指標變數的值, 用*ptr
	// *ptr表示讀取指標指向變數的值
	// *ptr 代表真正的資料
	fmt.Printf("指標變數ptr指向的值是: %v\n", *ptr)

}



  • 指標

指標 , 是一種資料型別, 用于表示資料的記憶體地址,

// 宣告一個 字串型別的變數 (默認初始化為空字串)
var v1 string 

// 宣告一個字串的指標型別
var v2 *string


// 宣告一個字串型別的變數并賦值
var name string = "zj"
// 宣告一個字串的指標型別的變數, 值為name對應的記憶體地址
var pointer *string = &name

  • 指標存在的意義

相當于創建了一個**參考**, 以后可以根據這個參考來獲取他里面的值 如果原本的資料發生變化參考也會發生變化,類似于軟連接, 快捷方式這種

v1 := "zh"
v2 := &v1 
fmt.println(v1, v2, *v2)

  • 使用指標的場景
// 下邊的這種函式傳參的方式testData字串會重新copy一份 
func main {
  
  Test("zjjjj")
}
func Test(testData string) {
  testData = "https://www.cnblogs.com/zjaiccn/archive/2021/04/22/hahah"
  fmt.println(testData)
}



// 
func main {
  
  Test("zzjj")
  
}
func Test(ptr *string){
  ptr = "fffff"
  fmt.println(ptr) // 修改指標的內容上邊的內容也會被修改 
  
}



  • 指標的指標

n1 := "zhjjj"

n2 := &n1
fmt.Println(n1, *n2)
n3 := &n2
fmt.Println(*n2, **n3)
n4 := &n3
fmt.Println(**n3, ***n4)


  • 指標的小高級操作
// 指標的相加操作
func Compl() {
	// 指標的相加操作
	// 定義一個int8陣列
	dataList := [3]int8{11, 22, 33}
	// 取出第一個元素的記憶體地址
	var firstData *int8 = &dataList[0]
	// 將第一個鐵元素的記憶體轉化成pointer型別
	ptr := unsafe.Pointer(firstData)
	// pointer型別 +1
	targetAddress := uintptr(ptr) + 1
	// 相加之后重新轉換成pointer型別
	newPtr := unsafe.Pointer(targetAddress)
	// 將pointer物件轉換成int8 指標型別
	value := (*int8)(newPtr)
	// 使用指標型別獲取資料
	fmt.Println(*value)

}


結構體

什么是結構體?

| 結構體是一個復合型別, 用于表示一組資料

| 結構體由一系列屬性組成, 每個資料都有自己的型別和 值

// 定義
	type Person struct {
		name string
		age int
		email string

	}

	//  初始化
	var p1 = Person{"zh", 12, "153"}
	fmt.Println(p1, reflect.TypeOf(p1))

結構體基本格式 
type 結構體名稱 struct{
  欄位 型別
  ...
  
}

  • 結構體的定義
type Person struct {
  name string
  age int
  hobby []string.  // 可以是切片哦
}


結構體


package main
import (
"fmt"
)

func main() {
// 定義結構體
type Person1 struct {
name, sex string
age int
}

var p1 = Person1{"ccn", "sunhat", 18}
fmt.Println(p1)
// 結構體嵌套
type Person2 struct {
score int
p1 Person1
}
v2 := Person2{99, Person1{"zj", "nan", 18}}
fmt.Println(v2)
//結構體匿名
type Person3 struct {
city string
hobby string
Person1 // 匿名結構體
}
v3 := Person3{city: "CN", hobby: "students", Person1:Person1{"11","nan",18}}
// 由于結構體是匿名的所以可以直接取p1中的name 否則必須v3.變數名.name
fmt.Println(v3.city, v3.name)
// 注意結構體賦值的時候 資料會全部都的拷貝一份 無論什么型別
// 指標結構體 是不拷貝的 軟連接
// 切片和map 資料拷貝的的時候可能會發現感覺并沒有拷貝 但其實不是的是因為切片和map沒有擴容的時候使用的都是同一塊地址導致的 資料的特型
}

// 如果我們想要在賦值時不拷貝資料 我們可以將資料變成指標型別資料即可
*[2]string
*map[string]int


  • 結構體指標
type Person2 struct {
   lambda string
   *Person
}
p3 := Person2{lambda: "這是一個匿名函式", Person: &Person{"Miho", "naming ", 12}}
p4 := p3
p3.name = "testzhizhen"
fmt.Println(p3, *p3.Person, p4, *p4.Person)
  • 結構體標簽
	// 標簽
	type Person2 struct {
		lambda  string "宣告匿名"
		*Person "我是Person"
	}
	p3 := Person2{lambda: "這是一個匿名函式", Person: &Person{"Miho", "naming ", 12}}
	p4 := p3
	p3.name = "testzhizhen"

	// 標簽1
	testStruct := reflect.TypeOf(p3)
	fmt.Println(testStruct.Field(1).Tag)
	// 標簽2
	field, _ := testStruct.FieldByName("lambda")
	fmt.Println(field.Tag)

	// 回圈獲取tag
	fieldNum := testStruct.NumField()
	for i := 0; i < fieldNum; i++ {
		fmt.Println(testStruct.Field(i).Tag)
	}

函式

可以把函式當作一個公用的代碼塊,用于實作某一個功能. 并且提高代碼的重用性和可讀性(函式傳參資料會重新拷貝 相當于賦值)

// 函式使用方法

func 函式名(引數 型別) 回傳值型別 {
  函式執行體
}


package main

import "fmt"

// 定義了回傳值的型別時必須有return , name為接收的引數  string 為回傳值的型別
func FirstFun(name string) string {
	if len(name) > 2 {
		//fmt.Println(name)
		return name
	}
	return "error"

}

func main() {
	
	res := FirstFun("zjccn")
	fmt.Println(res)
}

  • 函式呼叫指標型別 (節省記憶體)
package main

import "fmt"

func FirstFun(name *string) string {
	*name = "123"
	if len(*name) > 2 {
		//fmt.Println(name)
		return *name
	}
	return "error"

}

func main() {
	name := "ZjCcn"
	res := FirstFun(&name)
	fmt.Println(res, name)
}

  • 函式也可以作為引數
func main() {

	TestProxy(10, Add100)
}

func Add100(data int) (int, bool) {
	return data + 100, true
}

func TestProxy(data int, function func(int) (int, bool)) string {
	resData , flag := function(data)
	if flag{
		fmt.Println(resData, flag)
	}else {
		fmt.Println("error")
	}
	return "complete"

}


  • 函式傳遞可變長引數 可以不確定傳多少引數 (變長引數必須放在最后 類似 **kwargs 且只能存在一個)
// 這個num的型別實際上是切片型別,可以任意擴容
func Do(num ... int) int {
	sum := 0 
	for _, value := range num{
		sum += value

	}
	return sum
}

func main() {
	numcount := Do(1,2,2,2,2,2,2,2,2,3,3,3,3,3,3)
	fmt.Println(numcount)
}


  • 匿名函式
func main() {

	// 匿名函式
	f1 := func(data int) int {
		return data
	}
	fmt.Println(f1(11))
	// 第二種匿名函式表達
	f2 := func(data int) string {
		return strconv.Itoa(data)
	}(110)
	fmt.Println(f2, reflect.TypeOf(f2))
}

  • 函式不僅能做傳遞的引數 也可以作為回傳值
	test1 := Test1()
	fmt.Println(test1(120))

func Test1() func(t1 int) int  {
	funtion := func(t1 int) int {
		return t1
	}
	return funtion
}


  • 閉包函式(通過一個函式將我們需要的資料固定包裹在一個包里)
// 閉包函式的作用就是將生成好的函式封裝好值也傳好, 之后呼叫的函式的值回傳值 都是定義好的無需再次傳參 可直接呼叫
package main

import (
	"fmt"
)

func main() {
	// 這個切片會存盤著五個函式 分別列印 0 1 2 3 4
	var funtionList []func()

	for i := 0; i < 5; i++ {
		funtion := func(data int) func(){
			return func() {
				fmt.Println(data)
			}
		}(i)
		funtionList = append(funtionList, funtion)
	}
	// 運行封裝好的包
	funtionList[0]()
	funtionList[2]()
	funtionList[3]()

}


  • defer

用于在一個函式執行體完成之后自動觸發的陳述句, 一般用于結束操作之后釋放資源

// go中稱為延遲執行 可以隨意定義位置 
// 相當于python中的 __del__ 釋放資源
// 類似于堆疊,先進后出
package main

import "fmt"

func main() {
	TestDefer()
}

func TestDefer() {
	fmt.Println("這是一次測驗")
	for i := 0; i < 10; i++ {
		defer fmt.Println(i)

	}
}

  • 自執行函式 (類似于python init)
// ~= python __init__
// 自執行函式就是匿名函式

func TestDefer() {
	result := func(age int) int{
		return age
	}(18)
	fmt.Println(result)

}

  • 結構體做函式回傳值
type funstruct struct {
	name string
	age int
}
func StructTest(name string) (funstruct) {

	return funstruct{name: name, age: 18}
}
func main(){
  
  t1 := StructTest("testzh")
  fmt.PrintLn(t1)
}

型別方法

專案開發中可以為type宣告的型別撰寫一些方法, 從而實作物件.方法的操作

// 可以定義執行自己的代碼的方法

package main

import "fmt"
// 宣告型別
type myInt int

func main() {

	var testMy myInt = 110
	res := testMy.Dosomething(1,2)

	fmt.Println(res)
}

// 這個因為前邊加上了(i *myInt) 所以他就不是函式了 而是自我定義的方法
myInt可以是指標 也可以是正常的 為了節省記憶體我們是用指標
func (i *myInt) Dosomething (data1 int , data2 int) int {
	return data1+data2+int(*i)
}


  • 方法繼承(類似于面向物件的繼承)

type Base struct {
	name string
}
type Son struct {
	Base  // 匿名結構體
	age int
	test string
}

func (b1 *Base) Test1() int {
	return 123
  // return b1.name   name: "張無忌"
}
func (s1 *Son) Test2() int  {
	return 222
}


func main() {

	son := Son{Base:Base{name: "張無忌"}, age:13, test:"測驗"}
	fmt.Println(son.Test1(), son.Test2())

}
>>>>>>>>>>>>>>>執行結果:
123 222

  • 結構體工廠
宣告任何資料型別的時候,只要變數名稱為小寫 就是私有變數 大寫就是公共變數,私有就是只有當前這個.go檔案可以使用 公有都能使用

  • Go介面型別

GO語言中的介面是一種特殊的資料型別, 定義格式如下:

type name interface{

方法名稱() 回傳值

}

例如:

type Name interface {

f1()

f2() int

f3() (int, bool)

}

定義介面的特點是 不需要寫任何邏輯代碼 只需要將方法的名和回傳值傳引數 寫好即可

  • 介面的作用

    在程式開發中介面一般有兩大作用 : 代指型別 和 約束,

  • 空介面, 代指任意型別

package main

import (
"fmt"
"reflect"
)
// 定義個空介面 空介面中可以傳任意型別的值

type base interface {

}

func main() {
//dataList := make([]base, 0)
// 上邊這些介面定義可以簡寫為:

dataList := make([]interface{}, 0)
dataList = append(dataList, "test")
dataList = append(dataList, 234)
dataList = append(dataList, map[string]string{"key": "values"})
fmt.Println(dataList[2], reflect.TypeOf(dataList))
something("test")
something("test")
something("test")
caseSth("haha")
caseSth(123)
caseSth([]string{"xii", "haha"})
}

// 像python一樣 弱型別

type person struct {
name string
}

// 接收到資料型別都是介面型別 所以我們要使用.(型別或者型別結構體)轉換成我們想要的格式

func something(arg interface{}) {
s,ok := arg.(string)
if ok{
fmt.Println(s, reflect.TypeOf(s))
} else {
fmt.Println("轉換失敗")
}
}
func caseSth(arg interface{}) {

switch data := arg.(type) {
case string:
fmt.Println(data)
case int:
fmt.Println(data)
case []string:
fmt.Println(data)
default:
fmt.Println("WOZHAOBUDAO")
}
}
  • 非空介面 規范約束
type Ibase interface {

test1 () int

}

type Person struct {
name string
age int
}

func (p *Person) test1() int {
return 123
}

type User struct {
username string
password string
}

func (u *User) test1() int {
return 567
}

func main() {
p := Person{"zj", 18}
//u := User{"1533333", "520cnn.."}
p.test1()
List := []Ibase{&Person{"COMPELTE",12}, &User{"完成", "LE"}}
fmt.Println(List)
for _ , value := range List{
fmt.Println(value, reflect.TypeOf(value))
}
}


  • flag包

基于os.Args可以獲取傳入的所有引數


package main

import (
	"flag"
	"fmt"
)

func main() {
	//fmt.Println(os.Args)

	/* >>>結果
	mac$ ./main 213 123414155  5125
	[./main 213 123414155 5125]
	*/

	// h 是命令列要-h 默認是127.0.0.1 后邊是備注
	host := flag.String("h", "127.0.0.1", "主機名")
	port := flag.Int("p", 1234, "埠號")
	//host := flag.String("h", "127.0.0.1", "主機名")
	flag.Parse() // 必須parse 才能將命令列傳入的資料傳進來
	fmt.Println(*host, *port)

	/* >>結果
	mac$ ./main -h 192.168.1.1 -p 1212
	192.168.1.1 1212

	*/
}


  • Go regexp 正則運算式
package main

import (
	"fmt"
	"regexp"
)

func main() {
	// 根據字串匹配回傳 是否成功
	matchString, err := regexp.MatchString(".*?:(.*?)n.*?", "vaiuhguifg:xixizhangjiannihaonfaoghaiug")
	if err == nil {
		fmt.Println(matchString)  // true
	}


	// 根據匹配值字串查找
	reg1 := regexp.MustCompile(`\d{11}`)
	res1 := reg1.FindString("1242563837613")  // 結果 12425638376

	fmt.Println(res1)
	res2 := reg1.FindAllString("1241245569005837163414115", -1) // 結果就是找所有的11個數字的集合[12412455690 05837163414]

	fmt.Println(res2)

	// 獲取分組資訊
}

檔案路徑相關的內置包

  • 檔案增刪改查

// 創建單級目錄  付給權限
os.Mkdir("testDir", 0755)

// 創建多級目錄
os.MkdirAll("/test/123/code", 0755)

// 洗掉檔案或空檔案夾, 檔案夾下存在內容資料 就會報錯
os.Remove("testDir")

// 洗掉檔案或檔案夾 (同時洗掉子目錄中的資料)
os.RemoveAll("test123")

  • 判斷目錄 或檔案是否存在. os.IsNotExist(err)

_, err := os.Stat("test1/test2/123.go")
if err != nil {
  if os.IsNotExist(err) {
    fmt.Println("檔案或檔案夾不存在")
  }else{
    fmt.Println("存在")
  }
}

  • 判斷是否是檔案夾IsDir()
file, _ := os.Stat("test/123/")
res := file.IsDir()

  • 獲取絕對路徑
	abs, err := filepath.Abs("test/123/")
	// 獲得的結果就是絕對路徑+test/123/

// 獲得絕對路徑
	abs, err := filepath.Abs(".")
	if err == nil {
    // 獲得上層路徑
		fmt.Println(filepath.Dir(abs))
	}
  • 遍歷目錄下的檔案
package main

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
)

func main() {
	abs, _ := filepath.Abs(".")
	dir, err := ioutil.ReadDir(abs)
	fmt.Println(dir)
	if err == nil {
		for _, value :=range dir{
			if !value.IsDir(){
				fmt.Println(value.Name())
			}
		}

	}
}

  • walk
// walk會深層遞回的查詢資料
filepath.Walk(abs, func(path string, info os.FileInfo, err error) error {
		fmt.Println(info.Name())
  	fmt.Println(path)
		return nil
	})


  • 路徑拼接 or 檔案擴展名

// 拼接
filePath := path.Join("1","2","3","7.dmg")

// 檔案擴展名
ext := path.Ext("/fres/faf/xxx.txt")
獲取到的就是擴展名txt

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/279195.html

標籤:其他

上一篇:HTTP請求物件(獲取用戶請求資訊)

下一篇:讀心術【撲克牌魔術——21張撲克牌】——代碼實作【有趣小練習】

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more