大家好,我是馬甲哥,
學習新知識, 我的策略是模仿-->歸納--->舉一反三,
在同程倒騰Go語言一年有餘,本次記錄《聞道Go語言,6月齡必知必會》,形式是同我的主力語言C#做姿勢對比。
1. 宏觀預覽
1.1 常見結構對比
某些不一定完全對標,實現方式,側重點略點差異。
go語言 | --- | C#語言 | --- |
---|---|---|---|
module | assembly | ||
pkg | go get github.com/thoas/go-funk | package | Install-Package Masuit.Tools.Core |
struct | class | ||
pointer | reference | ||
net/http | web腳手架、 httpclient | ASP.NETCore、httpclient | |
net/http/DefaultServeMux | ASP.NETCore腳手架路由 | ||
goroutine | 異步任務、 async/await | ||
channel | CSP | TPL data flow | CSP模型在C#並非主流 |
context | timeout、 cancellation-token |
1.2 訪問級別
go語言使用[首字母大小]寫來體現公開/私有, 應用到package struct function;
C#顯式使用關鍵字來體現。
1.3 類型初始化
go語言有兩初始化的內置關鍵字
- new : 用於分配內存(帶內存零值),返回指針 new(int), new(Cat)
- make : 只用於slice、map、 channel 引用類型的初始化
C#基礎類型使用字面量, 引用類型使用new關鍵字
2. 編碼邏輯結構
2.1 順序
這沒什麼好說的,都是至上而下, 遇到函數進函數堆棧。
go語言每行代碼後不需要加分號;C#語言每行代碼後需要加分號。
go對於括號的使用有要求: 斜對稱, C#無要求。
2.2 分支
if --- elseif --- else
go和C#語言基本是一樣的
- go語言else if、 else 不允許換行,C#對此無要求。
- C#要求[使用括號包圍]條件判斷語句。
switch -- case [break]
- go語言case語句默認都加上了break,加不加都一樣,滿足當前case,執行完就會跳出當前switch, 不會一直case下去;
- C#語言執行分支需要主動break, 若沒有break,表示共用可用的執行體。
2.3 循環
- go語言只有for循環,C#還有while, do while
使用for來體現while/do while
3. 面向對象
封裝 抽象 繼承 多態
同樣是面向對象編程語言,go用結構體來體現,C#常用類來體現。
封裝
通常go語言基於結構體、接收者函數來[封裝/提煉]事物和行爲。
-
接收者函數分爲: 值接收者函數、指針接收者函數。
-
兩種都能體現封裝, 但[指針接收者函數]內的操作會體現到入參。
-
不管是值,還是指針,都能調用指針接收者函數/值對象接受者函數,效果還是如上一點一致。
C# 顯式使用Class
struct
等結構來封裝數據和行爲。
抽象 + 繼承
go語言沒有抽象函數、抽象類的說法,有接口抽象 和父子類繼承關係。
接口將具有共性的方法放在一起,其他任何類型只要實現了這些方法就是實現了接口,俗稱鴨子模式。
C#具備語義化的繼承/抽象/多態, 顯式繼承。
4. 指針 vs 引用
指針指向一個內存地址; 引用指向內存中某個對象。
一般認爲go是C語言的家族,但是go的指針弱化了C語言的指針操作,go指針的作用僅操作其指向的對象, 不能基於地址這個概念做指針移位, 也不能基於地址這個概念做類型轉化。
A value of a pointer type whose base type is T can only store the addresses of values of type T.
go的指針簡化了指針的使用,減少了指針出錯的概率。
引用可看做是指針的抽象,也基於code safe的理由,不能在引用上做算術運算和低級別的取巧。
從這個意義上看,C#的引用等價於go的指針, 都是類型安全的指針。
另一方面, 兩種語言都提供了對內存進行任意讀寫的姿勢(非代碼安全)。
go的unsafe.Pointer本質是一個int指針。
type Pointer *ArbitraryType
、
type ArbitraryType int
C# unsafe
關鍵字可用在函數、屬性、構造函數、代碼塊。
5. goroutine vs async-await
表象
- goroutine由go的原生函數生成,只要前面加上go的語法關鍵字
go
(可以有形參,返回值會被忽略)。 - await/async語法糖,簡化了異步編程的姿勢;實際會被編譯器編譯成一個狀態機。
goroutine是在runtime級別深度內置, async-await是在CLR之上基於C#語言構建。
核心對比
首先要知道: 線程是cpu調度的基本單位,不管是goroutine還是async-wait機制都是在嘗試提高[cpu調度線程的效率]。
-
go在os內核線程之上,原生支持了輕量級的用戶態線程goroutine,堆棧很小,開銷很小,(存在一個用戶態邏輯處理器給線程投餵goroutine)。
-
C#編譯器生成的狀態機,轉化並管控基於線程池線程的主調任務、異步任務、後繼任務。
兩者支持併發的思路有明顯差異:
go: 內核態線程切換開銷大,故原生提供用戶態線程,開銷極小,天然支持高併發,且不輕易墜落到內核態, 是一個革命派的思路。
C#:async-await針對線程做輾轉騰挪,高效利用, 是一個改良派的思路。
異步
都具備異步的能力,go語言沒有await的概念,goroutine在等待通道讀操作時[掛起自身,並將OS線程釋放給另一個goroutine], 跟C#執行時遇到await關鍵字的行爲效果是一樣的。
推薦附加閱讀
-
https://grantjam.es/concurrency-comparing-golangs-channels-to-c-sharps-asyncawait/
-
https://techstacks.io/posts/6628/go-vs-csharp-part-1-goroutines-vs-async-await
本文限於篇幅,只記錄了go語言和C#語言的入門6月齡的核心差異點和重難點,高手繞道, 後續會不斷完善, 請有心人持續關注左下角原文, 如果能點贊更是莫大的鼓勵。